(null);
const { css } = useYoshiki();
const onScroll = useCallback(() => {
if (!ref.current || !hasMore || isFetching) return;
const scroll =
layout.layout === "horizontal"
? ref.current.scrollWidth - ref.current.scrollLeft
: ref.current.scrollHeight - ref.current.scrollTop;
const offset =
layout.layout === "horizontal" ? ref.current.offsetWidth : ref.current.offsetHeight;
if (scroll <= offset * 1.2) loadMore();
}, [hasMore, isFetching, layout, loadMore]);
const scrollProps = { ref, onScroll };
// Automatically trigger a scroll check on start and after a fetch end in case the user is already
// at the bottom of the page or if there is no scroll bar (ultrawide or something like that)
useEffect(() => {
onScroll();
}, [isFetching, onScroll]);
const list = (props: object) => (
`${100 / x}%`),
gridTemplateRows: "max-content",
},
layout.layout === "grid" && {
gridTemplateColumns: ysMap(layout.numColumns, (x) => `repeat(${x}, 1fr)`),
justifyContent: "center",
alignItems: "flex-start",
overflowY: "auto",
},
],
props,
)}
>
{children}
{hasMore && isFetching && loader}
);
if (!Header) return list({ ...scrollProps, ...props });
if (!isValidElement(Header))
return (
// @ts-ignore
);
return (
<>
{Header}
{list({ ...scrollProps, ...props })}
>
);
};
export const InfiniteFetch = ({
query,
incremental = false,
placeholderCount = 15,
children,
layout,
empty,
divider: Divider = false,
Header,
headerProps: hprops,
getItemType,
...props
}: {
query: QueryIdentifier<_, Data>;
incremental?: boolean;
placeholderCount?: number;
layout: Layout;
children: (
item: Data extends Page ? WithLoading- : WithLoading,
i: number,
) => ReactElement | null;
empty?: string | JSX.Element;
divider?: boolean | ComponentType;
Header?: ComponentType<{ children: JSX.Element } & HeaderProps> | ReactElement;
headerProps: HeaderProps;
getItemType?: (item: Data, index: number) => string | number;
}): JSX.Element | null => {
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
const oldItems = useRef();
const { items, error, fetchNextPage, hasNextPage, isFetching } = useInfiniteFetch(query, {
useErrorBoundary: false,
});
if (incremental && items) oldItems.current = items;
// @ts-ignore
const headerProps: HeaderProps & { empty: boolean } = hprops
? { ...hprops, empty: items?.length === 0 }
: { empty: items?.length === 0 };
if (error) return addHeader(Header, , headerProps);
if (empty && items && items.length === 0) {
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
return addHeader(Header, , headerProps);
}
return (
(
{Divider && i !== 0 && (Divider === true ?
: )}
{children({ isLoading: true } as any, i)}
))}
Header={Header}
headerProps={headerProps}
{...props}
>
{(items ?? oldItems.current)?.map((item, i) => (
{Divider && i !== 0 && (Divider === true ?
: )}
{children({ ...item, isLoading: false } as any, i)}
))}
);
};