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 ? (
{data.map((value, idx) => {
const itemKey = `${value?.id ?? idx}`;
const key = suffix ? `${itemKey}-${suffix}` : itemKey;
return (
-
{children ? children(value, idx) : null}
);
})}
) : 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;
}