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