mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 11:39:50 +02:00
feat: lazy loaded tanks earch
This commit is contained in:
1 parent
6a2631ea72
commit
2286a398cf
3 files changed
+51
-16
No files matched your search
@@ -1,10 +1,25 @@
|
|||||||
import { Flex, Skeleton } from '@radix-ui/themes';
|
import { Flex, Skeleton } from '@radix-ui/themes';
|
||||||
|
import { memo, useRef } from 'react';
|
||||||
|
import { useIntersection } from '../../../../hooks/useIntersection';
|
||||||
|
|
||||||
export function SkeletonTankCard() {
|
interface SkeletonTankCardProps {
|
||||||
return (
|
onIntersection?: () => void;
|
||||||
<Flex direction="column" gap="2">
|
|
||||||
<Skeleton height="64px" />
|
|
||||||
<Skeleton height="1em" />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SkeletonTankCard = memo(
|
||||||
|
({ onIntersection }: SkeletonTankCardProps) => {
|
||||||
|
const card = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useIntersection(() => onIntersection?.(), card, {
|
||||||
|
disabled: onIntersection === undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex direction="column" gap="2" ref={card}>
|
||||||
|
<Skeleton height="64px" />
|
||||||
|
<Skeleton height="1em" />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
() => true,
|
||||||
|
);
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import { Callout, Flex, FlexProps, Link, Text } from '@radix-ui/themes';
|
import { Callout, Flex, FlexProps, Link, Text } from '@radix-ui/themes';
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { memo, use, useMemo } from 'react';
|
import { memo, use, useEffect, useMemo, useState } from 'react';
|
||||||
import { AdMidSectionResponsive } from '../../../../../components/AdMidSectionResponsive';
|
import { AdMidSectionResponsive } from '../../../../../components/AdMidSectionResponsive';
|
||||||
import { ExperimentIcon } from '../../../../../components/ExperimentIcon';
|
import { ExperimentIcon } from '../../../../../components/ExperimentIcon';
|
||||||
import { TANK_CLASSES } from '../../../../../components/Tanks/components/Item/constants';
|
import { TANK_CLASSES } from '../../../../../components/Tanks/components/Item/constants';
|
||||||
@@ -40,6 +42,9 @@ type TankSearchProps = Omit<FlexProps, 'onSelect'> & {
|
|||||||
onSelectAll?: (tanks: TankDefinition[]) => void;
|
onSelectAll?: (tanks: TankDefinition[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PREVIEW_COUNT = 25;
|
||||||
|
const DEFAULT_LOADED_CARDS = 75;
|
||||||
|
|
||||||
export const TankSearch = memo<TankSearchProps>(
|
export const TankSearch = memo<TankSearchProps>(
|
||||||
({ compact, onSelect, onSelectAll, ...props }) => {
|
({ compact, onSelect, onSelectAll, ...props }) => {
|
||||||
const exempt = useAdExempt();
|
const exempt = useAdExempt();
|
||||||
@@ -50,7 +55,7 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
const awaitedTankNames = use(tankNames);
|
const awaitedTankNames = use(tankNames);
|
||||||
const filters = TankFilters.use();
|
const filters = TankFilters.use();
|
||||||
const sort = TankopediaSort.use();
|
const sort = TankopediaSort.use();
|
||||||
const tanks = useMemo(() => {
|
const tanksFiltered = useMemo(() => {
|
||||||
if (filters.search === undefined) {
|
if (filters.search === undefined) {
|
||||||
const filtered = awaitedTanksDefinitionsArray.filter((tank) =>
|
const filtered = awaitedTanksDefinitionsArray.filter((tank) =>
|
||||||
filterTank(filters, tank),
|
filterTank(filters, tank),
|
||||||
@@ -379,6 +384,12 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
return searchedTanks;
|
return searchedTanks;
|
||||||
}
|
}
|
||||||
}, [filters]);
|
}, [filters]);
|
||||||
|
const [loadedRows, setLoadedRows] = useState(DEFAULT_LOADED_CARDS);
|
||||||
|
const tanks = tanksFiltered.slice(0, loadedRows);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoadedRows(DEFAULT_LOADED_CARDS);
|
||||||
|
}, [filters, sort]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="4" flexGrow="1" {...props}>
|
<Flex direction="column" gap="4" flexGrow="1" {...props}>
|
||||||
@@ -447,6 +458,22 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
{times(
|
||||||
|
Math.min(PREVIEW_COUNT, tanksFiltered.length - loadedRows),
|
||||||
|
(index) => {
|
||||||
|
return (
|
||||||
|
<SkeletonTankCard
|
||||||
|
key={index}
|
||||||
|
onIntersection={() => {
|
||||||
|
setLoadedRows((state) =>
|
||||||
|
Math.min(state + 2, tanksFiltered.length),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
</TankCardWrapper>
|
</TankCardWrapper>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,12 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { AdMidSectionResponsive } from '../../../components/AdMidSectionResponsive';
|
|
||||||
import PageWrapper from '../../../components/PageWrapper';
|
import PageWrapper from '../../../components/PageWrapper';
|
||||||
import { useAdExempt } from '../../../hooks/useAdExempt';
|
|
||||||
import * as TankFilters from '../../../stores/tankFilters';
|
import * as TankFilters from '../../../stores/tankFilters';
|
||||||
import { TankSearch } from './components/TankSearch';
|
import { TankSearch } from './components/TankSearch';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const exempt = useAdExempt();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TankFilters.Provider>
|
<TankFilters.Provider>
|
||||||
<PageWrapper color="purple" size={1028}>
|
<PageWrapper color="purple" size={1028}>
|
||||||
<TankSearch />
|
<TankSearch />
|
||||||
{!exempt && <AdMidSectionResponsive />}
|
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</TankFilters.Provider>
|
</TankFilters.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user