feat: extract components

This commit is contained in:
TrèsAbhi committed 2025-12-30 20:05:00 -06:00
1 parent 4462ebe82c
commit 727ced967b
1 file changed
+182 -186
@@ -1,10 +1,9 @@
import { asset, ShellType, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
import { TrashIcon } from "@radix-ui/react-icons";
import { Box, Button, DropdownMenu, Flex, Text } from "@radix-ui/themes";
import { isEqual, times } from "lodash-es";
import { times } from "lodash-es";
import { awaitableGameDefinitions } from "../../../core/awaitables/gameDefinitions";
import { useLocale } from "../../../hooks/useLocale";
import { App } from "../../../stores/app";
import { TankFilters } from "../../../stores/tankFilters";
const gameDefinitions = await awaitableGameDefinitions;
@@ -18,194 +17,191 @@ const shellTypeIcons: Record<ShellType, string> = {
const MAX_NATIONS = 3;
const MAX_TIERS = 2;
const TIERS = times(10, (i) => i + 1);
const TIERS = times(10, (i) => 10 - i);
export function FilterControl() {
const tankFilters = TankFilters.use();
const { strings } = useLocale();
const clearable = !isEqual(tankFilters, TankFilters.initial);
const wargaming = App.use((state) => state.logins.wargaming);
const tiers = tankFilters.tiers.length === 0 ? TIERS : tankFilters.tiers;
const nations =
tankFilters.nations.length === 0
? gameDefinitions.nations
: tankFilters.nations;
return (
<Flex align="center" gap="2">
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button color="gray" variant="surface">
Tiers
<Flex gap="1">
{tiers
.sort((a, b) => b - a)
.slice(0, MAX_TIERS)
.map((tier, index) => (
// <img
// style={{
// marginLeft: index > 0 ? "-0.5em" : undefined,
// width: "1.25em",
// height: "1.25em",
// }}
// src={asset(`flags/circle/${nation}.webp`)}
// />
<Text size="1" key={tier}>
{TIER_ROMAN_NUMERALS[tier]}
</Text>
))}
{nations.length > MAX_TIERS && (
<Text size="1">+{nations.length - MAX_TIERS}</Text>
)}
</Flex>
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{TIERS.map((tier) => {
const selected = tankFilters.tiers.includes(tier);
return (
<DropdownMenu.CheckboxItem
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
if (selected) {
draft.tiers = draft.tiers.filter((t) => t !== tier);
} else {
draft.tiers = [...draft.tiers, tier];
}
});
}}
checked={selected}
key={tier}
>
Tier {TIER_ROMAN_NUMERALS[tier]}
</DropdownMenu.CheckboxItem>
);
})}
<DropdownMenu.Separator />
<DropdownMenu.Item
color="red"
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
draft.tiers = [];
});
}}
>
<TrashIcon />
Clear
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button color="gray" variant="surface">
Nations
<Flex>
{nations.slice(0, MAX_NATIONS).map((nation, index) => (
<img
style={{
marginLeft: index > 0 ? "-0.5em" : undefined,
width: "1.25em",
height: "1.25em",
}}
src={asset(`flags/circle/${nation}.webp`)}
/>
))}
{nations.length > MAX_NATIONS && (
<Text size="1" ml="1">
+{nations.length - MAX_NATIONS}
</Text>
)}
</Flex>
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{gameDefinitions.nations.map((nation) => {
const selected = tankFilters.nations.includes(nation);
return (
<DropdownMenu.CheckboxItem
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
if (selected) {
draft.nations = draft.nations.filter((n) => n !== nation);
} else {
draft.nations = [...draft.nations, nation];
}
});
}}
checked={selected}
key={nation}
style={{
position: "relative",
}}
>
<Box
width="calc(100% + calc(4 * var(--space-2)))"
height="100%"
position="absolute"
right="0"
mr="-2"
style={{
backgroundImage: `url(${asset(
`flags/scratched/${nation}.webp`
)})`,
backgroundSize: "100%",
backgroundRepeat: "no-repeat",
backgroundPosition: "5rem center",
}}
>
<Box
width="100%"
height="100%"
style={{
background: `linear-gradient(90deg, var(--gray-2) 50%, transparent)`,
}}
/>
</Box>
<Text style={{ zIndex: 1 }}>
{
strings.common.nations[
nation as keyof typeof strings.common.nations
]
}
</Text>
</DropdownMenu.CheckboxItem>
);
})}
<DropdownMenu.Separator />
<DropdownMenu.Item
color="red"
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
draft.nations = [];
});
}}
>
<TrashIcon />
Clear
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
<TiersFilter />
<NationsFilter />
</Flex>
);
}
function TiersFilter() {
const tiersRaw = TankFilters.use((state) => state.tiers);
const tiers = tiersRaw.length === 0 ? TIERS : tiersRaw;
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button color="gray" variant="surface">
Tiers
<Flex gap="1">
{tiers.slice(0, MAX_TIERS).map((tier) => (
<Text size="1" key={tier}>
{TIER_ROMAN_NUMERALS[tier]}
</Text>
))}
{tiers.length > MAX_TIERS && (
<Text size="1">+{tiers.length - MAX_TIERS}</Text>
)}
</Flex>
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{TIERS.map((tier) => {
const selected = tiersRaw.includes(tier);
return (
<DropdownMenu.CheckboxItem
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
if (selected) {
draft.tiers = draft.tiers.filter((t) => t !== tier);
} else {
draft.tiers = [...draft.tiers, tier].sort((a, b) => b - a);
}
});
}}
checked={selected}
key={tier}
>
Tier {TIER_ROMAN_NUMERALS[tier]}
</DropdownMenu.CheckboxItem>
);
})}
<DropdownMenu.Separator />
<DropdownMenu.Item
color="red"
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
draft.tiers = [];
});
}}
>
<TrashIcon />
Clear
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
);
}
function NationsFilter() {
const rawNations = TankFilters.use((state) => state.nations);
const nations =
rawNations.length === 0 ? gameDefinitions.nations : rawNations;
const { strings } = useLocale();
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button color="gray" variant="surface">
Nations
<Flex>
{nations.slice(0, MAX_NATIONS).map((nation, index) => (
<img
style={{
marginLeft: index > 0 ? "-0.5em" : undefined,
width: "1.25em",
height: "1.25em",
}}
src={asset(`flags/circle/${nation}.webp`)}
/>
))}
{nations.length > MAX_NATIONS && (
<Text size="1" ml="1">
+{nations.length - MAX_NATIONS}
</Text>
)}
</Flex>
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{gameDefinitions.nations.map((nation) => {
const selected = rawNations.includes(nation);
return (
<DropdownMenu.CheckboxItem
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
if (selected) {
draft.nations = draft.nations.filter((n) => n !== nation);
} else {
draft.nations = [...draft.nations, nation];
}
});
}}
checked={selected}
key={nation}
style={{
position: "relative",
}}
>
<Box
width="calc(100% + calc(4 * var(--space-2)))"
height="100%"
position="absolute"
right="0"
mr="-2"
style={{
backgroundImage: `url(${asset(
`flags/scratched/${nation}.webp`
)})`,
backgroundSize: "100%",
backgroundRepeat: "no-repeat",
backgroundPosition: "5rem center",
}}
>
<Box
width="100%"
height="100%"
style={{
background: `linear-gradient(90deg, var(--gray-2) 50%, transparent)`,
}}
/>
</Box>
<Text style={{ zIndex: 1 }}>
{
strings.common.nations[
nation as keyof typeof strings.common.nations
]
}
</Text>
</DropdownMenu.CheckboxItem>
);
})}
<DropdownMenu.Separator />
<DropdownMenu.Item
color="red"
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
draft.nations = [];
});
}}
>
<TrashIcon />
Clear
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
);
}