From 06ce263668babf633a536676840eaf768cbdc5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Tue, 30 Dec 2025 19:53:06 -0600 Subject: [PATCH] feat: compact nations selector --- packages/i18n/strings/en.json | 2 + .../TankSearch/components/FilterControl.tsx | 719 +++--------------- 2 files changed, 98 insertions(+), 623 deletions(-) diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 8ccbe5487..07204b42a 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -759,11 +759,13 @@ "count_singular": "1 tank", "count_plural": "{count} tanks", "sorting_by": "Sorting by {name}", + "no_tanks_found": { "body": "No tanks found.", "search_again": "Try searching again.", "clear_filters": "Try clearing filters." }, + "sort": { "meta.none": "none", "survivability.health": "health", diff --git a/packages/website/src/components/TankSearch/components/FilterControl.tsx b/packages/website/src/components/TankSearch/components/FilterControl.tsx index 5739a3ee6..3f972ccd3 100644 --- a/packages/website/src/components/TankSearch/components/FilterControl.tsx +++ b/packages/website/src/components/TankSearch/components/FilterControl.tsx @@ -1,42 +1,28 @@ +import { asset, ShellType } from "@blitzkit/core"; import { - asset, - ShellType, - TANK_CLASSES, - TankType, - TIER_ROMAN_NUMERALS, -} from "@blitzkit/core"; -import { LockClosedIcon, LockOpen2Icon } from "@radix-ui/react-icons"; + LockClosedIcon, + LockOpen2Icon, + TrashIcon, +} from "@radix-ui/react-icons"; import { Box, + Button, + DropdownMenu, Flex, IconButton, Inset, - Link, Popover, Text, - Tooltip, - type ButtonProps, type FlexProps, } from "@radix-ui/themes"; -import { isEqual, times, chunk } from "lodash-es"; +import { isEqual } from "lodash-es"; import { awaitableGameDefinitions } from "../../../core/awaitables/gameDefinitions"; -import { awaitableConsumableDefinitions } from "../../../core/awaitables/consumableDefinitions"; -import { Var } from "../../../core/radix/var"; import { useLocale } from "../../../hooks/useLocale"; import { App } from "../../../stores/app"; import { TankFilters } from "../../../stores/tankFilters"; -import { TankSort } from "../../../stores/tankopediaSort"; -import { classIcons } from "../../ClassIcon"; -import { GunAutoloaderIcon } from "../../GunAutoloaderIcon"; -import { GunAutoreloaderIcon } from "../../GunAutoreloaderIcon"; -import { GunRegularIcon } from "../../GunRegularIcon"; import { MissingShellIcon } from "../../MissingShellIcon"; -import { ResearchedIcon } from "../../ResearchedIcon"; -import { ScienceIcon } from "../../ScienceIcon"; -import { ScienceOffIcon } from "../../ScienceOffIcon"; const gameDefinitions = await awaitableGameDefinitions; -const consumableDefinitions = await awaitableConsumableDefinitions; const shellTypeIcons: Record = { [ShellType.AP]: "ap", @@ -45,26 +31,7 @@ const shellTypeIcons: Record = { [ShellType.HEAT]: "hc", }; -// Get consumables -const regularConsumables = Object.values( - consumableDefinitions.consumables -).filter( - (consumable) => - !consumable.game_mode_exclusive && consumable.name.locales.en !== "" -); - -// Split consumables into chunks of 5 for columns -const consumableColumns = chunk(regularConsumables, 5); - -// Get game mode exclusive abilities -const gameModeAbilities = Object.values( - consumableDefinitions.consumables -).filter((consumable) => consumable.game_mode_exclusive); - -// Split abilities into chunks of 5 for columns -const abilityColumns = chunk(gameModeAbilities, 5); - -const size: ButtonProps["size"] = { initial: "1", xs: "2" }; +const MAX_NATIONS = 3; function ShellFilter({ index, premium }: { index: number; premium?: boolean }) { const shells = TankFilters.use((state) => state.shells); @@ -72,13 +39,7 @@ function ShellFilter({ index, premium }: { index: number; premium?: boolean }) { return ( - + {shells[index] === null && ( @@ -107,7 +68,6 @@ function ShellFilter({ index, premium }: { index: number; premium?: boolean }) { return ( state.logins.wargaming); + const nations = + tankFilters.nations.length === 0 + ? gameDefinitions.nations + : tankFilters.nations; + return ( - - - - - {times(5, (index) => { - const tier = index + 1; - const selected = tankFilters.tiers?.includes(tier); - - return ( - { - if (tankFilters.tiers.includes(tier)) { - TankFilters.mutate((draft) => { - draft.tiers = draft.tiers.filter((t) => t !== tier); - }); - } else { - TankFilters.mutate((draft) => { - draft.tiers = [...draft.tiers, tier]; - }); - } + + + + + - - {times(5, (index) => { - const tier = index + 6; - const selected = tankFilters.tiers?.includes(tier); - - return ( - { - if (tankFilters.tiers.includes(tier)) { - TankFilters.mutate((draft) => { - draft.tiers = draft.tiers.filter((t) => t !== tier); - }); - } else { - TankFilters.mutate((draft) => { - draft.tiers = [...draft.tiers, tier]; - }); - } - }} - > - {TIER_ROMAN_NUMERALS[tier]} - - ); - })} - - - - - - {gameDefinitions.nations - .slice(0, Math.ceil(gameDefinitions.nations.length / 2)) - .map((nation) => { - const selected = tankFilters.nations?.includes(nation); - - return ( - { - TankFilters.mutate((draft) => { - if (tankFilters.nations.includes(nation)) { - draft.nations = draft.nations.filter( - (n) => n !== nation - ); - } else { - draft.nations = [...draft.nations, nation]; - } - }); - }} - > - - - ); - })} - - - - - - {gameDefinitions.nations - .slice(Math.ceil(gameDefinitions.nations.length / 2)) - .map((nation) => { - const selected = tankFilters.nations?.includes(nation); - - return ( - { - TankFilters.mutate((draft) => { - if (tankFilters.nations.includes(nation)) { - draft.nations = draft.nations.filter( - (n) => n !== nation - ); - } else { - draft.nations = [...draft.nations, nation]; - } - }); - }} - > - - - ); - })} - - - - {/* Consumables Filter - RIGHT AFTER NATIONS */} - - - {consumableColumns[0]?.map((consumable) => { - const selected = tankFilters.consumables?.includes(consumable.id); - - return ( - { - TankFilters.mutate((draft) => { - if (tankFilters.consumables.includes(consumable.id)) { - draft.consumables = draft.consumables.filter( - (c) => c !== consumable.id - ); - } else { - draft.consumables = [ - ...draft.consumables, - consumable.id, - ]; - } - }); - }} - > - - - ); - })} - - - {consumableColumns[1] && ( - <> - - - - {consumableColumns[1].map((consumable) => { - const selected = tankFilters.consumables?.includes( - consumable.id - ); - - return ( - { - TankFilters.mutate((draft) => { - if (tankFilters.consumables.includes(consumable.id)) { - draft.consumables = draft.consumables.filter( - (c) => c !== consumable.id - ); - } else { - draft.consumables = [ - ...draft.consumables, - consumable.id, - ]; - } - }); - }} - > - - - ); - })} - - - )} - - {consumableColumns[2] && ( - <> - - - - {consumableColumns[2].map((consumable) => { - const selected = tankFilters.consumables?.includes( - consumable.id - ); - - return ( - { - TankFilters.mutate((draft) => { - if (tankFilters.consumables.includes(consumable.id)) { - draft.consumables = draft.consumables.filter( - (c) => c !== consumable.id - ); - } else { - draft.consumables = [ - ...draft.consumables, - consumable.id, - ]; - } - }); - }} - > - - - ); - })} - - - )} - - - - {TANK_CLASSES.map((tankClass) => { - const Icon = classIcons[tankClass]; - const selected = tankFilters.classes?.includes(tankClass); + + {gameDefinitions.nations.map((nation) => { + const selected = tankFilters.nations.includes(nation); return ( - { + { + event.preventDefault(); + TankFilters.mutate((draft) => { - if (tankFilters.classes.includes(tankClass)) { - draft.classes = draft.classes.filter( - (c) => c !== tankClass - ); + if (selected) { + draft.nations = draft.nations.filter((n) => n !== nation); } else { - draft.classes = [...draft.classes, tankClass]; + draft.nations = [...draft.nations, nation]; } }); }} + checked={selected} + key={nation} + style={{ + position: "relative", + }} > - - + + + + + + { + strings.common.nations[ + nation as keyof typeof strings.common.nations + ] + } + + ); })} - - - { - TankFilters.mutate((draft) => { - if (tankFilters.gunType.includes("regular")) { - draft.gunType = draft.gunType.filter((t) => t !== "regular"); - } else { - draft.gunType = [...draft.gunType, "regular"]; - } - }); - }} - > - - - { - TankFilters.mutate((draft) => { - if (tankFilters.gunType.includes("auto_loader")) { - draft.gunType = draft.gunType.filter( - (t) => t !== "auto_loader" - ); - } else { - draft.gunType = [...draft.gunType, "auto_loader"]; - } - }); - }} - > - - - { - TankFilters.mutate((draft) => { - if (tankFilters.gunType.includes("auto_reloader")) { - draft.gunType = draft.gunType.filter( - (t) => t !== "auto_reloader" - ); - } else { - draft.gunType = [...draft.gunType, "auto_reloader"]; - } - }); - }} - > - - - + - - - - - + { + event.preventDefault(); - - { TankFilters.mutate((draft) => { - if (tankFilters.types.includes(TankType.RESEARCHABLE)) { - draft.types = draft.types.filter( - (t) => t !== TankType.RESEARCHABLE - ); - } else { - draft.types = [...draft.types, TankType.RESEARCHABLE]; - } + draft.nations = []; }); }} > - - - { - TankFilters.mutate((draft) => { - if (tankFilters.types.includes(TankType.PREMIUM)) { - draft.types = draft.types.filter( - (t) => t !== TankType.PREMIUM - ); - } else { - draft.types = [...draft.types, TankType.PREMIUM]; - } - }); - }} - > - - - - - { - TankFilters.mutate((draft) => { - if (tankFilters.types.includes(TankType.COLLECTOR)) { - draft.types = draft.types.filter( - (t) => t !== TankType.COLLECTOR - ); - } else { - draft.types = [...draft.types, TankType.COLLECTOR]; - } - }); - }} - > - - - - - - - - { - TankFilters.mutate((draft) => { - draft.showNonTesting = !draft.showNonTesting; - }); - }} - > - - - - { - TankFilters.mutate((draft) => { - draft.showTesting = !draft.showTesting; - }); - }} - > - - - - - {wargaming ? ( - - ) : ( - - - - )} - - - {clearable && ( - { - event.preventDefault(); - TankFilters.set(TankFilters.initial); - TankSort.set(TankSort.initial); - }} - > - {strings.website.common.tank_search.clear_filters} - - )} + + Clear + + + ); }