UPDATE: responsive ui, optimaze ux
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 1m42s
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 1m42s
This commit is contained in:
6
bun.lock
6
bun.lock
@@ -6,8 +6,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/bundle-analyzer": "^15.5.4",
|
"@next/bundle-analyzer": "^15.5.4",
|
||||||
"@tanstack/react-query": "^5.83.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
"@vercel/analytics": "^1.5.0",
|
|
||||||
"@vercel/speed-insights": "^1.2.0",
|
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"fast-average-color": "^9.5.0",
|
"fast-average-color": "^9.5.0",
|
||||||
"framer-motion": "^12.12.1",
|
"framer-motion": "^12.12.1",
|
||||||
@@ -384,10 +382,6 @@
|
|||||||
|
|
||||||
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
|
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
|
||||||
|
|
||||||
"@vercel/analytics": ["@vercel/analytics@1.5.0", "", { "peerDependencies": { "@remix-run/react": "^2", "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@remix-run/react", "@sveltejs/kit", "next", "react", "svelte", "vue", "vue-router"] }, "sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g=="],
|
|
||||||
|
|
||||||
"@vercel/speed-insights": ["@vercel/speed-insights@1.2.0", "", { "peerDependencies": { "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@sveltejs/kit", "next", "react", "svelte", "vue", "vue-router"] }, "sha512-y9GVzrUJ2xmgtQlzFP2KhVRoCglwfRQgjyfY607aU0hh0Un6d0OUyrJkjuAlsV18qR4zfoFPs/BiIj9YDS6Wzw=="],
|
|
||||||
|
|
||||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||||
|
|
||||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||||
|
|||||||
@@ -242,9 +242,12 @@
|
|||||||
"hardMode": "困难模式",
|
"hardMode": "困难模式",
|
||||||
"selectPEAKEvent": "选择 PEAK 事件",
|
"selectPEAKEvent": "选择 PEAK 事件",
|
||||||
"mode": "模式",
|
"mode": "模式",
|
||||||
"selectMode": "选择模式",
|
"selectMode": "选择模式",
|
||||||
"rollBack": "回到之前的状态",
|
"rollBack": "回到之前的状态",
|
||||||
"upRoll": "增加副属性",
|
"upRoll": "增加副属性",
|
||||||
"downRoll": "减少副属性"
|
"downRoll": "减少副属性",
|
||||||
|
"actions": "操作",
|
||||||
|
"avatars": "头像",
|
||||||
|
"quickView": "快速预览"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,7 +246,9 @@
|
|||||||
"selectMode": "Select a mode",
|
"selectMode": "Select a mode",
|
||||||
"rollBack": "Roll Back",
|
"rollBack": "Roll Back",
|
||||||
"upRoll": "Up Roll",
|
"upRoll": "Up Roll",
|
||||||
"downRoll": "Down Roll"
|
"downRoll": "Down Roll",
|
||||||
|
"actions": "Actions",
|
||||||
|
"avatars": "Avatars",
|
||||||
|
"quickView": "Quick View"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"path": "運命",
|
"path": "運命",
|
||||||
"rarity": "レアリティ",
|
"rarity": "レアリティ",
|
||||||
"element": "属性",
|
"element": "属性",
|
||||||
"technique": "秘術",
|
"technique": "秘技",
|
||||||
"talent": "天賦",
|
"talent": "天賦",
|
||||||
"basic": "通常攻撃",
|
"basic": "通常攻撃",
|
||||||
"skill": "スキル",
|
"skill": "スキル",
|
||||||
@@ -19,17 +19,17 @@
|
|||||||
"servant": "サーバント",
|
"servant": "サーバント",
|
||||||
"damage": "ダメージ",
|
"damage": "ダメージ",
|
||||||
"type": "タイプ",
|
"type": "タイプ",
|
||||||
"warrior": "破壊",
|
"warrior": "壊滅",
|
||||||
"knight": "保護",
|
"knight": "存護",
|
||||||
"mage": "博識",
|
"mage": "知恵",
|
||||||
"priest": "豊穣",
|
"priest": "豊穣",
|
||||||
"rogue": "狩猟",
|
"rogue": "巡狩",
|
||||||
"shaman": "調和",
|
"shaman": "調和",
|
||||||
"warlock": "虚無",
|
"warlock": "虚無",
|
||||||
"memory": "記憶",
|
"memory": "記憶",
|
||||||
"fire": "火",
|
"fire": "火",
|
||||||
"ice": "氷",
|
"ice": "氷",
|
||||||
"imaginary": "幻影",
|
"imaginary": "虚数",
|
||||||
"physical": "物理",
|
"physical": "物理",
|
||||||
"quantum": "量子",
|
"quantum": "量子",
|
||||||
"thunder": "雷",
|
"thunder": "雷",
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
"speed": "速度",
|
"speed": "速度",
|
||||||
"critRate": "会心率",
|
"critRate": "会心率",
|
||||||
"critDmg": "会心ダメージ",
|
"critDmg": "会心ダメージ",
|
||||||
"breakEffect": "破甲ダメージ",
|
"breakEffect": "撃破特効",
|
||||||
"effectRes": "効果抵抗",
|
"effectRes": "効果抵抗",
|
||||||
"energyRegenerationRate": "エネルギー回復速度",
|
"energyRegenerationRate": "EP回復効率",
|
||||||
"effectHitRate": "効果命中率",
|
"effectHitRate": "効果命中率",
|
||||||
"outgoingHealingBoost": "回復増加",
|
"outgoingHealingBoost": "回復増加",
|
||||||
"fireDmgBoost": "火属性ダメージ強化",
|
"fireDmgBoost": "火属性ダメージ強化",
|
||||||
@@ -52,15 +52,15 @@
|
|||||||
"thunderDmgBoost": "雷属性ダメージ強化",
|
"thunderDmgBoost": "雷属性ダメージ強化",
|
||||||
"windDmgBoost": "風属性ダメージ強化",
|
"windDmgBoost": "風属性ダメージ強化",
|
||||||
"pursued": "追加ダメージ",
|
"pursued": "追加ダメージ",
|
||||||
"true damage": "真のダメージ",
|
"true damage": "確定ダメージ",
|
||||||
"follow-up": "追撃ダメージ",
|
"follow-up": "追撃ダメージ",
|
||||||
"elemental damage": "破甲および超破甲ダメージ",
|
"elemental damage": "属性ダメージ",
|
||||||
"dot": "継続ダメージ",
|
"dot": "継続ダメージ",
|
||||||
"qte": "QTEスキル",
|
"qte": "QTEスキル",
|
||||||
"level": "レベル",
|
"level": "レベル",
|
||||||
"relics": "遺物",
|
"relics": "遺物",
|
||||||
"eidolons": "エイドロン",
|
"eidolons": "エイドロン",
|
||||||
"lightcones": "ライトコーン",
|
"lightcones": "光円錐",
|
||||||
"loadData": "データ読み込み",
|
"loadData": "データ読み込み",
|
||||||
"exportData": "データ出力",
|
"exportData": "データ出力",
|
||||||
"connectSetting": "接続設定",
|
"connectSetting": "接続設定",
|
||||||
@@ -114,32 +114,32 @@
|
|||||||
"currentEnergy": "現在のエネルギー",
|
"currentEnergy": "現在のエネルギー",
|
||||||
"setTo50": "50%に設定",
|
"setTo50": "50%に設定",
|
||||||
"battleConfiguration": "バトル構成",
|
"battleConfiguration": "バトル構成",
|
||||||
"useTechnique": "秘術を使用",
|
"useTechnique": "秘技を使用",
|
||||||
"techniqueNote": "バトル前に秘術効果を有効化",
|
"techniqueNote": "バトル前に秘技効果を有効化",
|
||||||
"enhancement": "強化",
|
"enhancement": "強化",
|
||||||
"enhancementLevel": "強化レベル",
|
"enhancementLevel": "強化レベル",
|
||||||
"origin": "由来",
|
"origin": "由来",
|
||||||
"enhancedNote": "強化レベルが高いほど追加スキル解放",
|
"enhancedNote": "強化レベルに応じて追加スキル解放",
|
||||||
"lightconeEquipment": "ライトコーン装備",
|
"lightconeEquipment": "光円錐装備",
|
||||||
"lightconeSettings": "ライトコーン設定",
|
"lightconeSettings": "光円錐の設定",
|
||||||
"placeholderLevel": "レベルを入力",
|
"placeholderLevel": "レベルを入力",
|
||||||
"superimpositionRank": "重ねランク",
|
"superimpositionRank": "重ねランク",
|
||||||
"ranksNote": "ランクが高いほど効果強い",
|
"ranksNote": "ランクが高いほど効果強化",
|
||||||
"changeLightcone": "ライトコーン変更",
|
"changeLightcone": "光円錐を変更",
|
||||||
"removeLightcone": "ライトコーン解除",
|
"removeLightcone": "光円錐を外す",
|
||||||
"equipLightcone": "ライトコーン装備",
|
"equipLightcone": "光円錐を装備",
|
||||||
"noLightconeEquipped": "ライトコーン未装備",
|
"noLightconeEquipped": "光円錐が装備されていません",
|
||||||
"equipLightconeNote": "キャラクター強化のため装備してください",
|
"equipLightconeNote": "強化のため光円錐を装備してください",
|
||||||
"filter": "フィルター",
|
"filter": "フィルター",
|
||||||
"selectedCharacters": "選択されたキャラクター",
|
"selectedCharacters": "選択中のキャラクター",
|
||||||
"selectedProfiles": "選択されたプロファイル",
|
"selectedProfiles": "選択中のプロファイル",
|
||||||
"clearAll": "すべてクリア",
|
"clearAll": "全てクリア",
|
||||||
"selectAll": "すべて選択",
|
"selectAll": "全て選択",
|
||||||
"copy": "コピー",
|
"copy": "コピー",
|
||||||
"copied": "コピー済み",
|
"copied": "コピー済み",
|
||||||
"noAvatarSelected": "キャラクター未選択",
|
"noAvatarSelected": "キャラクターが選択されていません",
|
||||||
"noAvatarToCopySelected": "コピーするキャラクター未選択",
|
"noAvatarToCopySelected": "コピーするキャラクター選択されていません",
|
||||||
"pleaseSelectAtLeastOneProfile": "プロファイルを最低1つ選択してください",
|
"pleaseSelectAtLeastOneProfile": "プロファイルを少なくとも1つ選んでください",
|
||||||
"pleaseEnterUid": "UIDを入力してください",
|
"pleaseEnterUid": "UIDを入力してください",
|
||||||
"failedToFetchEnkaData": "Enka のデータ取得に失敗",
|
"failedToFetchEnkaData": "Enka のデータ取得に失敗",
|
||||||
"pleaseSelectAtLeastOneCharacter": "キャラクターを最低1つ選択してください",
|
"pleaseSelectAtLeastOneCharacter": "キャラクターを最低1つ選択してください",
|
||||||
@@ -159,7 +159,7 @@
|
|||||||
"pleaseSelectAllOptions": "すべてのオプションを選択してください",
|
"pleaseSelectAllOptions": "すべてのオプションを選択してください",
|
||||||
"relicSavedSuccessfully": "遺物の保存に成功しました",
|
"relicSavedSuccessfully": "遺物の保存に成功しました",
|
||||||
"mainSettings": "メイン設定",
|
"mainSettings": "メイン設定",
|
||||||
"mainStat": "主属性",
|
"mainStat": "メインステータス",
|
||||||
"set": "セット",
|
"set": "セット",
|
||||||
"pleaseSelectASet": "セットを選択してください",
|
"pleaseSelectASet": "セットを選択してください",
|
||||||
"effectBonus": "効果ボーナス",
|
"effectBonus": "効果ボーナス",
|
||||||
@@ -168,14 +168,14 @@
|
|||||||
"randomizeRolls": "ロール回数ランダム化",
|
"randomizeRolls": "ロール回数ランダム化",
|
||||||
"selectASubStat": "サブステータスを選択",
|
"selectASubStat": "サブステータスを選択",
|
||||||
"selectASet": "セットを選択",
|
"selectASet": "セットを選択",
|
||||||
"selectAMainStat": "主属性を選択",
|
"selectAMainStat": "メインステータスを選んでください",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"reset": "リセット",
|
"reset": "リセット",
|
||||||
"roll": "ロール",
|
"roll": "ロール",
|
||||||
"step": "ステップ",
|
"step": "ステップ",
|
||||||
"memoryOfChaos": "忘却の庭",
|
"memoryOfChaos": "混沌の記憶",
|
||||||
"pureFiction": "虚構叙事",
|
"pureFiction": "虚構叙事",
|
||||||
"apocalypticShadow": "終末の幻影",
|
"apocalypticShadow": "末日の幻影",
|
||||||
"customEnemy": "カスタム敵",
|
"customEnemy": "カスタム敵",
|
||||||
"simulatedUniverse": "模擬宇宙",
|
"simulatedUniverse": "模擬宇宙",
|
||||||
"floor": "階層",
|
"floor": "階層",
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
"firstHalfEnemies": "前半の敵",
|
"firstHalfEnemies": "前半の敵",
|
||||||
"secondHalfEnemies": "後半の敵",
|
"secondHalfEnemies": "後半の敵",
|
||||||
"turbulenceBuff": "乱気流バフ",
|
"turbulenceBuff": "乱気流バフ",
|
||||||
"noEventSelected": "未選択のイベント",
|
"noEventSelected": "イベントが選択されていません",
|
||||||
"noTurbulenceBuff": "乱気流バフがありません",
|
"noTurbulenceBuff": "乱気流バフがありません",
|
||||||
"upper": "上半",
|
"upper": "上半",
|
||||||
"lower": "下半",
|
"lower": "下半",
|
||||||
@@ -211,13 +211,13 @@
|
|||||||
"searchMonster": "敵を検索...",
|
"searchMonster": "敵を検索...",
|
||||||
"changeRelic": "遺物を変更",
|
"changeRelic": "遺物を変更",
|
||||||
"deleteRelic": "遺物を削除",
|
"deleteRelic": "遺物を削除",
|
||||||
"deleteRelicConfirm": "スロットの遺物を削除してもよろしいですか?",
|
"deleteRelicConfirm": "この遺物を削除してもよろしいですか?",
|
||||||
"setEffects": "効果を設定",
|
"setEffects": "効果を設定",
|
||||||
"details": "詳細",
|
"details": "詳細",
|
||||||
"normal": "通常攻撃",
|
"normal": "通常攻撃",
|
||||||
"bpskill": "スキル",
|
"bpskill": "戦闘スキル",
|
||||||
"maze": "技術",
|
"maze": "秘技",
|
||||||
"ultra": "アルティメット",
|
"ultra": "必殺技",
|
||||||
"servantskill": "メモスプライトスキル",
|
"servantskill": "メモスプライトスキル",
|
||||||
"severaltalent": "メモスプライトの才能",
|
"severaltalent": "メモスプライトの才能",
|
||||||
"singleattack": "単体攻撃",
|
"singleattack": "単体攻撃",
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
"maxAll": "すべて最大化",
|
"maxAll": "すべて最大化",
|
||||||
"maxAllSuccess": "スキルレベルを最大に設定しました。",
|
"maxAllSuccess": "スキルレベルを最大に設定しました。",
|
||||||
"maxAllFailed": "スキルレベルの最大設定に失敗しました。",
|
"maxAllFailed": "スキルレベルの最大設定に失敗しました。",
|
||||||
"noRelicEquipped": "聖遺物が装備されていません",
|
"noRelicEquipped": "遺物が装備されていません",
|
||||||
"anomalyArbitration": "異相の仲裁",
|
"anomalyArbitration": "異相の仲裁",
|
||||||
"normalMode": "通常モード",
|
"normalMode": "通常モード",
|
||||||
"hardMode": "困難モード",
|
"hardMode": "困難モード",
|
||||||
@@ -245,6 +245,9 @@
|
|||||||
"selectMode": "モードを選択",
|
"selectMode": "モードを選択",
|
||||||
"rollBack": "前の状態に戻る",
|
"rollBack": "前の状態に戻る",
|
||||||
"upRoll": "サブステータスを増やす",
|
"upRoll": "サブステータスを増やす",
|
||||||
"downRoll": "サブステータスを減らす"
|
"downRoll": "サブステータスを減らす",
|
||||||
|
"actions": "アクション",
|
||||||
|
"avatars": "アバター",
|
||||||
|
"quickView": "クイックビュー"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,6 +245,9 @@
|
|||||||
"selectMode": "모드를 선택",
|
"selectMode": "모드를 선택",
|
||||||
"rollBack": "이전 상태로 되돌리기",
|
"rollBack": "이전 상태로 되돌리기",
|
||||||
"upRoll": "부옵션 추가",
|
"upRoll": "부옵션 추가",
|
||||||
"downRoll": "부옵션 감소"
|
"downRoll": "부옵션 감소",
|
||||||
}
|
"actions": "동작",
|
||||||
|
"avatars": "아바타",
|
||||||
|
"quickView": "빠른 조회"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -245,6 +245,9 @@
|
|||||||
"selectMode": "Chọn chế độ",
|
"selectMode": "Chọn chế độ",
|
||||||
"rollBack": "Quay lại bước trước",
|
"rollBack": "Quay lại bước trước",
|
||||||
"upRoll": "Tăng dòng",
|
"upRoll": "Tăng dòng",
|
||||||
"downRoll": "Giảm dòng"
|
"downRoll": "Giảm dòng",
|
||||||
|
"actions": "Hành động",
|
||||||
|
"avatars": "Nhân vật",
|
||||||
|
"quickView": "Xem nhanh"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,6 +245,9 @@
|
|||||||
"selectMode": "选择模式",
|
"selectMode": "选择模式",
|
||||||
"rollBack": "回到之前的状态",
|
"rollBack": "回到之前的状态",
|
||||||
"upRoll": "增加副属性",
|
"upRoll": "增加副属性",
|
||||||
"downRoll": "减少副属性"
|
"downRoll": "减少副属性",
|
||||||
|
"actions": "操作",
|
||||||
|
"avatars": "头像",
|
||||||
|
"quickView": "快速预览"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/bundle-analyzer": "^15.5.4",
|
"@next/bundle-analyzer": "^15.5.4",
|
||||||
"@tanstack/react-query": "^5.83.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
"@vercel/analytics": "^1.5.0",
|
|
||||||
"@vercel/speed-insights": "^1.2.0",
|
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"fast-average-color": "^9.5.0",
|
"fast-average-color": "^9.5.0",
|
||||||
"framer-motion": "^12.12.1",
|
"framer-motion": "^12.12.1",
|
||||||
|
|||||||
@@ -7,12 +7,11 @@ import Footer from "@/components/footer";
|
|||||||
import { NextIntlClientProvider } from "next-intl";
|
import { NextIntlClientProvider } from "next-intl";
|
||||||
import { getLocale, getMessages } from "next-intl/server";
|
import { getLocale, getMessages } from "next-intl/server";
|
||||||
import { ToastContainer } from 'react-toastify';
|
import { ToastContainer } from 'react-toastify';
|
||||||
import AvatarBar from "@/components/avatarBar";
|
|
||||||
import ActionBar from "@/components/actionBar";
|
|
||||||
import QueryProviderWrapper from "@/components/queryProvider";
|
import QueryProviderWrapper from "@/components/queryProvider";
|
||||||
import ClientDataFetcher from "@/components/clientDataFetcher";
|
import ClientDataFetcher from "@/components/clientDataFetcher";
|
||||||
import { SpeedInsights } from "@vercel/speed-insights/next"
|
import AvatarBar from "@/components/avatarBar";
|
||||||
import { Analytics } from "@vercel/analytics/next"
|
import ActionBar from "@/components/actionBar";
|
||||||
|
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
@@ -74,19 +73,19 @@ export default async function RootLayout({
|
|||||||
<QueryProviderWrapper>
|
<QueryProviderWrapper>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<ClientThemeWrapper>
|
<ClientThemeWrapper>
|
||||||
<ClientDataFetcher />
|
<ClientDataFetcher />
|
||||||
<div className="min-h-screen w-full">
|
<div className="min-h-screen w-full">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="grid grid-cols-12 w-full">
|
<div className="grid grid-cols-12 w-full">
|
||||||
<div className="col-span-3 sticky top-0 self-start h-fit">
|
<div className="hidden sm:block col-span-3 sticky top-0 self-start h-fit">
|
||||||
<AvatarBar />
|
<AvatarBar />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-9">
|
<div className="col-span-12 sm:col-span-9 lg:col-span-9">
|
||||||
<ActionBar />
|
<ActionBar />
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -95,8 +94,7 @@ export default async function RootLayout({
|
|||||||
</QueryProviderWrapper>
|
</QueryProviderWrapper>
|
||||||
</NextIntlClientProvider>
|
</NextIntlClientProvider>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
<SpeedInsights />
|
|
||||||
<Analytics />
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import useGlobalStore from "@/stores/globalStore";
|
|||||||
import { connectToPS, syncDataToPS } from "@/helper";
|
import { connectToPS, syncDataToPS } from "@/helper";
|
||||||
import CopyImport from "../importBar/copy";
|
import CopyImport from "../importBar/copy";
|
||||||
import useCopyProfileStore from "@/stores/copyProfile";
|
import useCopyProfileStore from "@/stores/copyProfile";
|
||||||
|
import AvatarBar from "../avatarBar";
|
||||||
|
|
||||||
|
|
||||||
export default function ActionBar() {
|
export default function ActionBar() {
|
||||||
@@ -25,7 +26,14 @@ export default function ActionBar() {
|
|||||||
const { setListCopyAvatar } = useCopyProfileStore()
|
const { setListCopyAvatar } = useCopyProfileStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const { isOpenCreateProfile, setIsOpenCreateProfile, isOpenCopy, setIsOpenCopy } = useModelStore()
|
const {
|
||||||
|
isOpenCreateProfile,
|
||||||
|
setIsOpenCreateProfile,
|
||||||
|
isOpenCopy,
|
||||||
|
setIsOpenCopy,
|
||||||
|
isOpenAvatars,
|
||||||
|
setIsOpenAvatars
|
||||||
|
} = useModelStore()
|
||||||
const { avatars, setAvatar } = useUserDataStore()
|
const { avatars, setAvatar } = useUserDataStore()
|
||||||
const [profileName, setProfileName] = useState("");
|
const [profileName, setProfileName] = useState("");
|
||||||
const [formState, setFormState] = useState("EDIT");
|
const [formState, setFormState] = useState("EDIT");
|
||||||
@@ -85,6 +93,7 @@ export default function ActionBar() {
|
|||||||
|
|
||||||
// Handle ESC key to close modal
|
// Handle ESC key to close modal
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (!isOpenCreateProfile) {
|
if (!isOpenCreateProfile) {
|
||||||
handleCloseModal("update_profile_modal");
|
handleCloseModal("update_profile_modal");
|
||||||
return;
|
return;
|
||||||
@@ -93,6 +102,12 @@ export default function ActionBar() {
|
|||||||
handleCloseModal("copy_profile_modal");
|
handleCloseModal("copy_profile_modal");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log(isOpenAvatars)
|
||||||
|
if (!isOpenAvatars) {
|
||||||
|
|
||||||
|
handleCloseModal("avatars_modal");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const handleEscKey = (event: KeyboardEvent) => {
|
const handleEscKey = (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Escape' && isOpenCreateProfile) {
|
if (event.key === 'Escape' && isOpenCreateProfile) {
|
||||||
@@ -101,12 +116,15 @@ export default function ActionBar() {
|
|||||||
if (event.key === 'Escape' && isOpenCopy) {
|
if (event.key === 'Escape' && isOpenCopy) {
|
||||||
handleCloseModal("copy_profile_modal");
|
handleCloseModal("copy_profile_modal");
|
||||||
}
|
}
|
||||||
|
if (event.key === 'Escape' && isOpenAvatars) {
|
||||||
|
handleCloseModal("avatars_modal");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('keydown', handleEscKey);
|
window.addEventListener('keydown', handleEscKey);
|
||||||
|
|
||||||
return () => window.removeEventListener('keydown', handleEscKey);
|
return () => window.removeEventListener('keydown', handleEscKey);
|
||||||
}, [isOpenCopy, isOpenCreateProfile]);
|
}, [isOpenCopy, isOpenCreateProfile, isOpenAvatars]);
|
||||||
|
|
||||||
const actionMove = (path: string) => {
|
const actionMove = (path: string) => {
|
||||||
router.push(`/${path}`)
|
router.push(`/${path}`)
|
||||||
@@ -154,49 +172,48 @@ export default function ActionBar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full mb-2">
|
<div className="w-full px-4 pb-4 bg-base-200">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-center justify-items-center">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 items-center justify-items-center">
|
||||||
<div className="grid grid-rows-2 lg:grid-rows-1 items-center w-full">
|
|
||||||
<div className="flex justify-between gap-10 w-full">
|
<div className="flex flex-col justify-center w-full">
|
||||||
<div className="flex items-center p-1 h-full lg:p-2 opacity-80 lg:hover:opacity-100 cursor-pointer text-base md:text-lg lg:text-xl">
|
<div className="flex flex-wrap items-center gap-2 ">
|
||||||
|
<div className="flex flex-wrap items-center h-full opacity-80 lg:hover:opacity-100 cursor-pointer text-base md:text-lg lg:text-xl">
|
||||||
{avatarSelected && (
|
{avatarSelected && (
|
||||||
<>
|
<div className="flex items-center justify-start h-full w-full">
|
||||||
<Image
|
<Image
|
||||||
src={ `/icon/${avatarSelected.damageType.toLowerCase()}.webp`}
|
src={`/icon/${avatarSelected.damageType.toLowerCase()}.webp`}
|
||||||
alt={'fire'}
|
alt={'fire'}
|
||||||
className="h-[40px] w-[40px] object-contain"
|
className="h-[40px] w-[40px] object-contain"
|
||||||
width={100}
|
width={100}
|
||||||
height={100}
|
height={100}
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center justify-center h-full w-full">
|
<p className="text-center font-bold text-xl">
|
||||||
<p className="text-center font-bold text-xl">
|
{transI18n(avatarSelected.baseType.toLowerCase())}
|
||||||
{transI18n(avatarSelected.baseType.toLowerCase())}
|
</p>
|
||||||
</p>
|
<div className="text-center font-bold text-xl">{" / "}</div>
|
||||||
<div className="text-center font-bold text-xl">{" / "}</div>
|
<ParseText
|
||||||
<ParseText
|
locale={locale}
|
||||||
locale={locale}
|
text={getNameChar(locale, avatarSelected).toWellFormed()}
|
||||||
text={getNameChar(locale, avatarSelected).toWellFormed()}
|
className={"font-bold text-xl"}
|
||||||
className={"font-bold text-xl"}
|
/>
|
||||||
/>
|
{avatarSelected?.id && (
|
||||||
{avatarSelected?.id && (
|
<div className="text-center italic text-sm ml-2"> {`(${avatarSelected.id})`}</div>
|
||||||
<div className="text-center italic text-sm ml-2"> {`(${avatarSelected.id})`}</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 ml-4 w-full">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<span className="text-base opacity-70 font-bold w-16">{transI18n("profile")}:</span>
|
<span className="text-base opacity-70 font-bold w-16">{transI18n("profile")}:</span>
|
||||||
<div className="dropdown dropdown-end w-full">
|
<div className="dropdown dropdown-center md:dropdown-start">
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
role="button"
|
role="button"
|
||||||
className="btn btn-warning border-info btn-soft gap-1 min-w-0"
|
className="btn btn-warning border-info btn-soft gap-1"
|
||||||
>
|
>
|
||||||
<span className="truncate max-w-24 font-bold">
|
<span className="truncate max-w-48 font-bold">
|
||||||
{profileCurrent?.profile_name}
|
{profileCurrent?.profile_name}
|
||||||
</span>
|
</span>
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -204,7 +221,7 @@ export default function ActionBar() {
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className="dropdown-content z-10 menu p-2 shadow bg-base-100 rounded-box w-full mt-1 border border-base-300 max-h-60 overflow-y-auto">
|
<ul className="dropdown-content z-10 menu p-2 shadow bg-base-100 rounded-box min-w-max w-full mt-1 border border-base-300 max-h-60 overflow-y-auto">
|
||||||
{listProfile.map((profile, index) => (
|
{listProfile.map((profile, index) => (
|
||||||
<li key={index} className="grid grid-cols-12">
|
<li key={index} className="grid grid-cols-12">
|
||||||
<button
|
<button
|
||||||
@@ -284,23 +301,82 @@ export default function ActionBar() {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div className=" grid grid-cols-2 w-full sm:hidden gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpenAvatars(true)
|
||||||
|
handleShow("avatars_modal")
|
||||||
|
}}
|
||||||
|
className="col-span-1 btn btn-warning btn-sm w-full">
|
||||||
|
{transI18n("avatars")}
|
||||||
|
</button>
|
||||||
|
<div className="col-span-1 dropdown dropdown-center w-full">
|
||||||
|
<label tabIndex={0} className="btn btn-info btn-sm w-full">
|
||||||
|
{transI18n("actions")}
|
||||||
|
</label>
|
||||||
|
<ul
|
||||||
|
tabIndex={0}
|
||||||
|
className="dropdown-content z-10 menu p-2 shadow bg-base-100 rounded-box min-w-max w-full mt-1 border border-base-300 max-h-60 overflow-y-auto"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<button onClick={() => actionMove('')}>
|
||||||
|
{transI18n("characterInformation")}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onClick={() => actionMove('relics-info')}>
|
||||||
|
{transI18n("relics")}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onClick={() => actionMove('eidolons-info')}>
|
||||||
|
{transI18n("eidolons")}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onClick={() => actionMove('skills-info')}>
|
||||||
|
{transI18n("skills")}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onClick={() => actionMove('showcase-card')}>
|
||||||
|
{transI18n("showcaseCard")}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onClick={handleConnectOrSyncPS} className="btn btn-primary btn-sm">
|
||||||
|
{isConnectPS ? transI18n("sync") : transI18n("connectPs")}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2 w-full">
|
<div className="hidden sm:grid grid-cols-3 gap-2 w-full">
|
||||||
{/* Action Buttons */}
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("")}>
|
||||||
<div className="grid grid-cols-2 xl:grid-cols-3 gap-2 w-full">
|
{transI18n("characterInformation")}
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove('')}>{transI18n("characterInformation")}</button>
|
</button>
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove('relics-info')}>{transI18n("relics")}</button>
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("relics-info")}>
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove('eidolons-info')}>{transI18n("eidolons")}</button>
|
{transI18n("relics")}
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove('skills-info')}>{transI18n("skills")}</button>
|
</button>
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove('showcase-card')}>{transI18n("showcaseCard")}</button>
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("eidolons-info")}>
|
||||||
<button onClick={handleConnectOrSyncPS} className="btn btn-primary btn-sm"> {isConnectPS ? transI18n("sync") : transI18n("connectPs")}</button>
|
{transI18n("eidolons")}
|
||||||
</div>
|
</button>
|
||||||
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("skills-info")}>
|
||||||
|
{transI18n("skills")}
|
||||||
|
</button>
|
||||||
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("showcase-card")}>
|
||||||
|
{transI18n("showcaseCard")}
|
||||||
|
</button>
|
||||||
|
<button onClick={handleConnectOrSyncPS} className="btn btn-primary btn-sm">
|
||||||
|
{isConnectPS ? transI18n("sync") : transI18n("connectPs")}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<dialog id="update_profile_modal" className="modal ">
|
||||||
<dialog id="update_profile_modal" className="modal sm:modal-middle backdrop-blur-sm">
|
|
||||||
<div className="modal-box w-11/12 max-w-7xl bg-base-100 text-base-content border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
<div className="modal-box w-11/12 max-w-7xl bg-base-100 text-base-content border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
<div className="sticky top-0 z-10">
|
<div className="sticky top-0 z-10">
|
||||||
<motion.button
|
<motion.button
|
||||||
@@ -335,7 +411,7 @@ export default function ActionBar() {
|
|||||||
|
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
<button className="btn btn-success btn-sm sm:btn-md" onClick={handleUpdateProfile}>
|
<button className="btn btn-success btn-sm sm:btn-md" onClick={handleUpdateProfile}>
|
||||||
{formState === "CREATE" ? transI18n("create") : transI18n("update")}
|
{formState === "CREATE" ? transI18n("create") : transI18n("update")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -366,6 +442,32 @@ export default function ActionBar() {
|
|||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
|
||||||
|
<dialog id="avatars_modal" className="modal">
|
||||||
|
<div className="modal-box w-11/12 max-w-7xl bg-base-100 text-base-content border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
|
<div className="sticky top-0 z-10">
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="btn btn-circle btn-md absolute right-2 top-2 bg-red-600 hover:bg-red-700 text-white border-none"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpenAvatars(false)
|
||||||
|
handleCloseModal("avatars_modal")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</motion.button>
|
||||||
|
</div>
|
||||||
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
||||||
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
||||||
|
{transI18n("avatars").toUpperCase()}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<AvatarBar onClose={() => {setIsOpenAvatars(false); handleCloseModal("avatars_modal")}} />
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import useAvatarStore from "@/stores/avatarStore"
|
|||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
|
|
||||||
|
|
||||||
export default function AvatarBar() {
|
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 [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 [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 } = useAvatarStore()
|
||||||
@@ -15,7 +15,6 @@ export default function AvatarBar() {
|
|||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFilter({ ...filter, locale: locale, element: Object.keys(listElement).filter((key) => listElement[key]), path: Object.keys(listPath).filter((key) => listPath[key]) })
|
setFilter({ ...filter, locale: locale, element: Object.keys(listElement).filter((key) => listElement[key]), path: Object.keys(listPath).filter((key) => listPath[key]) })
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -31,12 +30,12 @@ export default function AvatarBar() {
|
|||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<input type="text"
|
<input type="text"
|
||||||
placeholder={transI18n("placeholderCharacter")}
|
placeholder={transI18n("placeholderCharacter")}
|
||||||
className="input input-bordered input-primary w-full max-w-xs"
|
className="input input-bordered input-primary w-full"
|
||||||
value={filter.name}
|
value={filter.name}
|
||||||
onChange={(e) => setFilter({ ...filter, name: e.target.value, locale: locale })}
|
onChange={(e) => setFilter({ ...filter, name: e.target.value, locale: locale })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 lg:grid-cols-7 mb-1 mx-1 gap-2 w-full max-h-[17vh] min-h-[5vh] overflow-y-auto">
|
<div className="grid grid-cols-4 lg:grid-cols-7 mb-1 mx-1 gap-2 w-full max-h-[17vh] min-h-[5vh] overflow-y-auto">
|
||||||
{Object.keys(listElement).map((key, index) => (
|
{Object.keys(listElement).map((key, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
@@ -56,7 +55,7 @@ export default function AvatarBar() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 lg:grid-cols-8 mb-1 mx-1 gap-2 overflow-y-auto w-full max-h-[17vh] min-h-[5vh]">
|
<div className="grid grid-cols-4 lg:grid-cols-8 mb-1 mx-1 gap-2 overflow-y-auto w-full max-h-[17vh] min-h-[5vh]">
|
||||||
{Object.keys(listPath).map((key, index) => (
|
{Object.keys(listPath).map((key, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
@@ -81,9 +80,13 @@ export default function AvatarBar() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start h-full">
|
<div className="flex items-start h-full">
|
||||||
<ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2 w-full h-[65vh] overflow-y-scroll overflow-x-hidden">
|
<ul className="grid grid-cols-2 lg:grid-cols-3 gap-2 w-full h-[65vh] overflow-y-scroll overflow-x-hidden">
|
||||||
{listAvatar.map((item, index) => (
|
{listAvatar.map((item, index) => (
|
||||||
<div key={index} onClick={() => {setAvatarSelected(item); setSkillSelected(null)}}>
|
<div key={index} onClick={() => {
|
||||||
|
setAvatarSelected(item);
|
||||||
|
setSkillSelected(null)
|
||||||
|
if (onClose) onClose()
|
||||||
|
}}>
|
||||||
<CharacterCard data={item} />
|
<CharacterCard data={item} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ import ParseText from "../parseText";
|
|||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
|
||||||
export default function EidolonsInfo() {
|
export default function EidolonsInfo() {
|
||||||
const { avatarSelected, mapAvatarInfo } = useListAvatarStore()
|
const { avatarSelected, mapAvatarInfo } = useListAvatarStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
|
const transI18n = useTranslations("DataPage")
|
||||||
const { setAvatars, avatars } = useUserDataStore()
|
const { setAvatars, avatars } = useUserDataStore()
|
||||||
|
|
||||||
const charRank = useMemo(() => {
|
const charRank = useMemo(() => {
|
||||||
@@ -23,6 +26,11 @@ export default function EidolonsInfo() {
|
|||||||
}, [avatarSelected, avatars, locale, mapAvatarInfo]);
|
}, [avatarSelected, avatars, locale, mapAvatarInfo]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
||||||
|
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
||||||
|
<div className="w-2 h-6 bg-gradient-to-b from-primary to-primary/50 rounded-full"></div>
|
||||||
|
{transI18n("eidolons")}
|
||||||
|
</h2>
|
||||||
<div className="grid grid-cols-1 m-4 p-4 font-bold gap-4 w-fit max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
<div className="grid grid-cols-1 m-4 p-4 font-bold gap-4 w-fit max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
||||||
{charRank && avatars[avatarSelected?.id || ""] && (
|
{charRank && avatars[avatarSelected?.id || ""] && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
@@ -63,5 +71,6 @@ export default function EidolonsInfo() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export default function CopyImport() {
|
|||||||
|
|
||||||
<div className="flex items-start flex-col gap-2 mt-2 w-full">
|
<div className="flex items-start flex-col gap-2 mt-2 w-full">
|
||||||
<div>{transI18n("filter")}</div>
|
<div>{transI18n("filter")}</div>
|
||||||
<div className="flex flex-wrap justify-start gap-10 mt-2 w-full">
|
<div className="flex flex-wrap justify-start gap-5 md:gap-10 mt-2 w-full">
|
||||||
{/* Path */}
|
{/* Path */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-wrap gap-2 justify-start items-center">
|
<div className="flex flex-wrap gap-2 justify-start items-center">
|
||||||
|
|||||||
@@ -160,7 +160,12 @@ export default function AsBar() {
|
|||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={as_config.event_id.toString()}
|
selectedCustomSet={as_config.event_id.toString()}
|
||||||
placeholder={transI18n("selectASEvent")}
|
placeholder={transI18n("selectASEvent")}
|
||||||
setSelectedCustomSet={(id) => setAsConfig({ ...as_config, event_id: Number(id), challenge_id: 0, buff_id: 0 })}
|
setSelectedCustomSet={(id) => setAsConfig({
|
||||||
|
...as_config,
|
||||||
|
event_id: Number(id),
|
||||||
|
challenge_id: mapASInfo[Number(id)]?.Level.slice(-1)[0]?.Id || 0,
|
||||||
|
buff_id: 0
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
|
|||||||
@@ -127,7 +127,11 @@ export default function MocBar() {
|
|||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={moc_config.event_id.toString()}
|
selectedCustomSet={moc_config.event_id.toString()}
|
||||||
placeholder={transI18n("selectMOCEvent")}
|
placeholder={transI18n("selectMOCEvent")}
|
||||||
setSelectedCustomSet={(id) => setMocConfig({ ...moc_config, event_id: Number(id), challenge_id: 0 })}
|
setSelectedCustomSet={(id) => setMocConfig({
|
||||||
|
...moc_config,
|
||||||
|
event_id: Number(id),
|
||||||
|
challenge_id: mapMOCInfo[Number(id)]?.slice(-1)[0]?.Id || 0,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
@@ -140,7 +144,10 @@ export default function MocBar() {
|
|||||||
<select
|
<select
|
||||||
value={moc_config.challenge_id}
|
value={moc_config.challenge_id}
|
||||||
className="select select-success"
|
className="select select-success"
|
||||||
onChange={(e) => setMocConfig({ ...moc_config, challenge_id: Number(e.target.value) })}
|
onChange={(e) => setMocConfig({
|
||||||
|
...moc_config,
|
||||||
|
challenge_id: Number(e.target.value)
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>Select a Floor</option>
|
<option value={0} disabled={true}>Select a Floor</option>
|
||||||
{mapMOCInfo[moc_config.event_id.toString()]?.map((moc) => (
|
{mapMOCInfo[moc_config.event_id.toString()]?.map((moc) => (
|
||||||
|
|||||||
@@ -134,7 +134,11 @@ export default function PfBar() {
|
|||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={pf_config.event_id.toString()}
|
selectedCustomSet={pf_config.event_id.toString()}
|
||||||
placeholder={transI18n("selectPFEvent")}
|
placeholder={transI18n("selectPFEvent")}
|
||||||
setSelectedCustomSet={(id) => setPfConfig({ ...pf_config, event_id: Number(id), challenge_id: 0, buff_id: 0 })}
|
setSelectedCustomSet={(id) => setPfConfig({
|
||||||
|
...pf_config,
|
||||||
|
event_id: Number(id),
|
||||||
|
challenge_id: mapPFInfo[Number(id)]?.Level.slice(-1)[0]?.Id || 0,
|
||||||
|
buff_id: 0 })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
|
|||||||
504
src/components/quickView/index.tsx
Normal file
504
src/components/quickView/index.tsx
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
"use client"
|
||||||
|
import NextImage from "next/image"
|
||||||
|
import useLightconeStore from "@/stores/lightconeStore";
|
||||||
|
import useAffixStore from "@/stores/affixStore";
|
||||||
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
|
import useRelicStore from "@/stores/relicStore";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
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";
|
||||||
|
export default function QuickView() {
|
||||||
|
const { avatarSelected, mapAvatarInfo } = useAvatarStore()
|
||||||
|
const { mapLightconeInfo } = useLightconeStore()
|
||||||
|
const { mapMainAffix, mapSubAffix } = useAffixStore()
|
||||||
|
const { avatars } = useUserDataStore()
|
||||||
|
const transI18n = useTranslations("DataPage")
|
||||||
|
const { mapRelicInfo } = useRelicStore()
|
||||||
|
|
||||||
|
const avatarInfo = useMemo(() => {
|
||||||
|
if (!avatarSelected) return
|
||||||
|
return mapAvatarInfo[avatarSelected.id]
|
||||||
|
}, [avatarSelected, mapAvatarInfo])
|
||||||
|
|
||||||
|
const avatarSkillTree = useMemo(() => {
|
||||||
|
if (!avatarSelected || !avatars[avatarSelected.id]) return {}
|
||||||
|
if (avatars[avatarSelected.id].enhanced) {
|
||||||
|
return avatarInfo?.Enhanced[avatars[avatarSelected.id].enhanced.toString()].SkillTrees || {}
|
||||||
|
}
|
||||||
|
return avatarInfo?.SkillTrees || {}
|
||||||
|
}, [avatarSelected, avatarInfo, avatars])
|
||||||
|
|
||||||
|
const avatarData = useMemo(() => {
|
||||||
|
if (!avatarSelected) return
|
||||||
|
return avatars[avatarSelected.id]
|
||||||
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
|
const avatarProfile = useMemo(() => {
|
||||||
|
if (!avatarSelected || !avatarData) return
|
||||||
|
return avatarData?.profileList?.[avatarData?.profileSelect]
|
||||||
|
}, [avatarSelected, avatarData])
|
||||||
|
|
||||||
|
const relicEffects = useMemo(() => {
|
||||||
|
const avatar = avatars[avatarSelected?.id || ""];
|
||||||
|
const relicCount: { [key: string]: number } = {};
|
||||||
|
if (avatar) {
|
||||||
|
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
||||||
|
if (relicCount[relic.relic_set_id]) {
|
||||||
|
relicCount[relic.relic_set_id]++;
|
||||||
|
} else {
|
||||||
|
relicCount[relic.relic_set_id] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const listEffects: { key: string, count: number }[] = [];
|
||||||
|
Object.entries(relicCount).forEach(([key, value]) => {
|
||||||
|
if (value >= 2) {
|
||||||
|
listEffects.push({ key: key, count: value });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return listEffects;
|
||||||
|
}, [avatars, avatarSelected]);
|
||||||
|
|
||||||
|
const relicStats = useMemo(() => {
|
||||||
|
if (!avatarSelected || !avatarProfile?.relics || !mapMainAffix || !mapSubAffix) return
|
||||||
|
|
||||||
|
return Object.entries(avatarProfile?.relics).map(([key, value]) => {
|
||||||
|
const mainAffixMap = mapMainAffix["5" + key]
|
||||||
|
const subAffixMap = mapSubAffix["5"]
|
||||||
|
if (!mainAffixMap || !subAffixMap) return
|
||||||
|
return {
|
||||||
|
img: `https://api.hakush.in/hsr/UI/relicfigures/IconRelic_${value.relic_set_id}_${key}.webp`,
|
||||||
|
mainAffix: {
|
||||||
|
property: mainAffixMap?.[value?.main_affix_id]?.property,
|
||||||
|
level: value?.level,
|
||||||
|
valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level),
|
||||||
|
detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.property]
|
||||||
|
},
|
||||||
|
subAffix: value?.sub_affixes?.map((subValue) => {
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [avatarSelected, avatarProfile, mapMainAffix, mapSubAffix])
|
||||||
|
|
||||||
|
const characterStats = useMemo(() => {
|
||||||
|
if (!avatarSelected || !avatarData) return
|
||||||
|
const charPromotion = calcPromotion(avatarData.level)
|
||||||
|
|
||||||
|
const statsData: Record<string, {
|
||||||
|
value: number,
|
||||||
|
base: number,
|
||||||
|
name: string,
|
||||||
|
icon: string,
|
||||||
|
unit: string,
|
||||||
|
round: number
|
||||||
|
}> = {
|
||||||
|
HP: {
|
||||||
|
value: calcBaseStatRaw(
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPBase,
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPAdd,
|
||||||
|
avatarData.level
|
||||||
|
),
|
||||||
|
base: calcBaseStatRaw(
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPBase,
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPAdd,
|
||||||
|
avatarData.level
|
||||||
|
),
|
||||||
|
name: "HP",
|
||||||
|
icon: "/icon/hp.webp",
|
||||||
|
unit: "",
|
||||||
|
round: 0
|
||||||
|
},
|
||||||
|
ATK: {
|
||||||
|
value: calcBaseStatRaw(
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackBase,
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackAdd,
|
||||||
|
avatarData.level
|
||||||
|
),
|
||||||
|
base: calcBaseStatRaw(
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackBase,
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackAdd,
|
||||||
|
avatarData.level
|
||||||
|
),
|
||||||
|
name: "ATK",
|
||||||
|
icon: "/icon/attack.webp",
|
||||||
|
unit: "",
|
||||||
|
round: 0
|
||||||
|
},
|
||||||
|
DEF: {
|
||||||
|
value: calcBaseStatRaw(
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceBase,
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceAdd,
|
||||||
|
avatarData.level
|
||||||
|
),
|
||||||
|
base: calcBaseStatRaw(
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceBase,
|
||||||
|
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceAdd,
|
||||||
|
avatarData.level
|
||||||
|
),
|
||||||
|
name: "DEF",
|
||||||
|
icon: "/icon/defence.webp",
|
||||||
|
unit: "",
|
||||||
|
round: 0
|
||||||
|
},
|
||||||
|
SPD: {
|
||||||
|
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.SpeedBase || 0,
|
||||||
|
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.SpeedBase || 0,
|
||||||
|
name: "SPD",
|
||||||
|
icon: "/icon/speed.webp",
|
||||||
|
unit: "",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
CRITRate: {
|
||||||
|
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalChance || 0,
|
||||||
|
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalChance || 0,
|
||||||
|
name: "CRIT Rate",
|
||||||
|
icon: "/icon/crit-rate.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
CRITDmg: {
|
||||||
|
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalDamage || 0,
|
||||||
|
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalDamage || 0,
|
||||||
|
name: "CRIT DMG",
|
||||||
|
icon: "/icon/crit-damage.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
BreakEffect: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Break Effect",
|
||||||
|
icon: "/icon/break-effect.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
EffectRES: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Effect RES",
|
||||||
|
icon: "/icon/effect-res.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
EnergyRate: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Energy Rate",
|
||||||
|
icon: "/icon/energy-rate.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
EffectHitRate: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Effect Hit Rate",
|
||||||
|
icon: "/icon/effect-hit-rate.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
HealBoost: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Healing Boost",
|
||||||
|
icon: "/icon/healing-boost.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
PhysicalAdd: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Physical Boost",
|
||||||
|
icon: "/icon/physical-add.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
FireAdd: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Fire Boost",
|
||||||
|
icon: "/icon/fire-add.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
IceAdd: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Ice Boost",
|
||||||
|
icon: "/icon/ice-add.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
ThunderAdd: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Thunder Boost",
|
||||||
|
icon: "/icon/thunder-add.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
WindAdd: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Wind Boost",
|
||||||
|
icon: "/icon/wind-add.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
QuantumAdd: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Quantum Boost",
|
||||||
|
icon: "/icon/quantum-add.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
ImaginaryAdd: {
|
||||||
|
value: 0,
|
||||||
|
base: 0,
|
||||||
|
name: "Imaginary Boost",
|
||||||
|
icon: "/icon/imaginary-add.webp",
|
||||||
|
unit: "%",
|
||||||
|
round: 1
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatarProfile?.lightcone && mapLightconeInfo[avatarProfile?.lightcone?.item_id]) {
|
||||||
|
const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level)
|
||||||
|
statsData.HP.value += calcBaseStatRaw(
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
||||||
|
avatarProfile?.lightcone?.level
|
||||||
|
)
|
||||||
|
statsData.HP.base += calcBaseStatRaw(
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
||||||
|
avatarProfile?.lightcone?.level
|
||||||
|
)
|
||||||
|
statsData.ATK.value += calcBaseStatRaw(
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
||||||
|
avatarProfile?.lightcone?.level
|
||||||
|
)
|
||||||
|
statsData.ATK.base += calcBaseStatRaw(
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
||||||
|
avatarProfile?.lightcone?.level
|
||||||
|
)
|
||||||
|
statsData.DEF.value += calcBaseStatRaw(
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
||||||
|
avatarProfile?.lightcone?.level
|
||||||
|
)
|
||||||
|
statsData.DEF.base += calcBaseStatRaw(
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
||||||
|
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
||||||
|
avatarProfile?.lightcone?.level
|
||||||
|
)
|
||||||
|
|
||||||
|
const bonusData = mapLightconeInfo[avatarProfile?.lightcone?.item_id].Bonus?.[avatarProfile?.lightcone.rank - 1]
|
||||||
|
if (bonusData && bonusData.length > 0) {
|
||||||
|
const bonusSpd = bonusData.filter((bonus) => bonus.type === "BaseSpeed")
|
||||||
|
const bonusOther = bonusData.filter((bonus) => bonus.type !== "BaseSpeed")
|
||||||
|
bonusSpd.forEach((bonus) => {
|
||||||
|
statsData.SPD.value += bonus.value
|
||||||
|
statsData.SPD.base += bonus.value
|
||||||
|
})
|
||||||
|
bonusOther.forEach((bonus) => {
|
||||||
|
const statsBase = mappingStats?.[bonus.type]?.baseStat
|
||||||
|
if (statsBase && statsData[statsBase]) {
|
||||||
|
statsData[statsBase].value += calcBonusStatRaw(bonus.type, statsData[statsBase].base, bonus.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (avatarSkillTree) {
|
||||||
|
Object.values(avatarSkillTree).forEach((value) => {
|
||||||
|
if (value?.["1"]
|
||||||
|
&& value?.["1"]?.PointID
|
||||||
|
&& typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number"
|
||||||
|
&& avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0
|
||||||
|
&& value?.["1"]?.StatusAddList
|
||||||
|
&& value?.["1"].StatusAddList.length > 0) {
|
||||||
|
value?.["1"]?.StatusAddList.forEach((status) => {
|
||||||
|
const statsBase = mappingStats?.[status?.PropertyType]?.baseStat
|
||||||
|
if (statsBase && statsData[statsBase]) {
|
||||||
|
statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (avatarProfile?.relics && mapMainAffix && mapSubAffix) {
|
||||||
|
Object.entries(avatarProfile?.relics).forEach(([key, value]) => {
|
||||||
|
const mainAffixMap = mapMainAffix["5" + key]
|
||||||
|
const subAffixMap = mapSubAffix["5"]
|
||||||
|
if (!mainAffixMap || !subAffixMap) return
|
||||||
|
const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.property]?.baseStat
|
||||||
|
if (mainStats && statsData[mainStats]) {
|
||||||
|
statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base)
|
||||||
|
}
|
||||||
|
value?.sub_affixes.forEach((subValue) => {
|
||||||
|
const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.property]?.baseStat
|
||||||
|
if (subStats && statsData[subStats]) {
|
||||||
|
statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relicEffects && relicEffects.length > 0) {
|
||||||
|
relicEffects.forEach((relic) => {
|
||||||
|
const dataBonus = mapRelicInfo?.[relic.key]?.Bonus
|
||||||
|
if (!dataBonus || Object.keys(dataBonus).length === 0) return
|
||||||
|
Object.entries(dataBonus || {}).forEach(([key, value]) => {
|
||||||
|
if (relic.count < Number(key)) return
|
||||||
|
value.forEach((bonus) => {
|
||||||
|
const statsBase = mappingStats?.[bonus.type]?.baseStat
|
||||||
|
if (statsBase && statsData[statsBase]) {
|
||||||
|
statsData[statsBase].value += calcBonusStatRaw(bonus.type, statsData[statsBase].base, bonus.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return statsData
|
||||||
|
}, [
|
||||||
|
avatarSelected,
|
||||||
|
avatarData,
|
||||||
|
mapAvatarInfo,
|
||||||
|
avatarProfile?.lightcone,
|
||||||
|
avatarProfile?.relics,
|
||||||
|
mapLightconeInfo,
|
||||||
|
mapMainAffix,
|
||||||
|
mapSubAffix,
|
||||||
|
relicEffects,
|
||||||
|
mapRelicInfo,
|
||||||
|
avatarSkillTree
|
||||||
|
])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
<div className="col-span-1 md:col-span-2 flex flex-col justify-between py-3 z-10">
|
||||||
|
<div className="flex w-full flex-col justify-between gap-y-0.5 text-base h-[500px]">
|
||||||
|
{Object.entries(characterStats || {})?.map(([key, stat], index) => {
|
||||||
|
if (!stat || (key.includes("Add") && stat.value === 0)) return null
|
||||||
|
return (
|
||||||
|
<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-2" />
|
||||||
|
<span className="font-bold">{stat.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="ml-3 mr-3 flex-grow border rounded opacity-50" />
|
||||||
|
<div className="flex cursor-default flex-col text-right font-bold">{
|
||||||
|
stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0
|
||||||
|
}{stat.unit}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-center gap-1 w-full my-2">
|
||||||
|
{relicEffects.map((setEffect, index) => {
|
||||||
|
const relicInfo = mapRelicInfo[setEffect.key];
|
||||||
|
if (!relicInfo) return null;
|
||||||
|
return (
|
||||||
|
<div key={index} className="flex w-full flex-row justify-between text-left">
|
||||||
|
<div
|
||||||
|
className="font-bold truncate max-w-full mr-1"
|
||||||
|
style={{
|
||||||
|
fontSize: 'clamp(0.5rem, 2vw, 1rem)'
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: replaceByParam(
|
||||||
|
relicInfo.Name,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<span className="black-blur bg-black/50 flex w-5 justify-center rounded px-1.5 py-0.5">{setEffect.count}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="z-10">
|
||||||
|
<div className="grid grid-cols-1 gap-2 justify-between py-3 text-lg">
|
||||||
|
|
||||||
|
{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
|
||||||
|
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)
|
||||||
|
`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✦✦✦✦✦
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import useModelStore from '@/stores/modelStore';
|
|||||||
import { replaceByParam } from '@/helper';
|
import { replaceByParam } from '@/helper';
|
||||||
import useRelicMakerStore from '@/stores/relicMakerStore';
|
import useRelicMakerStore from '@/stores/relicMakerStore';
|
||||||
import useAffixStore from '@/stores/affixStore';
|
import useAffixStore from '@/stores/affixStore';
|
||||||
|
import QuickView from "../quickView";
|
||||||
|
|
||||||
export default function RelicsInfo() {
|
export default function RelicsInfo() {
|
||||||
const { avatars, setAvatars } = useUserDataStore()
|
const { avatars, setAvatars } = useUserDataStore()
|
||||||
@@ -27,7 +28,12 @@ export default function RelicsInfo() {
|
|||||||
listSelectedSubStats,
|
listSelectedSubStats,
|
||||||
} = useRelicMakerStore()
|
} = useRelicMakerStore()
|
||||||
const { mapSubAffix } = useAffixStore()
|
const { mapSubAffix } = useAffixStore()
|
||||||
const { isOpenRelic, setIsOpenRelic } = useModelStore()
|
const {
|
||||||
|
isOpenRelic,
|
||||||
|
setIsOpenRelic,
|
||||||
|
isOpenQuickView,
|
||||||
|
setIsOpenQuickView
|
||||||
|
} = useModelStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const { mapRelicInfo } = useRelicStore()
|
const { mapRelicInfo } = useRelicStore()
|
||||||
@@ -35,14 +41,12 @@ export default function RelicsInfo() {
|
|||||||
const handleShow = (modalId: string) => {
|
const handleShow = (modalId: string) => {
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
if (modal) {
|
if (modal) {
|
||||||
setIsOpenRelic(true);
|
|
||||||
modal.showModal();
|
modal.showModal();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close modal handler
|
// Close modal handler
|
||||||
const handleCloseModal = (modalId: string) => {
|
const handleCloseModal = (modalId: string) => {
|
||||||
setIsOpenRelic(false);
|
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.close();
|
modal.close();
|
||||||
@@ -55,11 +59,18 @@ export default function RelicsInfo() {
|
|||||||
handleCloseModal("action_detail_modal");
|
handleCloseModal("action_detail_modal");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if (!isOpenQuickView) {
|
||||||
|
handleCloseModal("quick_view_modal");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
const handleEscKey = (event: KeyboardEvent) => {
|
const handleEscKey = (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Escape' && isOpenRelic) {
|
if (event.key === 'Escape' && isOpenRelic) {
|
||||||
handleCloseModal("action_detail_modal");
|
handleCloseModal("action_detail_modal");
|
||||||
}
|
}
|
||||||
|
if (event.key === 'Escape' && isOpenQuickView) {
|
||||||
|
handleCloseModal("quick_view_modal");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('keydown', handleEscKey);
|
window.addEventListener('keydown', handleEscKey);
|
||||||
@@ -114,7 +125,7 @@ export default function RelicsInfo() {
|
|||||||
|
|
||||||
setListSelectedSubStats(newSubAffixes)
|
setListSelectedSubStats(newSubAffixes)
|
||||||
}
|
}
|
||||||
|
setIsOpenRelic(true)
|
||||||
handleShow("action_detail_modal")
|
handleShow("action_detail_modal")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +164,7 @@ export default function RelicsInfo() {
|
|||||||
{transI18n("relics")}
|
{transI18n("relics")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-2xl mx-auto">
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-6 max-w-2xl mx-auto">
|
||||||
{["1", "2", "3", "4", "5", "6"].map((item, index) => (
|
{["1", "2", "3", "4", "5", "6"].map((item, index) => (
|
||||||
<div key={index} className="relative group">
|
<div key={index} className="relative group">
|
||||||
<div
|
<div
|
||||||
@@ -229,6 +240,15 @@ export default function RelicsInfo() {
|
|||||||
{transI18n("deleteRelic")}
|
{transI18n("deleteRelic")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpenQuickView(true)
|
||||||
|
handleShow("quick_view_modal")
|
||||||
|
}}
|
||||||
|
className="btn btn-info w-full mt-2"
|
||||||
|
>
|
||||||
|
{transI18n("quickView")}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -302,7 +322,10 @@ export default function RelicsInfo() {
|
|||||||
whileHover={{ scale: 1.1, rotate: 90 }}
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
className="btn btn-circle btn-md absolute right-2 top-2 bg-red-600 hover:bg-red-700 text-white border-none"
|
className="btn btn-circle btn-md absolute right-2 top-2 bg-red-600 hover:bg-red-700 text-white border-none"
|
||||||
onClick={() => handleCloseModal("action_detail_modal")}
|
onClick={() => {
|
||||||
|
setIsOpenRelic(false)
|
||||||
|
handleCloseModal("action_detail_modal")
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
</motion.button>
|
</motion.button>
|
||||||
@@ -311,6 +334,30 @@ export default function RelicsInfo() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="quick_view_modal" className="modal lg:backdrop-blur-sm z-10">
|
||||||
|
<div className="modal-box w-11/12 max-w-7xl bg-base-100 text-base-content border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
|
<div className="sticky top-0 z-10">
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="btn btn-circle btn-md absolute right-2 top-2 bg-red-600 hover:bg-red-700 text-white border-none"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpenQuickView(false)
|
||||||
|
handleCloseModal("quick_view_modal")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</motion.button>
|
||||||
|
</div>
|
||||||
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
||||||
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
||||||
|
{transI18n("quickView").toUpperCase()}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<QuickView />
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
import React, { useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
||||||
import useListAvatarStore from "@/stores/avatarStore";
|
import useAvatarStore from "@/stores/avatarStore";
|
||||||
import { FastAverageColor, FastAverageColorResult } from 'fast-average-color';
|
import { FastAverageColor, FastAverageColorResult } from 'fast-average-color';
|
||||||
import NextImage from 'next/image';
|
import NextImage from 'next/image';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
@@ -18,7 +18,7 @@ import useRelicStore from '@/stores/relicStore';
|
|||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
export default function ShowCaseInfo() {
|
export default function ShowCaseInfo() {
|
||||||
const { avatarSelected, mapAvatarInfo } = useListAvatarStore()
|
const { avatarSelected, mapAvatarInfo } = useAvatarStore()
|
||||||
const { mapLightconeInfo } = useLightconeStore()
|
const { mapLightconeInfo } = useLightconeStore()
|
||||||
const { mapMainAffix, mapSubAffix } = useAffixStore()
|
const { mapMainAffix, mapSubAffix } = useAffixStore()
|
||||||
const { avatars } = useUserDataStore()
|
const { avatars } = useUserDataStore()
|
||||||
@@ -469,6 +469,7 @@ export default function ShowCaseInfo() {
|
|||||||
mapRelicInfo,
|
mapRelicInfo,
|
||||||
avatarSkillTree
|
avatarSkillTree
|
||||||
])
|
])
|
||||||
|
|
||||||
const applyBrightness = useCallback((hex: string, brightness: number): string => {
|
const applyBrightness = useCallback((hex: string, brightness: number): string => {
|
||||||
const r = Math.round(parseInt(hex.slice(1, 3), 16) * brightness);
|
const r = Math.round(parseInt(hex.slice(1, 3), 16) * brightness);
|
||||||
const g = Math.round(parseInt(hex.slice(3, 5), 16) * brightness);
|
const g = Math.round(parseInt(hex.slice(3, 5), 16) * brightness);
|
||||||
|
|||||||
@@ -179,9 +179,9 @@ export default function SkillsInfo() {
|
|||||||
cursor-pointer transition-all duration-200 ease-in-out
|
cursor-pointer transition-all duration-200 ease-in-out
|
||||||
shadow-[0_0_5px_white] flex justify-center items-center
|
shadow-[0_0_5px_white] flex justify-center items-center
|
||||||
hover:scale-110
|
hover:scale-110
|
||||||
${btn.size === "small" ? "w-[2vw] h-[2vw] bg-white" : ""}
|
${btn.size === "small" ? "w-[6vw] h-[6vw] md:w-[2vw] md:h-[2vw] bg-white" : ""}
|
||||||
${btn.size === "medium" ? "w-[3vw] h-[3vw] bg-white" : ""}
|
${btn.size === "medium" ? "w-[8vw] h-[8vw] md:w-[3vw] md:h-[3vw] bg-white" : ""}
|
||||||
${btn.size === "big" ? "w-[3.5vw] h-[3.5vw] bg-black" : ""}
|
${btn.size === "big" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
||||||
${skillSelected === btn.id ? "border-4 border-primary" : ""}
|
${skillSelected === btn.id ? "border-4 border-primary" : ""}
|
||||||
${avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] === 0 ? "opacity-50 cursor-not-allowed" : ""}
|
${avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] === 0 ? "opacity-50 cursor-not-allowed" : ""}
|
||||||
`}
|
`}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ interface ModelState {
|
|||||||
isOpenCopy: boolean;
|
isOpenCopy: boolean;
|
||||||
isOpenMonster: boolean;
|
isOpenMonster: boolean;
|
||||||
isOpenConnect: boolean;
|
isOpenConnect: boolean;
|
||||||
|
isOpenAvatars: boolean;
|
||||||
|
isOpenQuickView: boolean;
|
||||||
|
setIsOpenQuickView: (newIsOpenQuickView: boolean) => void;
|
||||||
|
setIsOpenAvatars: (newIsOpenAvatars: boolean) => void;
|
||||||
setIsOpenConnect: (newIsOpenConnect: boolean) => void;
|
setIsOpenConnect: (newIsOpenConnect: boolean) => void;
|
||||||
setIsOpenMonster: (newIsOpenMonster: boolean) => void;
|
setIsOpenMonster: (newIsOpenMonster: boolean) => void;
|
||||||
setIsOpenLightcone: (newIsOpenLightcone: boolean) => void;
|
setIsOpenLightcone: (newIsOpenLightcone: boolean) => void;
|
||||||
@@ -29,6 +33,10 @@ const useModelStore = create<ModelState>((set) => ({
|
|||||||
isOpenCopy: false,
|
isOpenCopy: false,
|
||||||
isOpenMonster: false,
|
isOpenMonster: false,
|
||||||
isOpenConnect: false,
|
isOpenConnect: false,
|
||||||
|
isOpenAvatars: false,
|
||||||
|
isOpenQuickView: false,
|
||||||
|
setIsOpenQuickView: (newIsOpenQuickView: boolean) => set({ isOpenQuickView: newIsOpenQuickView }),
|
||||||
|
setIsOpenAvatars: (newIsOpenAvatars: boolean) => set({ isOpenAvatars: newIsOpenAvatars }),
|
||||||
setIsOpenConnect: (newIsOpenConnect: boolean) => set({ isOpenConnect: newIsOpenConnect }),
|
setIsOpenConnect: (newIsOpenConnect: boolean) => set({ isOpenConnect: newIsOpenConnect }),
|
||||||
setIsOpenMonster: (newIsOpenMonster: boolean) => set({ isOpenMonster: newIsOpenMonster }),
|
setIsOpenMonster: (newIsOpenMonster: boolean) => set({ isOpenMonster: newIsOpenMonster }),
|
||||||
setIsOpenLightcone: (newIsOpenLightcone: boolean) => set({ isOpenLightcone: newIsOpenLightcone }),
|
setIsOpenLightcone: (newIsOpenLightcone: boolean) => set({ isOpenLightcone: newIsOpenLightcone }),
|
||||||
|
|||||||
Reference in New Issue
Block a user