From 59ef6a3a25efed8b324506dc362052e158c75ae0 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Thu, 18 Aug 2022 16:39:55 -0700 Subject: [PATCH 01/17] Expand hit area of resizers --- .../components/table/index.css | 29 ++++++++++++-- .../components/table/skin.css | 5 +++ packages/@react-aria/table/intl/ar-AE.json | 3 +- .../table/src/useTableColumnResize.ts | 5 ++- packages/@react-spectrum/table/src/Nubbin.tsx | 19 +++++++++ .../@react-spectrum/table/src/Resizer.tsx | 31 ++++++++------ .../@react-spectrum/table/src/TableView.tsx | 40 ++++++++++++++++--- .../@react-stately/layout/src/TableLayout.ts | 3 ++ 8 files changed, 113 insertions(+), 22 deletions(-) create mode 100644 packages/@react-spectrum/table/src/Nubbin.tsx diff --git a/packages/@adobe/spectrum-css-temp/components/table/index.css b/packages/@adobe/spectrum-css-temp/components/table/index.css index 635cd6796a7..9f033b7064b 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/index.css +++ b/packages/@adobe/spectrum-css-temp/components/table/index.css @@ -21,6 +21,7 @@ governing permissions and limitations under the License. } .spectrum-Table { + position: relative; border-collapse: separate; border-spacing: 0; outline: none; @@ -101,10 +102,11 @@ svg.spectrum-Table-sortedIcon { .spectrum-Table-columnResizer { display: flex; - flex: 0 0 auto; - justify-content: flex-end; + position: absolute; + inset-inline-end: -10px; + justify-content: center; box-sizing: border-box; - inline-size: 10px; + inline-size: 20px; block-size: 100%; user-select: none; @@ -124,6 +126,13 @@ svg.spectrum-Table-sortedIcon { } } } +.spectrum-Table-columnResizerPlaceholder { + flex: 0 0 auto; + box-sizing: border-box; + inline-size: 10px; + block-size: 100%; + user-select: none; +} .spectrum-Table-cell--alignCenter { text-align: center; @@ -404,3 +413,17 @@ svg.spectrum-Table-sortedIcon { .spectrum-Table-checkbox { vertical-align: super; } + +.spectrum-Table-colResizeIndicator { + height: 100%; + width: 2px; + position: absolute; + pointer-events: none; +} +.spectrum-Table-colResizeNubbin { + position: absolute; + top: -6px; + width: 16px; + height: 16px; + inset-inline-start: -7px; +} diff --git a/packages/@adobe/spectrum-css-temp/components/table/skin.css b/packages/@adobe/spectrum-css-temp/components/table/skin.css index 1c20d9b89b7..848c93ae850 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/table/skin.css @@ -291,3 +291,8 @@ tbody.spectrum-Table-body { } } } + +.spectrum-Table-colResizeIndicator { + background-color: var(--spectrum-global-color-blue-400); +} +.spectrum-Table-colResizeNubbin {} diff --git a/packages/@react-aria/table/intl/ar-AE.json b/packages/@react-aria/table/intl/ar-AE.json index 3b5c0f94681..df582534569 100644 --- a/packages/@react-aria/table/intl/ar-AE.json +++ b/packages/@react-aria/table/intl/ar-AE.json @@ -6,5 +6,6 @@ "select": "تحديد", "selectAll": "تحديد الكل", "sortable": "عمود قابل للترتيب", - "resizeTextValue": "{value} pixels" + "resizeTextValue": "{value} pixels", + "columnSize": "{value} pixels" } diff --git a/packages/@react-aria/table/src/useTableColumnResize.ts b/packages/@react-aria/table/src/useTableColumnResize.ts index 5adfb7c51c2..274e951cad9 100644 --- a/packages/@react-aria/table/src/useTableColumnResize.ts +++ b/packages/@react-aria/table/src/useTableColumnResize.ts @@ -79,9 +79,12 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st } } }, - onMoveEnd() { + onMoveEnd({pointerType}) { columnResizeWidthRef.current = 0; document.body.style.cursor = cursor.current; + if (pointerType === 'mouse') { + stateRef.current.onColumnResizeEnd(item); + } } }); let min = Math.floor(stateRef.current.getColumnMinWidth(item.key)); diff --git a/packages/@react-spectrum/table/src/Nubbin.tsx b/packages/@react-spectrum/table/src/Nubbin.tsx new file mode 100644 index 00000000000..a0bbde304c4 --- /dev/null +++ b/packages/@react-spectrum/table/src/Nubbin.tsx @@ -0,0 +1,19 @@ + +import React from 'react'; + + +// TODO resize with scale? colors should be variables +export function Nubbin() { + return ( + + + + + + + + + + + ); +}; diff --git a/packages/@react-spectrum/table/src/Resizer.tsx b/packages/@react-spectrum/table/src/Resizer.tsx index fe7e33ffe53..7ae4564dd13 100644 --- a/packages/@react-spectrum/table/src/Resizer.tsx +++ b/packages/@react-spectrum/table/src/Resizer.tsx @@ -40,20 +40,27 @@ function Resizer(props: ResizerProps, ref: RefObject) { } return ( - + <> + +
+ + + +
+
+ {/* Placeholder so that the title doesn't intersect with space reserved by the resizer when it appears. */}
- - - -
-
+ className={classNames(styles, 'spectrum-Table-columnResizerPlaceholder')} /> + ); } diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 3a2483c0d5f..7467dbe1185 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -22,7 +22,7 @@ import intlMessages from '../intl/*.json'; import {Item, Menu, MenuTrigger} from '@react-spectrum/menu'; import {layoutInfoToStyle, ScrollView, setScrollLeft, useVirtualizer, VirtualizerItem} from '@react-aria/virtualizer'; import {ProgressCircle} from '@react-spectrum/progress'; -import React, {ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import React, {Key, ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import {Rect, ReusableView, useVirtualizerState} from '@react-stately/virtualizer'; import {Resizer} from './Resizer'; import {SpectrumColumnProps, SpectrumTableProps} from '@react-types/table'; @@ -46,6 +46,7 @@ import { useTableSelectionCheckbox } from '@react-aria/table'; import {VisuallyHidden} from '@react-aria/visually-hidden'; +import {Nubbin} from './Nubbin'; const DEFAULT_HEADER_HEIGHT = { medium: 34, @@ -81,7 +82,8 @@ interface TableContextValue { state: TableState, layout: TableLayout, columnState: TableColumnResizeState, - headerRowHovered: boolean + headerRowHovered: boolean, + resizeModeState: [boolean, (val: boolean) => void] } const TableContext = React.createContext>(null); @@ -107,13 +109,16 @@ function TableView(props: SpectrumTableProps, ref: DOMRef setIsInResizeMode(false)}), getDefaultWidth}, state.collection); // If the selection behavior changes in state, we need to update showSelectionCheckboxes here due to the circular dependency... let shouldShowCheckboxes = state.selectionManager.selectionBehavior !== 'replace'; @@ -306,7 +311,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef + (props: SpectrumTableProps, ref: DOMRef(); let loadingState = collection.body.props.loadingState; let isLoading = loadingState === 'loading' || loadingState === 'loadingMore'; @@ -413,6 +419,14 @@ function TableVirtualizer({layout, collection, focusedKey, renderView, renderWra } }, [state.contentSize, state.virtualizer, state.isAnimating, onLoadMore, isLoading]); + let keysBefore = []; + let key = columnState.currentlyResizingColumn; + do { + keysBefore.push(key); + key = tableState.collection.getKeyBefore(key); + } while (key != null); + let resizerPosition = keysBefore.reduce((acc, key) => acc + columnState.getColumnWidth(key), 0); + return (
{state.visibleViews[1]} +
+
+ +
+
); } @@ -530,7 +559,7 @@ function ResizableTableColumnHeader(props) { let ref = useRef(null); let triggerRef = useRef(null); let resizingRef = useRef(null); - let {state, columnState, headerRowHovered} = useTableContext(); + let {state, columnState, headerRowHovered, resizeModeState: [isInResizeMode, setIsInResizeMode]} = useTableContext(); let stringFormatter = useLocalizedStringFormatter(intlMessages); let {columnHeaderProps} = useTableColumnHeader({ node: column, @@ -557,6 +586,7 @@ function ResizableTableColumnHeader(props) { break; case 'resize': columnState.onColumnResizeStart(column); + setIsInResizeMode(true); break; } }; diff --git a/packages/@react-stately/layout/src/TableLayout.ts b/packages/@react-stately/layout/src/TableLayout.ts index fc61770edbb..1275cc078e9 100644 --- a/packages/@react-stately/layout/src/TableLayout.ts +++ b/packages/@react-stately/layout/src/TableLayout.ts @@ -101,6 +101,9 @@ export class TableLayout extends ListLayout { height = Math.max(height, layoutNode.layoutInfo.rect.height); columns.push(layoutNode); } + for (let [i, layout] of columns.entries()) { + layout.layoutInfo.zIndex = columns.length - i + 1; + } this.setChildHeights(columns, height); From d86feac9e2d7d48bf7b1a19996e40fb23f4f60d7 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Thu, 18 Aug 2022 16:51:50 -0700 Subject: [PATCH 02/17] fix lint --- packages/@react-spectrum/table/src/Nubbin.tsx | 13 ++++++++++++- packages/@react-spectrum/table/src/TableView.tsx | 5 +++-- .../@react-spectrum/table/test/TableSizing.test.js | 11 ++++++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/@react-spectrum/table/src/Nubbin.tsx b/packages/@react-spectrum/table/src/Nubbin.tsx index a0bbde304c4..5b18d3a0d76 100644 --- a/packages/@react-spectrum/table/src/Nubbin.tsx +++ b/packages/@react-spectrum/table/src/Nubbin.tsx @@ -1,3 +1,14 @@ +/* + * Copyright 2022 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ import React from 'react'; @@ -16,4 +27,4 @@ export function Nubbin() { ); -}; +} diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 7467dbe1185..ce732b9266c 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -21,8 +21,9 @@ import {GridNode} from '@react-types/grid'; import intlMessages from '../intl/*.json'; import {Item, Menu, MenuTrigger} from '@react-spectrum/menu'; import {layoutInfoToStyle, ScrollView, setScrollLeft, useVirtualizer, VirtualizerItem} from '@react-aria/virtualizer'; +import {Nubbin} from './Nubbin'; import {ProgressCircle} from '@react-spectrum/progress'; -import React, {Key, ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import React, {ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import {Rect, ReusableView, useVirtualizerState} from '@react-stately/virtualizer'; import {Resizer} from './Resizer'; import {SpectrumColumnProps, SpectrumTableProps} from '@react-types/table'; @@ -46,7 +47,6 @@ import { useTableSelectionCheckbox } from '@react-aria/table'; import {VisuallyHidden} from '@react-aria/visually-hidden'; -import {Nubbin} from './Nubbin'; const DEFAULT_HEADER_HEIGHT = { medium: 34, @@ -559,6 +559,7 @@ function ResizableTableColumnHeader(props) { let ref = useRef(null); let triggerRef = useRef(null); let resizingRef = useRef(null); + // eslint-disable-next-line @typescript-eslint/no-unused-vars let {state, columnState, headerRowHovered, resizeModeState: [isInResizeMode, setIsInResizeMode]} = useTableContext(); let stringFormatter = useLocalizedStringFormatter(intlMessages); let {columnHeaderProps} = useTableColumnHeader({ diff --git a/packages/@react-spectrum/table/test/TableSizing.test.js b/packages/@react-spectrum/table/test/TableSizing.test.js index e0e8342bd78..f4c92f1029a 100644 --- a/packages/@react-spectrum/table/test/TableSizing.test.js +++ b/packages/@react-spectrum/table/test/TableSizing.test.js @@ -324,10 +324,15 @@ describe('TableViewSizing', function () { it('should return the proper cell z-indexes for overflowMode="wrap"', function () { let tree = renderTable({overflowMode: 'wrap', selectionMode: 'multiple'}); - let rows = tree.getAllByRole('row'); - expect(rows).toHaveLength(3); + let [headerRow, ...bodyRows] = tree.getAllByRole('row'); + expect(bodyRows).toHaveLength(2); - for (let row of rows) { + for (let [index, cell] of headerRow.childNodes.entries()) { + // 4 because there is a checkbox column + expect(Number(cell.style.zIndex)).toBe(4 - index + 1); + } + + for (let row of bodyRows) { for (let [index, cell] of row.childNodes.entries()) { if (index === 0) { expect(cell.style.zIndex).toBe('2'); From 5d080ede8eb010389b22a182d0c96cd242e37947 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Thu, 18 Aug 2022 17:18:16 -0700 Subject: [PATCH 03/17] hide resizers if we change to keyboard modality while hovering --- packages/@react-spectrum/table/src/TableView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index ce732b9266c..b99fc199e4c 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -16,6 +16,7 @@ import {Checkbox} from '@react-spectrum/checkbox'; import {classNames, useDOMRef, useFocusableRef, useStyleProps, useUnwrapDOMRef} from '@react-spectrum/utils'; import {DOMRef, FocusableRef} from '@react-types/shared'; import {FocusRing, useFocusRing} from '@react-aria/focus'; +import {getInteractionModality, useHover, usePress} from '@react-aria/interactions'; import {GridNode} from '@react-types/grid'; // @ts-ignore import intlMessages from '../intl/*.json'; @@ -33,7 +34,6 @@ import {TableColumnResizeState, TableState, useTableColumnResizeState, useTableS import {TableLayout} from '@react-stately/layout'; import {Tooltip, TooltipTrigger} from '@react-spectrum/tooltip'; import {useButton} from '@react-aria/button'; -import {useHover, usePress} from '@react-aria/interactions'; import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n'; import {useProvider, useProviderProps} from '@react-spectrum/provider'; import { @@ -620,7 +620,7 @@ function ResizableTableColumnHeader(props) { } }, [columnState.currentlyResizingColumn, column.key]); - let showResizer = headerRowHovered || columnState.currentlyResizingColumn != null; + let showResizer = (headerRowHovered && getInteractionModality() !== 'keyboard') || columnState.currentlyResizingColumn != null; return ( From 10892481d594cfd5a03ac5bd854f5d725d325f78 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Thu, 18 Aug 2022 18:31:02 -0700 Subject: [PATCH 04/17] support RTL --- packages/@react-spectrum/table/src/TableView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index b99fc199e4c..842e499484b 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -462,7 +462,7 @@ function TableVirtualizer({layout, collection, focusedKey, renderView, renderWra aria-hidden className={classNames(styles, 'spectrum-Table-colResizeIndicator')} style={{ - left: `${resizerPosition}px`, + [direction === 'ltr' ? 'left' : 'right']: `${resizerPosition}px`, visibility: columnState.currentlyResizingColumn != null ? 'visible' : 'hidden' }}>
Date: Fri, 19 Aug 2022 17:15:26 -0700 Subject: [PATCH 05/17] use spectrum colors --- packages/@adobe/spectrum-css-temp/components/table/skin.css | 2 +- packages/@react-spectrum/table/src/Nubbin.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@adobe/spectrum-css-temp/components/table/skin.css b/packages/@adobe/spectrum-css-temp/components/table/skin.css index 848c93ae850..938e49123a0 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/table/skin.css @@ -293,6 +293,6 @@ tbody.spectrum-Table-body { } .spectrum-Table-colResizeIndicator { - background-color: var(--spectrum-global-color-blue-400); + background-color: var(--spectrum-global-color-blue-600); } .spectrum-Table-colResizeNubbin {} diff --git a/packages/@react-spectrum/table/src/Nubbin.tsx b/packages/@react-spectrum/table/src/Nubbin.tsx index 5b18d3a0d76..4800aac19ee 100644 --- a/packages/@react-spectrum/table/src/Nubbin.tsx +++ b/packages/@react-spectrum/table/src/Nubbin.tsx @@ -18,7 +18,7 @@ export function Nubbin() { return ( - + From 9b1c200af4d2884207849b0f7fe135e94b1beb1d Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Mon, 22 Aug 2022 14:17:54 -0700 Subject: [PATCH 06/17] support up down arrows --- .../table/src/useTableColumnResize.ts | 7 ++++-- .../table/test/TableSizing.test.js | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/@react-aria/table/src/useTableColumnResize.ts b/packages/@react-aria/table/src/useTableColumnResize.ts index 274e951cad9..9b457b09654 100644 --- a/packages/@react-aria/table/src/useTableColumnResize.ts +++ b/packages/@react-aria/table/src/useTableColumnResize.ts @@ -59,13 +59,16 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st columnResizeWidthRef.current = stateRef.current.getColumnWidth(item.key); cursor.current = document.body.style.cursor; }, - onMove({deltaX, pointerType}) { + onMove({deltaX, deltaY, pointerType}) { if (direction === 'rtl') { deltaX *= -1; } // if moving up/down only, no need to resize - if (deltaX !== 0) { + if (deltaX !== 0 || (pointerType === 'keyboard' && deltaY !== 0)) { if (pointerType === 'keyboard') { + if (deltaY !== 0 && deltaX === 0) { + deltaX = deltaY * -1; + } deltaX *= 10; } columnResizeWidthRef.current += deltaX; diff --git a/packages/@react-spectrum/table/test/TableSizing.test.js b/packages/@react-spectrum/table/test/TableSizing.test.js index f4c92f1029a..25d270e9f68 100644 --- a/packages/@react-spectrum/table/test/TableSizing.test.js +++ b/packages/@react-spectrum/table/test/TableSizing.test.js @@ -1004,6 +1004,30 @@ describe('TableViewSizing', function () { fireEvent.keyUp(document.activeElement, {key: 'ArrowLeft'}); + for (let row of rows) { + expect(row.childNodes[0].style.width).toBe('600px'); + expect(row.childNodes[1].style.width).toBe('200px'); + expect(row.childNodes[2].style.width).toBe('200px'); + } + + fireEvent.keyDown(document.activeElement, {key: 'ArrowUp'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowUp'}); + fireEvent.keyDown(document.activeElement, {key: 'ArrowUp'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowUp'}); + + + for (let row of rows) { + expect(row.childNodes[0].style.width).toBe('620px'); + expect(row.childNodes[1].style.width).toBe('190px'); + expect(row.childNodes[2].style.width).toBe('190px'); + } + + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + + for (let row of rows) { expect(row.childNodes[0].style.width).toBe('600px'); expect(row.childNodes[1].style.width).toBe('200px'); From 64ad9faffb499da09831a4beeb35e78613d787a2 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Mon, 22 Aug 2022 15:03:23 -0700 Subject: [PATCH 07/17] fix header truncation for resizable columns --- .../@adobe/spectrum-css-temp/components/table/index.css | 7 ++++++- packages/@react-spectrum/table/stories/Table.stories.tsx | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/@adobe/spectrum-css-temp/components/table/index.css b/packages/@adobe/spectrum-css-temp/components/table/index.css index 9f033b7064b..405c7d4bfb7 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/index.css +++ b/packages/@adobe/spectrum-css-temp/components/table/index.css @@ -95,7 +95,12 @@ svg.spectrum-Table-sortedIcon { } .spectrum-Table-headCellButton { box-sizing: border-box; - padding: var(--spectrum-table-header-padding-y) var(--spectrum-table-header-padding-x); + padding: var(--spectrum-table-header-padding-y) 0 var(--spectrum-table-header-padding-y) var(--spectrum-table-header-padding-x); + + /* truncate text with ellipsis */ + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } } } diff --git a/packages/@react-spectrum/table/stories/Table.stories.tsx b/packages/@react-spectrum/table/stories/Table.stories.tsx index 7ce6b67fe37..4cd56f31dce 100644 --- a/packages/@react-spectrum/table/stories/Table.stories.tsx +++ b/packages/@react-spectrum/table/stories/Table.stories.tsx @@ -1335,7 +1335,7 @@ storiesOf('TableView', module) () => ( - File Name + File name for reference Type Size Weight From ef8e093f3f21424f192eeff29139f8ea4ebddd7b Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Tue, 30 Aug 2022 13:56:45 -0700 Subject: [PATCH 08/17] fix double resize end and scroll position --- .../@react-aria/table/src/useTableColumnResize.ts | 14 +++++++------- packages/@react-spectrum/table/src/TableView.tsx | 6 ++++-- .../table/src/useTableColumnResizeState.ts | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/@react-aria/table/src/useTableColumnResize.ts b/packages/@react-aria/table/src/useTableColumnResize.ts index 9b457b09654..a839cd0e8d5 100644 --- a/packages/@react-aria/table/src/useTableColumnResize.ts +++ b/packages/@react-aria/table/src/useTableColumnResize.ts @@ -63,14 +63,14 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st if (direction === 'rtl') { deltaX *= -1; } - // if moving up/down only, no need to resize - if (deltaX !== 0 || (pointerType === 'keyboard' && deltaY !== 0)) { - if (pointerType === 'keyboard') { - if (deltaY !== 0 && deltaX === 0) { - deltaX = deltaY * -1; - } - deltaX *= 10; + if (pointerType === 'keyboard') { + if (deltaY !== 0 && deltaX === 0) { + deltaX = deltaY * -1; } + deltaX *= 10; + } + // if moving up/down only, no need to resize + if (deltaX !== 0) { columnResizeWidthRef.current += deltaX; stateRef.current.onColumnResize(item, columnResizeWidthRef.current); if (stateRef.current.getColumnMinWidth(item.key) >= stateRef.current.getColumnWidth(item.key)) { diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 842e499484b..1794d8bf6f6 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -393,10 +393,12 @@ function TableVirtualizer({layout, collection, focusedKey, renderView, renderWra let headerHeight = layout.getLayoutInfo('header')?.rect.height || 0; let visibleRect = state.virtualizer.visibleRect; + let [scrollPosition, setScrollPosition] = useState(0); // Sync the scroll position from the table body to the header container. let onScroll = useCallback(() => { headerRef.current.scrollLeft = bodyRef.current.scrollLeft; - }, [bodyRef]); + setScrollPosition(bodyRef.current.scrollLeft); + }, [bodyRef, setScrollPosition, headerRef]); let onVisibleRectChange = useCallback((rect: Rect) => { setTableWidth(rect.width); @@ -425,7 +427,7 @@ function TableVirtualizer({layout, collection, focusedKey, renderView, renderWra keysBefore.push(key); key = tableState.collection.getKeyBefore(key); } while (key != null); - let resizerPosition = keysBefore.reduce((acc, key) => acc + columnState.getColumnWidth(key), 0); + let resizerPosition = keysBefore.reduce((acc, key) => acc + columnState.getColumnWidth(key), 0) + (direction === 'ltr' ? -1 * scrollPosition : scrollPosition); return (
(props: TableColumnResizeStateProps, // eslint-disable-next-line @typescript-eslint/no-unused-vars function onColumnResizeEnd(column: GridNode) { + props.onColumnResizeEnd && isResizing.current && props.onColumnResizeEnd(affectedColumnWidthsRef.current); setCurrentlyResizingColumn(null); isResizing.current = false; - props.onColumnResizeEnd && props.onColumnResizeEnd(affectedColumnWidthsRef.current); affectedColumnWidthsRef.current = []; let widths = new Map(columnWidthsRef.current); From e2b262fd6e9c83bb2f6d20c9b0dc94ad52962cca Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Tue, 30 Aug 2022 14:31:51 -0700 Subject: [PATCH 09/17] add tests for resize end --- .../table/test/TableSizing.test.js | 117 ++++++++++++++++-- 1 file changed, 106 insertions(+), 11 deletions(-) diff --git a/packages/@react-spectrum/table/test/TableSizing.test.js b/packages/@react-spectrum/table/test/TableSizing.test.js index 25d270e9f68..f9949dcb781 100644 --- a/packages/@react-spectrum/table/test/TableSizing.test.js +++ b/packages/@react-spectrum/table/test/TableSizing.test.js @@ -636,8 +636,9 @@ describe('TableViewSizing', function () { it('dragging the resizer works - desktop', () => { jest.spyOn(window.screen, 'width', 'get').mockImplementation(() => 1024); + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -696,6 +697,19 @@ describe('TableViewSizing', function () { expect(row.childNodes[1].style.width).toBe('190px'); expect(row.childNodes[2].style.width).toBe('190px'); } + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([ + { + key: 'foo', + width: 595 + }, { + key: 'bar', + width: 200 + }, { + key: 'baz', + width: 200 + } + ]); fireEvent.pointerLeave(resizer, {pointerType: 'mouse', pointerId: 1}); fireEvent.pointerLeave(resizableHeader, {pointerType: 'mouse', pointerId: 1}); @@ -705,8 +719,9 @@ describe('TableViewSizing', function () { }); it('dragging the resizer works - mobile', () => { + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -765,6 +780,19 @@ describe('TableViewSizing', function () { expect(row.childNodes[1].style.width).toBe('190px'); expect(row.childNodes[2].style.width).toBe('190px'); } + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([ + { + key: 'foo', + width: 595 + }, { + key: 'bar', + width: 200 + }, { + key: 'baz', + width: 200 + } + ]); fireEvent.pointerLeave(resizer, {pointerType: 'mouse', pointerId: 1}); fireEvent.pointerLeave(resizableHeader, {pointerType: 'mouse', pointerId: 1}); @@ -780,8 +808,9 @@ describe('TableViewSizing', function () { it('dragging the resizer works - desktop', () => { setInteractionModality('pointer'); jest.spyOn(window.screen, 'width', 'get').mockImplementation(() => 1024); + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -848,17 +877,32 @@ describe('TableViewSizing', function () { expect(row.childNodes[2].style.width).toBe('190px'); } - // tapping on the document.body doesn't cause a blur because the body isn't focusable, so just call blur + // tapping on the document.body doesn't cause a blur in jest because the body isn't focusable, so just call blur act(() => resizer.blur()); act(() => {jest.runAllTimers();}); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([ + { + key: 'foo', + width: 620 + }, { + key: 'bar', + width: 190 + }, { + key: 'baz', + width: 190 + } + ]); + expect(tree.queryByRole('slider')).toBeNull(); }); it('dragging the resizer works - mobile', () => { setInteractionModality('pointer'); + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -929,10 +973,24 @@ describe('TableViewSizing', function () { expect(row.childNodes[2].style.width).toBe('190px'); } - // tapping on the document.body doesn't cause a blur because the body isn't focusable, so just call blur + // tapping on the document.body doesn't cause a blur in jest because the body isn't focusable, so just call blur act(() => resizer.blur()); act(() => {jest.runAllTimers();}); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([ + { + key: 'foo', + width: 620 + }, { + key: 'bar', + width: 190 + }, { + key: 'baz', + width: 190 + } + ]); + expect(tree.queryByRole('slider')).toBeNull(); }); }); @@ -940,8 +998,9 @@ describe('TableViewSizing', function () { describe('keyboard', () => { it('arrow keys the resizer works - desktop', async () => { jest.spyOn(window.screen, 'width', 'get').mockImplementation(() => 1024); + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -1036,14 +1095,28 @@ describe('TableViewSizing', function () { fireEvent.keyDown(document.activeElement, {key: 'Escape'}); fireEvent.keyUp(document.activeElement, {key: 'Escape'}); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([ + { + key: 'foo', + width: 600 + }, { + key: 'bar', + width: 200 + }, { + key: 'baz', + width: 200 + } + ]); expect(document.activeElement).toBe(resizableHeader); expect(tree.queryByRole('slider')).toBeNull(); }); it('arrow keys the resizer works - mobile', async () => { + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -1114,6 +1187,19 @@ describe('TableViewSizing', function () { fireEvent.keyDown(document.activeElement, {key: 'Escape'}); fireEvent.keyUp(document.activeElement, {key: 'Escape'}); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([ + { + key: 'foo', + width: 600 + }, { + key: 'bar', + width: 200 + }, { + key: 'baz', + width: 200 + } + ]); expect(document.activeElement).toBe(resizableHeader); @@ -1121,8 +1207,9 @@ describe('TableViewSizing', function () { }); it('can exit resize via Enter', async () => { jest.spyOn(window.screen, 'width', 'get').mockImplementation(() => 1024); + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -1162,6 +1249,8 @@ describe('TableViewSizing', function () { fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([]); expect(document.activeElement).toBe(resizableHeader); @@ -1169,8 +1258,9 @@ describe('TableViewSizing', function () { }); it('can exit resize via Tab', async () => { jest.spyOn(window.screen, 'width', 'get').mockImplementation(() => 1024); + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -1209,6 +1299,8 @@ describe('TableViewSizing', function () { expect(document.activeElement).toBe(resizer); userEvent.tab(); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([]); expect(document.activeElement).toBe(resizableHeader); @@ -1216,8 +1308,9 @@ describe('TableViewSizing', function () { }); it('can exit resize via shift Tab', async () => { jest.spyOn(window.screen, 'width', 'get').mockImplementation(() => 1024); + let onColumnResizeEnd = jest.fn(); let tree = render( - + Foo Bar @@ -1256,6 +1349,8 @@ describe('TableViewSizing', function () { expect(document.activeElement).toBe(resizer); userEvent.tab({shift: true}); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([]); expect(document.activeElement).toBe(resizableHeader); From e9d5eb2b789e52d2c3cd9c17046c809ee874870d Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Mon, 12 Sep 2022 16:55:26 -0700 Subject: [PATCH 10/17] review comments --- .../components/table/index.css | 10 +++++++ .../table/src/useTableColumnResize.ts | 18 ++++-------- .../@react-spectrum/table/src/Resizer.tsx | 28 ++++++++++++++++-- .../@react-spectrum/table/src/TableView.tsx | 29 +++++++++++-------- 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/packages/@adobe/spectrum-css-temp/components/table/index.css b/packages/@adobe/spectrum-css-temp/components/table/index.css index 405c7d4bfb7..534615e1340 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/index.css +++ b/packages/@adobe/spectrum-css-temp/components/table/index.css @@ -432,3 +432,13 @@ svg.spectrum-Table-sortedIcon { height: 16px; inset-inline-start: -7px; } + +.resize-ew * { + cursor: col-resize !important; +} +.resize-e * { + cursor: e-resize !important; +} +.resize-w * { + cursor: w-resize !important; +} diff --git a/packages/@react-aria/table/src/useTableColumnResize.ts b/packages/@react-aria/table/src/useTableColumnResize.ts index 93153443b2d..3842ddecf21 100644 --- a/packages/@react-aria/table/src/useTableColumnResize.ts +++ b/packages/@react-aria/table/src/useTableColumnResize.ts @@ -31,14 +31,14 @@ export interface AriaTableColumnResizeProps { column: GridNode, label: string, triggerRef: RefObject, - isDisabled?: boolean + isDisabled?: boolean, + onMove: () => void, + onMoveEnd: () => void } export function useTableColumnResize(props: AriaTableColumnResizeProps, state: TableState, columnState: TableColumnResizeState, ref: RefObject): TableColumnResizeAria { let {column: item, triggerRef, isDisabled} = props; const stateRef = useRef>(null); - // keep track of what the cursor on the body is so it can be restored back to that when done resizing - const cursor = useRef(null); stateRef.current = columnState; const stringFormatter = useLocalizedStringFormatter(intlMessages); let id = useId(); @@ -58,7 +58,7 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st const {moveProps} = useMove({ onMoveStart() { columnResizeWidthRef.current = stateRef.current.getColumnWidth(item.key); - cursor.current = document.body.style.cursor; + stateRef.current.onColumnResizeStart(item); }, onMove({deltaX, deltaY, pointerType}) { if (direction === 'rtl') { @@ -74,18 +74,12 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st if (deltaX !== 0) { columnResizeWidthRef.current += deltaX; stateRef.current.onColumnResize(item, columnResizeWidthRef.current); - if (stateRef.current.getColumnMinWidth(item.key) >= stateRef.current.getColumnWidth(item.key)) { - document.body.style.setProperty('cursor', direction === 'rtl' ? 'w-resize' : 'e-resize'); - } else if (stateRef.current.getColumnMaxWidth(item.key) <= stateRef.current.getColumnWidth(item.key)) { - document.body.style.setProperty('cursor', direction === 'rtl' ? 'e-resize' : 'w-resize'); - } else { - document.body.style.setProperty('cursor', 'col-resize'); - } + props.onMove(); } }, onMoveEnd({pointerType}) { columnResizeWidthRef.current = 0; - document.body.style.cursor = cursor.current; + props.onMoveEnd(); if (pointerType === 'mouse') { stateRef.current.onColumnResizeEnd(item); } diff --git a/packages/@react-spectrum/table/src/Resizer.tsx b/packages/@react-spectrum/table/src/Resizer.tsx index 2007f4e58a9..53c09191b97 100644 --- a/packages/@react-spectrum/table/src/Resizer.tsx +++ b/packages/@react-spectrum/table/src/Resizer.tsx @@ -4,8 +4,9 @@ import {FocusRing} from '@react-aria/focus'; import {GridNode} from '@react-types/grid'; // @ts-ignore import intlMessages from '../intl/*.json'; -import React, {RefObject} from 'react'; +import React, {RefObject, useRef} from 'react'; import styles from '@adobe/spectrum-css-temp/components/table/vars.css'; +import {TableColumnResizeState} from '@react-stately/table'; import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n'; import {useTableColumnResize} from '@react-aria/table'; import {useTableContext} from './TableView'; @@ -22,8 +23,31 @@ function Resizer(props: ResizerProps, ref: RefObject) { let {state, columnState, isEmpty} = useTableContext(); let stringFormatter = useLocalizedStringFormatter(intlMessages); let {direction} = useLocale(); + const stateRef = useRef>(null); + stateRef.current = columnState; - let {inputProps, resizerProps} = useTableColumnResize({...props, label: stringFormatter.format('columnResizer'), isDisabled: isEmpty}, state, columnState, ref); + let {inputProps, resizerProps} = useTableColumnResize({ + ...props, + label: stringFormatter.format('columnResizer'), + isDisabled: isEmpty, + onMove: () => { + document.body.classList.remove(classNames(styles, 'resize-ew')); + document.body.classList.remove(classNames(styles, 'resize-e')); + document.body.classList.remove(classNames(styles, 'resize-w')); + if (stateRef.current.getColumnMinWidth(column.key) >= stateRef.current.getColumnWidth(column.key)) { + document.body.classList.add(direction === 'rtl' ? classNames(styles, 'resize-w') : classNames(styles, 'resize-e')); + } else if (stateRef.current.getColumnMaxWidth(column.key) <= stateRef.current.getColumnWidth(column.key)) { + document.body.classList.add(direction === 'rtl' ? classNames(styles, 'resize-e') : classNames(styles, 'resize-w')); + } else { + document.body.classList.add(classNames(styles, 'resize-ew')); + } + }, + onMoveEnd: () => { + document.body.classList.remove(classNames(styles, 'resize-ew')); + document.body.classList.remove(classNames(styles, 'resize-e')); + document.body.classList.remove(classNames(styles, 'resize-w')); + } + }, state, columnState, ref); let style = { cursor: undefined, diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 98b46c79a4a..569435f3312 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -83,8 +83,10 @@ interface TableContextValue { layout: TableLayout, columnState: TableColumnResizeState, headerRowHovered: boolean, - resizeModeState: [boolean, (val: boolean) => void], - isEmpty: boolean + isInResizeMode: boolean, + setIsInResizeMode: (val: boolean) => void, + isEmpty: boolean, + isQuiet: boolean } const TableContext = React.createContext>(null); @@ -110,16 +112,16 @@ function TableView(props: SpectrumTableProps, ref: DOMRef setIsInResizeMode(false)}), getDefaultWidth}, state.collection); + const columnState = useTableColumnResizeState({...mergeProps(props, {onColumnResizeEnd: () => { + setIsInResizeMode(false); + }}), getDefaultWidth}, state.collection); // If the selection behavior changes in state, we need to update showSelectionCheckboxes here due to the circular dependency... let shouldShowCheckboxes = state.selectionManager.selectionBehavior !== 'replace'; @@ -315,7 +317,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef + (props: SpectrumTableProps, ref: DOMRef(); let loadingState = collection.body.props.loadingState; let isLoading = loadingState === 'loading' || loadingState === 'loadingMore'; @@ -433,11 +435,15 @@ function TableVirtualizer({layout, collection, focusedKey, renderView, renderWra key = tableState.collection.getKeyBefore(key); } while (key != null); let resizerPosition = keysBefore.reduce((acc, key) => acc + columnState.getColumnWidth(key), 0) + (direction === 'ltr' ? -1 * scrollPosition : scrollPosition); + if (isQuiet) { + // quiet doesn't have a border + resizerPosition = resizerPosition - 1; + } return (
@@ -580,8 +586,7 @@ function ResizableTableColumnHeader(props) { let ref = useRef(null); let triggerRef = useRef(null); let resizingRef = useRef(null); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - let {state, columnState, headerRowHovered, resizeModeState: [isInResizeMode, setIsInResizeMode], isEmpty} = useTableContext(); + let {state, columnState, headerRowHovered, setIsInResizeMode, isEmpty} = useTableContext(); let stringFormatter = useLocalizedStringFormatter(intlMessages); let {pressProps, isPressed} = usePress({isDisabled: isEmpty}); let {columnHeaderProps} = useTableColumnHeader({ From 6a9fdd5aad1e350f206b4a3e9f0a3006760b7003 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Wed, 14 Sep 2022 11:07:38 -0700 Subject: [PATCH 11/17] fix tests --- .../table/test/TableSizing.test.js | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/packages/@react-spectrum/table/test/TableSizing.test.js b/packages/@react-spectrum/table/test/TableSizing.test.js index f9949dcb781..e58296d9a17 100644 --- a/packages/@react-spectrum/table/test/TableSizing.test.js +++ b/packages/@react-spectrum/table/test/TableSizing.test.js @@ -685,6 +685,19 @@ describe('TableViewSizing', function () { expect(row.childNodes[1].style.width).toBe('200px'); expect(row.childNodes[2].style.width).toBe('200px'); } + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([ + { + key: 'foo', + width: 595 + }, { + key: 'bar', + width: 200 + }, { + key: 'baz', + width: 200 + } + ]); // actual locations do not matter, the delta matters between events for the calculation of useMove fireEvent.pointerDown(resizer, {pointerType: 'mouse', pointerId: 1, pageX: 595, pageY: 30}); @@ -697,17 +710,17 @@ describe('TableViewSizing', function () { expect(row.childNodes[1].style.width).toBe('190px'); expect(row.childNodes[2].style.width).toBe('190px'); } - expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(2); expect(onColumnResizeEnd).toHaveBeenCalledWith([ { key: 'foo', - width: 595 + width: 620 }, { key: 'bar', - width: 200 + width: 190 }, { key: 'baz', - width: 200 + width: 190 } ]); @@ -768,6 +781,19 @@ describe('TableViewSizing', function () { expect(row.childNodes[1].style.width).toBe('200px'); expect(row.childNodes[2].style.width).toBe('200px'); } + expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledWith([ + { + key: 'foo', + width: 595 + }, { + key: 'bar', + width: 200 + }, { + key: 'baz', + width: 200 + } + ]); // actual locations do not matter, the delta matters between events for the calculation of useMove fireEvent.pointerDown(resizer, {pointerType: 'mouse', pointerId: 1, pageX: 595, pageY: 30}); @@ -780,17 +806,17 @@ describe('TableViewSizing', function () { expect(row.childNodes[1].style.width).toBe('190px'); expect(row.childNodes[2].style.width).toBe('190px'); } - expect(onColumnResizeEnd).toHaveBeenCalledTimes(1); + expect(onColumnResizeEnd).toHaveBeenCalledTimes(2); expect(onColumnResizeEnd).toHaveBeenCalledWith([ { key: 'foo', - width: 595 + width: 620 }, { key: 'bar', - width: 200 + width: 190 }, { key: 'baz', - width: 200 + width: 190 } ]); From 4de5967465a125fd7cd3b30abd2e716e20adedac Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Wed, 14 Sep 2022 15:02:24 -0700 Subject: [PATCH 12/17] Move resizer inside header --- .../components/table/index.css | 34 ++++++++-- .../components/table/skin.css | 17 ++++- .../@react-spectrum/table/src/TableView.tsx | 63 ++++++++----------- 3 files changed, 71 insertions(+), 43 deletions(-) diff --git a/packages/@adobe/spectrum-css-temp/components/table/index.css b/packages/@adobe/spectrum-css-temp/components/table/index.css index 534615e1340..10b34e7c686 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/index.css +++ b/packages/@adobe/spectrum-css-temp/components/table/index.css @@ -108,7 +108,8 @@ svg.spectrum-Table-sortedIcon { .spectrum-Table-columnResizer { display: flex; position: absolute; - inset-inline-end: -10px; + /* -9 aligns us with already existing cell dividers inside the table itself */ + inset-inline-end: -9px; justify-content: center; box-sizing: border-box; inline-size: 20px; @@ -126,9 +127,6 @@ svg.spectrum-Table-sortedIcon { &:active, &.focus-ring { outline: none; - &::after { - inline-size: 2px; - } } } .spectrum-Table-columnResizerPlaceholder { @@ -292,6 +290,22 @@ svg.spectrum-Table-sortedIcon { border-inline-end-width: var(--spectrum-table-divider-border-size); } +.spectrum-Table-cell--resizing { + position: relative; + border-inline-end-width: calc(2 * var(--spectrum-table-divider-border-size)); + + /* cover border between rows, otherwise you get little 1px breaks in the vertical line every row */ + &::after { + position: absolute; + content: ''; + display: block; + top: -1px; + inset-inline-end: -2px; + inline-size: 2px; + height: 1px; + } +} + .spectrum-Table-row { position: relative; cursor: default; @@ -420,17 +434,27 @@ svg.spectrum-Table-sortedIcon { } .spectrum-Table-colResizeIndicator { + display: none; height: 100%; width: 2px; position: absolute; + top: 0; + inset-inline-end: 0; pointer-events: none; + &.spectrum-Table-colResizeIndicator--visible { + display: block; + } } .spectrum-Table-colResizeNubbin { + display: none; position: absolute; - top: -6px; + top: 0px; width: 16px; height: 16px; inset-inline-start: -7px; + &.spectrum-Table-colResizeNubbin--visible { + display: block; + } } .resize-ew * { diff --git a/packages/@adobe/spectrum-css-temp/components/table/skin.css b/packages/@adobe/spectrum-css-temp/components/table/skin.css index f196ddeb682..27701e75d47 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/table/skin.css @@ -225,6 +225,16 @@ tbody.spectrum-Table-body { border-inline-end-color: var(--spectrum-table-divider-border-color); } +.spectrum-Table-cell--resizing { + position: relative; + border-inline-end-style: solid; + border-inline-end-color: var(--spectrum-global-color-blue-600); + + &::after { + background-color: var(--spectrum-global-color-blue-600); + } +} + .spectrum-Table--quiet { .spectrum-Table-body { border-width: 1px 0; @@ -288,15 +298,18 @@ tbody.spectrum-Table-body { background-color: var(--spectrum-table-divider-border-color); } + /* don't want the divider to add to the resizer's width since it's */ &:active, &:focus-ring { &::after { - background-color: var(--spectrum-global-color-blue-400); + background-color: unset; } } } .spectrum-Table-colResizeIndicator { - background-color: var(--spectrum-global-color-blue-600); + &.spectrum-Table-colResizeIndicator--resizing { + background-color: var(--spectrum-global-color-blue-600); + } } .spectrum-Table-colResizeNubbin {} diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 569435f3312..ef648b514ef 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -85,8 +85,7 @@ interface TableContextValue { headerRowHovered: boolean, isInResizeMode: boolean, setIsInResizeMode: (val: boolean) => void, - isEmpty: boolean, - isQuiet: boolean + isEmpty: boolean } const TableContext = React.createContext>(null); @@ -317,7 +316,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef + (props: SpectrumTableProps, ref: DOMRef(); let loadingState = collection.body.props.loadingState; let isLoading = loadingState === 'loading' || loadingState === 'loadingMore'; @@ -400,12 +398,10 @@ function TableVirtualizer({layout, collection, focusedKey, renderView, renderWra let headerHeight = layout.getLayoutInfo('header')?.rect.height || 0; let visibleRect = state.virtualizer.visibleRect; - let [scrollPosition, setScrollPosition] = useState(0); // Sync the scroll position from the table body to the header container. let onScroll = useCallback(() => { headerRef.current.scrollLeft = bodyRef.current.scrollLeft; - setScrollPosition(bodyRef.current.scrollLeft); - }, [bodyRef, setScrollPosition, headerRef]); + }, [bodyRef, headerRef]); let onVisibleRectChange = useCallback((rect: Rect) => { setTableWidth(rect.width); @@ -428,18 +424,6 @@ function TableVirtualizer({layout, collection, focusedKey, renderView, renderWra } }, [state.contentSize, state.virtualizer, state.isAnimating, onLoadMore, isLoading]); - let keysBefore = []; - let key = columnState.currentlyResizingColumn; - do { - keysBefore.push(key); - key = tableState.collection.getKeyBefore(key); - } while (key != null); - let resizerPosition = keysBefore.reduce((acc, key) => acc + columnState.getColumnWidth(key), 0) + (direction === 'ltr' ? -1 * scrollPosition : scrollPosition); - if (isQuiet) { - // quiet doesn't have a border - resizerPosition = resizerPosition - 1; - } - return (
{state.visibleViews[1]} -
-
- -
-
); @@ -586,7 +555,7 @@ function ResizableTableColumnHeader(props) { let ref = useRef(null); let triggerRef = useRef(null); let resizingRef = useRef(null); - let {state, columnState, headerRowHovered, setIsInResizeMode, isEmpty} = useTableContext(); + let {state, columnState, headerRowHovered, setIsInResizeMode, isInResizeMode, isEmpty} = useTableContext(); let stringFormatter = useLocalizedStringFormatter(intlMessages); let {pressProps, isPressed} = usePress({isDisabled: isEmpty}); let {columnHeaderProps} = useTableColumnHeader({ @@ -700,6 +669,27 @@ function ResizableTableColumnHeader(props) { column={column} showResizer={showResizer} triggerRef={useUnwrapDOMRef(triggerRef)} /> +
+
+ +
+
); @@ -885,7 +875,7 @@ function TableCheckboxCell({cell}) { } function TableCell({cell}) { - let {state} = useTableContext(); + let {state, columnState} = useTableContext(); let ref = useRef(); let columnProps = cell.column.props as SpectrumColumnProps; let isDisabled = state.disabledKeys.has(cell.parentKey); @@ -905,6 +895,7 @@ function TableCell({cell}) { 'spectrum-Table-cell', { 'spectrum-Table-cell--divider': columnProps.showDivider && cell.column.nextKey !== null, + 'spectrum-Table-cell--resizing': columnState.currentlyResizingColumn === cell.column.key, 'spectrum-Table-cell--hideHeader': columnProps.hideHeader, 'is-disabled': isDisabled }, From 6ee3fe987201bd2ff69957148502895bb8919d59 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Wed, 14 Sep 2022 16:00:32 -0700 Subject: [PATCH 13/17] sync body to header scroll position on resize start --- packages/@react-spectrum/table/src/TableView.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index ef648b514ef..8fbcfb3900b 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -129,6 +129,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef(); let bodyRef = useRef(); let stringFormatter = useLocalizedStringFormatter(intlMessages); @@ -315,8 +316,12 @@ function TableView(props: SpectrumTableProps, ref: DOMRef { + bodyRef.current.scrollLeft = headerRef.current.scrollLeft; + }; + return ( - + (props: SpectrumTableProps, ref: DOMRef @@ -354,9 +360,8 @@ function TableView(props: SpectrumTableProps, ref: DOMRef(); let loadingState = collection.body.props.loadingState; let isLoading = loadingState === 'loading' || loadingState === 'loadingMore'; let onLoadMore = collection.body.props.onLoadMore; @@ -555,7 +560,7 @@ function ResizableTableColumnHeader(props) { let ref = useRef(null); let triggerRef = useRef(null); let resizingRef = useRef(null); - let {state, columnState, headerRowHovered, setIsInResizeMode, isInResizeMode, isEmpty} = useTableContext(); + let {state, columnState, headerRowHovered, setIsInResizeMode, isInResizeMode, isEmpty, onFocusedResizer} = useTableContext(); let stringFormatter = useLocalizedStringFormatter(intlMessages); let {pressProps, isPressed} = usePress({isDisabled: isEmpty}); let {columnHeaderProps} = useTableColumnHeader({ @@ -612,6 +617,7 @@ function ResizableTableColumnHeader(props) { // without the immediate timeout, Android Chrome doesn't move focus to the resizer setTimeout(() => { resizingRef.current.focus(); + onFocusedResizer(); }, 0); } }, [columnState.currentlyResizingColumn, column.key]); From 86644b16aae2d50e58adf67798194f50dad07dcc Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Wed, 14 Sep 2022 18:21:04 -0700 Subject: [PATCH 14/17] update scroll position for keyboard resize --- .../table/src/useTableColumnResize.ts | 16 +++++---- .../@react-spectrum/table/src/Resizer.tsx | 7 ++-- .../@react-spectrum/table/src/TableView.tsx | 33 +++++++++++++++---- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/@react-aria/table/src/useTableColumnResize.ts b/packages/@react-aria/table/src/useTableColumnResize.ts index 3842ddecf21..bba0c8bf28e 100644 --- a/packages/@react-aria/table/src/useTableColumnResize.ts +++ b/packages/@react-aria/table/src/useTableColumnResize.ts @@ -11,7 +11,7 @@ */ import {ChangeEvent, RefObject, useCallback, useRef} from 'react'; -import {DOMAttributes} from '@react-types/shared'; +import {DOMAttributes, MoveEndEvent, MoveMoveEvent} from '@react-types/shared'; import {focusSafely} from '@react-aria/focus'; import {focusWithoutScrolling, mergeProps, useId} from '@react-aria/utils'; import {getColumnHeaderId} from './utils'; @@ -32,8 +32,8 @@ export interface AriaTableColumnResizeProps { label: string, triggerRef: RefObject, isDisabled?: boolean, - onMove: () => void, - onMoveEnd: () => void + onMove: (e: MoveMoveEvent) => void, + onMoveEnd: (e: MoveEndEvent) => void } export function useTableColumnResize(props: AriaTableColumnResizeProps, state: TableState, columnState: TableColumnResizeState, ref: RefObject): TableColumnResizeAria { @@ -60,7 +60,8 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st columnResizeWidthRef.current = stateRef.current.getColumnWidth(item.key); stateRef.current.onColumnResizeStart(item); }, - onMove({deltaX, deltaY, pointerType}) { + onMove(e) { + let {deltaX, deltaY, pointerType} = e; if (direction === 'rtl') { deltaX *= -1; } @@ -74,12 +75,13 @@ export function useTableColumnResize(props: AriaTableColumnResizeProps, st if (deltaX !== 0) { columnResizeWidthRef.current += deltaX; stateRef.current.onColumnResize(item, columnResizeWidthRef.current); - props.onMove(); + props.onMove(e); } }, - onMoveEnd({pointerType}) { + onMoveEnd(e) { + let {pointerType} = e; columnResizeWidthRef.current = 0; - props.onMoveEnd(); + props.onMoveEnd(e); if (pointerType === 'mouse') { stateRef.current.onColumnResizeEnd(item); } diff --git a/packages/@react-spectrum/table/src/Resizer.tsx b/packages/@react-spectrum/table/src/Resizer.tsx index 53c09191b97..c9e902c893c 100644 --- a/packages/@react-spectrum/table/src/Resizer.tsx +++ b/packages/@react-spectrum/table/src/Resizer.tsx @@ -4,6 +4,7 @@ import {FocusRing} from '@react-aria/focus'; import {GridNode} from '@react-types/grid'; // @ts-ignore import intlMessages from '../intl/*.json'; +import {MoveMoveEvent} from '@react-types/shared'; import React, {RefObject, useRef} from 'react'; import styles from '@adobe/spectrum-css-temp/components/table/vars.css'; import {TableColumnResizeState} from '@react-stately/table'; @@ -15,7 +16,8 @@ import {VisuallyHidden} from '@react-aria/visually-hidden'; interface ResizerProps { column: GridNode, showResizer: boolean, - triggerRef: RefObject + triggerRef: RefObject, + onMovedResizer: (e: MoveMoveEvent) => void } function Resizer(props: ResizerProps, ref: RefObject) { @@ -30,7 +32,7 @@ function Resizer(props: ResizerProps, ref: RefObject) { ...props, label: stringFormatter.format('columnResizer'), isDisabled: isEmpty, - onMove: () => { + onMove: (e) => { document.body.classList.remove(classNames(styles, 'resize-ew')); document.body.classList.remove(classNames(styles, 'resize-e')); document.body.classList.remove(classNames(styles, 'resize-w')); @@ -41,6 +43,7 @@ function Resizer(props: ResizerProps, ref: RefObject) { } else { document.body.classList.add(classNames(styles, 'resize-ew')); } + props.onMovedResizer(e); }, onMoveEnd: () => { document.body.classList.remove(classNames(styles, 'resize-ew')); diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 8fbcfb3900b..7cbd89fc16b 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -14,7 +14,7 @@ import ArrowDownSmall from '@spectrum-icons/ui/ArrowDownSmall'; import {chain, mergeProps, useLayoutEffect} from '@react-aria/utils'; import {Checkbox} from '@react-spectrum/checkbox'; import {classNames, useDOMRef, useFocusableRef, useStyleProps, useUnwrapDOMRef} from '@react-spectrum/utils'; -import {DOMRef, FocusableRef} from '@react-types/shared'; +import {DOMRef, FocusableRef, MoveMoveEvent} from '@react-types/shared'; import {FocusRing, FocusScope, useFocusRing} from '@react-aria/focus'; import {getInteractionModality, useHover, usePress} from '@react-aria/interactions'; import {GridNode} from '@react-types/grid'; @@ -85,9 +85,12 @@ interface TableContextValue { headerRowHovered: boolean, isInResizeMode: boolean, setIsInResizeMode: (val: boolean) => void, - isEmpty: boolean + isEmpty: boolean, + onFocusedResizer: () => void, + onMovedResizer: (e: MoveMoveEvent) => void } + const TableContext = React.createContext>(null); export function useTableContext() { return useContext(TableContext); @@ -320,8 +323,17 @@ function TableView(props: SpectrumTableProps, ref: DOMRef { + if (e.pointerType === 'keyboard') { + lastResizeInteractionModality.current = e.pointerType; + } else { + lastResizeInteractionModality.current = undefined; + } + }; + return ( - + (props: SpectrumTableProps, ref: DOMRef @@ -360,7 +373,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef { + if (lastResizeInteractionModality.current === 'keyboard' && headerRef.current.contains(document.activeElement)) { + document.activeElement?.scrollIntoView?.(false); + bodyRef.current.scrollLeft = headerRef.current.scrollLeft; + } + }, [state.contentSize, headerRef, bodyRef, lastResizeInteractionModality]); + let headerHeight = layout.getLayoutInfo('header')?.rect.height || 0; let visibleRect = state.virtualizer.visibleRect; @@ -560,7 +580,7 @@ function ResizableTableColumnHeader(props) { let ref = useRef(null); let triggerRef = useRef(null); let resizingRef = useRef(null); - let {state, columnState, headerRowHovered, setIsInResizeMode, isInResizeMode, isEmpty, onFocusedResizer} = useTableContext(); + let {state, columnState, headerRowHovered, setIsInResizeMode, isInResizeMode, isEmpty, onFocusedResizer, onMovedResizer} = useTableContext(); let stringFormatter = useLocalizedStringFormatter(intlMessages); let {pressProps, isPressed} = usePress({isDisabled: isEmpty}); let {columnHeaderProps} = useTableColumnHeader({ @@ -674,7 +694,8 @@ function ResizableTableColumnHeader(props) { ref={resizingRef} column={column} showResizer={showResizer} - triggerRef={useUnwrapDOMRef(triggerRef)} /> + triggerRef={useUnwrapDOMRef(triggerRef)} + onMovedResizer={onMovedResizer} />
Date: Fri, 16 Sep 2022 15:19:50 -0700 Subject: [PATCH 15/17] rename prop --- packages/@react-spectrum/table/src/Resizer.tsx | 4 ++-- packages/@react-spectrum/table/src/TableView.tsx | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/@react-spectrum/table/src/Resizer.tsx b/packages/@react-spectrum/table/src/Resizer.tsx index c9e902c893c..46e69d429ad 100644 --- a/packages/@react-spectrum/table/src/Resizer.tsx +++ b/packages/@react-spectrum/table/src/Resizer.tsx @@ -17,7 +17,7 @@ interface ResizerProps { column: GridNode, showResizer: boolean, triggerRef: RefObject, - onMovedResizer: (e: MoveMoveEvent) => void + onMoveResizer: (e: MoveMoveEvent) => void } function Resizer(props: ResizerProps, ref: RefObject) { @@ -43,7 +43,7 @@ function Resizer(props: ResizerProps, ref: RefObject) { } else { document.body.classList.add(classNames(styles, 'resize-ew')); } - props.onMovedResizer(e); + props.onMoveResizer(e); }, onMoveEnd: () => { document.body.classList.remove(classNames(styles, 'resize-ew')); diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 7cbd89fc16b..5e61dc79a0f 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -87,7 +87,7 @@ interface TableContextValue { setIsInResizeMode: (val: boolean) => void, isEmpty: boolean, onFocusedResizer: () => void, - onMovedResizer: (e: MoveMoveEvent) => void + onMoveResizer: (e: MoveMoveEvent) => void } @@ -324,7 +324,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef { + let onMoveResizer = (e) => { if (e.pointerType === 'keyboard') { lastResizeInteractionModality.current = e.pointerType; } else { @@ -333,7 +333,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef + + onMoveResizer={onMoveResizer} />
Date: Mon, 19 Sep 2022 10:59:22 -0700 Subject: [PATCH 16/17] Make resize indicator full height --- .../components/table/index.css | 28 ++++++++----------- .../components/table/skin.css | 13 ++------- .../@react-spectrum/table/src/TableView.tsx | 15 ++++++++-- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/packages/@adobe/spectrum-css-temp/components/table/index.css b/packages/@adobe/spectrum-css-temp/components/table/index.css index 10b34e7c686..4de4cbe75f2 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/index.css +++ b/packages/@adobe/spectrum-css-temp/components/table/index.css @@ -46,6 +46,9 @@ svg.spectrum-Table-sortedIcon { border-right-width: 1px; border-right-style: solid; flex: 0 0 auto; + padding-bottom: 1px; + margin-bottom: -1px; + z-index: 1; } .spectrum-Table-headCellContents { display: inline-block; @@ -136,6 +139,13 @@ svg.spectrum-Table-sortedIcon { block-size: 100%; user-select: none; } +.spectrum-Table-bodyResizeIndicator { + display: none; + position: absolute; + width: 2px; + height: 100%; + top: 0px; +} .spectrum-Table-cell--alignCenter { text-align: center; @@ -290,22 +300,6 @@ svg.spectrum-Table-sortedIcon { border-inline-end-width: var(--spectrum-table-divider-border-size); } -.spectrum-Table-cell--resizing { - position: relative; - border-inline-end-width: calc(2 * var(--spectrum-table-divider-border-size)); - - /* cover border between rows, otherwise you get little 1px breaks in the vertical line every row */ - &::after { - position: absolute; - content: ''; - display: block; - top: -1px; - inset-inline-end: -2px; - inline-size: 2px; - height: 1px; - } -} - .spectrum-Table-row { position: relative; cursor: default; @@ -435,7 +429,7 @@ svg.spectrum-Table-sortedIcon { .spectrum-Table-colResizeIndicator { display: none; - height: 100%; + height: calc(100% + 1px); width: 2px; position: absolute; top: 0; diff --git a/packages/@adobe/spectrum-css-temp/components/table/skin.css b/packages/@adobe/spectrum-css-temp/components/table/skin.css index 27701e75d47..a1996e47700 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/table/skin.css @@ -225,16 +225,6 @@ tbody.spectrum-Table-body { border-inline-end-color: var(--spectrum-table-divider-border-color); } -.spectrum-Table-cell--resizing { - position: relative; - border-inline-end-style: solid; - border-inline-end-color: var(--spectrum-global-color-blue-600); - - &::after { - background-color: var(--spectrum-global-color-blue-600); - } -} - .spectrum-Table--quiet { .spectrum-Table-body { border-width: 1px 0; @@ -313,3 +303,6 @@ tbody.spectrum-Table-body { } } .spectrum-Table-colResizeNubbin {} +.spectrum-Table-bodyResizeIndicator { + background-color: var(--spectrum-global-color-blue-600); +} diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 5e61dc79a0f..d9a8dd832f1 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -375,6 +375,7 @@ function TableView(props: SpectrumTableProps, ref: DOMRef acc + columnState.getColumnWidth(key), 0) - 2; + return (
{state.visibleViews[1]} +
@@ -902,7 +914,7 @@ function TableCheckboxCell({cell}) { } function TableCell({cell}) { - let {state, columnState} = useTableContext(); + let {state} = useTableContext(); let ref = useRef(); let columnProps = cell.column.props as SpectrumColumnProps; let isDisabled = state.disabledKeys.has(cell.parentKey); @@ -922,7 +934,6 @@ function TableCell({cell}) { 'spectrum-Table-cell', { 'spectrum-Table-cell--divider': columnProps.showDivider && cell.column.nextKey !== null, - 'spectrum-Table-cell--resizing': columnState.currentlyResizingColumn === cell.column.key, 'spectrum-Table-cell--hideHeader': columnProps.hideHeader, 'is-disabled': isDisabled }, From c36d30c2f058876977339a75ada95fee106e1eae Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Mon, 19 Sep 2022 14:52:33 -0700 Subject: [PATCH 17/17] corner when resizer is at rounded corner --- .../@adobe/spectrum-css-temp/components/table/index.css | 3 +++ packages/@react-spectrum/table/src/TableView.tsx | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/@adobe/spectrum-css-temp/components/table/index.css b/packages/@adobe/spectrum-css-temp/components/table/index.css index 4de4cbe75f2..66d28948fd7 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/index.css +++ b/packages/@adobe/spectrum-css-temp/components/table/index.css @@ -182,6 +182,9 @@ svg.spectrum-Table-sortedIcon { &.is-drop-target { @inherit: %drop-target; } + &.spectrum-Table-body--resizerAtTableEdge { + border-start-end-radius: 0; + } } /* The tbody tag doesn't allow setting a border-radius, so these hacks are to make that work diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index d9a8dd832f1..75d7d3d11f5 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -457,6 +457,11 @@ function TableVirtualizer({layout, collection, lastResizeInteractionModality, fo key = tableState.collection.getKeyBefore(key); } while (key != null); let resizerPosition = keysBefore.reduce((acc, key) => acc + columnState.getColumnWidth(key), 0) - 2; + let resizerAtEdge = resizerPosition > Math.max(state.virtualizer.contentSize.width, state.virtualizer.visibleRect.width) - 3; + // this should be fine, every movement of the resizer causes a rerender + // scrolling can cause it to lag for a moment, but it's always updated + let resizerInVisibleRegion = resizerPosition < state.virtualizer.visibleRect.width + (isNaN(bodyRef.current?.scrollLeft) ? 0 : bodyRef.current?.scrollLeft); + let shouldHardCornerResizeCorner = resizerAtEdge && resizerInVisibleRegion; return ( @@ -485,7 +490,8 @@ function TableVirtualizer({layout, collection, lastResizeInteractionModality, fo styles, 'spectrum-Table-body', { - 'focus-ring': isFocusVisible + 'focus-ring': isFocusVisible, + 'spectrum-Table-body--resizerAtTableEdge': shouldHardCornerResizeCorner } ) }