diff --git a/.vscode/settings.json b/.vscode/settings.json index b8f63465e..5bf467b2c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -67,6 +67,7 @@ "DEBUFF", "debugflags", "deburr", + "deburred", "decalmask", "decompressor", "décor", diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 80ebee2a2..2930ee31c 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -121,5 +121,11 @@ "no_docs": "This method has no documentation.", "test": "Test", "view_full": "View full types" + }, + + "tanks": { + "search": { + "placeholder": "Search tanks..." + } } } diff --git a/packages/website-ue/src/components/BaseStyles.astro b/packages/website-ue/src/components/BaseStyles.astro index b93ef7255..abf6d7b75 100644 --- a/packages/website-ue/src/components/BaseStyles.astro +++ b/packages/website-ue/src/components/BaseStyles.astro @@ -43,13 +43,14 @@ import "@fontsource-variable/roboto-flex"; --weight-bold: 700; --weight-black: 900; + --outline-interactive: var(--space-1) solid var(--gray-4); --outline-focus: var(--space-1) solid var(--accent-6); - --shadow-cavity: + /* --shadow-cavity: inset calc(var(--space-1) * sin(45deg)) calc(var(--space-1) * sin(45deg)) var(--space-1) var(--black-a3), inset calc(var(--space-1) * sin(45deg) * -1) - calc(var(--space-1) * sin(45deg) * -1) var(--space-1) var(--white-a1); + calc(var(--space-1) * sin(45deg) * -1) var(--space-1) var(--white-a1); */ --shadow-bump: inset calc(var(--space-1) * sin(45deg)) calc(var(--space-1) * sin(45deg)) @@ -60,7 +61,7 @@ import "@fontsource-variable/roboto-flex"; --shadow-outline-width: 1.5px; --shadow-outline-width-negative: calc(var(--shadow-outline-width) * -1); - --shadow-interactive-background: 0 0 0 var(--shadow-outline-width) + /* --shadow-interactive-background: 0 0 0 var(--shadow-outline-width) var(--gray-6); --shadow-interactive-shine: var(--shadow-outline-width-negative) var(--shadow-outline-width-negative) var(--shadow-outline-width) @@ -69,7 +70,7 @@ import "@fontsource-variable/roboto-flex"; var(--shadow-outline-width) var(--shadow-outline-width) var(--black-a4); --shadow-interactive: var(--shadow-interactive-shine), var(--shadow-interactive-shadow), - var(--shadow-interactive-background); + var(--shadow-interactive-background); */ --shadow-aggressive: var(--space-1) var(--space-1) var(--space-4) var(--black-a11); diff --git a/packages/website-ue/src/components/Section/index.module.css b/packages/website-ue/src/components/Section/index.module.css index 1afbc70d3..ee8547fe3 100644 --- a/packages/website-ue/src/components/Section/index.module.css +++ b/packages/website-ue/src/components/Section/index.module.css @@ -6,7 +6,6 @@ .content { flex: 1; max-width: var(--page-width); - overflow: hidden; display: flex; flex-direction: column; diff --git a/packages/website-ue/src/components/TankSearch.tsx b/packages/website-ue/src/components/TankSearch.tsx new file mode 100644 index 000000000..9a5aa2208 --- /dev/null +++ b/packages/website-ue/src/components/TankSearch.tsx @@ -0,0 +1,28 @@ +import { useStrings } from "../hooks/useStrings"; +import { TankFilters } from "../stores/tankFilters"; +import { TextField } from "./TextField"; + +export function TankSearch() { + const strings = useStrings(); + + return ( + { + const trimmed = event.target.value.trim(); + + if (trimmed.length === 0) { + TankFilters.mutate((draft) => { + draft.search = undefined; + }); + + return; + } + + TankFilters.mutate((draft) => { + draft.search = trimmed; + }); + }} + /> + ); +} diff --git a/packages/website-ue/src/components/TextField/index.css b/packages/website-ue/src/components/TextField/index.module.css similarity index 95% rename from packages/website-ue/src/components/TextField/index.css rename to packages/website-ue/src/components/TextField/index.module.css index 2090c6627..cd3d511a2 100644 --- a/packages/website-ue/src/components/TextField/index.css +++ b/packages/website-ue/src/components/TextField/index.module.css @@ -14,7 +14,7 @@ border-radius: var(--max-size); background-color: var(--gray-1); - box-shadow: var(--shadow-interactive), var(--shadow-cavity); + outline: var(--outline-interactive); input { padding: var(--space-3) var(--space-4); diff --git a/packages/website-ue/src/components/TextField/index.tsx b/packages/website-ue/src/components/TextField/index.tsx index 7bc9a6956..1db3546ab 100644 --- a/packages/website-ue/src/components/TextField/index.tsx +++ b/packages/website-ue/src/components/TextField/index.tsx @@ -8,7 +8,7 @@ import { type ReactNode, } from "react"; import { classNames } from "../../core/ui/classNames"; -import "./index.css"; +import styles from "./index.module.css"; export interface TextFieldProps extends ComponentProps<"input"> { children?: ReactNode; @@ -51,7 +51,7 @@ export const TextField = forwardRef( return (
api.tanks(), "tanks"); + + return Object.values(tanks.tanks).map((tank) => { + const { id } = tank; + const name = gameStrings[tank.tank!.name!.value]; + const deburred = deburr(name); + + return { id, name, deburred }; + }); +} diff --git a/packages/website-ue/src/pages/[...locale]/tanks/_index.tsx b/packages/website-ue/src/pages/[...locale]/tanks/_index.tsx index b45bac9f2..0aaec7d91 100644 --- a/packages/website-ue/src/pages/[...locale]/tanks/_index.tsx +++ b/packages/website-ue/src/pages/[...locale]/tanks/_index.tsx @@ -1,10 +1,16 @@ +import fuzzysort from "fuzzysort"; import { useMemo } from "react"; import { TankCard } from "../../../components/TankCard"; import { TankCards } from "../../../components/TankCards"; +import { TankSearch } from "../../../components/TankSearch"; import { api } from "../../../core/api/dynamic"; import { useAwait } from "../../../hooks/useAwait"; import { LocaleProvider } from "../../../hooks/useLocale"; +import { useSearchableTanks } from "../../../hooks/useSearchableTanks"; import { useTierCompare } from "../../../hooks/useTierCompare"; +import { TankFilters } from "../../../stores/tankFilters"; + +const { go } = fuzzysort; interface PageProps { locale: string; @@ -20,7 +26,11 @@ export function Page({ locale, ...props }: PageProps) { function Content() { const tanks = useAwait(() => api.tanks(), "tanks"); + const compareTiers = useTierCompare(); + const searchableTanks = useSearchableTanks(); + const search = TankFilters.use((state) => state.search); + const tanksOrdered = useMemo(() => { let list = Object.values(tanks.tanks); @@ -30,12 +40,24 @@ function Content() { return list; }, []); + const tanksFiltered = useMemo(() => { + if (!search) return tanksOrdered; + + const results = go(search, searchableTanks, { + keys: ["id", "name", "deburred"], + }); + + return results.map(({ obj }) => obj); + }, [search]); return ( - - {tanksOrdered.slice(0, 32).map(({ id }) => ( - - ))} - + <> + + + {tanksFiltered.slice(0, 32).map(({ id }) => ( + + ))} + + ); } diff --git a/packages/website-ue/src/pages/[...locale]/tanks/index.astro b/packages/website-ue/src/pages/[...locale]/tanks/index.astro index 4a225d1d9..287debb7c 100644 --- a/packages/website-ue/src/pages/[...locale]/tanks/index.astro +++ b/packages/website-ue/src/pages/[...locale]/tanks/index.astro @@ -2,7 +2,6 @@ import "@radix-ui/colors/violet-dark.css"; import { Section } from "../../../components/Section"; import ToolBanner from "../../../components/ToolBanner.astro"; -import { api } from "../../../core/api/dynamic"; import { tools } from "../../../core/tools/tools"; import BaseLayout from "../../../layouts/BaseLayout.astro"; import { Page } from "./_index"; @@ -10,7 +9,6 @@ import { Page } from "./_index"; export { getStaticPaths } from "../index.astro"; const { locale } = Astro.props; -const tankList = await api.tankList(); --- diff --git a/packages/website-ue/src/stores/tankFilters.ts b/packages/website-ue/src/stores/tankFilters.ts new file mode 100644 index 000000000..59be78b76 --- /dev/null +++ b/packages/website-ue/src/stores/tankFilters.ts @@ -0,0 +1,9 @@ +import { Varuna } from "varuna"; + +interface TankFilters { + search?: string; +} + +export const TankFilters = new Varuna({ + search: undefined, +}); diff --git a/packages/website-ue/src/stores/tankopedia.ts b/packages/website-ue/src/stores/tankopedia.ts index 6d56241fc..ada47433d 100644 --- a/packages/website-ue/src/stores/tankopedia.ts +++ b/packages/website-ue/src/stores/tankopedia.ts @@ -1,4 +1,4 @@ -import type { TankCatalogComponent } from "@protos/blitz_static_tank_component"; +import type { TankCatalogComponent } from "@protos/game/proto/legacy/blitz_static_tank_component"; import { Varuna } from "varuna"; import { createTankState } from "../core/tankopedia/createTankState"; import { type TankState } from "../core/tankopedia/tankState"; @@ -18,5 +18,5 @@ export const Tankopedia = new Varuna( (tank) => ({ compare: TankopediaCompare.TierAndClass, protagonist: createTankState(tank), - }) + }), );