mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: filter tooltips
This commit is contained in:
1 parent
da6691928e
commit
2e97ccd40d
2 files changed
+248
-201
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ReloadIcon } from '@radix-ui/react-icons';
|
import { ReloadIcon } from '@radix-ui/react-icons';
|
||||||
import { Box, Flex, IconButton, Text } from '@radix-ui/themes';
|
import { Box, Flex, IconButton, Text, Tooltip } from '@radix-ui/themes';
|
||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { use } from 'react';
|
import { use } from 'react';
|
||||||
import { classIcons } from '../../../../components/ClassIcon';
|
import { classIcons } from '../../../../components/ClassIcon';
|
||||||
@@ -14,6 +14,7 @@ import { asset } from '../../../../core/blitzkit/asset';
|
|||||||
import { gameDefinitions } from '../../../../core/blitzkit/gameDefinitions';
|
import { gameDefinitions } from '../../../../core/blitzkit/gameDefinitions';
|
||||||
import { Tier } from '../../../../core/blitzkit/tankDefinitions';
|
import { Tier } from '../../../../core/blitzkit/tankDefinitions';
|
||||||
import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants';
|
import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants';
|
||||||
|
import strings from '../../../../lang/en-US.json';
|
||||||
import * as TankFilters from '../../../../stores/tankFilters';
|
import * as TankFilters from '../../../../stores/tankFilters';
|
||||||
|
|
||||||
interface FilterControlProps {
|
interface FilterControlProps {
|
||||||
@@ -39,24 +40,26 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
const selected = filters.tiers?.includes(tier);
|
const selected = filters.tiers?.includes(tier);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<Tooltip key={tier} content={`Tier ${tier}`}>
|
||||||
key={tier}
|
<IconButton
|
||||||
variant={selected ? 'solid' : 'soft'}
|
key={tier}
|
||||||
radius="none"
|
variant={selected ? 'solid' : 'soft'}
|
||||||
color={selected ? undefined : 'gray'}
|
radius="none"
|
||||||
highContrast
|
color={selected ? undefined : 'gray'}
|
||||||
onClick={() =>
|
highContrast
|
||||||
mutateTankFilters((draft) => {
|
onClick={() =>
|
||||||
if (draft.tiers?.includes(tier)) {
|
mutateTankFilters((draft) => {
|
||||||
draft.tiers = draft.tiers?.filter((t) => t !== tier);
|
if (draft.tiers?.includes(tier)) {
|
||||||
} else {
|
draft.tiers = draft.tiers?.filter((t) => t !== tier);
|
||||||
draft.tiers = [...(draft.tiers ?? []), tier];
|
} else {
|
||||||
}
|
draft.tiers = [...(draft.tiers ?? []), tier];
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
>
|
}
|
||||||
<Text size="2">{TIER_ROMAN_NUMERALS[tier]}</Text>
|
>
|
||||||
</IconButton>
|
<Text size="2">{TIER_ROMAN_NUMERALS[tier]}</Text>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -66,24 +69,26 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
const selected = filters.tiers?.includes(tier);
|
const selected = filters.tiers?.includes(tier);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<Tooltip key={tier} content={`Tier ${tier}`}>
|
||||||
key={tier}
|
<IconButton
|
||||||
variant={selected ? 'solid' : 'soft'}
|
key={tier}
|
||||||
radius="none"
|
variant={selected ? 'solid' : 'soft'}
|
||||||
color={selected ? undefined : 'gray'}
|
radius="none"
|
||||||
highContrast
|
color={selected ? undefined : 'gray'}
|
||||||
onClick={() =>
|
highContrast
|
||||||
mutateTankFilters((draft) => {
|
onClick={() =>
|
||||||
if (draft.tiers?.includes(tier)) {
|
mutateTankFilters((draft) => {
|
||||||
draft.tiers = draft.tiers?.filter((t) => t !== tier);
|
if (draft.tiers?.includes(tier)) {
|
||||||
} else {
|
draft.tiers = draft.tiers?.filter((t) => t !== tier);
|
||||||
draft.tiers = [...(draft.tiers ?? []), tier];
|
} else {
|
||||||
}
|
draft.tiers = [...(draft.tiers ?? []), tier];
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
>
|
}
|
||||||
<Text size="2">{TIER_ROMAN_NUMERALS[tier]}</Text>
|
>
|
||||||
</IconButton>
|
<Text size="2">{TIER_ROMAN_NUMERALS[tier]}</Text>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -101,29 +106,37 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
const selected = filters.nations?.includes(nation);
|
const selected = filters.nations?.includes(nation);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<Tooltip
|
||||||
key={nation}
|
content={
|
||||||
variant={selected ? 'solid' : 'soft'}
|
strings.common.nations[
|
||||||
color={selected ? undefined : 'gray'}
|
nation as keyof typeof strings.common.nations
|
||||||
highContrast
|
]
|
||||||
radius="none"
|
|
||||||
onClick={() =>
|
|
||||||
mutateTankFilters((draft) => {
|
|
||||||
if (draft.nations?.includes(nation)) {
|
|
||||||
draft.nations = draft.nations?.filter(
|
|
||||||
(n) => n !== nation,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.nations = [...(draft.nations ?? []), nation];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
key={nation}
|
||||||
>
|
>
|
||||||
<img
|
<IconButton
|
||||||
style={{ width: '1em', height: '1em' }}
|
variant={selected ? 'solid' : 'soft'}
|
||||||
src={asset(`flags/circle/${nation}.webp`)}
|
color={selected ? undefined : 'gray'}
|
||||||
/>
|
highContrast
|
||||||
</IconButton>
|
radius="none"
|
||||||
|
onClick={() =>
|
||||||
|
mutateTankFilters((draft) => {
|
||||||
|
if (draft.nations?.includes(nation)) {
|
||||||
|
draft.nations = draft.nations?.filter(
|
||||||
|
(n) => n !== nation,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draft.nations = [...(draft.nations ?? []), nation];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{ width: '1em', height: '1em' }}
|
||||||
|
src={asset(`flags/circle/${nation}.webp`)}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -134,30 +147,39 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
const selected = filters.nations?.includes(nation);
|
const selected = filters.nations?.includes(nation);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<Tooltip
|
||||||
key={nation}
|
content={
|
||||||
style={{ flex: 1 }}
|
strings.common.nations[
|
||||||
variant={selected ? 'solid' : 'soft'}
|
nation as keyof typeof strings.common.nations
|
||||||
color={selected ? undefined : 'gray'}
|
]
|
||||||
highContrast
|
|
||||||
radius="none"
|
|
||||||
onClick={() =>
|
|
||||||
mutateTankFilters((draft) => {
|
|
||||||
if (draft.nations?.includes(nation)) {
|
|
||||||
draft.nations = draft.nations?.filter(
|
|
||||||
(n) => n !== nation,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.nations = [...(draft.nations ?? []), nation];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
key={nation}
|
||||||
>
|
>
|
||||||
<img
|
<IconButton
|
||||||
style={{ width: '1em', height: '1em' }}
|
key={nation}
|
||||||
src={asset(`flags/circle/${nation}.webp`)}
|
style={{ flex: 1 }}
|
||||||
/>
|
variant={selected ? 'solid' : 'soft'}
|
||||||
</IconButton>
|
color={selected ? undefined : 'gray'}
|
||||||
|
highContrast
|
||||||
|
radius="none"
|
||||||
|
onClick={() =>
|
||||||
|
mutateTankFilters((draft) => {
|
||||||
|
if (draft.nations?.includes(nation)) {
|
||||||
|
draft.nations = draft.nations?.filter(
|
||||||
|
(n) => n !== nation,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draft.nations = [...(draft.nations ?? []), nation];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{ width: '1em', height: '1em' }}
|
||||||
|
src={asset(`flags/circle/${nation}.webp`)}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -173,26 +195,30 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
const selected = filters.classes?.includes(tankClass);
|
const selected = filters.classes?.includes(tankClass);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<Tooltip
|
||||||
|
content={strings.common.tank_class_medium[tankClass]}
|
||||||
key={tankClass}
|
key={tankClass}
|
||||||
variant={selected ? 'solid' : 'soft'}
|
|
||||||
radius="none"
|
|
||||||
color={selected ? undefined : 'gray'}
|
|
||||||
highContrast
|
|
||||||
onClick={() =>
|
|
||||||
mutateTankFilters((draft) => {
|
|
||||||
if (draft.classes?.includes(tankClass)) {
|
|
||||||
draft.classes = draft.classes?.filter(
|
|
||||||
(c) => c !== tankClass,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.classes = [...(draft.classes ?? []), tankClass];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Icon style={{ width: '1em', height: '1em' }} />
|
<IconButton
|
||||||
</IconButton>
|
variant={selected ? 'solid' : 'soft'}
|
||||||
|
radius="none"
|
||||||
|
color={selected ? undefined : 'gray'}
|
||||||
|
highContrast
|
||||||
|
onClick={() =>
|
||||||
|
mutateTankFilters((draft) => {
|
||||||
|
if (draft.classes?.includes(tankClass)) {
|
||||||
|
draft.classes = draft.classes?.filter(
|
||||||
|
(c) => c !== tankClass,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draft.classes = [...(draft.classes ?? []), tankClass];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon style={{ width: '1em', height: '1em' }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -202,67 +228,75 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
style={{ borderRadius: 'var(--radius-full)' }}
|
style={{ borderRadius: 'var(--radius-full)' }}
|
||||||
direction={compact ? 'column' : { sm: 'row', initial: 'column' }}
|
direction={compact ? 'column' : { sm: 'row', initial: 'column' }}
|
||||||
>
|
>
|
||||||
<IconButton
|
<Tooltip content="Researchable">
|
||||||
variant={filters.types?.includes('researchable') ? 'solid' : 'soft'}
|
<IconButton
|
||||||
radius="none"
|
variant={filters.types?.includes('researchable') ? 'solid' : 'soft'}
|
||||||
color={filters.types?.includes('researchable') ? undefined : 'gray'}
|
radius="none"
|
||||||
highContrast
|
color={filters.types?.includes('researchable') ? undefined : 'gray'}
|
||||||
onClick={() =>
|
highContrast
|
||||||
mutateTankFilters((draft) => {
|
onClick={() =>
|
||||||
if (draft.types?.includes('researchable')) {
|
mutateTankFilters((draft) => {
|
||||||
draft.types = draft.types?.filter((t) => t !== 'researchable');
|
if (draft.types?.includes('researchable')) {
|
||||||
} else {
|
draft.types = draft.types?.filter(
|
||||||
draft.types = [...(draft.types ?? []), 'researchable'];
|
(t) => t !== 'researchable',
|
||||||
}
|
);
|
||||||
})
|
} else {
|
||||||
}
|
draft.types = [...(draft.types ?? []), 'researchable'];
|
||||||
>
|
}
|
||||||
<ResearchedIcon style={{ width: '1em', height: '1em' }} />
|
})
|
||||||
</IconButton>
|
}
|
||||||
<IconButton
|
|
||||||
variant={filters.types?.includes('premium') ? 'solid' : 'soft'}
|
|
||||||
radius="none"
|
|
||||||
color={filters.types?.includes('premium') ? 'amber' : 'gray'}
|
|
||||||
highContrast
|
|
||||||
onClick={() =>
|
|
||||||
mutateTankFilters((draft) => {
|
|
||||||
if (draft.types?.includes('premium')) {
|
|
||||||
draft.types = draft.types?.filter((t) => t !== 'premium');
|
|
||||||
} else {
|
|
||||||
draft.types = [...(draft.types ?? []), 'premium'];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={filters.types?.includes('premium') ? undefined : 'amber'}
|
|
||||||
style={{ display: 'flex', justifyContent: 'center' }}
|
|
||||||
>
|
>
|
||||||
<ResearchedIcon style={{ width: '1em', height: '1em' }} />
|
<ResearchedIcon style={{ width: '1em', height: '1em' }} />
|
||||||
</Text>
|
</IconButton>
|
||||||
</IconButton>
|
</Tooltip>
|
||||||
<IconButton
|
<Tooltip content="Premium">
|
||||||
variant={filters.types?.includes('collector') ? 'solid' : 'soft'}
|
<IconButton
|
||||||
radius="none"
|
variant={filters.types?.includes('premium') ? 'solid' : 'soft'}
|
||||||
color={filters.types?.includes('collector') ? 'blue' : 'gray'}
|
radius="none"
|
||||||
highContrast
|
color={filters.types?.includes('premium') ? 'amber' : 'gray'}
|
||||||
onClick={() =>
|
highContrast
|
||||||
mutateTankFilters((draft) => {
|
onClick={() =>
|
||||||
if (draft.types?.includes('collector')) {
|
mutateTankFilters((draft) => {
|
||||||
draft.types = draft.types?.filter((t) => t !== 'collector');
|
if (draft.types?.includes('premium')) {
|
||||||
} else {
|
draft.types = draft.types?.filter((t) => t !== 'premium');
|
||||||
draft.types = [...(draft.types ?? []), 'collector'];
|
} else {
|
||||||
}
|
draft.types = [...(draft.types ?? []), 'premium'];
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
>
|
}
|
||||||
<Text
|
|
||||||
color={filters.types?.includes('collector') ? undefined : 'blue'}
|
|
||||||
style={{ display: 'flex', justifyContent: 'center' }}
|
|
||||||
>
|
>
|
||||||
<ResearchedIcon style={{ width: '1em', height: '1em' }} />
|
<Text
|
||||||
</Text>
|
color={filters.types?.includes('premium') ? undefined : 'amber'}
|
||||||
</IconButton>
|
style={{ display: 'flex', justifyContent: 'center' }}
|
||||||
|
>
|
||||||
|
<ResearchedIcon style={{ width: '1em', height: '1em' }} />
|
||||||
|
</Text>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip content="Collector">
|
||||||
|
<IconButton
|
||||||
|
variant={filters.types?.includes('collector') ? 'solid' : 'soft'}
|
||||||
|
radius="none"
|
||||||
|
color={filters.types?.includes('collector') ? 'blue' : 'gray'}
|
||||||
|
highContrast
|
||||||
|
onClick={() =>
|
||||||
|
mutateTankFilters((draft) => {
|
||||||
|
if (draft.types?.includes('collector')) {
|
||||||
|
draft.types = draft.types?.filter((t) => t !== 'collector');
|
||||||
|
} else {
|
||||||
|
draft.types = [...(draft.types ?? []), 'collector'];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
color={filters.types?.includes('collector') ? undefined : 'blue'}
|
||||||
|
style={{ display: 'flex', justifyContent: 'center' }}
|
||||||
|
>
|
||||||
|
<ResearchedIcon style={{ width: '1em', height: '1em' }} />
|
||||||
|
</Text>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
@@ -270,51 +304,57 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
style={{ borderRadius: 'var(--radius-full)' }}
|
style={{ borderRadius: 'var(--radius-full)' }}
|
||||||
direction={compact ? 'column' : { sm: 'row', initial: 'column' }}
|
direction={compact ? 'column' : { sm: 'row', initial: 'column' }}
|
||||||
>
|
>
|
||||||
<IconButton
|
<Tooltip content="Include test tank">
|
||||||
variant={filters.testing === 'include' ? 'solid' : 'soft'}
|
<IconButton
|
||||||
radius="none"
|
variant={filters.testing === 'include' ? 'solid' : 'soft'}
|
||||||
color={filters.testing === 'include' ? undefined : 'gray'}
|
radius="none"
|
||||||
highContrast
|
color={filters.testing === 'include' ? undefined : 'gray'}
|
||||||
onClick={() =>
|
highContrast
|
||||||
mutateTankFilters((draft) => {
|
onClick={() =>
|
||||||
draft.testing = 'include';
|
mutateTankFilters((draft) => {
|
||||||
})
|
draft.testing = 'include';
|
||||||
}
|
})
|
||||||
>
|
}
|
||||||
<ExperimentIcon
|
>
|
||||||
style={{ width: '1em', height: '1em', color: 'currentColor' }}
|
<ExperimentIcon
|
||||||
/>
|
style={{ width: '1em', height: '1em', color: 'currentColor' }}
|
||||||
</IconButton>
|
/>
|
||||||
<IconButton
|
</IconButton>
|
||||||
variant={filters.testing === 'exclude' ? 'solid' : 'soft'}
|
</Tooltip>
|
||||||
radius="none"
|
<Tooltip content="Exclude test tank">
|
||||||
color={filters.testing === 'exclude' ? undefined : 'gray'}
|
<IconButton
|
||||||
highContrast
|
variant={filters.testing === 'exclude' ? 'solid' : 'soft'}
|
||||||
onClick={() =>
|
radius="none"
|
||||||
mutateTankFilters((draft) => {
|
color={filters.testing === 'exclude' ? undefined : 'gray'}
|
||||||
draft.testing = 'exclude';
|
highContrast
|
||||||
})
|
onClick={() =>
|
||||||
}
|
mutateTankFilters((draft) => {
|
||||||
>
|
draft.testing = 'exclude';
|
||||||
<ScienceOffIcon
|
})
|
||||||
style={{ width: '1em', height: '1em', color: 'currentColor' }}
|
}
|
||||||
/>
|
>
|
||||||
</IconButton>
|
<ScienceOffIcon
|
||||||
<IconButton
|
style={{ width: '1em', height: '1em', color: 'currentColor' }}
|
||||||
variant={filters.testing === 'only' ? 'solid' : 'soft'}
|
/>
|
||||||
radius="none"
|
</IconButton>
|
||||||
color={filters.testing === 'only' ? undefined : 'gray'}
|
</Tooltip>
|
||||||
highContrast
|
<Tooltip content="Only test tank">
|
||||||
onClick={() =>
|
<IconButton
|
||||||
mutateTankFilters((draft) => {
|
variant={filters.testing === 'only' ? 'solid' : 'soft'}
|
||||||
draft.testing = 'only';
|
radius="none"
|
||||||
})
|
color={filters.testing === 'only' ? undefined : 'gray'}
|
||||||
}
|
highContrast
|
||||||
>
|
onClick={() =>
|
||||||
<ScienceIcon
|
mutateTankFilters((draft) => {
|
||||||
style={{ width: '1em', height: '1em', color: 'currentColor' }}
|
draft.testing = 'only';
|
||||||
/>
|
})
|
||||||
</IconButton>
|
}
|
||||||
|
>
|
||||||
|
<ScienceIcon
|
||||||
|
style={{ width: '1em', height: '1em', color: 'currentColor' }}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
@@ -58,6 +58,13 @@
|
|||||||
"AT-SPG": "TD"
|
"AT-SPG": "TD"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"tank_class_medium": {
|
||||||
|
"lightTank": "Light",
|
||||||
|
"mediumTank": "Medium",
|
||||||
|
"heavyTank": "Heavy",
|
||||||
|
"AT-SPG": "Tank destroyer"
|
||||||
|
},
|
||||||
|
|
||||||
"tree_type": {
|
"tree_type": {
|
||||||
"researchable": "Tech tree",
|
"researchable": "Tech tree",
|
||||||
"premium": "Premium",
|
"premium": "Premium",
|
||||||
|
|||||||
Reference in new issue
Block a user