feat: combine tabs

This commit is contained in:
TrèsAbhi committed 2026-01-04 14:58:59 -06:00
1 parent 43c14665de
commit 5a0b309b0b
2 files changed
+142 -136

No files matched your search

+2
View File
@@ -762,6 +762,8 @@
"sorting_by": "Sorting by {name}", "sorting_by": "Sorting by {name}",
"clear": "Clear", "clear": "Clear",
"tier": "Tier {tier}", "tier": "Tier {tier}",
"passive": "{name} (Passive)",
"active": "{name} (Active)",
"no_tanks_found": { "no_tanks_found": {
"body": "No tanks found.", "body": "No tanks found.",
@@ -26,7 +26,7 @@ import {
type FlexProps, type FlexProps,
} from "@radix-ui/themes"; } from "@radix-ui/themes";
import { times } from "lodash-es"; import { times } from "lodash-es";
import { Fragment, useState, type ComponentProps, type ReactNode } from "react"; import { Fragment, type ComponentProps, type ReactNode } from "react";
import { awaitableConsumableDefinitions } from "../../../core/awaitables/consumableDefinitions"; import { awaitableConsumableDefinitions } from "../../../core/awaitables/consumableDefinitions";
import { awaitableGameDefinitions } from "../../../core/awaitables/gameDefinitions"; import { awaitableGameDefinitions } from "../../../core/awaitables/gameDefinitions";
import { awaitableProvisionDefinitions } from "../../../core/awaitables/provisionDefinitions"; import { awaitableProvisionDefinitions } from "../../../core/awaitables/provisionDefinitions";
@@ -160,13 +160,16 @@ export function FilterControl() {
<ClassFilter /> <ClassFilter />
<TypeFilter /> <TypeFilter />
<NationsFilter /> <NationsFilter />
<OwnershipFilter /> <OwnershipFilter />
<TestFilter /> <TestFilter />
<GunTypeFilter /> <GunTypeFilter />
<ShellFilter /> <ShellFilter />
<ConsumablesFilter /> <ConsumablesFilter />
<ProvisionsFilter /> <ProvisionsFilter />
<GameModeAbilitiesFilter /> <GameModeAbilitiesFilter />
<ResetButton /> <ResetButton />
</Flex> </Flex>
); );
@@ -1028,165 +1031,166 @@ function ProvisionsFilter() {
} }
function GameModeAbilitiesFilter() { function GameModeAbilitiesFilter() {
const { unwrap } = useLocale(); const { unwrap, strings } = useLocale();
const rawAbilities = TankFilters.use((state) => state.abilities); const rawAbilities = TankFilters.use((state) => state.abilities);
const rawPowers = TankFilters.use((state) => state.powers); const rawPowers = TankFilters.use((state) => state.powers);
const [activeTab, setActiveTab] = useState<"active" | "passive">("active");
const totalSelected = rawAbilities.length + rawPowers.length; const abilities =
rawAbilities.length === 0 && rawPowers.length === 0
? Array.from(allGameModeConsumables.values())
: rawAbilities;
const powers =
rawPowers.length === 0 && rawAbilities.length === 0
? Array.from(allGameModeProvisions.values())
: rawPowers;
const icons = [
...abilities.map((ability) => `icons/consumables/${ability}.webp`),
...powers.map((power) => `icons/provisions/${power}.webp`),
];
return ( return (
<DropdownMenu.Root modal={false}> <DropdownMenu.Root modal={false}>
<DropdownMenu.Trigger> <DropdownMenu.Trigger>
<Button color="gray" variant="surface"> <Button color="gray" variant="surface">
Game Mode Abilities <Flex>
{totalSelected > 0 && ( {icons.slice(0, MAX_ICONS).map((icon, index) => (
<Text size="1" ml="1"> <img
({totalSelected}) key={icon}
</Text> style={{
)} filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
marginLeft: index > 0 ? "-0.5em" : undefined,
width: "1.25em",
height: "1.25em",
objectFit: "contain",
}}
src={asset(icon)}
/>
))}
{icons.length > MAX_ICONS && (
<Text size="1" ml="1">
{literals(strings.common.units.plus, {
value: icons.length - MAX_ICONS,
})}
</Text>
)}
</Flex>
</Button> </Button>
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
<DropdownMenu.Content style={{ minWidth: "249px", maxWidth: "249px" }}> <DropdownMenu.Content>
{/* Tab buttons */} {gameModeRoles.map(({ gameModeId, consumables, provisions }) => {
<Flex gap="2" p="2" mb="2" width="100%"> const gameMode = gameDefinitions.gameModes[gameModeId];
<Button const hasNeither =
size="1" consumables.length === 0 && provisions.length === 0;
variant={activeTab === "active" ? "solid" : "soft"}
color={activeTab === "active" ? "violet" : "gray"}
onClick={() => setActiveTab("active")}
style={{ flex: 1, minWidth: 0 }}
>
Active
</Button>
<Button
size="1"
variant={activeTab === "passive" ? "solid" : "soft"}
color={activeTab === "passive" ? "violet" : "gray"}
onClick={() => setActiveTab("passive")}
style={{ flex: 1, minWidth: 0 }}
>
Passive
</Button>
</Flex>
<DropdownMenu.Separator /> if (hasNeither) return null;
{/* Active Abilities Content */} const hasBoth = consumables.length > 0 && provisions.length > 0;
{activeTab === "active" && (
<>
{gameModeRoles.map(({ gameModeId, consumables }) => {
const gameMode = gameDefinitions.gameModes[gameModeId];
if (consumables.length === 0) return null; return (
<Fragment key={gameModeId}>
{consumables.length > 0 && (
<DropdownMenu.Label>
{hasBoth
? literals(strings.website.common.tank_search.active, {
name: unwrap(gameMode.name),
})
: unwrap(gameMode.name)}
</DropdownMenu.Label>
)}
return ( {consumables.map((consumable) => {
<Fragment key={`active-${gameModeId}`}> {
<DropdownMenu.Label> const selected = rawAbilities.includes(consumable);
{unwrap(gameMode.name)} const abilityDefinition =
</DropdownMenu.Label> consumableDefinitions.consumables[consumable];
{consumables.map((consumable) => { return (
const selected = rawAbilities.includes(consumable); <DropdownMenu.CheckboxItem
const abilityDefinition = onClick={(event) => {
consumableDefinitions.consumables[consumable]; event.preventDefault();
return ( TankFilters.mutate((draft) => {
<DropdownMenu.CheckboxItem if (selected) {
onClick={(event) => { draft.abilities = draft.abilities.filter(
event.preventDefault(); (n) => n !== consumable
);
TankFilters.mutate((draft) => { } else {
if (selected) { draft.abilities = [...draft.abilities, consumable];
draft.abilities = draft.abilities.filter( }
(n) => n !== consumable });
); }}
} else { checked={selected}
draft.abilities = [ key={consumable}
...draft.abilities, >
consumable, <img
]; style={{
} width: "1.25em",
}); height: "1.25em",
objectFit: "contain",
}} }}
checked={selected} src={asset(`icons/consumables/${consumable}.webp`)}
key={consumable} />
>
<img
style={{
width: "1.25em",
height: "1.25em",
objectFit: "contain",
}}
src={asset(`icons/consumables/${consumable}.webp`)}
/>
{unwrap(abilityDefinition.name)} {unwrap(abilityDefinition.name)}
</DropdownMenu.CheckboxItem> </DropdownMenu.CheckboxItem>
); );
})} }
</Fragment> })}
);
})}
</>
)}
{/* Passive Abilities Content */} {provisions.length > 0 && (
{activeTab === "passive" && ( <DropdownMenu.Label>
<> {hasBoth
{gameModeRoles.map(({ gameModeId, provisions }) => { ? literals(strings.website.common.tank_search.passive, {
const gameMode = gameDefinitions.gameModes[gameModeId]; name: unwrap(gameMode.name),
})
: unwrap(gameMode.name)}
</DropdownMenu.Label>
)}
if (provisions.length === 0) return null; {provisions.map((provision) => {
{
const selected = rawPowers.includes(provision);
const powerDefinition =
provisionDefinitions.provisions[provision];
return ( return (
<Fragment key={`passive-${gameModeId}`}> <DropdownMenu.CheckboxItem
<DropdownMenu.Label> onClick={(event) => {
{unwrap(gameMode.name)} event.preventDefault();
</DropdownMenu.Label>
{provisions.map((provision) => { TankFilters.mutate((draft) => {
const selected = rawPowers.includes(provision); if (selected) {
const powerDefinition = draft.powers = draft.powers.filter(
provisionDefinitions.provisions[provision]; (n) => n !== provision
);
return ( } else {
<DropdownMenu.CheckboxItem draft.powers = [...draft.powers, provision];
onClick={(event) => { }
event.preventDefault(); });
}}
TankFilters.mutate((draft) => { checked={selected}
if (selected) { key={provision}
draft.powers = draft.powers.filter( >
(n) => n !== provision <img
); style={{
} else { width: "1.25em",
draft.powers = [...draft.powers, provision]; height: "1.25em",
} objectFit: "contain",
});
}} }}
checked={selected} src={asset(`icons/provisions/${provision}.webp`)}
key={provision} />
>
<img
style={{
width: "1.25em",
height: "1.25em",
objectFit: "contain",
}}
src={asset(`icons/provisions/${provision}.webp`)}
/>
{unwrap(powerDefinition.name)} {unwrap(powerDefinition.name)}
</DropdownMenu.CheckboxItem> </DropdownMenu.CheckboxItem>
); );
})} }
</Fragment> })}
); </Fragment>
})} );
</> })}
)}
<DropdownMenu.Separator /> <DropdownMenu.Separator />
@@ -1202,7 +1206,7 @@ function GameModeAbilitiesFilter() {
}} }}
> >
<TrashIcon /> <TrashIcon />
Clear All {strings.website.common.tank_search.clear}
</DropdownMenu.Item> </DropdownMenu.Item>
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Root> </DropdownMenu.Root>