feat: cleaner filters

This commit is contained in:
TrèsAbhi committed 2023-12-06 12:40:43 -06:00
1 parent b804e60bac
commit 84a1af8100
5 files changed
+388 -185

No files matched your search

@@ -0,0 +1,17 @@
import { style } from '@vanilla-extract/css';
export const splitFilters = style({
'@media': {
'screen and (min-width: 513px)': {
display: 'none',
},
},
});
export const oneLineFilters = style({
'@media': {
'screen and (max-width: 512px)': {
display: 'none',
},
},
});
@@ -9,19 +9,18 @@ import {
TREE_TYPE_ICONS, TREE_TYPE_ICONS,
TREE_TYPE_IMAGES, TREE_TYPE_IMAGES,
TreeTypeEnum, TreeTypeEnum,
} from '../../../../components/Tanks'; } from '../../../../../components/Tanks';
import { asset } from '../../../../core/blitzkrieg/asset'; import { asset } from '../../../../../core/blitzkrieg/asset';
import { import {
NATIONS, NATIONS,
TIERS, TIERS,
TIER_ROMAN_NUMERALS, TIER_ROMAN_NUMERALS,
} from '../../../../core/blitzkrieg/definitions/tanks'; } from '../../../../../core/blitzkrieg/definitions/tanks';
import mutateTankopedia, { import mutateTankopedia, {
TankopediaSortBy,
TankopediaSortDirection,
TankopediaTestTankDisplay, TankopediaTestTankDisplay,
useTankopedia, useTankopedia,
} from '../../../../stores/tankopedia'; } from '../../../../../stores/tankopedia';
import * as styles from './index.css';
export function Options() { export function Options() {
const nations = use(NATIONS); const nations = use(NATIONS);
@@ -29,55 +28,22 @@ export function Options() {
return ( return (
<Flex justify="center" wrap="wrap" gap="2"> <Flex justify="center" wrap="wrap" gap="2">
<Flex gap="1"> <Button
<DropdownMenu.Root> color="red"
<DropdownMenu.Trigger> onClick={() =>
<Button variant="soft"> useTankopedia.setState({
Sort by filters: {
<CaretDownIcon /> nations: [],
</Button> tiers: [],
</DropdownMenu.Trigger> treeTypes: [],
<DropdownMenu.Content> types: [],
<DropdownMenu.RadioGroup test: 'include',
value={tankopediaState.sort.by} },
onValueChange={(value) => })
mutateTankopedia((draft) => { }
draft.sort.by = value as TankopediaSortBy; >
}) Clear
} </Button>
>
<DropdownMenu.RadioItem value="tier">Tier</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="name">Name</DropdownMenu.RadioItem>
</DropdownMenu.RadioGroup>
</DropdownMenu.Content>
</DropdownMenu.Root>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button variant="soft">
Sort order
<CaretDownIcon />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.RadioGroup
value={tankopediaState.sort.direction}
onValueChange={(value) =>
mutateTankopedia((draft) => {
draft.sort.direction = value as TankopediaSortDirection;
})
}
>
<DropdownMenu.RadioItem value="ascending">
Ascending
</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="descending">
Descending
</DropdownMenu.RadioItem>
</DropdownMenu.RadioGroup>
</DropdownMenu.Content>
</DropdownMenu.Root>
</Flex>
<DropdownMenu.Root> <DropdownMenu.Root>
<DropdownMenu.Trigger> <DropdownMenu.Trigger>
@@ -106,39 +72,46 @@ export function Options() {
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Root> </DropdownMenu.Root>
<Flex> <Flex style={{ flex: 1 }}>
{[...TIERS].reverse().map((tier, index) => ( {TREE_TYPES.map((type, index) => (
<Button <Button
key={tier} key={type}
variant={ variant={
tankopediaState.filters.tiers.includes(tier) ? 'solid' : 'soft' tankopediaState.filters.treeTypes.includes(type)
? 'solid'
: 'soft'
} }
style={{ style={{
flex: 1,
margin: -0.5, margin: -0.5,
borderTopLeftRadius: index === 0 ? undefined : 0, borderTopLeftRadius: index === 0 ? undefined : 0,
borderBottomLeftRadius: index === 0 ? undefined : 0, borderBottomLeftRadius: index === 0 ? undefined : 0,
borderTopRightRadius: index === TIERS.length - 1 ? undefined : 0, borderTopRightRadius:
index === TREE_TYPES.length - 1 ? undefined : 0,
borderBottomRightRadius: borderBottomRightRadius:
index === TIERS.length - 1 ? undefined : 0, index === TREE_TYPES.length - 1 ? undefined : 0,
}} }}
onClick={() => onClick={() =>
mutateTankopedia((draft) => { mutateTankopedia((draft) => {
if (draft.filters.tiers.includes(tier)) { if (draft.filters.treeTypes.includes(type)) {
draft.filters.tiers = draft.filters.tiers.filter( draft.filters.treeTypes = draft.filters.treeTypes.filter(
(preexistingTier) => preexistingTier !== tier, (preexistingType) => preexistingType !== type,
); );
} else { } else {
draft.filters.tiers.push(tier); draft.filters.treeTypes.push(type);
} }
}) })
} }
> >
{TIER_ROMAN_NUMERALS[tier]} <img
src={TREE_TYPE_IMAGES[type]}
style={{ width: '1em', height: '1em' }}
/>
</Button> </Button>
))} ))}
</Flex> </Flex>
<Flex> <Flex style={{ flex: 1 }}>
{[...TANK_TYPES].reverse().map((type, index) => ( {[...TANK_TYPES].reverse().map((type, index) => (
<Button <Button
key={type} key={type}
@@ -146,6 +119,7 @@ export function Options() {
tankopediaState.filters.types.includes(type) ? 'solid' : 'soft' tankopediaState.filters.types.includes(type) ? 'solid' : 'soft'
} }
style={{ style={{
flex: 1,
margin: -0.5, margin: -0.5,
borderTopLeftRadius: index === 0 ? undefined : 0, borderTopLeftRadius: index === 0 ? undefined : 0,
borderBottomLeftRadius: index === 0 ? undefined : 0, borderBottomLeftRadius: index === 0 ? undefined : 0,
@@ -174,7 +148,40 @@ export function Options() {
))} ))}
</Flex> </Flex>
<Flex> <Flex className={styles.oneLineFilters} style={{ flex: 1 }}>
{[...TIERS].reverse().map((tier, index) => (
<Button
key={tier}
variant={
tankopediaState.filters.tiers.includes(tier) ? 'solid' : 'soft'
}
style={{
flex: 1,
margin: -0.5,
borderTopLeftRadius: index === 0 ? undefined : 0,
borderBottomLeftRadius: index === 0 ? undefined : 0,
borderTopRightRadius: index === TIERS.length - 1 ? undefined : 0,
borderBottomRightRadius:
index === TIERS.length - 1 ? undefined : 0,
}}
onClick={() =>
mutateTankopedia((draft) => {
if (draft.filters.tiers.includes(tier)) {
draft.filters.tiers = draft.filters.tiers.filter(
(preexistingTier) => preexistingTier !== tier,
);
} else {
draft.filters.tiers.push(tier);
}
})
}
>
{TIER_ROMAN_NUMERALS[tier]}
</Button>
))}
</Flex>
<Flex className={styles.oneLineFilters} style={{ flex: 1 }}>
{nations.map((nation, index) => ( {nations.map((nation, index) => (
<Button <Button
key={nation} key={nation}
@@ -184,6 +191,7 @@ export function Options() {
: 'soft' : 'soft'
} }
style={{ style={{
flex: 1,
margin: -0.5, margin: -0.5,
borderTopLeftRadius: index === 0 ? undefined : 0, borderTopLeftRadius: index === 0 ? undefined : 0,
borderBottomLeftRadius: index === 0 ? undefined : 0, borderBottomLeftRadius: index === 0 ? undefined : 0,
@@ -217,60 +225,181 @@ export function Options() {
))} ))}
</Flex> </Flex>
<Flex> <Flex
{TREE_TYPES.map((type, index) => ( direction="column"
<Button className={styles.splitFilters}
key={type} style={{ width: '100%' }}
variant={ >
tankopediaState.filters.treeTypes.includes(type) <Flex>
? 'solid' {[...TIERS]
: 'soft' .reverse()
} .slice(0, 5)
style={{ .map((tier, index) => (
margin: -0.5, <Button
borderTopLeftRadius: index === 0 ? undefined : 0, key={tier}
borderBottomLeftRadius: index === 0 ? undefined : 0, variant={
borderTopRightRadius: tankopediaState.filters.tiers.includes(tier)
index === TREE_TYPES.length - 1 ? undefined : 0, ? 'solid'
borderBottomRightRadius: : 'soft'
index === TREE_TYPES.length - 1 ? undefined : 0,
}}
onClick={() =>
mutateTankopedia((draft) => {
if (draft.filters.treeTypes.includes(type)) {
draft.filters.treeTypes = draft.filters.treeTypes.filter(
(preexistingType) => preexistingType !== type,
);
} else {
draft.filters.treeTypes.push(type);
} }
}) style={{
} flex: 1,
> margin: -0.5,
<img borderTopLeftRadius: index === 0 ? undefined : 0,
src={TREE_TYPE_IMAGES[type]} borderBottomLeftRadius: 0,
style={{ width: '1em', height: '1em' }} borderTopRightRadius:
/> index === TIERS.length - 6 ? undefined : 0,
</Button> borderBottomRightRadius: 0,
))} }}
onClick={() =>
mutateTankopedia((draft) => {
if (draft.filters.tiers.includes(tier)) {
draft.filters.tiers = draft.filters.tiers.filter(
(preexistingTier) => preexistingTier !== tier,
);
} else {
draft.filters.tiers.push(tier);
}
})
}
>
{TIER_ROMAN_NUMERALS[tier]}
</Button>
))}
</Flex>
<Flex>
{[...TIERS]
.reverse()
.slice(5, 10)
.map((tier, index) => (
<Button
key={tier}
variant={
tankopediaState.filters.tiers.includes(tier)
? 'solid'
: 'soft'
}
style={{
flex: 1,
margin: -0.5,
borderTopLeftRadius: 0,
borderBottomLeftRadius: index === 0 ? undefined : 0,
borderTopRightRadius: 0,
borderBottomRightRadius:
index === TIERS.length - 6 ? undefined : 0,
}}
onClick={() =>
mutateTankopedia((draft) => {
if (draft.filters.tiers.includes(tier)) {
draft.filters.tiers = draft.filters.tiers.filter(
(preexistingTier) => preexistingTier !== tier,
);
} else {
draft.filters.tiers.push(tier);
}
})
}
>
{TIER_ROMAN_NUMERALS[tier]}
</Button>
))}
</Flex>
</Flex> </Flex>
<Button <Flex
color="red" direction="column"
onClick={() => className={styles.splitFilters}
useTankopedia.setState({ style={{ width: '100%' }}
filters: {
nations: [],
tiers: [],
treeTypes: [],
types: [],
test: 'include',
},
})
}
> >
Clear <Flex>
</Button> {nations.slice(0, 5).map((nation, index) => (
<Button
key={nation}
variant={
tankopediaState.filters.nations.includes(nation)
? 'solid'
: 'soft'
}
style={{
flex: 1,
margin: -0.5,
borderTopLeftRadius: index === 0 ? undefined : 0,
borderBottomLeftRadius: 0,
borderTopRightRadius:
index === nations.length - 5 ? undefined : 0,
borderBottomRightRadius:
index === nations.length - 1 ? undefined : 0,
}}
onClick={() =>
mutateTankopedia((draft) => {
if (draft.filters.nations.includes(nation)) {
draft.filters.nations = draft.filters.nations.filter(
(preexistingType) => preexistingType !== nation,
);
} else {
draft.filters.nations.push(nation);
}
})
}
>
<img
src={asset(`flags/circle/${nation}.webp`)}
style={{
width: '1em',
height: '1em',
scale: 2,
transform: 'translate(15%, 15%)',
}}
/>
</Button>
))}
</Flex>
<Flex>
{nations.slice(5, 10).map((nation, index) => (
<Button
key={nation}
variant={
tankopediaState.filters.nations.includes(nation)
? 'solid'
: 'soft'
}
style={{
flex: 1,
margin: -0.5,
borderTopLeftRadius: 0,
borderBottomLeftRadius: index === 0 ? undefined : 0,
borderTopRightRadius:
index === nations.length - 1 ? undefined : 0,
borderBottomRightRadius:
index === nations.length - 6 ? undefined : 0,
}}
onClick={() =>
mutateTankopedia((draft) => {
if (draft.filters.nations.includes(nation)) {
draft.filters.nations = draft.filters.nations.filter(
(preexistingType) => preexistingType !== nation,
);
} else {
draft.filters.nations.push(nation);
}
})
}
>
<img
src={asset(`flags/circle/${nation}.webp`)}
style={{
width: '1em',
height: '1em',
scale: 2,
transform: 'translate(15%, 15%)',
}}
/>
</Button>
))}
</Flex>
</Flex>
</Flex> </Flex>
); );
} }
+94 -27
View File
@@ -1,8 +1,16 @@
'use client'; 'use client';
import { slateDark } from '@radix-ui/colors'; import { slateDark } from '@radix-ui/colors';
import { MagnifyingGlassIcon } from '@radix-ui/react-icons'; import { CaretDownIcon, MagnifyingGlassIcon } from '@radix-ui/react-icons';
import { Card, Flex, Inset, Text, TextField } from '@radix-ui/themes'; import {
Button,
Card,
DropdownMenu,
Flex,
Inset,
Text,
TextField,
} from '@radix-ui/themes';
import { go } from 'fuzzysort'; import { go } from 'fuzzysort';
import { Suspense, use, useEffect, useMemo, useRef, useState } from 'react'; import { Suspense, use, useEffect, useMemo, useRef, useState } from 'react';
import PageWrapper from '../../../components/PageWrapper'; import PageWrapper from '../../../components/PageWrapper';
@@ -17,7 +25,12 @@ import {
} from '../../../core/blitzkrieg/definitions/tanks'; } from '../../../core/blitzkrieg/definitions/tanks';
import { tankIcon } from '../../../core/blitzkrieg/tankIcon'; import { tankIcon } from '../../../core/blitzkrieg/tankIcon';
import { theme } from '../../../stitches.config'; import { theme } from '../../../stitches.config';
import { useTankopedia } from '../../../stores/tankopedia'; import mutateTankopedia, {
TankopediaSortBy,
TankopediaSortDirection,
TankopediaTestTankDisplay,
useTankopedia,
} from '../../../stores/tankopedia';
import { Options } from './components/Options'; import { Options } from './components/Options';
import { PageTurner } from './components/PageTurner'; import { PageTurner } from './components/PageTurner';
import * as styles from './page.css'; import * as styles from './page.css';
@@ -90,30 +103,79 @@ export default function Page() {
<PageWrapper size="wide" color="purple"> <PageWrapper size="wide" color="purple">
<Suspense fallback={<Text>Loading...</Text>}> <Suspense fallback={<Text>Loading...</Text>}>
<Flex direction="column" gap="6"> <Flex direction="column" gap="6">
<TextField.Root> <Flex gap="2">
<TextField.Slot> <TextField.Root style={{ flex: 1 }}>
<MagnifyingGlassIcon height="16" width="16" /> <TextField.Slot>
</TextField.Slot> <MagnifyingGlassIcon height="16" width="16" />
<TextField.Input </TextField.Slot>
ref={input} <TextField.Input
placeholder="Search tanks..." ref={input}
onChange={(event) => { placeholder="Search tanks..."
if (event.target.value.length === 0) { onChange={(event) => {
setSearchedList(searchableTanks); if (event.target.value.length === 0) {
} else { setSearchedList(searchableTanks);
setSearchedList( } else {
go(event.target.value, searchableTanks, { setSearchedList(
keys: [ go(event.target.value, searchableTanks, {
'name', keys: [
'name_short', 'name',
'id', 'name_short',
] satisfies (keyof TankDefinition)[], 'id',
}).map(({ obj }) => obj), ] satisfies (keyof TankDefinition)[],
); }).map(({ obj }) => obj),
} );
}} }
/> }}
</TextField.Root> />
</TextField.Root>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button variant="soft">
Sort
<CaretDownIcon />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Label>By</DropdownMenu.Label>
<DropdownMenu.RadioGroup
value={tankopediaState.sort.by}
onValueChange={(value) =>
mutateTankopedia((draft) => {
draft.sort.by = value as TankopediaSortBy;
})
}
>
<DropdownMenu.RadioItem value="tier">
Tier
</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="name">
Name
</DropdownMenu.RadioItem>
</DropdownMenu.RadioGroup>
<DropdownMenu.Separator />
<DropdownMenu.Label>Direction</DropdownMenu.Label>
<DropdownMenu.RadioGroup
value={tankopediaState.sort.direction}
onValueChange={(value) =>
mutateTankopedia((draft) => {
draft.sort.direction = value as TankopediaSortDirection;
})
}
>
<DropdownMenu.RadioItem value="ascending">
Ascending
</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="descending">
Descending
</DropdownMenu.RadioItem>
</DropdownMenu.RadioGroup>
</DropdownMenu.Content>
</DropdownMenu.Root>
</Flex>
<Options /> <Options />
@@ -222,6 +284,11 @@ export default function Page() {
tank.tree_type === 'researchable' tank.tree_type === 'researchable'
? slateDark.slate12 ? slateDark.slate12
: undefined, : undefined,
whiteSpace: 'nowrap',
maxWidth: 160,
display: 'block',
textOverflow: 'ellipsis',
overflow: 'hidden',
}} }}
> >
{tank.name_short ?? {tank.name_short ??
+17 -19
View File
@@ -1,6 +1,6 @@
import { produce } from 'immer'; import { produce } from 'immer';
import { create } from 'zustand'; import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { CustomColumnDisplay } from '../app/tools/session/components/CustomColumn'; import { CustomColumnDisplay } from '../app/tools/session/components/CustomColumn';
import { AccentColor, GrayColor } from '../constants/radixColors'; import { AccentColor, GrayColor } from '../constants/radixColors';
import { Region } from '../constants/regions'; import { Region } from '../constants/regions';
@@ -65,25 +65,23 @@ export function resetSession() {
} }
export const useSession = create<Session>()( export const useSession = create<Session>()(
devtools( persist(
persist( (set) => ({
(set) => ({ isTracking: false,
isTracking: false, resetPrompt: true,
resetPrompt: true, showTotal: true,
showTotal: true, showCareer: true,
showCareer: true, embedPrompt: true,
embedPrompt: true,
customColumns: [ customColumns: [
{ display: 'battles', showDelta: false }, { display: 'battles', showDelta: false },
{ display: 'winrate', showDelta: true }, { display: 'winrate', showDelta: true },
{ display: 'wn8', showDelta: false }, { display: 'wn8', showDelta: false },
{ display: 'damage', showDelta: true }, { display: 'damage', showDelta: true },
], ],
color: 'slate', color: 'slate',
}), }),
{ name: 'session' }, { name: 'session' },
),
), ),
); );
+13 -21
View File
@@ -1,6 +1,5 @@
import { produce } from 'immer'; import { produce } from 'immer';
import { create } from 'zustand'; import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { TankType, TreeTypeString } from '../components/Tanks'; import { TankType, TreeTypeString } from '../components/Tanks';
import { Tier } from '../core/blitzkrieg/definitions/tanks'; import { Tier } from '../core/blitzkrieg/definitions/tanks';
@@ -22,26 +21,19 @@ type Tankopedia = {
}; };
}; };
export const useTankopedia = create<Tankopedia>()( export const useTankopedia = create<Tankopedia>()(() => ({
devtools( sort: {
persist( by: 'tier',
(set) => ({ direction: 'descending',
sort: { },
by: 'tier', filters: {
direction: 'descending', tiers: [],
}, types: [],
filters: { treeTypes: [],
tiers: [], nations: [],
types: [], test: 'include',
treeTypes: [], },
nations: [], }));
test: 'include',
},
}),
{ name: 'tankopedia' },
),
),
);
export default function mutateTankopedia(recipe: (draft: Tankopedia) => void) { export default function mutateTankopedia(recipe: (draft: Tankopedia) => void) {
useTankopedia.setState(produce(recipe)); useTankopedia.setState(produce(recipe));