diff --git a/packages/website/src/components/Gallery/Card.tsx b/packages/website/src/components/Gallery/Card.tsx new file mode 100644 index 000000000..1095d55ea --- /dev/null +++ b/packages/website/src/components/Gallery/Card.tsx @@ -0,0 +1,46 @@ +import { Flex, Skeleton, Text } from '@radix-ui/themes'; +import { useRef } from 'react'; +import { useIntersection } from '../../hooks/useIntersection'; +import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps'; + +type GalleryCardProps = MaybeSkeletonComponentProps<{ + id: string; + name: string; +}>; + +export function GalleryCard(props: GalleryCardProps) { + const card = useRef(null); + + useIntersection(() => props.skeleton && props.onIntersection?.(), card, { + disabled: !props.skeleton || props.onIntersection === undefined, + }); + + return ( + + {props.skeleton && ( + <> + + + + )} + + {!props.skeleton && ( + <> + + + + {props.name} + + + )} + + ); +} diff --git a/packages/website/src/components/Gallery/List.tsx b/packages/website/src/components/Gallery/List.tsx index 066b63cf9..d1042af2d 100644 --- a/packages/website/src/components/Gallery/List.tsx +++ b/packages/website/src/components/Gallery/List.tsx @@ -1,7 +1,9 @@ -import { Flex, Text } from '@radix-ui/themes'; +import { Flex } from '@radix-ui/themes'; import fuzzysort from 'fuzzysort'; -import { useMemo } from 'react'; +import { times } from 'lodash-es'; +import { useMemo, useState } from 'react'; import { GalleryEphemeral } from '../../stores/galleryEphemeral'; +import { GalleryCard } from './Card'; interface GalleryListProps { avatars: Avatar[]; @@ -12,9 +14,15 @@ export interface Avatar { id: string; } +const DEFAULT_LOADED = 25; +const PREVIEW_COUNT = 10; + export function GalleryList({ avatars }: GalleryListProps) { const search = GalleryEphemeral.use((state) => state.search); + const [loadedCards, setLoadedCards] = useState(DEFAULT_LOADED); const filtered = useMemo(() => { + setLoadedCards(DEFAULT_LOADED); + if (search === undefined) { return avatars; } else { @@ -26,23 +34,20 @@ export function GalleryList({ avatars }: GalleryListProps) { return ( - {filtered.map((avatar) => { - return ( - - - - {avatar.name} - - - ); - })} + {filtered.slice(0, loadedCards).map((avatar) => ( + + ))} + + {times( + Math.min(PREVIEW_COUNT, filtered.length - loadedCards), + (index) => ( + setLoadedCards((state) => state + 2)} + /> + ), + )} ); } diff --git a/packages/website/src/types/maybeSkeletonComponentProps.ts b/packages/website/src/types/maybeSkeletonComponentProps.ts index 70b1412b8..381d72dfa 100644 --- a/packages/website/src/types/maybeSkeletonComponentProps.ts +++ b/packages/website/src/types/maybeSkeletonComponentProps.ts @@ -1,3 +1,8 @@ -export interface MaybeSkeletonComponentProps { - skeleton?: boolean; -} +export type MaybeSkeletonComponentProps = + | { + skeleton: true; + onIntersection?: () => void; + } + | (T & { + skeleton?: false; + });