mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +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 { Skeleton, Table } from '@radix-ui/themes';
|
||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
|
import { tankPerformanceSortTypeNamesArray } from '../../../../stores/tankPerformanceSort';
|
||||||
|
|
||||||
export function RowLoader() {
|
export function RowLoader() {
|
||||||
return (
|
return (
|
||||||
@@ -8,7 +9,7 @@ export function RowLoader() {
|
|||||||
<Skeleton height="100%" width="128px" />
|
<Skeleton height="100%" width="128px" />
|
||||||
</Table.RowHeaderCell>
|
</Table.RowHeaderCell>
|
||||||
|
|
||||||
{times(13, (index) => (
|
{times(tankPerformanceSortTypeNamesArray.length, (index) => (
|
||||||
<Table.Cell key={index} align="center">
|
<Table.Cell key={index} align="center">
|
||||||
<Skeleton height="1em" width="32px" />
|
<Skeleton height="1em" width="32px" />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export function Tanks() {
|
|||||||
return tanksSorted.filter((tank) =>
|
return tanksSorted.filter((tank) =>
|
||||||
filterTank(filters, awaitedTankDefinitions[tank.id]),
|
filterTank(filters, awaitedTankDefinitions[tank.id]),
|
||||||
);
|
);
|
||||||
}, [filters, tanksSorted]);
|
}, [filters, tanksSorted, sort]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import PageWrapper from '../../../components/PageWrapper';
|
import PageWrapper from '../../../components/PageWrapper';
|
||||||
|
import { TankFiltersProvider } 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';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="jade" noMaxWidth>
|
<TankFiltersProvider>
|
||||||
<Info />
|
<PageWrapper color="jade" noMaxWidth>
|
||||||
<FilterControl />
|
<Info />
|
||||||
<TankTable />
|
<FilterControl />
|
||||||
</PageWrapper>
|
<TankTable />
|
||||||
|
</PageWrapper>
|
||||||
|
</TankFiltersProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -15,8 +15,9 @@ 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 {
|
import {
|
||||||
mutateTankFilters,
|
|
||||||
useTankFilters,
|
useTankFilters,
|
||||||
|
useTankFiltersContext,
|
||||||
|
useTankFiltersMutation,
|
||||||
} from '../../../../stores/tankFilters';
|
} from '../../../../stores/tankFilters';
|
||||||
|
|
||||||
interface FilterControlProps {
|
interface FilterControlProps {
|
||||||
@@ -26,6 +27,8 @@ interface FilterControlProps {
|
|||||||
export function FilterControl({ compact }: FilterControlProps) {
|
export function FilterControl({ compact }: FilterControlProps) {
|
||||||
const awaitedGameDefinitions = use(gameDefinitions);
|
const awaitedGameDefinitions = use(gameDefinitions);
|
||||||
const filters = useTankFilters();
|
const filters = useTankFilters();
|
||||||
|
const mutateTankFilters = useTankFiltersMutation();
|
||||||
|
const tankFiltersContext = useTankFiltersContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex height="fit-content" gap="2" align="start" justify="center">
|
<Flex height="fit-content" gap="2" align="start" justify="center">
|
||||||
@@ -321,9 +324,12 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
<Box>
|
<Box>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="red"
|
color="red"
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
useTankFilters.setState(useTankFilters.getInitialState(), true)
|
tankFiltersContext.setState(
|
||||||
}
|
tankFiltersContext.getInitialState(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ReloadIcon />
|
<ReloadIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { memo } from 'react';
|
|||||||
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 {
|
import {
|
||||||
mutateTankFilters,
|
|
||||||
useTankFilters,
|
useTankFilters,
|
||||||
|
useTankFiltersMutation,
|
||||||
} from '../../../../stores/tankFilters';
|
} from '../../../../stores/tankFilters';
|
||||||
import { useTankopediaSort } from '../../../../stores/tankopediaSort';
|
import { useTankopediaSort } from '../../../../stores/tankopediaSort';
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ export const TierFilter = memo(() => {
|
|||||||
const tierFilter = useTankFilters((state) => state.tiers);
|
const tierFilter = useTankFilters((state) => state.tiers);
|
||||||
const sort = useTankopediaSort();
|
const sort = useTankopediaSort();
|
||||||
const search = useTankFilters((state) => state.search);
|
const search = useTankFilters((state) => state.search);
|
||||||
|
const mutateTankFilters = useTankFiltersMutation();
|
||||||
|
|
||||||
if (sort.by !== 'meta.none' || search) return null;
|
if (sort.by !== 'meta.none' || search) return null;
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ export const TierFilter = memo(() => {
|
|||||||
>
|
>
|
||||||
{times(10, (index) => {
|
{times(10, (index) => {
|
||||||
const tier = (10 - index) as Tier;
|
const tier = (10 - index) as Tier;
|
||||||
const selected = tierFilter === tier;
|
const selected = tierFilter.includes(tier);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -36,7 +37,11 @@ export const TierFilter = memo(() => {
|
|||||||
highContrast
|
highContrast
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
mutateTankFilters((draft) => {
|
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 { AdMidSectionResponsive } from '../../../components/AdMidSectionResponsive';
|
||||||
import PageWrapper from '../../../components/PageWrapper';
|
import PageWrapper from '../../../components/PageWrapper';
|
||||||
import { useAdExempt } from '../../../hooks/useAdExempt';
|
import { useAdExempt } from '../../../hooks/useAdExempt';
|
||||||
|
import { TankFiltersProvider } from '../../../stores/tankFilters';
|
||||||
import { TankSearch } from './components/TankSearch';
|
import { TankSearch } from './components/TankSearch';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const exempt = useAdExempt();
|
const exempt = useAdExempt();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="purple" size={1028}>
|
<TankFiltersProvider>
|
||||||
<TankSearch />
|
<PageWrapper color="purple" size={1028}>
|
||||||
{!exempt && <AdMidSectionResponsive />}
|
<TankSearch />
|
||||||
</PageWrapper>
|
{!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