mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-26 20:19:50 +01:00
tank search
This commit is contained in:
1 parent
5477616917
commit
4a0fccc526
6 files changed
+54
-73
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
import { BlitzKitAPI } from "@blitzkit/core";
|
import { BlitzKitAPI } from "@blitzkit/core/src/blitzkit/api/base";
|
||||||
import locales from "@blitzkit/i18n/locales.json";
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { deburr } from "lodash-es";
|
import { deburr, times } from "lodash-es";
|
||||||
import { I18nString } from "../protos";
|
import { I18nString } from "../protos";
|
||||||
|
|
||||||
export async function fetchTankNames(api: BlitzKitAPI) {
|
export async function fetchTankNames(api: BlitzKitAPI) {
|
||||||
@@ -12,40 +12,53 @@ export async function fetchTankNames(api: BlitzKitAPI) {
|
|||||||
|
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
tankDefinitionsArray.map(async (tank) => {
|
tankDefinitionsArray.map(async (tank) => {
|
||||||
const searchableNameDeburr: I18nString = { locales: {} };
|
const name_deburr: I18nString = I18nString.create();
|
||||||
|
const camouflages_deburr: I18nString[] = [];
|
||||||
|
|
||||||
|
const camouflages = tank.camouflages.map(
|
||||||
|
(id) => camouflageDefinitions.camouflages[id].name!,
|
||||||
|
);
|
||||||
|
|
||||||
Object.entries(tank.name!).forEach(([key, value]) => {
|
Object.entries(tank.name!).forEach(([key, value]) => {
|
||||||
searchableNameDeburr.locales[key] = deburr(value);
|
name_deburr.locales[key] = deburr(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
camouflages.forEach((camouflage, index) => {
|
||||||
|
camouflages_deburr[index] = I18nString.create();
|
||||||
|
Object.entries(camouflage).forEach(([key, value]) => {
|
||||||
|
camouflages_deburr[index].locales[key] = deburr(value);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: tank.id,
|
id: tank.id,
|
||||||
name: tank.name,
|
slug: tank.slug,
|
||||||
searchableName: tank.name,
|
dev_name: tank.dev_name,
|
||||||
searchableNameDeburr,
|
|
||||||
camouflages: tank.camouflages
|
name: tank.name!,
|
||||||
?.map((id) =>
|
name_deburr,
|
||||||
locales.supported.map(
|
|
||||||
({ locale }) =>
|
camouflages,
|
||||||
camouflageDefinitions.camouflages[id]?.name!.locales[locale],
|
camouflages_deburr,
|
||||||
),
|
|
||||||
)
|
|
||||||
.flat()
|
|
||||||
.filter(Boolean)
|
|
||||||
.map(deburr)
|
|
||||||
.join(" "),
|
|
||||||
treeType: tank.type,
|
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SEARCH_KEYS = [
|
export const SEARCH_KEYS = [
|
||||||
|
"id",
|
||||||
|
"slug",
|
||||||
|
"dev_name",
|
||||||
|
|
||||||
...locales.supported
|
...locales.supported
|
||||||
.map(({ locale }) => [
|
.map(({ locale }) => [
|
||||||
`searchableName.locales.${locale}`,
|
`name.locales.${locale}`,
|
||||||
`searchableNameDeburr.locales.${locale}`,
|
`name_deburr.locales.${locale}`,
|
||||||
|
|
||||||
|
...times(8, (index) => [
|
||||||
|
`camouflages.${index}.locales.${locale}`,
|
||||||
|
`camouflages_deburr.${index}.locales.${locale}`,
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
.flat(),
|
.flat(2),
|
||||||
"camouflages",
|
|
||||||
];
|
];
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
.recent {
|
.recent {
|
||||||
|
margin-top: var(--space-4);
|
||||||
padding: var(--space-4);
|
padding: var(--space-4);
|
||||||
|
|
||||||
background-color: var(--gray-a2);
|
background-color: var(--gray-a2);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useLocale } from "../../../hooks/useLocale";
|
import { useLocale } from "../../../hooks/useLocale";
|
||||||
import { TankFilters } from "../../../stores/tankFilters";
|
import { TankFilters } from "../../../stores/tankFilters";
|
||||||
|
import { Flex } from "../../Flex";
|
||||||
|
import { Link } from "../../Link";
|
||||||
|
import { Text } from "../../Text";
|
||||||
|
|
||||||
interface NoResultsProps {
|
interface NoResultsProps {
|
||||||
type?: "filters" | "search";
|
type?: "filters" | "search";
|
||||||
@@ -9,8 +12,8 @@ export function NoResults({ type = "filters" }: NoResultsProps) {
|
|||||||
const { strings } = useLocale();
|
const { strings } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexGrow="1" align="center" justify="center">
|
<Flex align="center" justify="center">
|
||||||
<Text color="gray">
|
<Text lowContrast>
|
||||||
{strings.website.common.tank_search.no_tanks_found.body}{" "}
|
{strings.website.common.tank_search.no_tanks_found.body}{" "}
|
||||||
<Link
|
<Link
|
||||||
href="#"
|
href="#"
|
||||||
|
|||||||
@@ -73,9 +73,7 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
} else {
|
} else {
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
fuzzysort
|
fuzzysort
|
||||||
.go(tankFilters.search, tankNames, {
|
.go(tankFilters.search, tankNames, { keys: SEARCH_KEYS })
|
||||||
keys: SEARCH_KEYS,
|
|
||||||
})
|
|
||||||
.map((result) => tankDefinitions.tanks[result.obj.id]),
|
.map((result) => tankDefinitions.tanks[result.obj.id]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -437,7 +435,7 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!tankFilters.search && !tankFilters.searching && <TankSearchFilters />}
|
{!tankFilters.search && <TankSearchFilters />}
|
||||||
|
|
||||||
{!skeleton && !compact && <RecentlyViewedTanks />}
|
{!skeleton && !compact && <RecentlyViewedTanks />}
|
||||||
|
|
||||||
@@ -497,7 +495,7 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!skeleton && !tankFilters.searching && (
|
{!skeleton && (
|
||||||
<>
|
<>
|
||||||
{tanks.length > 0 && (
|
{tanks.length > 0 && (
|
||||||
<TankCardWrapper>
|
<TankCardWrapper>
|
||||||
@@ -530,19 +528,6 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
{tanks.length === 0 && <NoResults type="search" />}
|
{tanks.length === 0 && <NoResults type="search" />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(skeleton || tankFilters.searching) && (
|
|
||||||
<TankCardWrapper>
|
|
||||||
{times(
|
|
||||||
Math.round(
|
|
||||||
skeleton ? DEFAULT_LOADED_CARDS : 10 + 10 * Math.random(),
|
|
||||||
),
|
|
||||||
(index) => (
|
|
||||||
<TankCardSkeleton key={index} />
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</TankCardWrapper>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import type { TankDefinition } from "@blitzkit/core";
|
import type { TankDefinition } from "@blitzkit/core";
|
||||||
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
|
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
|
||||||
import { debounce } from "lodash-es";
|
|
||||||
import { type KeyboardEventHandler, useCallback, useRef } from "react";
|
import { type KeyboardEventHandler, useCallback, useRef } from "react";
|
||||||
import { useLocale } from "../../hooks/useLocale";
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import { TankFilters } from "../../stores/tankFilters";
|
import { TankFilters } from "../../stores/tankFilters";
|
||||||
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
|
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
|
||||||
import { Flex } from "../Flex";
|
import { Flex } from "../Flex";
|
||||||
import { Spinner } from "../Spinner";
|
|
||||||
import { Sort } from "../TankSearch/components/Sort";
|
import { Sort } from "../TankSearch/components/Sort";
|
||||||
import { TextField } from "../TextField";
|
import { TextField } from "../TextField";
|
||||||
|
|
||||||
@@ -22,36 +20,19 @@ export function TankSearchBar({
|
|||||||
}: SearchBarProps) {
|
}: SearchBarProps) {
|
||||||
const { strings } = useLocale();
|
const { strings } = useLocale();
|
||||||
const search = TankFilters.use((state) => state.search);
|
const search = TankFilters.use((state) => state.search);
|
||||||
const searching = TankFilters.use((state) => state.searching);
|
|
||||||
const input = useRef<HTMLInputElement>(null);
|
const input = useRef<HTMLInputElement>(null);
|
||||||
const performSearch = useCallback(
|
|
||||||
debounce(() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
draft.searching = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!input.current) return;
|
|
||||||
|
|
||||||
const sanitized = input.current.value.trim();
|
|
||||||
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
draft.search = sanitized.length === 0 ? null : sanitized;
|
|
||||||
});
|
|
||||||
}, 500),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
const handleChange = useCallback(() => {
|
const handleChange = useCallback(() => {
|
||||||
if (!searching) {
|
if (!input.current) return;
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
draft.searching = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
performSearch();
|
const sanitized = input.current.value.trim();
|
||||||
}, [searching]);
|
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
draft.search = sanitized.length === 0 ? null : sanitized;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
const handleKeyDown = useCallback<KeyboardEventHandler>(
|
const handleKeyDown = useCallback<KeyboardEventHandler>(
|
||||||
(event) => {
|
(event) => {
|
||||||
if (event.key !== "Enter" || !topResult || searching) return;
|
if (event.key !== "Enter" || !topResult) return;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@@ -75,7 +56,7 @@ export function TankSearchBar({
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
>
|
>
|
||||||
{searching ? <Spinner /> : <MagnifyingGlassIcon />}
|
<MagnifyingGlassIcon />
|
||||||
</TextField>
|
</TextField>
|
||||||
|
|
||||||
<Sort />
|
<Sort />
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export interface TankFilters {
|
|||||||
classes: TankClass[];
|
classes: TankClass[];
|
||||||
types: TankType[];
|
types: TankType[];
|
||||||
search: string | null;
|
search: string | null;
|
||||||
searching: boolean;
|
|
||||||
gunType: CaseType<GunDefinition>[];
|
gunType: CaseType<GunDefinition>[];
|
||||||
shells: [ShellType | null, ShellType | null, ShellType | null];
|
shells: [ShellType | null, ShellType | null, ShellType | null];
|
||||||
consumables: number[];
|
consumables: number[];
|
||||||
@@ -38,7 +37,6 @@ export const TankFilters = new Varuna<TankFilters>({
|
|||||||
classes: [],
|
classes: [],
|
||||||
types: [],
|
types: [],
|
||||||
search: null,
|
search: null,
|
||||||
searching: false,
|
|
||||||
gunType: [],
|
gunType: [],
|
||||||
shells: [null, null, null],
|
shells: [null, null, null],
|
||||||
consumables: [],
|
consumables: [],
|
||||||
|
|||||||
Reference in new issue
Block a user