mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: skeleton search
This commit is contained in:
1 parent
9d916a588d
commit
45938c875f
4 files changed
+36
-19
No files matched your search
@@ -6,15 +6,16 @@ import { debounce } from 'lodash-es';
|
|||||||
import { type KeyboardEventHandler, useCallback, useRef } from 'react';
|
import { type KeyboardEventHandler, useCallback, useRef } from 'react';
|
||||||
import { useLocale } from '../../../hooks/useLocale';
|
import { useLocale } from '../../../hooks/useLocale';
|
||||||
import { $tankFilters } from '../../../stores/tankFilters';
|
import { $tankFilters } from '../../../stores/tankFilters';
|
||||||
|
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
|
||||||
import { QuickLink } from './QuickLink';
|
import { QuickLink } from './QuickLink';
|
||||||
import { Sort } from './Sort';
|
import { Sort } from './Sort';
|
||||||
|
|
||||||
interface SearchBarProps {
|
type SearchBarProps = MaybeSkeletonComponentProps & {
|
||||||
topResult?: TankDefinition;
|
topResult?: TankDefinition;
|
||||||
onSelect?: (tank: TankDefinition) => void;
|
onSelect?: (tank: TankDefinition) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function SearchBar({ topResult, onSelect }: SearchBarProps) {
|
export function SearchBar({ topResult, skeleton, onSelect }: SearchBarProps) {
|
||||||
const { strings } = useLocale();
|
const { strings } = useLocale();
|
||||||
const tankFilters = useStore($tankFilters);
|
const tankFilters = useStore($tankFilters);
|
||||||
const input = useRef<HTMLInputElement>(null);
|
const input = useRef<HTMLInputElement>(null);
|
||||||
@@ -66,6 +67,7 @@ export function SearchBar({ topResult, onSelect }: SearchBarProps) {
|
|||||||
<Flex justify="center">
|
<Flex justify="center">
|
||||||
<Flex gap="2" flexGrow="1">
|
<Flex gap="2" flexGrow="1">
|
||||||
<TextField.Root
|
<TextField.Root
|
||||||
|
disabled={skeleton}
|
||||||
defaultValue={tankFilters.search}
|
defaultValue={tankFilters.search}
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
ref={input}
|
ref={input}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { $tankFilters } from '../../stores/tankFilters';
|
|||||||
import { TankopediaPersistent } from '../../stores/tankopediaPersistent';
|
import { TankopediaPersistent } from '../../stores/tankopediaPersistent';
|
||||||
import { SORT_UNITS } from '../../stores/tankopediaPersistent/constants';
|
import { SORT_UNITS } from '../../stores/tankopediaPersistent/constants';
|
||||||
import { $tankopediaSort } from '../../stores/tankopediaSort';
|
import { $tankopediaSort } from '../../stores/tankopediaSort';
|
||||||
|
import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps';
|
||||||
import { ExperimentIcon } from '../ExperimentIcon';
|
import { ExperimentIcon } from '../ExperimentIcon';
|
||||||
import { TankSearchCard } from './components/Card';
|
import { TankSearchCard } from './components/Card';
|
||||||
import { FilterControl } from './components/FilterControl';
|
import { FilterControl } from './components/FilterControl';
|
||||||
@@ -33,11 +34,12 @@ import { SkeletonTankCard } from './components/SkeletonTankCard';
|
|||||||
import { TankCardWrapper } from './components/TankCardWrapper';
|
import { TankCardWrapper } from './components/TankCardWrapper';
|
||||||
import { MAX_RECENTLY_VIEWED } from './constants';
|
import { MAX_RECENTLY_VIEWED } from './constants';
|
||||||
|
|
||||||
type TankSearchProps = Omit<FlexProps, 'onSelect'> & {
|
type TankSearchProps = MaybeSkeletonComponentProps &
|
||||||
compact?: boolean;
|
Omit<FlexProps, 'onSelect'> & {
|
||||||
onSelect?: (tank: TankDefinition) => void;
|
compact?: boolean;
|
||||||
onSelectAll?: (tanks: TankDefinition[]) => void;
|
onSelect?: (tank: TankDefinition) => void;
|
||||||
};
|
onSelectAll?: (tanks: TankDefinition[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
const PREVIEW_COUNT = 20;
|
const PREVIEW_COUNT = 20;
|
||||||
const DEFAULT_LOADED_CARDS = 64;
|
const DEFAULT_LOADED_CARDS = 64;
|
||||||
@@ -51,7 +53,7 @@ const [gameDefinitions, modelDefinitions, tankDefinitions, tankNames] =
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const TankSearch = memo<TankSearchProps>(
|
export const TankSearch = memo<TankSearchProps>(
|
||||||
({ compact, onSelect, onSelectAll, ...props }) => {
|
({ compact, onSelect, onSelectAll, skeleton, ...props }) => {
|
||||||
const { strings, locale } = useLocale();
|
const { strings, locale } = useLocale();
|
||||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||||
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
||||||
@@ -428,13 +430,17 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="4" flexGrow="1" {...props}>
|
<Flex direction="column" gap="4" flexGrow="1" {...props}>
|
||||||
<SearchBar topResult={tanks?.[0]} onSelect={onSelect} />
|
<SearchBar
|
||||||
|
skeleton={skeleton}
|
||||||
|
topResult={tanks?.[0]}
|
||||||
|
onSelect={onSelect}
|
||||||
|
/>
|
||||||
|
|
||||||
{!tankFilters.search && !tankFilters.searching && (
|
{!tankFilters.search && !tankFilters.searching && (
|
||||||
<FilterControl compact={compact} />
|
<FilterControl compact={compact} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!compact && <RecentlyViewed />}
|
{!skeleton && !compact && <RecentlyViewed />}
|
||||||
|
|
||||||
<Flex mt="2" gap="1" align="center" justify="center" direction="column">
|
<Flex mt="2" gap="1" align="center" justify="center" direction="column">
|
||||||
<Flex gap="2">
|
<Flex gap="2">
|
||||||
@@ -494,7 +500,7 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!tankFilters.searching && (
|
{!skeleton && !tankFilters.searching && (
|
||||||
<>
|
<>
|
||||||
{tanks.length > 0 && (
|
{tanks.length > 0 && (
|
||||||
<TankCardWrapper>
|
<TankCardWrapper>
|
||||||
@@ -528,11 +534,16 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tankFilters.searching && (
|
{(skeleton || tankFilters.searching) && (
|
||||||
<TankCardWrapper>
|
<TankCardWrapper>
|
||||||
{times(Math.round(10 + 10 * Math.random()), (index) => (
|
{times(
|
||||||
<SkeletonTankCard key={index} />
|
Math.round(
|
||||||
))}
|
skeleton ? DEFAULT_LOADED_CARDS : 10 + 10 * Math.random(),
|
||||||
|
),
|
||||||
|
(index) => (
|
||||||
|
<SkeletonTankCard key={index} />
|
||||||
|
),
|
||||||
|
)}
|
||||||
</TankCardWrapper>
|
</TankCardWrapper>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -6,14 +6,18 @@ import {
|
|||||||
} from '../../../../hooks/useLocale';
|
} from '../../../../hooks/useLocale';
|
||||||
import { App } from '../../../../stores/app';
|
import { App } from '../../../../stores/app';
|
||||||
import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent';
|
import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent';
|
||||||
|
import type { MaybeSkeletonComponentProps } from '../../../../types/maybeSkeletonComponentProps';
|
||||||
|
|
||||||
export function Page({ locale }: LocaleAcceptorProps) {
|
export function Page({
|
||||||
|
locale,
|
||||||
|
skeleton,
|
||||||
|
}: LocaleAcceptorProps & MaybeSkeletonComponentProps) {
|
||||||
return (
|
return (
|
||||||
<LocaleProvider locale={locale}>
|
<LocaleProvider locale={locale}>
|
||||||
<PageWrapper color="purple" maxWidth="80rem">
|
<PageWrapper color="purple" maxWidth="80rem">
|
||||||
<TankopediaPersistent.Provider>
|
<TankopediaPersistent.Provider>
|
||||||
<App.Provider>
|
<App.Provider>
|
||||||
<TankSearch />
|
<TankSearch skeleton={skeleton} />
|
||||||
</App.Provider>
|
</App.Provider>
|
||||||
</TankopediaPersistent.Provider>
|
</TankopediaPersistent.Provider>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ const { locale } = Astro.params;
|
|||||||
|
|
||||||
<ToolLayout tool={tankopediaTool}>
|
<ToolLayout tool={tankopediaTool}>
|
||||||
<Page {locale} client:only="react">
|
<Page {locale} client:only="react">
|
||||||
<Page {locale} slot="fallback" />
|
<Page {locale} skeleton slot="fallback" />
|
||||||
</Page>
|
</Page>
|
||||||
</ToolLayout>
|
</ToolLayout>
|
||||||
Reference in new issue
Block a user