import { useEffect, useRef } from "react" import { useWindowVirtualizer } from "@tanstack/react-virtual" import { Spinner } from "@/components/ui/spinner" type VirtualListProps = { items: T[] estimateSize: number renderItem: (item: T, index: number) => React.ReactNode hasMore?: boolean isFetching?: boolean onLoadMore?: () => void overscan?: number } export function VirtualList({ items, estimateSize, renderItem, hasMore = false, isFetching = false, onLoadMore, overscan = 5, }: VirtualListProps) { const listRef = useRef(null) const virtualizer = useWindowVirtualizer({ count: items.length, estimateSize: () => estimateSize, overscan, scrollMargin: listRef.current?.offsetTop ?? 0, }) const virtualItems = virtualizer.getVirtualItems() const lastItem = virtualItems.at(-1) useEffect(() => { if (!lastItem || !hasMore || isFetching || !onLoadMore) return if (lastItem.index >= items.length - 5) { onLoadMore() } }, [lastItem?.index, items.length, hasMore, isFetching, onLoadMore]) return (
{virtualItems.map((virtualRow) => (
{renderItem(items[virtualRow.index]!, virtualRow.index)}
))}
{isFetching && (
)}
) }