feat: tank performance filters

This commit is contained in:
TrèsAbhi committed 2024-07-02 19:19:15 -05:00
1 parent 4b46eb59e0
commit 4c6369b8b4
9 files changed
+106 -85

No files matched your search

@@ -4,6 +4,8 @@ import { Table } from '@radix-ui/themes';
import { Suspense, use, useMemo } from 'react';
import { averageDefinitionsArray } from '../../../../core/blitzkit/averageDefinitions';
import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions';
import { tankopediaFilterTank } from '../../../../core/blitzkit/tankopediaFilterTank';
import { useTankopediaFilters } from '../../../../stores/tankopediaFilters';
import { useTankPerformanceSort } from '../../../../stores/tankPerformanceSort';
import { RowLoader } from './RowLoader';
import { TankRow } from './TankRow';
@@ -11,8 +13,9 @@ import { TankRow } from './TankRow';
export function Tanks() {
const awaitedTankDefinitions = use(tankDefinitions);
const awaitedAverageDefinitionsArray = use(averageDefinitionsArray);
const filters = useTankopediaFilters();
const sort = useTankPerformanceSort();
const tankAverages = useMemo(() => {
const tanksSorted = useMemo(() => {
switch (sort.type) {
case 'accuracy':
return awaitedAverageDefinitionsArray.sort(
@@ -101,10 +104,15 @@ export function Tanks() {
);
}
}, [sort]);
const tanks = useMemo(() => {
return tanksSorted.filter((tank) =>
tankopediaFilterTank(filters, awaitedTankDefinitions[tank.id]),
);
}, [filters, tanksSorted]);
return (
<Table.Body>
{tankAverages.map((averages) => {
{tanks.map((averages) => {
const tank = awaitedTankDefinitions[averages.id];
return (
<Suspense key={tank.id} fallback={<RowLoader />}>
+3
View File
@@ -1,6 +1,7 @@
import { Flex, Table } from '@radix-ui/themes';
import { Suspense } from 'react';
import PageWrapper from '../../../components/PageWrapper';
import { FilterControl } from '../tankopedia/components/FilterControl';
import { BodyLoader } from './components/BodyLoader';
import { Header } from './components/Header';
import { Info } from './components/Info';
@@ -11,6 +12,8 @@ export default function Page() {
<PageWrapper color="jade" noMaxWidth>
<Info />
<FilterControl />
<Flex justify="center">
<Table.Root variant="surface" style={{ maxWidth: '100%' }}>
<Header />
@@ -1,3 +1,5 @@
'use client';
import { ReloadIcon } from '@radix-ui/react-icons';
import { Box, Flex, IconButton, Text } from '@radix-ui/themes';
import { times } from 'lodash';
+60 -60
View File
@@ -2,12 +2,12 @@ import { CaretDownIcon } from '@radix-ui/react-icons';
import { Button, DropdownMenu } from '@radix-ui/themes';
import { TankopediaSortDirection } from '../../../../stores/tankopedia';
import {
mutateTankopediaFilters,
useTankopediaFilters,
} from '../../../../stores/tankopediaFilters';
mutateTankopediaSort,
useTankopediaSort,
} from '../../../../stores/tankopediaSort';
export function Sort() {
const sort = useTankopediaFilters((state) => state.sort);
const sort = useTankopediaSort();
return (
<DropdownMenu.Root modal={false}>
@@ -22,8 +22,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'meta.none'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'meta.none';
mutateTankopediaSort((draft) => {
draft.by = 'meta.none';
});
}}
>
@@ -38,8 +38,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.dpm'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.dpm';
mutateTankopediaSort((draft) => {
draft.by = 'fire.dpm';
});
}}
>
@@ -48,8 +48,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.dpmPremium'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.dpmPremium';
mutateTankopediaSort((draft) => {
draft.by = 'fire.dpmPremium';
});
}}
>
@@ -58,8 +58,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.reload'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.reload';
mutateTankopediaSort((draft) => {
draft.by = 'fire.reload';
});
}}
>
@@ -68,8 +68,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.caliber'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.caliber';
mutateTankopediaSort((draft) => {
draft.by = 'fire.caliber';
});
}}
>
@@ -78,8 +78,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.standardPenetration'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.standardPenetration';
mutateTankopediaSort((draft) => {
draft.by = 'fire.standardPenetration';
});
}}
>
@@ -88,8 +88,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.premiumPenetration'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.premiumPenetration';
mutateTankopediaSort((draft) => {
draft.by = 'fire.premiumPenetration';
});
}}
>
@@ -98,8 +98,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.damage'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.damage';
mutateTankopediaSort((draft) => {
draft.by = 'fire.damage';
});
}}
>
@@ -108,8 +108,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.shellVelocity'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.shellVelocity';
mutateTankopediaSort((draft) => {
draft.by = 'fire.shellVelocity';
});
}}
>
@@ -118,8 +118,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.aimTime'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.aimTime';
mutateTankopediaSort((draft) => {
draft.by = 'fire.aimTime';
});
}}
>
@@ -128,8 +128,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.dispersionStill'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.dispersionStill';
mutateTankopediaSort((draft) => {
draft.by = 'fire.dispersionStill';
});
}}
>
@@ -138,8 +138,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.dispersionMoving'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.dispersionMoving';
mutateTankopediaSort((draft) => {
draft.by = 'fire.dispersionMoving';
});
}}
>
@@ -148,8 +148,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.gunDepression'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.gunDepression';
mutateTankopediaSort((draft) => {
draft.by = 'fire.gunDepression';
});
}}
>
@@ -158,8 +158,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'fire.gunElevation'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.gunElevation';
mutateTankopediaSort((draft) => {
draft.by = 'fire.gunElevation';
});
}}
>
@@ -174,8 +174,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'maneuverability.forwardsSpeed'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.forwardsSpeed';
mutateTankopediaSort((draft) => {
draft.by = 'maneuverability.forwardsSpeed';
});
}}
>
@@ -184,8 +184,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'maneuverability.backwardsSpeed'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.backwardsSpeed';
mutateTankopediaSort((draft) => {
draft.by = 'maneuverability.backwardsSpeed';
});
}}
>
@@ -194,8 +194,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'maneuverability.power'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.power';
mutateTankopediaSort((draft) => {
draft.by = 'maneuverability.power';
});
}}
>
@@ -204,8 +204,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'maneuverability.powerToWeight'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.powerToWeight';
mutateTankopediaSort((draft) => {
draft.by = 'maneuverability.powerToWeight';
});
}}
>
@@ -214,8 +214,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'maneuverability.weight'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.weight';
mutateTankopediaSort((draft) => {
draft.by = 'maneuverability.weight';
});
}}
>
@@ -224,8 +224,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'maneuverability.traverseSpeed'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.traverseSpeed';
mutateTankopediaSort((draft) => {
draft.by = 'maneuverability.traverseSpeed';
});
}}
>
@@ -240,8 +240,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'survivability.health'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.health';
mutateTankopediaSort((draft) => {
draft.by = 'survivability.health';
});
}}
>
@@ -250,8 +250,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'survivability.viewRange'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.viewRange';
mutateTankopediaSort((draft) => {
draft.by = 'survivability.viewRange';
});
}}
>
@@ -260,8 +260,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'survivability.camouflageStill'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.camouflageStill';
mutateTankopediaSort((draft) => {
draft.by = 'survivability.camouflageStill';
});
}}
>
@@ -270,8 +270,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'survivability.camouflageMoving'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.camouflageMoving';
mutateTankopediaSort((draft) => {
draft.by = 'survivability.camouflageMoving';
});
}}
>
@@ -280,8 +280,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'survivability.camouflageShooting'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.camouflageShooting';
mutateTankopediaSort((draft) => {
draft.by = 'survivability.camouflageShooting';
});
}}
>
@@ -290,8 +290,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'survivability.volume'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.volume';
mutateTankopediaSort((draft) => {
draft.by = 'survivability.volume';
});
}}
>
@@ -300,8 +300,8 @@ export function Sort() {
<DropdownMenu.CheckboxItem
checked={sort.by === 'survivability.length'}
onClick={() => {
mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.length';
mutateTankopediaSort((draft) => {
draft.by = 'survivability.length';
});
}}
>
@@ -314,8 +314,8 @@ export function Sort() {
<DropdownMenu.RadioGroup
value={sort.direction}
onValueChange={(value) =>
mutateTankopediaFilters((draft) => {
draft.sort.direction = value as TankopediaSortDirection;
mutateTankopediaSort((draft) => {
draft.direction = value as TankopediaSortDirection;
})
}
>
@@ -12,7 +12,7 @@ import { resolveReload } from '../../../../core/blitzkit/resolveReload';
import { TankDefinition } from '../../../../core/blitzkit/tankDefinitions';
import { tankIcon } from '../../../../core/blitzkit/tankIcon';
import { unionBoundingBox } from '../../../../core/blitzkit/unionBoundingBox';
import { useTankopediaFilters } from '../../../../stores/tankopediaFilters';
import { useTankopediaSort } from '../../../../stores/tankopediaSort';
import * as styles from './TankCard.css';
interface TankCardProps {
@@ -23,8 +23,7 @@ interface TankCardProps {
export function TankCard({ tank, onSelect }: TankCardProps) {
const awaitedModelDefinitions = use(modelDefinitions);
const Icon = classIcons[tank.class];
// const link = useRef<HTMLAnchorElement>(null);
const sortBy = useTankopediaFilters((state) => state.sort.by);
const sortBy = useTankopediaSort((state) => state.by);
const discriminator = useMemo(() => {
if (sortBy.startsWith('meta')) return undefined;
@@ -22,6 +22,7 @@ import { unionBoundingBox } from '../../../../../core/blitzkit/unionBoundingBox'
import { useAdExempt } from '../../../../../hooks/useAdExempt';
import { SORT_NAMES, SORT_UNITS } from '../../../../../stores/tankopedia';
import { useTankopediaFilters } from '../../../../../stores/tankopediaFilters';
import { useTankopediaSort } from '../../../../../stores/tankopediaSort';
import { FilterControl } from '../FilterControl';
import { NoResults } from '../NoResults';
import { SearchBar } from '../SearchBar';
@@ -45,6 +46,7 @@ export const TankSearch = memo<TankSearchProps>(
const awaitedTanksDefinitionsArray = use(tanksDefinitionsArray);
const awaitedTankNames = use(tankNames);
const filters = useTankopediaFilters();
const sort = useTankopediaSort();
const tanks = useMemo(() => {
if (filters.search === undefined) {
const filtered = awaitedTanksDefinitionsArray.filter((tank) =>
@@ -52,7 +54,7 @@ export const TankSearch = memo<TankSearchProps>(
);
let sorted: TankDefinition[];
switch (filters.sort.by) {
switch (sort.by) {
case 'meta.none':
sorted = filtered
.sort((a, b) => b.tier - a.tier)
@@ -362,9 +364,7 @@ export const TankSearch = memo<TankSearchProps>(
break;
}
return filters.sort.direction === 'ascending'
? sorted
: sorted.reverse();
return sort.direction === 'ascending' ? sorted : sorted.reverse();
} else {
const searchedRaw = go(filters.search, awaitedTankNames, {
key: 'combined',
@@ -385,7 +385,7 @@ export const TankSearch = memo<TankSearchProps>(
<FilterControl compact={compact} />
)}
{(filters.sort.by !== 'meta.none' || onSelectAll) && (
{(sort.by !== 'meta.none' || onSelectAll) && (
<Flex
py="2"
mt="2"
@@ -393,13 +393,13 @@ export const TankSearch = memo<TankSearchProps>(
align="center"
justify="center"
>
{filters.sort.by !== 'meta.none' && (
{sort.by !== 'meta.none' && (
<Text color="gray">
Sorting by {SORT_NAMES[filters.sort.by]}
{SORT_UNITS[filters.sort.by] === undefined
Sorting by {SORT_NAMES[sort.by]}
{SORT_UNITS[sort.by] === undefined
? ''
: ` (${SORT_UNITS[filters.sort.by]})`}
, {filters.sort.direction}
: ` (${SORT_UNITS[sort.by]})`}
, {sort.direction}
</Text>
)}
@@ -7,10 +7,11 @@ import {
mutateTankopediaFilters,
useTankopediaFilters,
} from '../../../../stores/tankopediaFilters';
import { useTankopediaSort } from '../../../../stores/tankopediaSort';
export const TierFilter = memo(() => {
const tierFilter = useTankopediaFilters((state) => state.tier);
const sort = useTankopediaFilters((state) => state.sort);
const sort = useTankopediaSort();
const search = useTankopediaFilters((state) => state.search);
if (sort.by !== 'meta.none' || search) return null;
-9
View File
@@ -2,7 +2,6 @@ import { produce } from 'immer';
import { create } from 'zustand';
import { TankClass, TreeType } from '../components/Tanks';
import { Tier } from '../core/blitzkit/tankDefinitions';
import { TankopediaSortBy, TankopediaSortDirection } from './tankopedia';
export interface TankopediaFilters {
tier?: Tier;
@@ -11,19 +10,11 @@ export interface TankopediaFilters {
type?: TreeType;
testing: 'include' | 'exclude' | 'only';
search?: string;
sort: {
by: TankopediaSortBy;
direction: TankopediaSortDirection;
};
searching: boolean;
}
export const useTankopediaFilters = create<TankopediaFilters>()(() => ({
testing: 'include',
sort: {
by: 'meta.none',
direction: 'descending',
},
searching: false,
}));
+17
View File
@@ -0,0 +1,17 @@
import { produce } from 'immer';
import { create } from 'zustand';
import { TankopediaSortBy, TankopediaSortDirection } from './tankopedia';
export interface TankopediaSort {
by: TankopediaSortBy;
direction: TankopediaSortDirection;
}
export const useTankopediaSort = create<TankopediaSort>()(() => ({
by: 'meta.none',
direction: 'descending',
}));
export function mutateTankopediaSort(recipe: (draft: TankopediaSort) => void) {
useTankopediaSort.setState(produce(recipe));
}