All files / Rindu/components/VirtualizedList VirtualizedList.tsx

15.38% Statements 2/13
0% Branches 0/10
0% Functions 0/5
16.66% Lines 2/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 11719x               19x                                                                                                                                                                                                                        
import { CSSProperties, ReactElement, useCallback } from "react";
 
import {
  AutoSizer,
  IndexRange,
  InfiniteLoader,
  List,
  WindowScroller,
} from "react-virtualized";
 
interface ItemRendererProps<T> {
  key: string;
  style: CSSProperties;
  index: number;
  item: T | undefined;
  additionalProps?: Record<string, any>;
}
 
interface VirtualizedListProps<T> {
  items: T[] | null;
  totalItems: number;
  itemHeight: number;
  loadMoreItems?: (range: IndexRange) => Promise<void>;
  renderItem: (props: ItemRendererProps<T>) => ReactElement;
  isItemLoaded?: (index: number) => boolean;
  scrollElementSelector?: string;
  overscanRowCount?: number;
  additionalProps?: Record<string, any>;
}
 
export default function VirtualizedList<T>({
  items,
  totalItems,
  itemHeight,
  loadMoreItems,
  renderItem,
  isItemLoaded,
  scrollElementSelector = "#right .simplebar-content-wrapper",
  overscanRowCount = 2,
}: Readonly<VirtualizedListProps<T>>): ReactElement | null {
  const scrollElement =
    typeof window !== "undefined"
      ? (document.querySelector(scrollElementSelector) as HTMLElement)
      : undefined;
 
  const defaultIsItemLoaded = useCallback(
    (index: number) => !!items?.[index],
    [items]
  );
 
  const handleLoadMoreRows = useCallback(
    async (range: IndexRange) => {
      Iif (loadMoreItems) {
        await loadMoreItems(range);
      }
    },
    [loadMoreItems]
  );
 
  function rowRenderer({
    key,
    style,
    index,
  }: {
    key: string;
    style: CSSProperties;
    index: number;
  }) {
    return renderItem({
      key,
      style: style,
      index,
      item: items?.[index],
    });
  }
 
  Iif (!scrollElement) return null;
 
  return (
    <WindowScroller scrollElement={scrollElement}>
      {({ height, isScrolling, onChildScroll, scrollTop }) => (
        <AutoSizer disableHeight>
          {({ width }) => (
            <InfiniteLoader
              isRowLoaded={({ index }) =>
                (isItemLoaded ?? defaultIsItemLoaded)(index)
              }
              loadMoreRows={handleLoadMoreRows}
              rowCount={totalItems}
              minimumBatchSize={50}
              threshold={100}
            >
              {({ onRowsRendered, registerChild }) => (
                <List
                  autoHeight
                  height={height ?? 0}
                  isScrolling={isScrolling}
                  onRowsRendered={onRowsRendered}
                  ref={registerChild}
                  onScroll={onChildScroll}
                  overscanRowCount={overscanRowCount}
                  rowCount={totalItems}
                  rowHeight={itemHeight}
                  scrollTop={scrollTop}
                  width={width}
                  rowRenderer={rowRenderer}
                  tabIndex={-1}
                />
              )}
            </InfiniteLoader>
          )}
        </AutoSizer>
      )}
    </WindowScroller>
  );
}