import type {DependencyList, RefObject} from 'react'; import {useCallback, useEffect, useMemo, useState} from 'react'; import type {CellMeasurerCacheParams, MultiGrid} from 'react-virtualized'; import {CellMeasurerCache} from 'react-virtualized'; type Opts = { /** * Options for the CellMeasurerCache constructor */ cellMeasurer: CellMeasurerCacheParams; /** * How many columns are being rendered */ columnCount: number; /** * List of other values that should trigger re-computing column sizes */ deps: DependencyList; /** * There must be one column with a dynamic width, so the table can fill all available width inside the container */ dynamicColumnIndex: number; /** * The elem. */ gridRef: RefObject; }; const globalCellMeasurerCache = new WeakMap(); function useVirtualizedGrid({ cellMeasurer, columnCount, deps, dynamicColumnIndex, gridRef, }: Opts) { const cache = useMemo(() => { if (globalCellMeasurerCache.has(cellMeasurer)) { return globalCellMeasurerCache.get(cellMeasurer)!; } const newCellMeasurer = new CellMeasurerCache(cellMeasurer); globalCellMeasurerCache.set(cellMeasurer, newCellMeasurer); return newCellMeasurer; }, [cellMeasurer]); const [scrollBarWidth, setScrollBarWidth] = useState(0); const onWrapperResize = useCallback(() => { // TODO: debounce? gridRef.current?.recomputeGridSize({columnIndex: dynamicColumnIndex}); }, [gridRef, dynamicColumnIndex]); // Recompute the width of the dynamic column when deps change (ie: a search/filter is applied) useEffect(onWrapperResize, [onWrapperResize, deps]); const onScrollbarPresenceChange = useCallback(({vertical, size}) => { setScrollBarWidth(vertical ? size : 0); }, []); const getColumnWidth = useCallback( (width: number) => ({index}) => { if (index !== dynamicColumnIndex) { return cache.columnWidth({index}); } const columns = Array.from(new Array(columnCount)); const fullWidth = width - scrollBarWidth; // Take the full width available, and remove all the static/cached widths // so we know how much space is available for our dynamic column. const colWidth = columns.reduce( (remainingWidth, _, i) => i === dynamicColumnIndex ? remainingWidth : remainingWidth - cache.columnWidth({index: i}), fullWidth ); return Math.max(colWidth, 200); }, [cache, columnCount, dynamicColumnIndex, scrollBarWidth] ); return { cache, getColumnWidth, onScrollbarPresenceChange, onWrapperResize, }; } export default useVirtualizedGrid;