mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: translate tank search
This commit is contained in:
1 parent
c7856364ad
commit
5a65fee1d1
9 files changed
+231
-355
No files matched your search
@@ -4,6 +4,7 @@ import { MagnifyingGlassIcon } from '@radix-ui/react-icons';
|
||||
import { Flex, Spinner, TextField } from '@radix-ui/themes';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { type KeyboardEventHandler, useCallback, useRef } from 'react';
|
||||
import { useLocale } from '../../../hooks/useLocale';
|
||||
import { $tankFilters } from '../../../stores/tankFilters';
|
||||
import { QuickLink } from './QuickLink';
|
||||
import { Sort } from './Sort';
|
||||
@@ -14,6 +15,7 @@ interface SearchBarProps {
|
||||
}
|
||||
|
||||
export function SearchBar({ topResult, onSelect }: SearchBarProps) {
|
||||
const { strings } = useLocale();
|
||||
const tankFilters = useStore($tankFilters);
|
||||
const input = useRef<HTMLInputElement>(null);
|
||||
const performSearch = useCallback(
|
||||
@@ -67,7 +69,7 @@ export function SearchBar({ topResult, onSelect }: SearchBarProps) {
|
||||
defaultValue={tankFilters.search}
|
||||
style={{ flex: 1 }}
|
||||
ref={input}
|
||||
placeholder="Search tanks..."
|
||||
placeholder={strings.website.common.tank_search.search_bar_hint}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
|
||||
@@ -1,308 +1,122 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { CaretDownIcon } from '@radix-ui/react-icons';
|
||||
import { Button, DropdownMenu } from '@radix-ui/themes';
|
||||
import type { TankopediaSortDirection } from '../../../stores/tankopediaPersistent';
|
||||
import { useLocale } from '../../../hooks/useLocale';
|
||||
import type {
|
||||
TankopediaSortBy,
|
||||
TankopediaSortDirection,
|
||||
} from '../../../stores/tankopediaPersistent';
|
||||
import { $tankopediaSort } from '../../../stores/tankopediaSort';
|
||||
|
||||
interface ItemProps {
|
||||
by: TankopediaSortBy;
|
||||
}
|
||||
|
||||
interface ItemProps {
|
||||
by: TankopediaSortBy;
|
||||
}
|
||||
|
||||
function Item({ by }: ItemProps) {
|
||||
const { strings } = useLocale();
|
||||
const tankopediaSort = useStore($tankopediaSort);
|
||||
|
||||
return (
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === by}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', by);
|
||||
}}
|
||||
>
|
||||
{strings.website.common.tank_search.sort[by]}
|
||||
</DropdownMenu.CheckboxItem>
|
||||
);
|
||||
}
|
||||
|
||||
export function Sort() {
|
||||
const tankopediaSort = useStore($tankopediaSort);
|
||||
const { strings } = useLocale();
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="surface" color="gray">
|
||||
Sort
|
||||
{strings.website.common.tank_search.sort_dropdown.label}
|
||||
<CaretDownIcon />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'meta.none'}
|
||||
onClick={() => {
|
||||
// mutateTankopediaSort((draft) => {
|
||||
// draft.by = 'meta.none';
|
||||
// });
|
||||
$tankopediaSort.setKey('by', 'meta.none');
|
||||
}}
|
||||
>
|
||||
No sort
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.Label>
|
||||
{strings.website.common.tank_search.sort_dropdown.groups.sort.label}
|
||||
</DropdownMenu.Label>
|
||||
|
||||
<DropdownMenu.Label>Sort by</DropdownMenu.Label>
|
||||
<Item by="meta.none" />
|
||||
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger>Fire</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubTrigger>
|
||||
{strings.website.common.tank_search.sort_dropdown.groups.sort.fire}
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.dpm'}
|
||||
onClick={() => {
|
||||
// mutateTankopediaSort((draft) => {
|
||||
// draft.by = 'fire.dpm';
|
||||
// });
|
||||
$tankopediaSort.setKey('by', 'fire.dpm');
|
||||
}}
|
||||
>
|
||||
Standard DPM
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.dpmPremium'}
|
||||
onClick={() => {
|
||||
// mutateTankopediaSort((draft) => {
|
||||
// draft.by = 'fire.dpmPremium';
|
||||
// });
|
||||
$tankopediaSort.setKey('by', 'fire.dpmPremium');
|
||||
}}
|
||||
>
|
||||
Premium DPM
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.reload'}
|
||||
onClick={() => {
|
||||
// mutateTankopediaSort((draft) => {
|
||||
// draft.by = 'fire.reload';
|
||||
// });
|
||||
$tankopediaSort.setKey('by', 'fire.reload');
|
||||
}}
|
||||
>
|
||||
Reload
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.caliber'}
|
||||
onClick={() => {
|
||||
// mutateTankopediaSort((draft) => {
|
||||
// draft.by = 'fire.caliber';
|
||||
// });
|
||||
$tankopediaSort.setKey('by', 'fire.caliber');
|
||||
}}
|
||||
>
|
||||
Caliber
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.standardPenetration'}
|
||||
onClick={() => {
|
||||
// mutateTankopediaSort((draft) => {
|
||||
// draft.by = 'fire.standardPenetration';
|
||||
// });
|
||||
$tankopediaSort.setKey('by', 'fire.standardPenetration');
|
||||
}}
|
||||
>
|
||||
Standard penetration
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.premiumPenetration'}
|
||||
onClick={() => {
|
||||
// mutateTankopediaSort((draft) => {
|
||||
// draft.by = 'fire.premiumPenetration';
|
||||
// });
|
||||
$tankopediaSort.setKey('by', 'fire.premiumPenetration');
|
||||
}}
|
||||
>
|
||||
Premium penetration
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.damage'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.damage');
|
||||
}}
|
||||
>
|
||||
Damage
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.moduleDamage'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.moduleDamage');
|
||||
}}
|
||||
>
|
||||
Module damage
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.shellVelocity'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.shellVelocity');
|
||||
}}
|
||||
>
|
||||
Shell velocity
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.shellRange'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.shellRange');
|
||||
}}
|
||||
>
|
||||
Shell range
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.shellCapacity'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.shellCapacity');
|
||||
}}
|
||||
>
|
||||
Shell capacity
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.aimTime'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.aimTime');
|
||||
}}
|
||||
>
|
||||
Aim time
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.dispersionStill'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.dispersionStill');
|
||||
}}
|
||||
>
|
||||
Dispersion still
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.dispersionMoving'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.dispersionMoving');
|
||||
}}
|
||||
>
|
||||
Dispersion moving
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.gunDepression'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.gunDepression');
|
||||
}}
|
||||
>
|
||||
Gun depression
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'fire.gunElevation'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'fire.gunElevation');
|
||||
}}
|
||||
>
|
||||
Gun elevation
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<Item by="fire.dpm" />
|
||||
<Item by="fire.dpmPremium" />
|
||||
<Item by="fire.reload" />
|
||||
<Item by="fire.caliber" />
|
||||
<Item by="fire.standardPenetration" />
|
||||
<Item by="fire.premiumPenetration" />
|
||||
<Item by="fire.damage" />
|
||||
<Item by="fire.moduleDamage" />
|
||||
<Item by="fire.shellVelocity" />
|
||||
<Item by="fire.shellRange" />
|
||||
<Item by="fire.shellCapacity" />
|
||||
<Item by="fire.aimTime" />
|
||||
<Item by="fire.dispersionStill" />
|
||||
<Item by="fire.dispersionMoving" />
|
||||
<Item by="fire.gunDepression" />
|
||||
<Item by="fire.gunElevation" />
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger>Maneuverability</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubTrigger>
|
||||
{
|
||||
strings.website.common.tank_search.sort_dropdown.groups.sort
|
||||
.maneuverability
|
||||
}
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'maneuverability.forwardsSpeed'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'maneuverability.forwardsSpeed');
|
||||
}}
|
||||
>
|
||||
Forwards speed
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'maneuverability.backwardsSpeed'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'maneuverability.backwardsSpeed');
|
||||
}}
|
||||
>
|
||||
Backwards speed
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'maneuverability.power'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'maneuverability.power');
|
||||
}}
|
||||
>
|
||||
Power
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'maneuverability.powerToWeight'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'maneuverability.powerToWeight');
|
||||
}}
|
||||
>
|
||||
Power to weight
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'maneuverability.weight'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'maneuverability.weight');
|
||||
}}
|
||||
>
|
||||
Weight
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'maneuverability.traverseSpeed'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'maneuverability.traverseSpeed');
|
||||
}}
|
||||
>
|
||||
Traverse speed
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<Item by="maneuverability.forwardsSpeed" />
|
||||
<Item by="maneuverability.backwardsSpeed" />
|
||||
<Item by="maneuverability.power" />
|
||||
<Item by="maneuverability.powerToWeight" />
|
||||
<Item by="maneuverability.weight" />
|
||||
<Item by="maneuverability.traverseSpeed" />
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger>Survivability</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubTrigger>
|
||||
{
|
||||
strings.website.common.tank_search.sort_dropdown.groups.sort
|
||||
.survivability
|
||||
}
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'survivability.health'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'survivability.health');
|
||||
}}
|
||||
>
|
||||
Health
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'survivability.viewRange'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'survivability.viewRange');
|
||||
}}
|
||||
>
|
||||
View range
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'survivability.camouflageStill'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'survivability.camouflageStill');
|
||||
}}
|
||||
>
|
||||
Camouflage
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'survivability.camouflageMoving'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'survivability.camouflageMoving');
|
||||
}}
|
||||
>
|
||||
Camouflage moving
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'survivability.camouflageShooting'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey(
|
||||
'by',
|
||||
'survivability.camouflageShooting',
|
||||
);
|
||||
}}
|
||||
>
|
||||
Camouflage shooting
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'survivability.volume'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'survivability.volume');
|
||||
}}
|
||||
>
|
||||
Volume
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={tankopediaSort.by === 'survivability.length'}
|
||||
onClick={() => {
|
||||
$tankopediaSort.setKey('by', 'survivability.length');
|
||||
}}
|
||||
>
|
||||
Length
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<Item by="survivability.health" />
|
||||
<Item by="survivability.viewRange" />
|
||||
<Item by="survivability.camouflageStill" />
|
||||
<Item by="survivability.camouflageMoving" />
|
||||
<Item by="survivability.camouflageShooting" />
|
||||
<Item by="survivability.volume" />
|
||||
<Item by="survivability.length" />
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
|
||||
<DropdownMenu.Label>Direction</DropdownMenu.Label>
|
||||
<DropdownMenu.Label>
|
||||
{
|
||||
strings.website.common.tank_search.sort_dropdown.groups.direction
|
||||
.label
|
||||
}
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.RadioGroup
|
||||
value={tankopediaSort.direction}
|
||||
onValueChange={(value) => {
|
||||
@@ -313,10 +127,16 @@ export function Sort() {
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.RadioItem value="ascending">
|
||||
Ascending
|
||||
{
|
||||
strings.website.common.tank_search.sort_dropdown.groups.direction
|
||||
.ascending
|
||||
}
|
||||
</DropdownMenu.RadioItem>
|
||||
<DropdownMenu.RadioItem value="descending">
|
||||
Descending
|
||||
{
|
||||
strings.website.common.tank_search.sort_dropdown.groups.direction
|
||||
.descending
|
||||
}
|
||||
</DropdownMenu.RadioItem>
|
||||
</DropdownMenu.RadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
|
||||
@@ -16,12 +16,11 @@ import { awaitableTankDefinitions } from '../../core/awaitables/tankDefinitions'
|
||||
import { awaitableTankNames } from '../../core/awaitables/tankNames';
|
||||
import { filterTank } from '../../core/blitzkit/filterTank';
|
||||
import { resolveReload } from '../../core/blitzkit/resolveReload';
|
||||
import { literals } from '../../core/i18n/literals';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { $tankFilters } from '../../stores/tankFilters';
|
||||
import { TankopediaPersistent } from '../../stores/tankopediaPersistent';
|
||||
import {
|
||||
SORT_NAMES,
|
||||
SORT_UNITS,
|
||||
} from '../../stores/tankopediaPersistent/constants';
|
||||
import { SORT_UNITS } from '../../stores/tankopediaPersistent/constants';
|
||||
import { $tankopediaSort } from '../../stores/tankopediaSort';
|
||||
import { ExperimentIcon } from '../ExperimentIcon';
|
||||
import { TankSearchCard } from './components/Card';
|
||||
@@ -52,6 +51,7 @@ const [gameDefinitions, modelDefinitions, tankDefinitions, tankNames] =
|
||||
|
||||
export const TankSearch = memo<TankSearchProps>(
|
||||
({ compact, onSelect, onSelectAll, ...props }) => {
|
||||
const { strings, locale } = useLocale();
|
||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
||||
const tankFilters = useStore($tankFilters);
|
||||
@@ -438,8 +438,11 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
<Flex mt="2" gap="1" align="center" justify="center" direction="column">
|
||||
<Flex gap="2">
|
||||
<Text color="gray">
|
||||
{tanksFiltered.length} tank
|
||||
{tanksFiltered.length === 1 ? '' : 's'}
|
||||
{tanksFiltered.length === 1
|
||||
? strings.website.common.tank_search.count_singular
|
||||
: literals(strings.website.common.tank_search.count_plural, [
|
||||
`${tanksFiltered.length.toLocaleString(locale)}`,
|
||||
])}
|
||||
</Text>
|
||||
|
||||
{onSelectAll && (
|
||||
@@ -457,14 +460,16 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
});
|
||||
}}
|
||||
>
|
||||
Select all
|
||||
{strings.website.common.tank_search.select_all}
|
||||
</Link>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{tankopediaSort.by !== 'meta.none' && (
|
||||
<Text color="gray">
|
||||
Sorting by {SORT_NAMES[tankopediaSort.by]}
|
||||
{literals(strings.website.common.tank_search.sorting_by, [
|
||||
strings.website.common.tank_search.sort[tankopediaSort.by],
|
||||
])}
|
||||
{SORT_UNITS[tankopediaSort.by] === undefined
|
||||
? ''
|
||||
: ` (${SORT_UNITS[tankopediaSort.by]})`}
|
||||
@@ -480,8 +485,7 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
<ExperimentIcon style={{ width: '1em', height: '1em' }} />
|
||||
</Callout.Icon>
|
||||
<Callout.Text>
|
||||
Tanks in testing are subject to change and may not represent the
|
||||
final product.
|
||||
{strings.website.common.warnings.test}
|
||||
</Callout.Text>
|
||||
</Callout.Root>
|
||||
</Flex>
|
||||
|
||||
Reference in new issue
Block a user