import React, { ReactNode, useEffect, useRef } from 'react'; import { Grid, GridCellProps, WindowScroller } from 'react-virtualized'; import ModelBase from 'App/ModelBase'; import Scroller from 'Components/Scroller/Scroller'; import useMeasure from 'Helpers/Hooks/useMeasure'; import usePrevious from 'Helpers/Hooks/usePrevious'; import { scrollDirections } from 'Helpers/Props'; import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder'; import styles from './VirtualTable.css'; const ROW_HEIGHT = 38; function overscanIndicesGetter(options: { cellCount: number; overscanCellsCount: number; startIndex: number; stopIndex: number; }) { const { cellCount, overscanCellsCount, startIndex, stopIndex } = options; // The default getter takes the scroll direction into account, // but that can cause issues. Ignore the scroll direction and // always over return more items. const overscanStartIndex = startIndex - overscanCellsCount; const overscanStopIndex = stopIndex + overscanCellsCount; return { overscanStartIndex: Math.max(0, overscanStartIndex), overscanStopIndex: Math.min(cellCount - 1, overscanStopIndex), }; } interface VirtualTableProps { isSmallScreen: boolean; className?: string; items: T[]; scrollIndex?: number; scrollTop?: number; scroller: Element; header: React.ReactNode; headerHeight?: number; rowRenderer: (rowProps: GridCellProps) => ReactNode; rowHeight?: number; } function VirtualTable({ isSmallScreen, className = styles.tableContainer, items, scroller, scrollIndex, scrollTop, header, headerHeight = 38, rowHeight = ROW_HEIGHT, rowRenderer, ...otherProps }: VirtualTableProps) { const [measureRef, bounds] = useMeasure(); const gridRef = useRef(null); const scrollRestored = useRef(false); const previousScrollIndex = usePrevious(scrollIndex); const previousItems = usePrevious(items); const width = bounds.width; const gridStyle = { boxSizing: undefined, direction: undefined, height: undefined, position: undefined, willChange: undefined, overflow: undefined, width: undefined, }; const containerStyle = { position: undefined, }; useEffect(() => { if (gridRef.current && width > 0) { gridRef.current.recomputeGridSize(); } }, [width]); useEffect(() => { if ( gridRef.current && previousItems && hasDifferentItemsOrOrder(previousItems, items) ) { gridRef.current.recomputeGridSize(); } }, [items, previousItems]); useEffect(() => { if (gridRef.current && scrollTop && !scrollRestored.current) { gridRef.current.scrollToPosition({ scrollLeft: 0, scrollTop }); scrollRestored.current = true; } }, [scrollTop]); useEffect(() => { if ( gridRef.current && scrollIndex != null && scrollIndex !== previousScrollIndex ) { gridRef.current.scrollToCell({ rowIndex: scrollIndex, columnIndex: 0, }); } }, [scrollIndex, previousScrollIndex]); return ( {({ height, registerChild, onChildScroll, scrollTop }) => { if (!height) { return null; } return (
{header} {/* @ts-expect-error - ref type is incompatible */}
); }}
); } export default VirtualTable;