diff --git a/src/components/HighTable/HighTable.tsx b/src/components/HighTable/HighTable.tsx index 70d47db7..e6eadccf 100644 --- a/src/components/HighTable/HighTable.tsx +++ b/src/components/HighTable/HighTable.tsx @@ -140,7 +140,7 @@ export function HighTableInner({ const { numRows } = data const { enterCellsNavigation, setEnterCellsNavigation, onTableKeyDown: onNavigationTableKeyDown, onScrollKeyDown, cellPosition, focusFirstCell } = useCellsNavigation() const { containerRef } = usePortalContainer() - const { setAvailableWidth } = useColumnWidths() + const { setAvailableWidthAndAdjustMeasured } = useColumnWidths() const { isHiddenColumn } = useColumnVisibilityStates() const { orderBy, onOrderByChange } = useOrderBy() const { selectable, toggleAllRows, pendingSelectionGesture, onTableKeyDown: onSelectionTableKeyDown, allRowsSelected, isRowSelected, toggleRowNumber, toggleRangeToRowNumber } = useSelection() @@ -209,7 +209,7 @@ export function HighTableInner({ } }, [cellPosition, rowsRange, lastCellPosition, padding, enterCellsNavigation, setEnterCellsNavigation]) - // handle scrolling and window resizing + // handle scrolling and component resizing useEffect(() => { let abortController: AbortController | undefined = undefined @@ -220,7 +220,7 @@ export function HighTableInner({ // abort the previous fetches if any abortController?.abort() abortController = new AbortController() - // view window height (0 is not allowed - the syntax is verbose, but makes it clear) + // view height (0 is not allowed - the syntax is verbose, but makes it clear) const currentClientHeight = scrollRef.current?.clientHeight const clientHeight = currentClientHeight === undefined || currentClientHeight === 0 ? 100 : currentClientHeight // scroll position @@ -262,27 +262,44 @@ export function HighTableInner({ // we use the scrollRef client width, because we're interested in the content area const tableWidth = getClientWidth(scrollRef.current) const leftColumnWidth = getOffsetWidth(tableCornerRef.current) - setAvailableWidth?.(tableWidth - leftColumnWidth) + setAvailableWidthAndAdjustMeasured?.(tableWidth - leftColumnWidth) } } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const resizeObserver = window.ResizeObserver && new window.ResizeObserver((entries) => { + for (const entry of entries) { + if (entry.target === scrollRef.current) { + handleScroll() + reportWidth() + } + } + }) + // run once handleScroll() reportWidth() // listeners const scroller = scrollRef.current - scroller?.addEventListener('scroll', handleScroll) - window.addEventListener('resize', handleScroll) - window.addEventListener('resize', reportWidth) + + if (scroller) { + scroller.addEventListener('scroll', handleScroll) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + resizeObserver?.observe(scroller) + } return () => { abortController?.abort() // cancel the fetches if any - scroller?.removeEventListener('scroll', handleScroll) - window.removeEventListener('resize', handleScroll) - window.removeEventListener('resize', reportWidth) + if (scroller) { + scroller.removeEventListener('scroll', handleScroll) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + resizeObserver?.unobserve(scroller) + } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + resizeObserver?.disconnect() } - }, [numRows, overscan, padding, scrollHeight, setAvailableWidth, data, orderBy, onError, columnsParameters]) + }, [numRows, overscan, padding, scrollHeight, setAvailableWidthAndAdjustMeasured, data, orderBy, onError, columnsParameters]) // focus table on mount, or on later changes, so arrow keys work // Note that the dependency upon data and nowRows was removed, because focusFirstCell should depend on them diff --git a/src/hooks/useColumnWidths.tsx b/src/hooks/useColumnWidths.tsx index bc15f252..f3606ccb 100644 --- a/src/hooks/useColumnWidths.tsx +++ b/src/hooks/useColumnWidths.tsx @@ -6,7 +6,7 @@ interface ColumnWidthsContextType { getColumnWidth?: (columnIndex: number) => number | undefined getColumnStyle?: (columnIndex: number) => CSSProperties isFixedColumn?: (columnIndex: number) => boolean // returns true if the column has a fixed width - setAvailableWidth?: (width: number) => void // used to set the width of the wrapper element + setAvailableWidthAndAdjustMeasured?: (width: number) => void // used to set the width of the wrapper element, and update the measured columns forceWidth?: (options: { columnIndex: number; width: number, minWidth?: number }) => void // used to set a fixed width for a column (will be stored and overrides the auto width) measureWidth?: (options: { columnIndex: number; measured: number }) => void // used to set the measured width (and adjust all the measured columns) removeWidth?: (options: { columnIndex: number }) => void // used to remove the width of a column, so it can be measured again @@ -30,7 +30,6 @@ export function ColumnWidthsProvider({ children, localStorageKey, numColumns, mi } const [availableWidth, setAvailableWidth] = useState(0) - // ^ TODO: add a validation for availableWidth? const clamp = useCallback((width: number, configMinWidth?: number) => { const minWidthToUse = configMinWidth ?? minWidth @@ -122,17 +121,28 @@ export function ColumnWidthsProvider({ children, localStorageKey, numColumns, mi }) }, [isValidIndex, setColumnWidths]) + const setAvailableWidthAndAdjustMeasured = useCallback((nextAvailableWidth: number) => { + if (!isValidWidth(nextAvailableWidth)) { + return + } + setAvailableWidth(nextAvailableWidth) + setColumnWidths(columnWidths => { + // compute the adjusted widths + return adjustMeasuredWidths({ columnWidths: columnWidths ?? [], availableWidth: nextAvailableWidth, clamp, numColumns }) + }) + }, [clamp, numColumns, setColumnWidths]) + const value = useMemo(() => { return { getColumnWidth, getColumnStyle, isFixedColumn, - setAvailableWidth, + setAvailableWidthAndAdjustMeasured, forceWidth, measureWidth, removeWidth, } - }, [getColumnWidth, getColumnStyle, isFixedColumn, setAvailableWidth, forceWidth, measureWidth, removeWidth]) + }, [getColumnWidth, getColumnStyle, isFixedColumn, setAvailableWidthAndAdjustMeasured, forceWidth, measureWidth, removeWidth]) return (