mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: safe sort
This commit is contained in:
1 parent
4d99e61bfb
commit
f0fc23712a
13 files changed
+73
-65
No files matched your search
@@ -6,14 +6,15 @@ import {
|
|||||||
CaretUpIcon,
|
CaretUpIcon,
|
||||||
} from '@radix-ui/react-icons';
|
} from '@radix-ui/react-icons';
|
||||||
import { Flex, IconButton, Table } from '@radix-ui/themes';
|
import { Flex, IconButton, Table } from '@radix-ui/themes';
|
||||||
|
import * as TankPerformanceSort from '../../../../stores/tankPerformanceSort';
|
||||||
import {
|
import {
|
||||||
tankPerformanceSortTypeNames,
|
tankPerformanceSortTypeNames,
|
||||||
tankPerformanceSortTypeNamesArray,
|
tankPerformanceSortTypeNamesArray,
|
||||||
useTankPerformanceSort,
|
} from '../../../../stores/tankPerformanceSort/constants';
|
||||||
} from '../../../../stores/tankPerformanceSort';
|
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
const sort = useTankPerformanceSort();
|
const sort = TankPerformanceSort.use();
|
||||||
|
const store = TankPerformanceSort.useStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
@@ -40,11 +41,9 @@ export function Header() {
|
|||||||
highContrast={!isSelected}
|
highContrast={!isSelected}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
useTankPerformanceSort.setState({
|
store.setState({ direction: -sort.direction as 1 | -1 });
|
||||||
direction: -sort.direction as 1 | -1,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
useTankPerformanceSort.setState({ type, direction: -1 });
|
store.setState({ type, direction: -1 });
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function Info() {
|
|||||||
</Callout.Icon>
|
</Callout.Icon>
|
||||||
<Callout.Text>
|
<Callout.Text>
|
||||||
Career stats based on {numberFormat.format(Math.round(samples))}{' '}
|
Career stats based on {numberFormat.format(Math.round(samples))}{' '}
|
||||||
players with at least 5K career battles and 1 battle in the past 30
|
players with at least 5K career battles and 1 battle in the past 120
|
||||||
days; updated daily.
|
days; updated daily.
|
||||||
</Callout.Text>
|
</Callout.Text>
|
||||||
</Callout.Root>
|
</Callout.Root>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Skeleton, Table } from '@radix-ui/themes';
|
|||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { memo, useRef } from 'react';
|
import { memo, useRef } from 'react';
|
||||||
import { useIntersection } from '../../../../hooks/useIntersection';
|
import { useIntersection } from '../../../../hooks/useIntersection';
|
||||||
import { tankPerformanceSortTypeNamesArray } from '../../../../stores/tankPerformanceSort';
|
import { tankPerformanceSortTypeNamesArray } from '../../../../stores/tankPerformanceSort/constants';
|
||||||
|
|
||||||
interface RowLoaderProps {
|
interface RowLoaderProps {
|
||||||
onIntersection?: () => void;
|
onIntersection?: () => void;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { averageDefinitionsArray } from '../../../../core/blitzkit/averageDefini
|
|||||||
import { filterTank } from '../../../../core/blitzkit/filterTank';
|
import { filterTank } from '../../../../core/blitzkit/filterTank';
|
||||||
import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions';
|
import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions';
|
||||||
import * as TankFilters from '../../../../stores/tankFilters';
|
import * as TankFilters from '../../../../stores/tankFilters';
|
||||||
import { useTankPerformanceSort } from '../../../../stores/tankPerformanceSort';
|
import * as TankPerformanceSort from '../../../../stores/tankPerformanceSort';
|
||||||
import { RowLoader } from './RowLoader';
|
import { RowLoader } from './RowLoader';
|
||||||
import { TankRow } from './TankRow';
|
import { TankRow } from './TankRow';
|
||||||
import { Total } from './Total';
|
import { Total } from './Total';
|
||||||
@@ -19,7 +19,7 @@ export function Tanks() {
|
|||||||
const awaitedTankDefinitions = use(tankDefinitions);
|
const awaitedTankDefinitions = use(tankDefinitions);
|
||||||
const awaitedAverageDefinitionsArray = use(averageDefinitionsArray);
|
const awaitedAverageDefinitionsArray = use(averageDefinitionsArray);
|
||||||
const filters = TankFilters.use();
|
const filters = TankFilters.use();
|
||||||
const sort = useTankPerformanceSort();
|
const sort = TankPerformanceSort.use();
|
||||||
const tanksSorted = useMemo(() => {
|
const tanksSorted = useMemo(() => {
|
||||||
switch (sort.type) {
|
switch (sort.type) {
|
||||||
case 'accuracy':
|
case 'accuracy':
|
||||||
@@ -126,6 +126,9 @@ export function Tanks() {
|
|||||||
|
|
||||||
{tanks.slice(0, loadedRows).map((averages) => {
|
{tanks.slice(0, loadedRows).map((averages) => {
|
||||||
const tank = awaitedTankDefinitions[averages.id];
|
const tank = awaitedTankDefinitions[averages.id];
|
||||||
|
|
||||||
|
if (tank === undefined) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense key={tank.id} fallback={<RowLoader />}>
|
<Suspense key={tank.id} fallback={<RowLoader />}>
|
||||||
<TankRow tank={tank} />
|
<TankRow tank={tank} />
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export function TierBreakdown() {
|
|||||||
data={times(10, (index) => {
|
data={times(10, (index) => {
|
||||||
const tier = (index + 1) as Tier;
|
const tier = (index + 1) as Tier;
|
||||||
const tierTanks = awaitedAverageDefinitionsArray.filter(
|
const tierTanks = awaitedAverageDefinitionsArray.filter(
|
||||||
({ id }) => awaitedTankDefinitions[id].tier === tier,
|
({ id }) => awaitedTankDefinitions[id]?.tier === tier,
|
||||||
);
|
);
|
||||||
const populations: Record<TankClass, number> = {
|
const populations: Record<TankClass, number> = {
|
||||||
'AT-SPG': 0,
|
'AT-SPG': 0,
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import * as TankFilters from '../../../stores/tankFilters';
|
||||||
|
import * as TankPerformanceSort from '../../../stores/tankPerformanceSort';
|
||||||
|
|
||||||
|
export default function Layout({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<TankPerformanceSort.Provider>
|
||||||
|
<TankFilters.Provider>{children}</TankFilters.Provider>
|
||||||
|
</TankPerformanceSort.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import PageWrapper from '../../../components/PageWrapper';
|
import PageWrapper from '../../../components/PageWrapper';
|
||||||
import * as TankFilters from '../../../stores/tankFilters';
|
|
||||||
import { FilterControl } from '../tankopedia/components/FilterControl';
|
import { FilterControl } from '../tankopedia/components/FilterControl';
|
||||||
import { Info } from './components/Info';
|
import { Info } from './components/Info';
|
||||||
import { TankTable } from './components/Table';
|
import { TankTable } from './components/Table';
|
||||||
@@ -7,13 +6,11 @@ import { TierBreakdown } from './components/TierBreakdown';
|
|||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<TankFilters.Provider>
|
<PageWrapper color="jade" noMaxWidth>
|
||||||
<PageWrapper color="jade" noMaxWidth>
|
<Info />
|
||||||
<Info />
|
<TierBreakdown />
|
||||||
<TierBreakdown />
|
<FilterControl />
|
||||||
<FilterControl />
|
<TankTable />
|
||||||
<TankTable />
|
</PageWrapper>
|
||||||
</PageWrapper>
|
|
||||||
</TankFilters.Provider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { TankFilters } from '../../stores/tankFilters';
|
||||||
import { TankDefinition } from './tankDefinitions';
|
import { TankDefinition } from './tankDefinitions';
|
||||||
|
|
||||||
export function filterTank(filters: TankFilters, tank: TankDefinition) {
|
export function filterTank(filters: TankFilters, tank: TankDefinition) {
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ export function createNextSafeStore<Type>(initialState: Type) {
|
|||||||
|
|
||||||
function useStore() {
|
function useStore() {
|
||||||
const store = useContext(Context);
|
const store = useContext(Context);
|
||||||
if (!store) {
|
|
||||||
throw new Error('useStoreContext must be used within Provider');
|
if (!store) throw new Error('useStore must be used within Provider');
|
||||||
}
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File renamed without changes.
@@ -1,41 +0,0 @@
|
|||||||
import { produce } from 'immer';
|
|
||||||
import { create } from 'zustand';
|
|
||||||
|
|
||||||
export const tankPerformanceSortTypeNames = {
|
|
||||||
winrate: 'Winrate',
|
|
||||||
players: 'Players (30d)',
|
|
||||||
battles: 'Battles (career)',
|
|
||||||
damage: 'Damage',
|
|
||||||
survival: 'Survival',
|
|
||||||
xp: 'XP',
|
|
||||||
kills: 'Kills',
|
|
||||||
spots: 'Spots',
|
|
||||||
accuracy: 'Accuracy',
|
|
||||||
shots: 'Shots',
|
|
||||||
hits: 'Hits',
|
|
||||||
damageRatio: 'Dmg. ratio',
|
|
||||||
damageTaken: 'Dmg. taken',
|
|
||||||
capturePoints: 'Cap points',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const tankPerformanceSortTypeNamesArray = Object.keys(
|
|
||||||
tankPerformanceSortTypeNames,
|
|
||||||
) as TankPerformanceSortType[];
|
|
||||||
|
|
||||||
type TankPerformanceSortType = keyof typeof tankPerformanceSortTypeNames;
|
|
||||||
|
|
||||||
export interface TankPerformanceSort {
|
|
||||||
type: TankPerformanceSortType;
|
|
||||||
direction: -1 | 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useTankPerformanceSort = create<TankPerformanceSort>()(() => ({
|
|
||||||
type: 'winrate',
|
|
||||||
direction: -1,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export function mutateTankPerformanceSort(
|
|
||||||
recipe: (draft: TankPerformanceSort) => void,
|
|
||||||
) {
|
|
||||||
useTankPerformanceSort.setState(produce(recipe));
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { TankPerformanceSortType } from '.';
|
||||||
|
|
||||||
|
export const tankPerformanceSortTypeNames = {
|
||||||
|
winrate: 'Winrate',
|
||||||
|
players: 'Players (30d)',
|
||||||
|
battles: 'Battles (career)',
|
||||||
|
damage: 'Damage',
|
||||||
|
survival: 'Survival',
|
||||||
|
xp: 'XP',
|
||||||
|
kills: 'Kills',
|
||||||
|
spots: 'Spots',
|
||||||
|
accuracy: 'Accuracy',
|
||||||
|
shots: 'Shots',
|
||||||
|
hits: 'Hits',
|
||||||
|
damageRatio: 'Dmg. ratio',
|
||||||
|
damageTaken: 'Dmg. taken',
|
||||||
|
capturePoints: 'Cap points',
|
||||||
|
} as const;
|
||||||
|
export const tankPerformanceSortTypeNamesArray = Object.keys(
|
||||||
|
tankPerformanceSortTypeNames,
|
||||||
|
) as TankPerformanceSortType[];
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { createNextSafeStore } from '../../core/zustand/createNextSafeStore';
|
||||||
|
import { tankPerformanceSortTypeNames } from './constants';
|
||||||
|
|
||||||
|
export type TankPerformanceSortType = keyof typeof tankPerformanceSortTypeNames;
|
||||||
|
|
||||||
|
export interface TankPerformanceSort {
|
||||||
|
type: TankPerformanceSortType;
|
||||||
|
direction: -1 | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const { Provider, use, useMutation, useStore } =
|
||||||
|
createNextSafeStore<TankPerformanceSort>({
|
||||||
|
type: 'winrate',
|
||||||
|
direction: -1,
|
||||||
|
});
|
||||||
Reference in new issue
Block a user