tianji/src/client/components/SimpleVirtualList.tsx

93 lines
2.5 KiB
TypeScript
Raw Normal View History

2024-07-02 22:53:58 +08:00
import { useWatch } from '@/hooks/useWatch';
import { useTranslation } from '@i18next-toolkit/react';
import { useVirtualizer } from '@tanstack/react-virtual';
2024-07-02 22:53:58 +08:00
import { Empty } from 'antd';
import { last } from 'lodash-es';
import React, { useRef } from 'react';
interface VirtualListProps<T = any> {
allData: T[];
hasNextPage: boolean | undefined;
isFetchingNextPage: boolean;
onFetchNextPage: () => void;
estimateSize: number;
renderItem: (item: T) => React.ReactElement;
renderEmpty?: () => React.ReactElement;
2024-07-02 22:53:58 +08:00
}
export const SimpleVirtualList: React.FC<VirtualListProps> = React.memo(
(props) => {
const {
allData,
hasNextPage,
isFetchingNextPage,
onFetchNextPage,
estimateSize,
renderItem,
renderEmpty,
2024-07-02 22:53:58 +08:00
} = props;
const parentRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation();
2024-07-02 22:53:58 +08:00
const rowVirtualizer = useVirtualizer({
count: hasNextPage ? allData.length + 1 : allData.length,
getScrollElement: () => parentRef.current,
estimateSize: () => estimateSize,
2024-07-02 22:53:58 +08:00
overscan: 5,
});
const virtualItems = rowVirtualizer.getVirtualItems();
useWatch([virtualItems], () => {
const lastItem = last(virtualItems);
if (!lastItem) {
return;
}
if (
lastItem.index >= allData.length - 1 &&
hasNextPage &&
!isFetchingNextPage
) {
onFetchNextPage();
}
});
return (
<div ref={parentRef} className="h-full w-full overflow-auto">
{virtualItems.length === 0 && (renderEmpty ? renderEmpty() : <Empty />)}
2024-07-02 22:53:58 +08:00
<div
className="relative w-full"
style={{
height: `${rowVirtualizer.getTotalSize()}px`,
}}
>
{virtualItems.map((virtualItem) => {
const isLoaderRow = virtualItem.index > allData.length - 1;
const data = allData[virtualItem.index];
return (
<div
key={virtualItem.index}
className="absolute left-0 top-0 w-full"
style={{
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)`,
}}
>
{isLoaderRow
? hasNextPage
? t('Loading more...')
: t('Nothing more to load')
: renderItem(data)}
</div>
);
})}
2024-07-02 22:53:58 +08:00
</div>
</div>
);
}
);
SimpleVirtualList.displayName = 'SimpleVirtualList';