diff --git a/src/app/tools/tankopedia/components/Results.tsx b/src/app/tools/tankopedia/components/Results.tsx new file mode 100644 index 000000000..f417195f3 --- /dev/null +++ b/src/app/tools/tankopedia/components/Results.tsx @@ -0,0 +1,149 @@ +import { Flex, Grid, Heading, Link, Separator, Text } from '@radix-ui/themes'; +import { range } from 'lodash'; +import { memo, use } from 'react'; +import { classIcons } from '../../../../components/ClassIcon'; +import { TANK_CLASSES } from '../../../../components/Tanks/components/Item/constants'; +import { asset } from '../../../../core/blitzkit/asset'; +import { gameDefinitions } from '../../../../core/blitzkit/gameDefinitions'; +import { + Tier, + tanksDefinitionsArray, +} from '../../../../core/blitzkit/tankDefinitions'; +import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants'; +import { tankIcon } from '../../../../core/blitzkit/tankIcon'; +import { TankopediaSearchPageFilters } from '../page'; +import { treeTypeOrder } from './TankSearch/constants'; + +interface ResultsProps { + filters: TankopediaSearchPageFilters; +} + +export const Results = memo(({ filters }) => { + const awaitedTanksDefinitionsArray = use(tanksDefinitionsArray); + const tanksFiltered = awaitedTanksDefinitionsArray.filter((tank) => { + return ( + (filters.tier === undefined || filters.tier === tank.tier) && + (filters.nation === undefined || filters.nation === tank.nation) && + (filters.class === undefined || filters.class === tank.class) && + (filters.type === undefined || filters.type === tank.treeType) && + (filters.testing === 'include' || + (filters.testing === 'only' && tank.testing) || + (filters.testing === 'exclude' && !tank.testing)) + ); + }); + const awaitedGameDefinitions = use(gameDefinitions); + + return ( + + {range(10, 0).map((tierUntyped) => { + const tier = tierUntyped as Tier; + const tierTanks = tanksFiltered + .filter((tank) => tank.tier === tier) + .sort( + (a, b) => + treeTypeOrder.indexOf(a.treeType) - + treeTypeOrder.indexOf(b.treeType), + ) + .sort( + (a, b) => + TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class), + ) + .sort( + (a, b) => + awaitedGameDefinitions.nations.indexOf(a.nation) - + awaitedGameDefinitions.nations.indexOf(b.nation), + ); + + if (tierTanks.length === 0) return null; + + return ( + + + + Tier {TIER_ROMAN_NUMERALS[tier]} + + + + + + {tierTanks.map((tank) => { + const Icon = classIcons[tank.class]; + + return ( + + {tank.name} + + + + + {tank.name} + + + + ); + })} + + + ); + })} + + ); +}); diff --git a/src/app/tools/tankopedia/page.tsx b/src/app/tools/tankopedia/page.tsx index 00b529612..9966f1d77 100644 --- a/src/app/tools/tankopedia/page.tsx +++ b/src/app/tools/tankopedia/page.tsx @@ -1,35 +1,45 @@ -import { - Button, - Flex, - Grid, - Heading, - IconButton, - Separator, - Text, -} from '@radix-ui/themes'; -import { range, times } from 'lodash'; -import { use } from 'react'; +'use client'; + +import { TrashIcon } from '@radix-ui/react-icons'; +import { Box, Button, Flex, IconButton, Text } from '@radix-ui/themes'; +import { produce } from 'immer'; +import { times } from 'lodash'; +import { use, useState } from 'react'; import { classIcons } from '../../../components/ClassIcon'; import { ExperimentIcon } from '../../../components/ExperimentIcon'; -import { Link } from '../../../components/Link'; import PageWrapper from '../../../components/PageWrapper'; import { ResearchedIcon } from '../../../components/ResearchedIcon'; import { ScienceIcon } from '../../../components/ScienceIcon'; import { ScienceOffIcon } from '../../../components/ScienceOffIcon'; +import { TankClass, TreeType } from '../../../components/Tanks'; import { TANK_CLASSES } from '../../../components/Tanks/components/Item/constants'; import { asset } from '../../../core/blitzkit/asset'; import { gameDefinitions } from '../../../core/blitzkit/gameDefinitions'; -import { - Tier, - tanksDefinitionsArray, -} from '../../../core/blitzkit/tankDefinitions'; +import { Tier } from '../../../core/blitzkit/tankDefinitions'; import { TIER_ROMAN_NUMERALS } from '../../../core/blitzkit/tankDefinitions/constants'; -import { tankIcon } from '../../../core/blitzkit/tankIcon'; -import { treeTypeOrder } from './components/TankSearch/constants'; +import { Results } from './components/Results'; + +export interface TankopediaSearchPageFilters { + tier?: Tier; + nation?: string; + class?: TankClass; + type?: TreeType; + testing: 'include' | 'exclude' | 'only'; +} + +const defaultTankopediaSearchPageFilters: TankopediaSearchPageFilters = { + testing: 'include', +}; export default function Page() { - const awaitedTanksDefinitionsArray = use(tanksDefinitionsArray); const awaitedGameDefinitions = use(gameDefinitions); + const [filters, setFilters] = useState( + defaultTankopediaSearchPageFilters, + ); + + function mutateFilters(recipe: (draft: TankopediaSearchPageFilters) => void) { + setFilters(produce(recipe)); + } return ( @@ -58,10 +68,6 @@ export default function Page() { initial: 'center', sm: 'start', }} - pt={{ - initial: '0', - sm: '8', - }} > {times(5, (index) => { const tier = (index + 1) as Tier; + const selected = filters.tier === tier; return ( - + {TIER_ROMAN_NUMERALS[tier]} + ); })} @@ -106,20 +115,23 @@ export default function Page() { > {times(5, (index) => { const tier = (index + 6) as Tier; + const selected = filters.tier === tier; return ( - + {TIER_ROMAN_NUMERALS[tier]} + ); })} @@ -143,20 +155,29 @@ export default function Page() { > {awaitedGameDefinitions.nations .slice(0, Math.ceil(awaitedGameDefinitions.nations.length / 2)) - .map((nation) => ( - - - - ))} + .map((nation) => { + const selected = filters.nation === nation; + + return ( + + mutateFilters((draft) => { + draft.nation = + draft.nation === nation ? undefined : nation; + }) + } + > + + + ); + })} {awaitedGameDefinitions.nations .slice(Math.ceil(awaitedGameDefinitions.nations.length / 2)) - .map((nation) => ( - - {/* TODO: trim icons */} - - - ))} + .map((nation) => { + const selected = filters.nation === nation; + + return ( + + mutateFilters((draft) => { + draft.nation = + draft.nation === nation ? undefined : nation; + }) + } + > + + + ); + })} @@ -196,14 +226,21 @@ export default function Page() { > {TANK_CLASSES.map((tankClass) => { const Icon = classIcons[tankClass]; + const selected = filters.class === tankClass; return ( + mutateFilters((draft) => { + draft.class = + draft.class === tankClass ? undefined : tankClass; + }) + } > @@ -221,20 +258,52 @@ export default function Page() { initial: 'column', }} > - + + mutateFilters((draft) => { + draft.type = + draft.type === 'researchable' ? undefined : 'researchable'; + }) + } + > - + + mutateFilters((draft) => { + draft.type = draft.type === 'premium' ? undefined : 'premium'; + }) + } + > - + + mutateFilters((draft) => { + draft.type = + draft.type === 'collector' ? undefined : 'collector'; + }) + } + > @@ -252,133 +321,74 @@ export default function Page() { initial: 'column', }} > - + + mutateFilters((draft) => { + draft.testing = 'include'; + }) + } + > - - - - + + mutateFilters((draft) => { + draft.testing = 'exclude'; + }) + } + > + + mutateFilters((draft) => { + draft.testing = 'only'; + }) + } + > + + + + + setFilters(defaultTankopediaSearchPageFilters)} + > + + + + + + - - {range(10, 0).map((tierUntyped) => { - const tier = tierUntyped as Tier; - const tierTanks = awaitedTanksDefinitionsArray - .filter((tank) => tank.tier === tier) - .sort( - (a, b) => - treeTypeOrder.indexOf(a.treeType) - - treeTypeOrder.indexOf(b.treeType), - ) - .sort( - (a, b) => - TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class), - ) - .sort( - (a, b) => - awaitedGameDefinitions.nations.indexOf(a.nation) - - awaitedGameDefinitions.nations.indexOf(b.nation), - ); - - return ( - - - - Tier {TIER_ROMAN_NUMERALS[tier]} - - - - - - {tierTanks.map((tank) => { - const Icon = classIcons[tank.class]; - - return ( - - {tank.name} - - - - - {tank.name} - - - - ); - })} - - - ); - })} - + );