UPDATE: add more ui for showcase card, fix some bug
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 2m1s

This commit is contained in:
2025-10-05 17:01:07 +07:00
parent b8d7c1ab78
commit b76a4a4e9c
11 changed files with 301 additions and 146 deletions

View File

@@ -1,6 +1,6 @@
"use client"
import Image from "next/image"
import { useEffect, useState } from "react"
import { useEffect } from "react"
import CharacterCard from "../card/characterCard"
import useLocaleStore from "@/stores/localeStore"
import useAvatarStore from "@/stores/avatarStore"
@@ -8,9 +8,17 @@ import { useTranslations } from "next-intl"
export default function AvatarBar({ onClose }: { onClose?: () => void }) {
const [listElement, setListElement] = useState<Record<string, boolean>>({ "fire": false, "ice": false, "imaginary": false, "physical": false, "quantum": false, "thunder": false, "wind": false })
const [listPath, setListPath] = useState<Record<string, boolean>>({ "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false })
const { listAvatar, setAvatarSelected, setSkillSelected, setFilter, filter } = useAvatarStore()
const {
listAvatar,
setAvatarSelected,
setSkillSelected,
setFilter,
filter,
listElement,
listPath,
setListElement,
setListPath
} = useAvatarStore()
const transI18n = useTranslations("DataPage")
const { locale } = useLocaleStore()
@@ -40,7 +48,7 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
<div
key={index}
onClick={() => {
setListElement((prev) => ({ ...prev, [key]: !prev[key] }))
setListElement({ ...listElement, [key]: !listElement[key] })
}}
className="hover:bg-gray-600 grid items-center justify-items-center cursor-pointer rounded-md shadow-lg"
style={{
@@ -60,7 +68,7 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
<div
key={index}
onClick={() => {
setListPath((prev) => ({ ...prev, [key]: !prev[key] }))
setListPath({ ...listPath, [key]: !listPath[key] })
}}
className="hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-lg cursor-pointer"
style={{

View File

@@ -13,7 +13,7 @@ import SelectCustomImage from "../select/customSelectImage";
export default function CopyImport() {
const { avatars, setAvatar } = useUserDataStore();
const {avatarSelected} = useListAvatarStore()
const { avatarSelected } = useListAvatarStore()
const { locale } = useLocaleStore()
const {
selectedProfiles,
@@ -22,12 +22,16 @@ export default function CopyImport() {
setSelectedProfiles,
filterCopy,
setFilterCopy,
setAvatarCopySelected
setAvatarCopySelected,
listElement,
listPath,
listRank,
setListElement,
setListPath,
setListRank
} = useCopyProfileStore()
const transI18n = useTranslations("DataPage")
const [listPath, setListPath] = useState<Record<string, boolean>>({ "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false })
const [listElement, setListElement] = useState<Record<string, boolean>>({ "fire": false, "ice": false, "imaginary": false, "physical": false, "quantum": false, "thunder": false, "wind": false })
const [listRank, setListRank] = useState<Record<string, boolean>>({ "4": false, "5": false })
const [message, setMessage] = useState({
type: "",
text: ""
@@ -49,8 +53,9 @@ export default function CopyImport() {
element: Object.keys(listElement).filter((key) => listElement[key]),
rarity: Object.keys(listRank).filter((key) => listRank[key])
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [listPath, listRank, locale])
}, [listPath, listRank, listElement, locale, setFilterCopy])
const clearSelection = () => {
setSelectedProfiles([]);
@@ -142,7 +147,7 @@ export default function CopyImport() {
<div
key={index}
onClick={() => {
setListPath((prev) => ({ ...prev, [key]: !prev[key] }))
setListPath({ ...listPath, [key]: !listPath[key] })
}}
className="w-[50px] h-[50px] hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
style={{
@@ -167,7 +172,7 @@ export default function CopyImport() {
<div
key={index}
onClick={() => {
setListElement((prev) => ({ ...prev, [key]: !prev[key] }))
setListElement({ ...listElement, [key]: !listElement[key] })
}}
className="w-[50px] h-[50px] hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
style={{
@@ -192,7 +197,7 @@ export default function CopyImport() {
<div
key={index}
onClick={() => {
setListRank((prev) => ({ ...prev, [key]: !prev[key] }))
setListRank({ ...listRank, [key]: !listRank[key] })
}}
className="w-[50px] h-[50px] hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
style={{
@@ -207,9 +212,10 @@ export default function CopyImport() {
</div>
<div className="grid grid-cols-1 gap-2">
{listCopyAvatar.length > 0 && (
<div>
<div>{transI18n("characterName")}</div>
{listCopyAvatar.length > 0 && (
<SelectCustomImage
customSet={listCopyAvatar.map((avatar) => ({
value: avatar.id.toString(),
@@ -221,8 +227,9 @@ export default function CopyImport() {
placeholder="Character Select"
setSelectedCustomSet={(value) => setAvatarCopySelected(listCopyAvatar.find((avatar) => avatar.id.toString() === value) || null)}
/>
)}
</div>
)}
</div>
</div>

View File

@@ -1,6 +1,6 @@
"use client"
import { useEffect, useState } from "react"
import { useEffect } from "react"
import Image from "next/image";
import useLocaleStore from "@/stores/localeStore"
import useLightconeStore from "@/stores/lightconeStore";
@@ -11,10 +11,17 @@ import useModelStore from "@/stores/modelStore";
import { useTranslations } from "next-intl";
export default function LightconeBar() {
const [listPath, setListPath] = useState<Record<string, boolean>>({ "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false })
const [listRank, setListRank] = useState<Record<string, boolean>>({ "3": false, "4": false, "5": false })
const { locale } = useLocaleStore()
const { listLightcone, filter, setFilter, defaultFilter } = useLightconeStore()
const {
listLightcone,
filter,
setFilter,
defaultFilter,
listPath,
listRank,
setListPath,
setListRank
} = useLightconeStore()
const { setAvatar, avatars } = useUserDataStore()
const { avatarSelected } = useAvatarStore()
const { setIsOpenLightcone } = useModelStore()
@@ -35,7 +42,7 @@ export default function LightconeBar() {
}
setListPath(newListPath)
setListRank(newListRank)
}, [defaultFilter])
}, [defaultFilter, setListPath, setListRank])
useEffect(() => {
setFilter({
@@ -72,7 +79,7 @@ export default function LightconeBar() {
<div
key={index}
onClick={() => {
setListPath((prev) => ({ ...prev, [key]: !prev[key] }))
setListPath({ ...listPath, [key]: !listPath[key] })
}}
className="h-[38px] w-[38px] md:h-[50px] md:w-[50px] hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
style={{
@@ -96,7 +103,7 @@ export default function LightconeBar() {
<div
key={index}
onClick={() => {
setListRank((prev) => ({ ...prev, [key]: !prev[key] }))
setListRank({ ...listRank, [key]: !listRank[key] })
}}
className="h-[38px] w-[38px] md:h-[50px] md:w-[50px] hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
style={{

View File

@@ -9,6 +9,7 @@ import { useMemo } from "react";
import useAvatarStore from "@/stores/avatarStore";
import { calcAffixBonus, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, replaceByParam } from "@/helper";
import { mappingStats } from "@/constant/constant";
import RelicShowcase from "../showcaseCard/relicShowcase";
export default function QuickView() {
const { avatarSelected, mapAvatarInfo } = useAvatarStore()
const { mapLightconeInfo } = useLightconeStore()
@@ -80,7 +81,9 @@ export default function QuickView() {
return {
property: subAffixMap?.[subValue?.sub_affix_id]?.property,
valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count),
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.property]
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.property],
step: subValue?.step,
count: subValue?.count
}
})
}
@@ -398,7 +401,7 @@ export default function QuickView() {
<div key={index} className="flex flex-row items-center justify-between">
<div className="flex flex-row items-center">
<NextImage src={stat?.icon || ""} alt="Stat Icon" width={40} height={40} className="h-auto w-10 p-1 mx-1 bg-black/20 rounded-full" />
<span className="font-bold">{stat.name}</span>
<div className="font-bold">{stat.name}</div>
</div>
<div className="ml-3 mr-3 flex-grow border rounded opacity-50" />
<div className="flex cursor-default flex-col text-right font-bold">{
@@ -442,60 +445,76 @@ export default function QuickView() {
{relicStats?.map((relic, index) => {
if (!relic) return null
return (
<div key={index} className="black-blur relative flex flex-row items-center rounded-lg border-2 p-1 border-yellow-600">
<div className="flex">
<NextImage src={relic?.img || ""} width={80} height={80} alt="Relic Icon" className="h-auto w-20" />
<div
key={index}
className="relative w-full flex flex-row items-center rounded-s-lg border-l-2 p-1 border-yellow-600/60 bg-gradient-to-r from-yellow-600/20 to-transparent"
>
{/* Subtle glow overlay */}
<div className="absolute inset-0 rounded-s-lg pointer-events-none"></div>
<div className="flex relative">
<div className="absolute inset-0 rounded-lg blur-lg -z-10"></div>
<NextImage
src={relic?.img || ""}
width={78}
height={78}
alt="Relic Icon"
className="h-auto w-[78px] rounded-lg"
/>
<div
className="absolute text-yellow-500 font-bold z-10"
className="absolute text-yellow-400 font-bold z-10 drop-shadow-[0_0_6px_rgba(251,191,36,0.8)]"
style={{
left: '0.7rem',
bottom: '-0.5rem',
fontSize: '1.1rem',
left: '0.65rem',
bottom: '-0.45rem',
fontSize: '1.05rem',
letterSpacing: '-0.1em',
textShadow: `
0 0 0.2em #f59e0b,
0 0 0.4em #f59e0b,
0 0 0.8em #f59e0b,
-0.05em -0.05em 0.05em rgba(0,0,0,0.7),
0.05em 0.05em 0.05em rgba(0,0,0,0.7)
`
}}
>
</div>
</div>
<div className="mx-1 flex w-1/6 flex-col items-center justify-center">
<NextImage src={relic?.mainAffix?.detail?.icon || ""} width={36} height={36} alt="Main Affix Icon" className="h-auto w-9 bg-black/20 rounded-full" />
<span className="text-base text-[#f1a23c]">{relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit}</span>
<span className="black-blur rounded px-1 text-xs">+{relic?.mainAffix?.level}</span>
<div className=" flex w-1/6 flex-col items-center justify-center">
<div className="relative">
<div className="absolute inset-0 bg-yellow-500/15 rounded-full blur-md -z-10"></div>
<NextImage
src={relic?.mainAffix?.detail?.icon || ""}
width={35}
height={35}
alt="Main Affix Icon"
className="h-auto w-[35px]"
/>
</div>
<div style={{ opacity: 0.5, height: '80px', borderLeftWidth: '1px' }}></div>
<div className="m-auto grid w-1/2 grid-cols-2 gap-2">
<span className="text-base text-yellow-400 font-semibold drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]">
{relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit}
</span>
<span className="bg-black/20 backdrop-blur-sm rounded px-1.5 py-0.5 text-xs border border-white/10">
+{relic?.mainAffix?.level}
</span>
</div>
<div style={{ opacity: 0.3, height: '78px', borderLeftWidth: '1px' }}></div>
<div className="grid w-[65%] m-2 grid-cols-2 gap-1">
{relic?.subAffix?.map((subAffix, index) => {
if (!subAffix) return null
return (
<div key={index} className="flex flex-col">
<div className="flex flex-row items-center">
{subAffix?.detail?.icon ? (
<NextImage src={subAffix?.detail?.icon || ""} width={36} height={36} alt="Sub Affix Icon" className="h-auto w-9 bg-black/20 rounded-full" />
) : (
<div className="h-9 w-9 bg-black/20 rounded flex items-center justify-center">
<span className="text-xs text-white">?</span>
<RelicShowcase key={index} relic={relic} />
)
})}
</div>
</div>
)
})}
{(!relicStats || !relicStats?.length) && (
<div className="flex flex-col items-center justify-center">
<div className="text-center p-6 rounded-lg bg-black/40 backdrop-blur-sm border border-white/10">
<span className="text-lg text-gray-400">{transI18n("noRelicEquipped")}</span>
</div>
</div>
)}
<span className="text-sm">+{subAffix?.valueAffix + subAffix?.detail?.unit}</span>
</div>
</div>
)
})}
</div>
</div>
)
})}
{(!relicStats || !relicStats?.length) && <div className="flex flex-col items-center justify-center ">
<span className="text-lg">{transI18n("noRelicEquipped")}</span>
</div>}
</div>
</div>
)

View File

@@ -16,6 +16,8 @@ import { useTranslations } from 'next-intl';
import useAffixStore from '@/stores/affixStore';
import useRelicStore from '@/stores/relicStore';
import { toast } from 'react-toastify';
import RelicShowcase from './relicShowcase';
export default function ShowCaseInfo() {
const { avatarSelected, mapAvatarInfo } = useAvatarStore()
@@ -162,7 +164,9 @@ export default function ShowCaseInfo() {
return {
property: subAffixMap?.[subValue?.sub_affix_id]?.property,
valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count),
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.property]
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.property],
step: subValue?.step,
count: subValue?.count
}
})
}
@@ -495,7 +499,7 @@ export default function ShowCaseInfo() {
}, [avatarSelected])
return (
<div className="flex flex-col justify-start m-2 text-white">
<div className="flex flex-col justify-start m-1 text-white">
<div className="flex items-center justify-start mt-4 mb-4">
<button className="btn btn-success w-24 text-sm" onClick={handleSaveImage}>Save Img</button>
</div>
@@ -503,7 +507,7 @@ export default function ShowCaseInfo() {
<div className="overflow-auto">
<div
ref={cardRef}
className=" relative min-h-[650px] w-[1400px] rounded-3xl transition-all duration-500"
className=" relative min-h-[650px] w-[1600px] rounded-3xl transition-all duration-500 overflow-hidden"
style={{
backgroundColor: `${applyBrightness(avgColor, 0.3)}`,
backdropFilter: "blur(50px)",
@@ -522,7 +526,7 @@ export default function ShowCaseInfo() {
<NextImage
ref={imgRef}
src={`https://api.hakush.in/hsr/UI/avatardrawcard/${avatarSelected?.id}.webp`}
className="object-cover scale-[2]"
className="object-cover scale-[2] overflow-hidden"
alt="Character Preview"
width={1024}
height={1024}
@@ -607,7 +611,6 @@ export default function ShowCaseInfo() {
{avatarData && avatarInfo && avatarSkillTree && traceShowCaseMap[avatarSelected?.baseType || ""]
&& Object.values(traceShowCaseMap[avatarSelected?.baseType || ""] || []).map((item, index) => {
return (
<div key={`row-${index}`} className="flex flex-row items-center">
{item.map((btn, idx) => {
@@ -803,7 +806,6 @@ export default function ShowCaseInfo() {
</div>
</div>
{/* Chỉ số */}
<div className="flex justify-center items-center flex-col gap-1 mt-1 ">
<div className="flex gap-1 text-sm ">
<div className="flex items-center gap-1 rounded bg-black/30 px-1 w-fit py-1">
@@ -872,7 +874,7 @@ export default function ShowCaseInfo() {
}}
/>
<div>
<span className="black-blur bg-black/50 flex w-5 justify-center rounded px-1.5 py-0.5">{setEffect.count}</span>
<span className="black-blur bg-black/30 flex w-5 justify-center rounded px-1.5 py-0.5">{setEffect.count}</span>
</div>
</div>
)
@@ -881,65 +883,22 @@ export default function ShowCaseInfo() {
</div>
<div className="w-1/3 z-10">
<div className="flex h-[650px] flex-col justify-between py-3 text-lg">
<div className="flex h-[650px] flex-col justify-between py-3 mr-1 text-lg w-full" >
{relicStats?.map((relic, index) => {
if (!relic) return null
return (
<div key={index} className="black-blur relative flex flex-row items-center rounded-s-lg border-l-2 p-1 border-yellow-600">
<div className="flex">
<NextImage src={relic?.img || ""} width={80} height={80} alt="Relic Icon" className="h-auto w-20" />
<RelicShowcase key={index} relic={relic} />
)
})}
<div
className="absolute text-yellow-500 font-bold z-10"
style={{
left: '0.7rem',
bottom: '-0.5rem',
fontSize: '1.1rem',
letterSpacing: '-0.1em',
textShadow: `
0 0 0.2em #f59e0b,
0 0 0.4em #f59e0b,
0 0 0.8em #f59e0b,
-0.05em -0.05em 0.05em rgba(0,0,0,0.7),
0.05em 0.05em 0.05em rgba(0,0,0,0.7)
`
}}
>
{(!relicStats || !relicStats?.length) && (
<div className="flex flex-col items-center justify-center">
<div className="text-center p-6">
<span className="text-lg text-white">{transI18n("noRelicEquipped")}</span>
</div>
</div>
<div className="mx-1 flex w-1/6 flex-col items-center justify-center">
<NextImage src={relic?.mainAffix?.detail?.icon || ""} width={36} height={36} alt="Main Affix Icon" className="h-auto w-9" />
<span className="text-base text-[#f1a23c]">{relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit}</span>
<span className="black-blur rounded px-1 text-xs">+{relic?.mainAffix?.level}</span>
</div>
<div style={{ opacity: 0.5, height: '80px', borderLeftWidth: '1px' }}></div>
<div className="m-auto grid w-1/2 grid-cols-2 gap-2">
{relic?.subAffix?.map((subAffix, index) => {
if (!subAffix) return null
return (
<div key={index} className="flex flex-col">
<div className="flex flex-row items-center">
{subAffix?.detail?.icon ? (
<NextImage src={subAffix?.detail?.icon || ""} width={36} height={36} alt="Sub Affix Icon" className="h-auto w-9" />
) : (
<div className="h-9 w-9 bg-black/50 rounded flex items-center justify-center">
<span className="text-xs text-white">?</span>
</div>
)}
<span className="text-sm">+{subAffix?.valueAffix + subAffix?.detail?.unit}</span>
</div>
</div>
)
})}
</div>
</div>
)
})}
{(!relicStats || !relicStats?.length) && <div className="flex flex-col items-center justify-center ">
<span className="text-lg">{transI18n("noRelicEquipped")}</span>
</div>}
</div>
</div>

View File

@@ -0,0 +1,99 @@
"use client"
import NextImage from "next/image"
import { RelicShowcaseType } from "@/types";
export default function RelicShowcase({
relic,
}: {
relic: RelicShowcaseType;
}) {
return (
<>
<div
className="relative w-full flex flex-row items-center rounded-s-lg border-l-2 p-1 border-yellow-600/60 bg-gradient-to-r from-yellow-600/20 to-transparent"
>
{/* Subtle glow overlay */}
<div className="absolute inset-0 rounded-s-lg pointer-events-none"></div>
<div className="flex relative">
<div className="absolute inset-0 rounded-lg blur-lg -z-10"></div>
<NextImage
src={relic?.img || ""}
width={78}
height={78}
alt="Relic Icon"
className="h-auto w-[78px] rounded-lg"
/>
<div
className="absolute text-yellow-400 font-bold z-10 drop-shadow-[0_0_6px_rgba(251,191,36,0.8)]"
style={{
left: '0.65rem',
bottom: '-0.45rem',
fontSize: '1.05rem',
letterSpacing: '-0.1em',
}}
>
</div>
</div>
<div className=" flex w-1/6 flex-col items-center justify-center">
<div className="relative">
<div className="absolute inset-0 bg-yellow-500/15 rounded-full blur-md -z-10"></div>
<NextImage
src={relic?.mainAffix?.detail?.icon || ""}
width={35}
height={35}
alt="Main Affix Icon"
className="h-auto w-[35px]"
/>
</div>
<span className="text-base text-yellow-400 font-semibold drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]">
{relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit}
</span>
<span className="bg-black/20 backdrop-blur-sm rounded px-1.5 py-0.5 text-xs border border-white/10">
+{relic?.mainAffix?.level}
</span>
</div>
<div style={{ opacity: 0.3, height: '78px', borderLeftWidth: '1px' }}></div>
<div className="grid w-[65%] m-2 grid-cols-2 gap-1">
{relic?.subAffix?.map((subAffix, index) => {
if (!subAffix) return null
return (
<div key={index} className="flex flex-col">
<div className="relative flex flex-row items-center bg-black/20 backdrop-blur-sm rounded-md p-1 border border-white/5 min-w-0">
{subAffix?.detail?.icon ? (
<NextImage
src={subAffix?.detail?.icon || ""}
width={32}
height={32}
alt="Sub Affix Icon"
className="h-auto w-6 flex-shrink-0"
/>
) : (
<div className="h-6 w-6 bg-black/60 rounded flex items-center justify-center border border-white/10 flex-shrink-0">
<span className="text-xs text-white/50">?</span>
</div>
)}
<span className="text-xs text-gray-200 ml-0.5 truncate flex-1 min-w-0">
+{subAffix?.valueAffix + subAffix?.detail?.unit}
</span>
{subAffix.step > 1 && (
<span className="ml-1 bg-yellow-600/20 text-yellow-400 rounded-full px-1 py-0.5 text-[10px] font-semibold border border-yellow-600/30 flex-shrink-0 leading-none">
{subAffix?.step}
</span>
)}
</div>
</div>
)
})}
</div>
</div>
</>
)
}

View File

@@ -9,6 +9,10 @@ interface AvatarState {
avatarSelected: CharacterBasic | null;
mapAvatarInfo: Record<string, CharacterDetail>;
skillSelected: string | null;
listElement: Record<string, boolean>;
listPath: Record<string, boolean>;
setListElement: (newListElement: Record<string, boolean>) => void;
setListPath: (newListPath: Record<string, boolean>) => void;
setListAvatar: (newListAvatar: CharacterBasic[]) => void;
setAvatarSelected: (newAvatarSelected: CharacterBasic) => void;
setFilter: (newFilter: FilterAvatarType) => void;
@@ -30,6 +34,10 @@ const useAvatarStore = create<AvatarState>((set, get) => ({
avatarSelected: null,
skillSelected: null,
mapAvatarInfo: {},
listElement: { "fire": false, "ice": false, "imaginary": false, "physical": false, "quantum": false, "thunder": false, "wind": false },
listPath: { "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false },
setListElement: (newListElement: Record<string, boolean>) => set({ listElement: newListElement }),
setListPath: (newListPath: Record<string, boolean>) => set({ listPath: newListPath }),
setSkillSelected: (newSkillSelected: string | null) => set({ skillSelected: newSkillSelected }),
setListAvatar: (newListAvatar: CharacterBasic[]) => set({ listAvatar: newListAvatar, listRawAvatar: newListAvatar }),
setAvatarSelected: (newAvatarSelected: CharacterBasic) => set({ avatarSelected: newAvatarSelected }),

View File

@@ -7,6 +7,12 @@ interface CopyProfileState {
listRawCopyAvatar: CharacterBasic[];
filterCopy: FilterAvatarType;
avatarCopySelected: CharacterBasic | null;
listElement: Record<string, boolean>;
listPath: Record<string, boolean>;
listRank: Record<string, boolean>;
setListElement: (newListElement: Record<string, boolean>) => void;
setListPath: (newListPath: Record<string, boolean>) => void;
setListRank: (newListRank: Record<string, boolean>) => void;
setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => void;
setAvatarCopySelected: (newAvatarSelected: CharacterBasic | null) => void;
setFilterCopy: (newFilter: FilterAvatarType) => void;
@@ -25,6 +31,12 @@ const useCopyProfileStore = create<CopyProfileState>((set, get) => ({
locale: "",
},
avatarCopySelected: null,
listElement: { "fire": false, "ice": false, "imaginary": false, "physical": false, "quantum": false, "thunder": false, "wind": false },
listPath: { "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false },
listRank: { "4": false, "5": false },
setListElement: (newListElement: Record<string, boolean>) => set({ listElement: newListElement }),
setListPath: (newListPath: Record<string, boolean>) => set({ listPath: newListPath }),
setListRank: (newListRank: Record<string, boolean>) => set({ listRank: newListRank }),
setListCopyAvatar: (newListAvatar: CharacterBasic[]) => set({ listCopyAvatar: newListAvatar, listRawCopyAvatar: newListAvatar }),
setAvatarCopySelected: (newAvatarSelected: CharacterBasic | null) => set({ avatarCopySelected: newAvatarSelected }),
setFilterCopy: (newFilter: FilterAvatarType) => {

View File

@@ -4,9 +4,13 @@ import { create } from 'zustand'
interface LightconeState {
listLightcone: LightConeBasic[];
listRawLightcone: LightConeBasic[];
listPath: Record<string, boolean>;
listRank: Record<string, boolean>;
filter: FilterLightconeType;
defaultFilter: { path: string[], rarity: string[] };
mapLightconeInfo: Record<string, LightConeDetail>;
setListPath: (newListPath: Record<string, boolean>) => void;
setListRank: (newListRank: Record<string, boolean>) => void;
setDefaultFilter: (newDefaultFilter: { path: string[], rarity: string[] }) => void;
setListLightcone: (newListLightcone: LightConeBasic[]) => void;
setFilter: (newFilter: FilterLightconeType) => void;
@@ -18,6 +22,7 @@ const useLightconeStore = create<LightconeState>((set, get) => ({
listLightcone: [],
listRawLightcone: [],
mapLightconeInfo: {},
filter: {
name: "",
path: [],
@@ -25,6 +30,10 @@ const useLightconeStore = create<LightconeState>((set, get) => ({
rarity: [],
},
defaultFilter: { path: [], rarity: [] },
listPath: { "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false },
listRank: { "3": false, "4": false, "5": false },
setListPath: (newListPath: Record<string, boolean>) => set({ listPath: newListPath }),
setListRank: (newListRank: Record<string, boolean>) => set({ listRank: newListRank }),
setDefaultFilter: (newDefaultFilter: { path: string[], rarity: string[] }) => set({ defaultFilter: newDefaultFilter }),
setListLightcone: (newListLightcone: LightConeBasic[]) => set({ listLightcone: newListLightcone, listRawLightcone: newListLightcone }),
setFilter: (newFilter: FilterLightconeType) => {

View File

@@ -20,3 +20,4 @@ export * from "./monsterValue"
export * from "./peakDetail"
export * from "./monsterDetail"
export * from "./extraData"
export * from "./showcase"

26
src/types/showcase.tsx Normal file
View File

@@ -0,0 +1,26 @@
export type RelicShowcaseType = {
img: string;
mainAffix: {
property: string;
level: number;
valueAffix: string;
detail: {
name: string;
icon: string;
unit: string;
baseStat: string;
};
};
subAffix: {
property: string;
valueAffix: string;
detail: {
name: string;
icon: string;
unit: string;
baseStat: string;
};
step: number;
count: number;
}[]
}