Skip to content

Commit

Permalink
feat: 🎸 update move page
Browse files Browse the repository at this point in the history
  • Loading branch information
HuskyHsu committed Nov 6, 2023
1 parent d085486 commit 4973248
Show file tree
Hide file tree
Showing 13 changed files with 350 additions and 15 deletions.
2 changes: 1 addition & 1 deletion public/data/base_list_201.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/data/move/心靈互換.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"levelingUps": [{"types": ["\u6c34"], "link": "490", "nameZh": "\u746a\u7d0d\u970f", "altForm": null, "level": 1}], "egg": [], "pid": 391, "nameZh": "\u5fc3\u9748\u4e92\u63db", "nameJp": "\u30cf\u30fc\u30c8\u30b9\u30ef\u30c3\u30d7", "nameEn": "Heart Swap", "type": "\u8d85\u80fd\u529b", "category": "\u8b8a\u5316", "power": -999, "accuracy": -999, "PP": -999, "description": "\u7528\u8d85\u80fd\u529b\u5c07\u81ea\u5df1\u548c\u5c0d\u624b\u7684\u80fd\u529b\u8b8a\u5316\u4e92\u76f8\u4ea4\u63db\u3002"}
{"levelingUps": [{"types": ["\u6c34"], "link": "490", "nameZh": "\u746a\u7d0d\u970f", "altForm": null, "level": 1}], "egg": [], "pid": 391, "nameZh": "\u5fc3\u9748\u4e92\u63db", "nameJp": "\u30cf\u30fc\u30c8\u30b9\u30ef\u30c3\u30d7", "nameEn": "Heart Swap", "type": "\u8d85\u80fd\u529b", "category": "\u8b8a\u5316", "power": 0, "accuracy": 0, "PP": 10, "description": "\u7528\u8d85\u80fd\u529b\u5c07\u81ea\u5df1\u548c\u5c0d\u624b\u7684\u80fd\u529b\u8b8a\u5316\u4e92\u76f8\u4ea4\u63db\u3002"}
2 changes: 1 addition & 1 deletion public/data/move/破滅之願.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"levelingUps": [{"types": ["\u92fc", "\u8d85\u80fd\u529b"], "link": "385", "nameZh": "\u57fa\u62c9\u7948", "altForm": null, "level": 98}], "egg": [], "pid": 353, "nameZh": "\u7834\u6ec5\u4e4b\u9858", "nameJp": "\u306f\u3081\u3064\u306e\u306d\u304c\u3044", "nameEn": "Doom Desire", "type": "\u92fc", "category": "\u7279\u6b8a", "power": -999, "accuracy": -999, "PP": -999, "description": "\u4f7f\u7528\u62db\u5f0f\u7684\uff12\u56de\u5408\u5f8c\uff0c\u5411\u5c0d\u624b\u767c\u5c04\u7121\u6578\u9053\u5149\u675f\u9032\u884c\u653b\u64ca\u3002"}
{"levelingUps": [{"types": ["\u92fc", "\u8d85\u80fd\u529b"], "link": "385", "nameZh": "\u57fa\u62c9\u7948", "altForm": null, "level": 98}], "egg": [], "pid": 353, "nameZh": "\u7834\u6ec5\u4e4b\u9858", "nameJp": "\u306f\u3081\u3064\u306e\u306d\u304c\u3044", "nameEn": "Doom Desire", "type": "\u92fc", "category": "\u7279\u6b8a", "power": 140, "accuracy": 100, "PP": 5, "description": "\u4f7f\u7528\u62db\u5f0f\u7684\uff12\u56de\u5408\u5f8c\uff0c\u5411\u5c0d\u624b\u767c\u5c04\u7121\u6578\u9053\u5149\u675f\u9032\u884c\u653b\u64ca\u3002"}
2 changes: 1 addition & 1 deletion public/data/move/螢火.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"levelingUps": [{"types": ["\u87f2"], "link": "313", "nameZh": "\u96fb\u87a2\u87f2", "altForm": null, "level": 22}, {"types": ["\u6c34"], "link": "490", "nameZh": "\u746a\u7d0d\u970f", "altForm": null, "level": 1}], "egg": [], "pid": 294, "nameZh": "\u87a2\u706b", "nameJp": "\u307b\u305f\u308b\u3073", "nameEn": "Tail Glow", "type": "\u87f2", "category": "\u8b8a\u5316", "power": -999, "accuracy": -999, "PP": -999, "description": "\u51dd\u8996\u9583\u720d\u7684\u5149\u8292\uff0c\u96c6\u4e2d\u7cbe\u795e\uff0c\u6975\u5927\u5e45\u63d0\u9ad8\u81ea\u5df1\u7684\u7279\u653b\u3002"}
{"levelingUps": [{"types": ["\u87f2"], "link": "313", "nameZh": "\u96fb\u87a2\u87f2", "altForm": null, "level": 22}, {"types": ["\u6c34"], "link": "490", "nameZh": "\u746a\u7d0d\u970f", "altForm": null, "level": 1}], "egg": [], "pid": 294, "nameZh": "\u87a2\u706b", "nameJp": "\u307b\u305f\u308b\u3073", "nameEn": "Tail Glow", "type": "\u87f2", "category": "\u8b8a\u5316", "power": 0, "accuracy": 0, "PP": 20, "description": "\u51dd\u8996\u9583\u720d\u7684\u5149\u8292\uff0c\u96c6\u4e2d\u7cbe\u795e\uff0c\u6975\u5927\u5e45\u63d0\u9ad8\u81ea\u5df1\u7684\u7279\u653b\u3002"}
1 change: 0 additions & 1 deletion public/data/moveList.json

This file was deleted.

1 change: 1 addition & 0 deletions public/data/move_list_201.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/data/pm/313.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/data/pm/385.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/data/pm/490.json

Large diffs are not rendered by default.

330 changes: 325 additions & 5 deletions src/pages/MoveDex/MoveDex.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Hr } from '@/newComponents/common';
import clsx from 'clsx';

import { Buttons, Hr, SubTitleSlide } from '@/newComponents/common';
import { PokemonBadge } from '@/newComponents/game';
import { Icon } from '@/newComponents';
import { Accuracy, FullMove, LevelMap, Move, TYPE_MAP } from '@/types/Pokemon';
import { ValueKeys, api } from '@/utils';

import { Header } from './components';
import { ValueKeys } from '@/utils';
import { useMoveListInfo } from './api';

export type Filter = {
keyword: string;
Expand All @@ -10,17 +18,170 @@ export type Filter = {
page: number;
};

type colValue = {
row: Move;
fn?: () => void;
checked?: boolean;
};

const columns = [
{
header: '挑選',
value: ({ fn = () => {}, checked = false }: colValue) => (
<input
type="checkbox"
checked={checked}
onChange={() => {}}
onClick={(e) => {
e.stopPropagation();
fn();
}}
className="h-5 w-5 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-2 focus:ring-blue-500"
/>
),
meta: 'w-[10%]',
},
{
header: '招式名稱',
value: ({ row }: colValue) => (
<a
href={`https://wiki.52poke.com/zh-hant/${row.nameZh}(招式)`}
target="_blank"
rel="noreferrer"
className="text-blue-800 underline"
onClick={(e) => e.stopPropagation()}
>
{row.nameZh}
</a>
),
meta: 'w-[21%]',
},
{
header: '屬性',
value: ({ row }: colValue) => <Icon.Game.Type type={row.type} className="h-6 w-full" />,
meta: 'w-[15%]',
},
{
header: '分類',
value: ({ row }: colValue) => (
<Icon.Game.MoveCategory type={row.category} className="h-6 w-full" />
),
meta: 'w-[15%]',
},
{
header: '威力',
value: ({ row }: colValue) =>
row.power < 1 ? Accuracy[row.power.toString() as keyof typeof Accuracy] : row.power,
meta: 'w-[13%]',
},
{
header: '命中',
value: ({ row }: colValue) =>
row.accuracy < 1 ? Accuracy[row.accuracy.toString() as keyof typeof Accuracy] : row.accuracy,
meta: 'w-[13%]',
},
{
header: 'PP',
value: ({ row }: colValue) => row.PP,
meta: 'w-[13%]',
},
];

function MoveDetail({
move,
onlyEvolve,
setOnlyEvolve,
}: {
move: FullMove;
onlyEvolve: boolean;
setOnlyEvolve: React.Dispatch<React.SetStateAction<boolean>>;
}) {
return (
<div className="flex w-full flex-col text-gray-500">
<h6 className="flex justify-between py-2 text-lg font-bold">
<span>說明</span>
<div className="flex items-center">
<input
id={'showChild'}
type="checkbox"
checked={onlyEvolve}
className="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-800 focus:ring-1 focus:ring-blue-800"
onChange={(e) => setOnlyEvolve(e.target.checked)}
/>
<label htmlFor={'showChild'} className="ml-2 text-sm">
僅顯示進化型
</label>
</div>
</h6>
<p>{move.description}</p>
{move.levelingUps.length > 0 && (
<>
<hr className="my-3 h-px border-0 bg-gray-200" />
<h6 className="py-2 text-lg font-bold">升等 / 進化 / 回憶招式</h6>
<div className="flex flex-wrap gap-2">
{move.levelingUps
.filter((pm) => (onlyEvolve ? pm.child === undefined : true))
.map((pm) => {
let text = `Lv${pm.level}`;
if (pm.level < 1) {
text = LevelMap[pm.level];
}

return <PokemonBadge pm={pm} key={pm.link} text={text} />;
})}
</div>
</>
)}
{move.egg.length > 0 && (
<>
<hr className="my-3 h-px border-0 bg-gray-200" />
<h6 className="py-2 text-lg font-bold">遺傳招式(模仿香草)</h6>
<div className="flex flex-wrap gap-2">
{move.egg
.filter((pm) => (onlyEvolve ? pm.child === undefined : true))
.map((pm) => {
return <PokemonBadge pm={pm} key={pm.link} />;
})}
</div>
</>
)}
{move.TM && (
<>
<hr className="my-3 h-px border-0 bg-gray-200" />
<h6 className="py-2 text-lg font-bold">招式機</h6>
<ul className="text-gray-5000 max-w-md list-inside list-disc space-y-1 pb-2">
<li>編號:#{move.TM.pid.toString().padStart(3, '0')}</li>
<li>聯盟點數:{move.TM.leaguePoint}</li>
<li>材料:{move.TM.materials.map((pm) => `${pm.part}x${pm.count}`).join('; ')}</li>
</ul>
<div className="flex flex-wrap gap-2">
{move.TM?.pm
.filter((pm) => (onlyEvolve ? pm.child === undefined : true))
.map((pm) => {
return <PokemonBadge pm={pm} key={pm.link} />;
})}
</div>
</>
)}
</div>
);
}

function MoveDex() {
const [searchParams, setSearchParams] = useSearchParams();
const [expandedPanels, setExpandedPanels] = useState<Set<number>>(new Set());
const [pick, setPick] = useState<Set<number>>(new Set());
const [moveMap, setMoveMap] = useState<Record<number, FullMove>>({});
const [onlyEvolve, setOnlyEvolve] = useState<boolean>(true);

const filter: Filter = {
keyword: searchParams.get('keyword') || '',
type: searchParams.get('types') || '',
type: searchParams.get('type') || '',
category: searchParams.get('category') || '',
page: Number(searchParams.get('page') || 1),
};

console.log(filter.keyword);
const { data: allMoves } = useMoveListInfo();

const updateState = (key: ValueKeys<Filter, string>[keyof Filter]) => {
return (val: string) => {
Expand All @@ -43,11 +204,170 @@ function MoveDex() {
};
};

const typeUpdate = updateState('type');
const categoryUpdate = updateState('category');

const handleClick = async (panelKey: number, nameZh: string) => {
const updatedPanels = new Set(expandedPanels);
if (updatedPanels.has(panelKey)) {
updatedPanels.delete(panelKey);
} else {
updatedPanels.add(panelKey);

if (moveMap[panelKey] === undefined) {
const moveData = await api<FullMove>(`/data/move/${nameZh}.json`);
setMoveMap((prev) => {
prev[panelKey] = moveData;
return prev;
});
}
}

setExpandedPanels(updatedPanels);
};

return (
<div className="mb-4 flex flex-col gap-y-4">
<Header filter={filter} updateState={updateState} />
<Hr />
新的招式清單施工ing
<Hr />
<div className="-mx-4 flex flex-col gap-4 md:mx-0">
<div className="mx-4 flex flex-col gap-y-2 md:mx-0">
<SubTitleSlide title="屬性" />
<div className="flex w-full flex-wrap justify-items-center gap-x-4 gap-y-3 pb-2 pl-2">
{TYPE_MAP.map((type) => (
<button onClick={() => typeUpdate(type)} key={type}>
<Icon.Game.Type
type={type}
className={clsx(
'h-8 w-8',
filter.type !== '' && filter.type !== type && 'opacity-30'
)}
/>
</button>
))}
</div>
<SubTitleSlide title="分類" />
<Buttons
list={[
{ name: '物理', val: '物理' },
{ name: '特殊', val: '特殊' },
{ name: '變化', val: '變化' },
]}
currVal={filter.category}
updateState={(val) => categoryUpdate(val)}
/>
</div>
<ul className="text-sm">
<li
className={clsx(
'sticky top-0 flex bg-custom-gold/50',
'py-1 px-2 text-gray-100 md:-top-4 md:px-0'
)}
>
{columns.map((col) => (
<span
className={clsx('whitespace-nowrap py-1 text-center', col.meta)}
key={col.header}
>
{col.header}
</span>
))}
</li>
{allMoves
.filter((move) => {
let display = true;

if (filter.keyword !== '') {
display =
display &&
(move.nameZh.includes(filter.keyword) ||
move.description.includes(filter.keyword) ||
move.nameEn.includes(filter.keyword) ||
move.nameJp.includes(filter.keyword));
}

if (filter.type !== '') {
display = display && filter.type === move.type;
}

if (filter.category !== '') {
display = display && filter.category === move.category;
}

return display;
})
.sort((a, b) => {
if (pick.has(a.pid) && pick.has(b.pid)) {
return 0;
} else if (pick.has(a.pid)) {
return -1;
} else if (pick.has(b.pid)) {
return 1;
}
return 0;
})
.map((move) => {
const lilist = [
<li
className={clsx(
'flex cursor-pointer border-b-[1px] py-1 px-2 md:px-0',
'hover:bg-gray-50',
pick.has(move.pid) && 'bg-gray-100'
)}
key={move.pid}
onClick={() => {
handleClick(move.pid, move.nameZh);
}}
>
{columns.map((col) => (
<span
className={clsx(
'whitespace-nowrap py-1 text-center leading-6',
'flex justify-center',
col.meta
)}
key={col.header}
>
{col.value({
row: move,
checked: pick.has(move.pid),
fn: () => {
setPick((prev) => {
if (prev.has(move.pid)) {
prev.delete(move.pid);
} else {
prev.add(move.pid);
}
return new Set([...prev]);
});
},
})}
</span>
))}
</li>,
];

if (expandedPanels.has(move.pid)) {
lilist.push(
<li
className={clsx('flex border-b-[1px] p-4', 'bg-slate-50')}
key={`${move.pid}-d`}
>
<MoveDetail
move={moveMap[move.pid as keyof typeof moveMap]}
onlyEvolve={onlyEvolve}
setOnlyEvolve={setOnlyEvolve}
/>
</li>
);
}

return lilist;
})
.flat()}
</ul>
</div>
</div>
);
}
Expand Down
15 changes: 15 additions & 0 deletions src/pages/MoveDex/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import { api } from '@/utils';
import { Move } from '@/types/Pokemon';

export const useMoveListInfo = () => {
const { data, status, ...rest } = useQuery<Move[]>(['move'], () =>
api<Move[]>(`/data/move_list_201.json`)
);

return {
status,
data: data ?? [],
...rest,
};
};
Loading

0 comments on commit 4973248

Please sign in to comment.