import { useVirtualizer } from "@tanstack/react-virtual"; import clsx from "clsx"; import * as React from "react"; import { SuffixContext, useSuffixContext } from "../../contexts"; import { useComposedRefs } from "../../utils/mergeRefs"; import * as styles from "./styles.module.css"; interface Props { className: string; data: any[]; type?: string; __length?: number; children?: (value: any, index: number) => React.ReactElement; } export function List({ className, data, type: _type, __length, children, ...props }: Props) { const suffix = useSuffixContext(); return children && Array.isArray(data) && data.length ? ( ) : null; } export function Repeater({ data, children }: Props) { const suffix = useSuffixContext(); return children && Array.isArray(data) && data.length ? ( <> {data.map((value, idx) => { const itemKey = `${value?.id ?? idx}`; const key = suffix ? `${itemKey}-${suffix}` : itemKey; return ( {children ? children(value, idx) : null} ); })} ) : null; } export function VirtualList({ ref, className, data, type: _type, __length, children, estimatedSize = 35, overscan = 5, hasNextPage = false, isFetchingNextPage = false, fetchNextPage, ...props }: Props & { ref?: React.Ref; estimatedSize?: number; overscan?: number; hasNextPage?: boolean; isFetchingNextPage?: boolean; fetchNextPage?: () => void; }) { const parentRef = React.useRef(null); const suffix = useSuffixContext(); const items = Array.isArray(data) ? data : []; // The virtualizer const rowVirtualizer = useVirtualizer({ count: hasNextPage ? items.length + 1 : items.length, getScrollElement: () => parentRef.current, estimateSize: () => estimatedSize, overscan: overscan, }); React.useEffect(() => { const lastItem = rowVirtualizer.getVirtualItems().at(-1); if (!lastItem) return; if ( lastItem.index === items.length - 1 && hasNextPage && fetchNextPage && !isFetchingNextPage ) { fetchNextPage(); } }, [ hasNextPage, fetchNextPage, items.length, isFetchingNextPage, rowVirtualizer.getVirtualItems(), ]); const allRefs = useComposedRefs(parentRef, ref); return children && items.length ? (
    {rowVirtualizer.getVirtualItems().map((virtualItem) => { const isLoaderRow = virtualItem.index > items.length - 1; const value = items[virtualItem.index]; const itemKey = `${value?.id ?? virtualItem.index}`; const key = suffix ? `${itemKey}-${suffix}` : itemKey; return (
  • {isLoaderRow ? ( {hasNextPage ? "Loading more..." : "No more items"} ) : children ? ( children(value, virtualItem.index) ) : null}
  • ); })}
) : null; }