(null);
const { css } = useYoshiki();
const onScroll = useCallback(() => {
if (!ref.current || !hasMore || isFetching || !fetchMore) 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;
// Load more if less than 3 element's worth of scroll is left
if (scroll <= offset * 3) loadMore();
}, [hasMore, isFetching, layout, loadMore, fetchMore]);
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",
paddingX: layout.gap as any,
},
layout.layout === "grid" && {
gridTemplateColumns: ysMap(layout.numColumns, (x) => `repeat(${x}, 1fr)`),
justifyContent: "center",
alignItems: "flex-start",
overflowY: "auto",
padding: layout.gap as any,
},
contentContainerStyle as any,
],
nativeStyleToCss(props),
)}
>
{children}
{isFetching && loader}
);
if (!Header) return list({ ...scrollProps, ...props });
if (!isValidElement(Header))
return (
// @ts-ignore
);
return (
<>
{Header}
{list({ ...scrollProps, ...props })}
>
);
};
export const InfiniteFetchList = ({
query,
incremental = false,
placeholderCount = 2,
children,
layout,
empty,
divider: Divider = false,
Header,
headerProps,
getItemType,
nested,
...props
}: {
query: ReturnType>;
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: WithLoading, index: number) => Kind;
getItemSize?: (kind: Kind) => number;
fetchMore?: boolean;
contentContainerStyle?: ContentStyle;
nested?: boolean;
}): JSX.Element | null => {
const oldItems = useRef();
const { items, error, fetchNextPage, hasNextPage, isFetching } = query;
if (incremental && items) oldItems.current = items;
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)}
))}
);
};
export const InfiniteFetch = ({
query,
...props
}: {
query: QueryIdentifier<_, Data>;
} & Omit>, "query">) => {
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
const ret = useInfiniteFetch(query);
return ;
};