mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: transition to next safe zustand
This commit is contained in:
1 parent
595941ab9c
commit
b965893bfc
9 files changed
+109
-45
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { Skeleton, Table } from '@radix-ui/themes';
|
||||
import { times } from 'lodash';
|
||||
import { tankPerformanceSortTypeNamesArray } from '../../../../stores/tankPerformanceSort';
|
||||
|
||||
export function RowLoader() {
|
||||
return (
|
||||
@@ -8,7 +9,7 @@ export function RowLoader() {
|
||||
<Skeleton height="100%" width="128px" />
|
||||
</Table.RowHeaderCell>
|
||||
|
||||
{times(13, (index) => (
|
||||
{times(tankPerformanceSortTypeNamesArray.length, (index) => (
|
||||
<Table.Cell key={index} align="center">
|
||||
<Skeleton height="1em" width="32px" />
|
||||
</Table.Cell>
|
||||
|
||||
@@ -108,7 +108,7 @@ export function Tanks() {
|
||||
return tanksSorted.filter((tank) =>
|
||||
filterTank(filters, awaitedTankDefinitions[tank.id]),
|
||||
);
|
||||
}, [filters, tanksSorted]);
|
||||
}, [filters, tanksSorted, sort]);
|
||||
|
||||
return (
|
||||
<Table.Body>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import PageWrapper from '../../../components/PageWrapper';
|
||||
import { TankFiltersProvider } from '../../../stores/tankFilters';
|
||||
import { FilterControl } from '../tankopedia/components/FilterControl';
|
||||
import { Info } from './components/Info';
|
||||
import { TankTable } from './components/Table';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<PageWrapper color="jade" noMaxWidth>
|
||||
<Info />
|
||||
<FilterControl />
|
||||
<TankTable />
|
||||
</PageWrapper>
|
||||
<TankFiltersProvider>
|
||||
<PageWrapper color="jade" noMaxWidth>
|
||||
<Info />
|
||||
<FilterControl />
|
||||
<TankTable />
|
||||
</PageWrapper>
|
||||
</TankFiltersProvider>
|
||||
);
|
||||
}
|
||||
@@ -15,8 +15,9 @@ import { gameDefinitions } from '../../../../core/blitzkit/gameDefinitions';
|
||||
import { Tier } from '../../../../core/blitzkit/tankDefinitions';
|
||||
import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants';
|
||||
import {
|
||||
mutateTankFilters,
|
||||
useTankFilters,
|
||||
useTankFiltersContext,
|
||||
useTankFiltersMutation,
|
||||
} from '../../../../stores/tankFilters';
|
||||
|
||||
interface FilterControlProps {
|
||||
@@ -26,6 +27,8 @@ interface FilterControlProps {
|
||||
export function FilterControl({ compact }: FilterControlProps) {
|
||||
const awaitedGameDefinitions = use(gameDefinitions);
|
||||
const filters = useTankFilters();
|
||||
const mutateTankFilters = useTankFiltersMutation();
|
||||
const tankFiltersContext = useTankFiltersContext();
|
||||
|
||||
return (
|
||||
<Flex height="fit-content" gap="2" align="start" justify="center">
|
||||
@@ -321,9 +324,12 @@ export function FilterControl({ compact }: FilterControlProps) {
|
||||
<Box>
|
||||
<IconButton
|
||||
color="red"
|
||||
onClick={() =>
|
||||
useTankFilters.setState(useTankFilters.getInitialState(), true)
|
||||
}
|
||||
onClick={() => {
|
||||
tankFiltersContext.setState(
|
||||
tankFiltersContext.getInitialState(),
|
||||
true,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<ReloadIcon />
|
||||
</IconButton>
|
||||
|
||||
@@ -4,8 +4,8 @@ import { memo } from 'react';
|
||||
import { Tier } from '../../../../core/blitzkit/tankDefinitions';
|
||||
import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants';
|
||||
import {
|
||||
mutateTankFilters,
|
||||
useTankFilters,
|
||||
useTankFiltersMutation,
|
||||
} from '../../../../stores/tankFilters';
|
||||
import { useTankopediaSort } from '../../../../stores/tankopediaSort';
|
||||
|
||||
@@ -13,6 +13,7 @@ export const TierFilter = memo(() => {
|
||||
const tierFilter = useTankFilters((state) => state.tiers);
|
||||
const sort = useTankopediaSort();
|
||||
const search = useTankFilters((state) => state.search);
|
||||
const mutateTankFilters = useTankFiltersMutation();
|
||||
|
||||
if (sort.by !== 'meta.none' || search) return null;
|
||||
|
||||
@@ -25,7 +26,7 @@ export const TierFilter = memo(() => {
|
||||
>
|
||||
{times(10, (index) => {
|
||||
const tier = (10 - index) as Tier;
|
||||
const selected = tierFilter === tier;
|
||||
const selected = tierFilter.includes(tier);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -36,7 +37,11 @@ export const TierFilter = memo(() => {
|
||||
highContrast
|
||||
onClick={() =>
|
||||
mutateTankFilters((draft) => {
|
||||
draft.tiers = tier;
|
||||
if (draft.tiers?.includes(tier)) {
|
||||
draft.tiers = draft.tiers?.filter((t) => t !== tier);
|
||||
} else {
|
||||
draft.tiers = [...(draft.tiers ?? []), tier];
|
||||
}
|
||||
})
|
||||
}
|
||||
>
|
||||
|
||||
@@ -3,15 +3,18 @@
|
||||
import { AdMidSectionResponsive } from '../../../components/AdMidSectionResponsive';
|
||||
import PageWrapper from '../../../components/PageWrapper';
|
||||
import { useAdExempt } from '../../../hooks/useAdExempt';
|
||||
import { TankFiltersProvider } from '../../../stores/tankFilters';
|
||||
import { TankSearch } from './components/TankSearch';
|
||||
|
||||
export default function Page() {
|
||||
const exempt = useAdExempt();
|
||||
|
||||
return (
|
||||
<PageWrapper color="purple" size={1028}>
|
||||
<TankSearch />
|
||||
{!exempt && <AdMidSectionResponsive />}
|
||||
</PageWrapper>
|
||||
<TankFiltersProvider>
|
||||
<PageWrapper color="purple" size={1028}>
|
||||
<TankSearch />
|
||||
{!exempt && <AdMidSectionResponsive />}
|
||||
</PageWrapper>
|
||||
</TankFiltersProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { produce } from 'immer';
|
||||
import { create } from 'zustand';
|
||||
import { TankClass, TreeType } from '../components/Tanks';
|
||||
import { Tier } from '../core/blitzkit/tankDefinitions';
|
||||
|
||||
export interface TankFilters {
|
||||
tiers: Tier[];
|
||||
nations: string[];
|
||||
classes: TankClass[];
|
||||
types: TreeType[];
|
||||
testing: 'include' | 'exclude' | 'only';
|
||||
search?: string;
|
||||
searching: boolean;
|
||||
}
|
||||
|
||||
export const useTankFilters = create<TankFilters>()(() => ({
|
||||
testing: 'include',
|
||||
searching: false,
|
||||
classes: [],
|
||||
nations: [],
|
||||
tiers: [],
|
||||
types: [],
|
||||
}));
|
||||
|
||||
export function mutateTankFilters(recipe: (draft: TankFilters) => void) {
|
||||
useTankFilters.setState(produce(recipe));
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
'use client';
|
||||
|
||||
import { produce } from 'immer';
|
||||
import { createContext, ReactNode, useContext, useRef } from 'react';
|
||||
import { createStore, StoreApi, useStore } from 'zustand';
|
||||
import { TankClass, TreeType } from '../components/Tanks';
|
||||
import { Tier } from '../core/blitzkit/tankDefinitions';
|
||||
|
||||
export interface TankFilters {
|
||||
tiers: Tier[];
|
||||
nations: string[];
|
||||
classes: TankClass[];
|
||||
types: TreeType[];
|
||||
testing: 'include' | 'exclude' | 'only';
|
||||
search?: string;
|
||||
searching: boolean;
|
||||
}
|
||||
|
||||
export const tankFiltersInitialState: TankFilters = {
|
||||
testing: 'include',
|
||||
searching: false,
|
||||
classes: [],
|
||||
nations: [],
|
||||
tiers: [],
|
||||
types: [],
|
||||
};
|
||||
|
||||
const createTankFiltersStore = (
|
||||
initialState: TankFilters = tankFiltersInitialState,
|
||||
) => createStore<TankFilters>()(() => ({ ...initialState }));
|
||||
|
||||
const TankFiltersContext = createContext<StoreApi<TankFilters> | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
interface TankFiltersProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function TankFiltersProvider({ children }: TankFiltersProviderProps) {
|
||||
const storeRef = useRef<StoreApi<TankFilters>>();
|
||||
if (!storeRef.current) storeRef.current = createTankFiltersStore();
|
||||
|
||||
return (
|
||||
<TankFiltersContext.Provider value={storeRef.current}>
|
||||
{children}
|
||||
</TankFiltersContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useTankFiltersContext() {
|
||||
const context = useContext(TankFiltersContext);
|
||||
if (!context) {
|
||||
throw new Error('useTankFilters must be used within TankFiltersContext');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
export function useTankFilters<Type = TankFilters>(
|
||||
selector: (store: TankFilters) => Type = (store) => store as Type,
|
||||
): Type {
|
||||
const counterStoreContext = useTankFiltersContext();
|
||||
|
||||
return useStore(counterStoreContext, selector);
|
||||
}
|
||||
|
||||
export function useTankFiltersMutation() {
|
||||
const counterStoreContext = useTankFiltersContext();
|
||||
|
||||
return function (recipe: (draft: TankFilters) => void) {
|
||||
counterStoreContext.setState(produce(recipe));
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user