diff --git a/packages/@react-aria/table/src/useTableColumnResize.ts b/packages/@react-aria/table/src/useTableColumnResize.ts index 8f2c3b65850..aadcc565716 100644 --- a/packages/@react-aria/table/src/useTableColumnResize.ts +++ b/packages/@react-aria/table/src/useTableColumnResize.ts @@ -11,7 +11,6 @@ */ import {ChangeEvent, Key, RefObject, useCallback, useRef} from 'react'; -import {ColumnSize} from '@react-types/table'; import {DOMAttributes} from '@react-types/shared'; import {focusSafely} from '@react-aria/focus'; import {focusWithoutScrolling, mergeProps, useId} from '@react-aria/utils'; @@ -19,7 +18,7 @@ import {getColumnHeaderId} from './utils'; import {GridNode} from '@react-types/grid'; // @ts-ignore import intlMessages from '../intl/*.json'; -import {TableState} from '@react-stately/table'; +import {TableColumnResizeState} from '@react-stately/table'; import {useKeyboard, useMove, usePress} from '@react-aria/interactions'; import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n'; @@ -48,28 +47,9 @@ export interface AriaTableColumnResizeProps { onResizeEnd?: (widths: Map) => void } +export interface AriaTableColumnResizeState extends Omit, 'widths'> {} -export interface TableLayoutState { - /** Get the current width of the specified column. */ - getColumnWidth: (key: Key) => number, - /** Get the current min width of the specified column. */ - getColumnMinWidth: (key: Key) => number, - /** Get the current max width of the specified column. */ - getColumnMaxWidth: (key: Key) => number, - /** Get the currently resizing column. */ - resizingColumn: Key, - /** Called to update the state that resizing has started. */ - onColumnResizeStart: (key: Key) => void, - /** - * Called to update the state that a resize event has occurred. - * Returns the new widths for all columns based on the resized column. - **/ - onColumnResize: (column: Key, width: number) => Map, - /** Called to update the state that resizing has ended. */ - onColumnResizeEnd: (key: Key) => void -} - -export function useTableColumnResize(props: AriaTableColumnResizeProps, state: TableState, layoutState: TableLayoutState, ref: RefObject): TableColumnResizeAria { +export function useTableColumnResize(props: AriaTableColumnResizeProps, state: AriaTableColumnResizeState, ref: RefObject): TableColumnResizeAria { let {column: item, triggerRef, isDisabled, onResizeStart, onResize, onResizeEnd} = props; const stringFormatter = useLocalizedStringFormatter(intlMessages); let id = useId(); @@ -89,35 +69,35 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st let startResize = useCallback((item) => { if (!isResizing.current) { - lastSize.current = layoutState.onColumnResize(item.key, layoutState.getColumnWidth(item.key)); - layoutState.onColumnResizeStart(item.key); + lastSize.current = state.onColumnResize(item.key, state.getColumnWidth(item.key)); + state.onColumnResizeStart(item.key); onResizeStart?.(lastSize.current); } isResizing.current = true; - }, [isResizing, onResizeStart, layoutState]); + }, [isResizing, onResizeStart, state]); let resize = useCallback((item, newWidth) => { - let sizes = layoutState.onColumnResize(item.key, newWidth); + let sizes = state.onColumnResize(item.key, newWidth); onResize?.(sizes); lastSize.current = sizes; - }, [onResize, layoutState]); + }, [onResize, state]); let endResize = useCallback((item) => { if (lastSize.current == null) { - lastSize.current = layoutState.onColumnResize(item.key, layoutState.getColumnWidth(item.key)); + lastSize.current = state.onColumnResize(item.key, state.getColumnWidth(item.key)); } if (isResizing.current) { - layoutState.onColumnResizeEnd(item.key); + state.onColumnResizeEnd(item.key); onResizeEnd?.(lastSize.current); } isResizing.current = false; lastSize.current = null; - }, [isResizing, onResizeEnd, layoutState]); + }, [isResizing, onResizeEnd, state]); const columnResizeWidthRef = useRef(0); const {moveProps} = useMove({ onMoveStart() { - columnResizeWidthRef.current = layoutState.getColumnWidth(item.key); + columnResizeWidthRef.current = state.getColumnWidth(item.key); startResize(item); }, onMove(e) { @@ -146,16 +126,16 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st } }); - let min = Math.floor(layoutState.getColumnMinWidth(item.key)); - let max = Math.floor(layoutState.getColumnMaxWidth(item.key)); + let min = Math.floor(state.getColumnMinWidth(item.key)); + let max = Math.floor(state.getColumnMaxWidth(item.key)); if (max === Infinity) { max = Number.MAX_SAFE_INTEGER; } - let value = Math.floor(layoutState.getColumnWidth(item.key)); + let value = Math.floor(state.getColumnWidth(item.key)); let ariaProps = { 'aria-label': props.label, 'aria-orientation': 'horizontal' as 'horizontal', - 'aria-labelledby': `${id} ${getColumnHeaderId(state, item.key)}`, + 'aria-labelledby': `${id} ${getColumnHeaderId(state.tableState, item.key)}`, 'aria-valuetext': stringFormatter.format('columnSize', {value}), min, max, @@ -169,7 +149,7 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st }, [ref]); let onChange = (e: ChangeEvent) => { - let currentWidth = layoutState.getColumnWidth(item.key); + let currentWidth = state.getColumnWidth(item.key); let nextValue = parseFloat(e.target.value); if (nextValue > currentWidth) { @@ -185,7 +165,7 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st if (e.ctrlKey || e.altKey || e.metaKey || e.shiftKey || e.pointerType === 'keyboard') { return; } - if (e.pointerType === 'virtual' && layoutState.resizingColumn != null) { + if (e.pointerType === 'virtual' && state.resizingColumn != null) { endResize(item); if (triggerRef?.current) { focusSafely(triggerRef.current); @@ -218,11 +198,11 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st // useMove calls onMoveStart for every keypress, but we want resize start to only be called when we start resize mode // call instead during focus and blur startResize(item); - state.setKeyboardNavigationDisabled(true); + state.tableState.setKeyboardNavigationDisabled(true); }, onBlur: () => { endResize(item); - state.setKeyboardNavigationDisabled(false); + state.tableState.setKeyboardNavigationDisabled(false); }, onChange, disabled: isDisabled diff --git a/packages/@react-aria/table/stories/example-resizing.tsx b/packages/@react-aria/table/stories/example-resizing.tsx index cde1b8f37f9..b3f3d020659 100644 --- a/packages/@react-aria/table/stories/example-resizing.tsx +++ b/packages/@react-aria/table/stories/example-resizing.tsx @@ -132,8 +132,9 @@ function Resizer({column, state, layoutState, onResizeStart, onResize, onResizeE label: 'Resizer', onResizeStart, onResize, - onResizeEnd - } as AriaTableColumnResizeProps, state, layoutState, ref); + onResizeEnd, + tableState: state + } as AriaTableColumnResizeProps, layoutState, ref); return ( <> diff --git a/packages/@react-spectrum/table/src/Resizer.tsx b/packages/@react-spectrum/table/src/Resizer.tsx index ffc6fdf1dfa..91272df175b 100644 --- a/packages/@react-spectrum/table/src/Resizer.tsx +++ b/packages/@react-spectrum/table/src/Resizer.tsx @@ -25,7 +25,7 @@ interface ResizerProps { function Resizer(props: ResizerProps, ref: RefObject) { let {column, showResizer} = props; - let {state, isEmpty, layout} = useTableContext(); + let {isEmpty, layout} = useTableContext(); // Virtualizer re-renders, but these components are all cached // in order to get around that and cause a rerender here, we use context // but we don't actually need any value, they are available on the layout object @@ -56,7 +56,7 @@ function Resizer(props: ResizerProps, ref: RefObject) { document.body.classList.remove(classNames(styles, 'resize-e')); document.body.classList.remove(classNames(styles, 'resize-w')); } - }), state, layout, ref); + }), layout, ref); let style = { cursor: undefined, diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 635b038c66c..89860fba723 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -88,7 +88,7 @@ const SELECTION_CELL_DEFAULT_WIDTH = { interface TableContextValue { state: TableState, - layout: TableLayout, + layout: TableLayout & {tableState: TableState}, headerRowHovered: boolean, isInResizeMode: boolean, setIsInResizeMode: (val: boolean) => void, @@ -171,7 +171,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef new TableLayout({ + let tableLayout = useMemo(() => new TableLayout({ // If props.rowHeight is auto, then use estimated heights based on scale, otherwise use fixed heights. rowHeight: props.overflowMode === 'wrap' ? null @@ -192,6 +192,14 @@ function TableView(props: SpectrumTableProps, ref: DOMRef { + let proxy = new Proxy(tableLayout, { + get(target, prop, receiver) { + return prop === 'tableState' ? state : Reflect.get(target, prop, receiver); + } + }); + return proxy as TableLayout & {tableState: TableState}; + }, [state, tableLayout]); let {gridProps} = useTable({ ...props, diff --git a/packages/@react-stately/table/src/useTableColumnResizeState.ts b/packages/@react-stately/table/src/useTableColumnResizeState.ts index 877ac1eb378..54021dd3aaf 100644 --- a/packages/@react-stately/table/src/useTableColumnResizeState.ts +++ b/packages/@react-stately/table/src/useTableColumnResizeState.ts @@ -33,9 +33,12 @@ export interface TableColumnResizeStateProps { /** Callback that is invoked when the resize event is ended. */ onColumnResizeEnd?: (key: Key) => void } -export interface TableColumnResizeState { - /** Trigger a resize and recalculation. */ - onColumnResize: (key: Key, width: number) => void, +export interface TableColumnResizeState { + /** + * Called to update the state that a resize event has occurred. + * Returns the new widths for all columns based on the resized column. + **/ + onColumnResize: (key: Key, width: number) => Map, /** Callback for when onColumnResize has started. */ onColumnResizeStart: (key: Key) => void, /** Callback for when onColumnResize has ended. */ @@ -47,11 +50,15 @@ export interface TableColumnResizeState { /** Gets the current maxWidth for the specified column. */ getColumnMaxWidth: (key: Key) => number, /** Currently calculated widths for all columns. */ - widths: Map + widths: Map, + /** Key of the currently resizing column. */ + resizingColumn: Key | null, + /** A reference to the table state. */ + tableState: TableState } -export function useTableColumnResizeState(props: TableColumnResizeStateProps, state: TableState): TableColumnResizeState { +export function useTableColumnResizeState(props: TableColumnResizeStateProps, state: TableState): TableColumnResizeState { let { getDefaultWidth, getDefaultMinWidth, @@ -118,13 +125,15 @@ export function useTableColumnResizeState(props: TableColumnResizeStateProps< columnLayout.getColumnMinWidth(key), getColumnMaxWidth: (key: Key) => columnLayout.getColumnMaxWidth(key), - widths: columnWidths + widths: columnWidths, + tableState: state }), [ columnLayout, resizingColumn, onColumnResize, onColumnResizeStart, onColumnResizeEnd, - columnWidths + columnWidths, + state ]); }