@react-spectrum/table 3.12.11-nightly.4674 → 3.12.11-nightly.4683

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,6 @@
11
11
  */
12
12
 
13
13
  import ArrowDownSmall from '@spectrum-icons/ui/ArrowDownSmall';
14
- import {chain, isAndroid, mergeProps, scrollIntoView, scrollIntoViewport} from '@react-aria/utils';
15
14
  import {Checkbox} from '@react-spectrum/checkbox';
16
15
  import ChevronDownMedium from '@spectrum-icons/ui/ChevronDownMedium';
17
16
  import ChevronLeftMedium from '@spectrum-icons/ui/ChevronLeftMedium';
@@ -23,7 +22,7 @@ import {
23
22
  useStyleProps,
24
23
  useUnwrapDOMRef
25
24
  } from '@react-spectrum/utils';
26
- import {ColumnSize, SpectrumColumnProps} from '@react-types/table';
25
+ import {ColumnSize, SpectrumColumnProps, TableCollection} from '@react-types/table';
27
26
  import {DOMRef, DropTarget, FocusableElement, FocusableRef, Key} from '@react-types/shared';
28
27
  import type {DragAndDropHooks} from '@react-spectrum/dnd';
29
28
  import type {DraggableCollectionState, DroppableCollectionState} from '@react-stately/dnd';
@@ -34,21 +33,23 @@ import {GridNode} from '@react-types/grid';
34
33
  import {InsertionIndicator} from './InsertionIndicator';
35
34
  // @ts-ignore
36
35
  import intlMessages from '../intl/*.json';
36
+ import {isAndroid, mergeProps, scrollIntoView, scrollIntoViewport} from '@react-aria/utils';
37
37
  import {Item, Menu, MenuTrigger} from '@react-spectrum/menu';
38
- import {LayoutInfo, ReusableView, useVirtualizerState} from '@react-stately/virtualizer';
38
+ import {LayoutInfo, Rect, ReusableView, useVirtualizerState} from '@react-stately/virtualizer';
39
39
  import {layoutInfoToStyle, ScrollView, setScrollLeft, useVirtualizer, VirtualizerItem} from '@react-aria/virtualizer';
40
40
  import ListGripper from '@spectrum-icons/ui/ListGripper';
41
+ import {ListKeyboardDelegate} from '@react-aria/selection';
41
42
  import {Nubbin} from './Nubbin';
42
43
  import {ProgressCircle} from '@react-spectrum/progress';
43
- import React, {DOMAttributes, HTMLAttributes, ReactElement, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
44
- import {Resizer} from './Resizer';
44
+ import React, {DOMAttributes, HTMLAttributes, ReactElement, ReactNode, RefObject, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
45
+ import {Resizer, ResizeStateContext} from './Resizer';
45
46
  import {RootDropIndicator} from './RootDropIndicator';
46
47
  import {DragPreview as SpectrumDragPreview} from './DragPreview';
47
48
  import {SpectrumTableProps} from './TableViewWrapper';
48
49
  import styles from '@adobe/spectrum-css-temp/components/table/vars.css';
49
50
  import stylesOverrides from './table.css';
50
- import {TableColumnLayout, TableState, TreeGridState} from '@react-stately/table';
51
51
  import {TableLayout} from '@react-stately/layout';
52
+ import {TableState, TreeGridState, useTableColumnResizeState} from '@react-stately/table';
52
53
  import {Tooltip, TooltipTrigger} from '@react-spectrum/tooltip';
53
54
  import {useButton} from '@react-aria/button';
54
55
  import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
@@ -112,7 +113,7 @@ export interface TableContextValue<T> {
112
113
  dragAndDropHooks: DragAndDropHooks['dragAndDropHooks'],
113
114
  isTableDraggable: boolean,
114
115
  isTableDroppable: boolean,
115
- layout: TableLayout<T> & {tableState: TableState<T> | TreeGridState<T>},
116
+ layout: TableLayout<T>,
116
117
  headerRowHovered: boolean,
117
118
  isInResizeMode: boolean,
118
119
  setIsInResizeMode: (val: boolean) => void,
@@ -140,6 +141,8 @@ interface TableBaseProps<T> extends SpectrumTableProps<T> {
140
141
  state: TableState<T> | TreeGridState<T>
141
142
  }
142
143
 
144
+ type View = ReusableView<GridNode<unknown>, ReactNode>;
145
+
143
146
  function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<HTMLDivElement>) {
144
147
  props = useProviderProps(props);
145
148
  let {
@@ -169,29 +172,6 @@ function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<H
169
172
  let {styleProps} = useStyleProps(props);
170
173
  let {scale} = useProvider();
171
174
 
172
- const getDefaultWidth = useCallback(({props: {hideHeader, isSelectionCell, showDivider, isDragButtonCell}}: GridNode<T>): ColumnSize | null | undefined => {
173
- if (hideHeader) {
174
- let width = DEFAULT_HIDE_HEADER_CELL_WIDTH[scale];
175
- return showDivider ? width + 1 : width;
176
- } else if (isSelectionCell) {
177
- return SELECTION_CELL_DEFAULT_WIDTH[scale];
178
- } else if (isDragButtonCell) {
179
- return DRAG_BUTTON_CELL_DEFAULT_WIDTH[scale];
180
- }
181
- }, [scale]);
182
-
183
- const getDefaultMinWidth = useCallback(({props: {hideHeader, isSelectionCell, showDivider, isDragButtonCell}}: GridNode<T>): ColumnSize | null | undefined => {
184
- if (hideHeader) {
185
- let width = DEFAULT_HIDE_HEADER_CELL_WIDTH[scale];
186
- return showDivider ? width + 1 : width;
187
- } else if (isSelectionCell) {
188
- return SELECTION_CELL_DEFAULT_WIDTH[scale];
189
- } else if (isDragButtonCell) {
190
- return DRAG_BUTTON_CELL_DEFAULT_WIDTH[scale];
191
- }
192
- return 75;
193
- }, [scale]);
194
-
195
175
  // Starts when the user selects resize from the menu, ends when resizing ends
196
176
  // used to control the visibility of the resizer Nubbin
197
177
  let [isInResizeMode, setIsInResizeMode] = useState(false);
@@ -201,18 +181,11 @@ function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<H
201
181
  let [, setIsResizing] = useState(false);
202
182
 
203
183
  let domRef = useDOMRef(ref);
204
- let headerRef = useRef<HTMLDivElement>();
205
- let bodyRef = useRef<HTMLDivElement>();
184
+ let headerRef = useRef<HTMLDivElement>(undefined);
185
+ let bodyRef = useRef<HTMLDivElement>(undefined);
206
186
 
207
187
  let density = props.density || 'regular';
208
- let columnLayout = useMemo(
209
- () => new TableColumnLayout({
210
- getDefaultWidth,
211
- getDefaultMinWidth
212
- }),
213
- [getDefaultWidth, getDefaultMinWidth]
214
- );
215
- let tableLayout = useMemo(() => new TableLayout({
188
+ let layout = useMemo(() => new TableLayout({
216
189
  // If props.rowHeight is auto, then use estimated heights based on scale, otherwise use fixed heights.
217
190
  rowHeight: props.overflowMode === 'wrap'
218
191
  ? null
@@ -226,25 +199,14 @@ function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<H
226
199
  estimatedHeadingHeight: props.overflowMode === 'wrap'
227
200
  ? DEFAULT_HEADER_HEIGHT[scale]
228
201
  : null,
229
- columnLayout,
230
- initialCollection: state.collection
202
+ scrollContainer: 'body',
203
+ enableEmptyState: true
231
204
  }),
232
205
  // don't recompute when state.collection changes, only used for initial value
233
206
  // eslint-disable-next-line react-hooks/exhaustive-deps
234
- [props.overflowMode, scale, density, columnLayout]
207
+ [props.overflowMode, scale, density]
235
208
  );
236
209
 
237
- // Use a proxy so that a new object is created for each render so that alternate instances aren't affected by mutation.
238
- // This can be thought of as equivalent to `{…tableLayout, tableState: state}`, but works with classes as well.
239
- let layout = useMemo(() => {
240
- let proxy = new Proxy(tableLayout, {
241
- get(target, prop, receiver) {
242
- return prop === 'tableState' ? state : Reflect.get(target, prop, receiver);
243
- }
244
- });
245
- return proxy as TableLayout<T> & {tableState: TableState<T> | TreeGridState<T>};
246
- }, [state, tableLayout]);
247
-
248
210
  let dragState: DraggableCollectionState;
249
211
  let preview = useRef(null);
250
212
  if (isTableDraggable) {
@@ -266,7 +228,12 @@ function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<H
266
228
  selectionManager: state.selectionManager
267
229
  });
268
230
  droppableCollection = dragAndDropHooks.useDroppableCollection({
269
- keyboardDelegate: layout,
231
+ keyboardDelegate: new ListKeyboardDelegate({
232
+ collection: state.collection,
233
+ disabledKeys: state.selectionManager.disabledKeys,
234
+ ref: domRef,
235
+ layoutDelegate: layout
236
+ }),
270
237
  dropTargetDelegate: layout
271
238
  }, dropState, domRef);
272
239
 
@@ -276,7 +243,7 @@ function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<H
276
243
  let {gridProps} = useTable({
277
244
  ...props,
278
245
  isVirtualized: true,
279
- layout,
246
+ layoutDelegate: layout,
280
247
  onRowAction: onAction,
281
248
  scrollRef: bodyRef
282
249
  }, state, domRef);
@@ -284,7 +251,6 @@ function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<H
284
251
  let [headerRowHovered, setHeaderRowHovered] = useState(false);
285
252
 
286
253
  // This overrides collection view's renderWrapper to support DOM hierarchy.
287
- type View = ReusableView<GridNode<T>, ReactNode>;
288
254
  let renderWrapper = useCallback((parent: View, reusableView: View, children: View[], renderChildren: (views: View[]) => ReactElement[]) => {
289
255
  if (reusableView.viewType === 'rowgroup') {
290
256
  return (
@@ -495,9 +461,8 @@ function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<H
495
461
  styleProps.className
496
462
  )
497
463
  }
498
- // This should be `tableLayout` rather than `layout` so it doesn't
499
- // change objects and invalidate virtualizer.
500
- layout={tableLayout}
464
+ tableState={state}
465
+ layout={layout}
501
466
  collection={state.collection}
502
467
  focusedKey={focusedKey}
503
468
  renderView={renderView}
@@ -527,13 +492,65 @@ function TableViewBase<T extends object>(props: TableBaseProps<T>, ref: DOMRef<H
527
492
  );
528
493
  }
529
494
 
495
+ interface TableVirtualizerProps<T> extends HTMLAttributes<HTMLElement> {
496
+ tableState: TableState<T>,
497
+ layout: TableLayout<T>,
498
+ collection: TableCollection<T>,
499
+ focusedKey: Key | null,
500
+ renderView: (type: string, content: GridNode<T>) => ReactElement,
501
+ renderWrapper?: (
502
+ parent: View | null,
503
+ reusableView: View,
504
+ children: View[],
505
+ renderChildren: (views: View[]) => ReactElement[]
506
+ ) => ReactElement,
507
+ domRef: RefObject<HTMLDivElement>,
508
+ bodyRef: RefObject<HTMLDivElement>,
509
+ headerRef: RefObject<HTMLDivElement>,
510
+ onVisibleRectChange: (rect: Rect) => void,
511
+ isFocusVisible: boolean,
512
+ isVirtualDragging: boolean,
513
+ isRootDropTarget: boolean
514
+ }
515
+
530
516
  // This is a custom Virtualizer that also has a header that syncs its scroll position with the body.
531
- function TableVirtualizer(props) {
532
- let {layout, collection, focusedKey, renderView, renderWrapper, domRef, bodyRef, headerRef, onVisibleRectChange: onVisibleRectChangeProp, isFocusVisible, isVirtualDragging, isRootDropTarget, ...otherProps} = props;
517
+ function TableVirtualizer<T>(props: TableVirtualizerProps<T>) {
518
+ let {tableState, layout, collection, focusedKey, renderView, renderWrapper, domRef, bodyRef, headerRef, onVisibleRectChange: onVisibleRectChangeProp, isFocusVisible, isVirtualDragging, isRootDropTarget, ...otherProps} = props;
533
519
  let {direction} = useLocale();
534
520
  let loadingState = collection.body.props.loadingState;
535
521
  let isLoading = loadingState === 'loading' || loadingState === 'loadingMore';
536
522
  let onLoadMore = collection.body.props.onLoadMore;
523
+ let [tableWidth, setTableWidth] = useState(0);
524
+ let {scale} = useProvider();
525
+
526
+ const getDefaultWidth = useCallback(({props: {hideHeader, isSelectionCell, showDivider, isDragButtonCell}}: GridNode<T>): ColumnSize | null | undefined => {
527
+ if (hideHeader) {
528
+ let width = DEFAULT_HIDE_HEADER_CELL_WIDTH[scale];
529
+ return showDivider ? width + 1 : width;
530
+ } else if (isSelectionCell) {
531
+ return SELECTION_CELL_DEFAULT_WIDTH[scale];
532
+ } else if (isDragButtonCell) {
533
+ return DRAG_BUTTON_CELL_DEFAULT_WIDTH[scale];
534
+ }
535
+ }, [scale]);
536
+
537
+ const getDefaultMinWidth = useCallback(({props: {hideHeader, isSelectionCell, showDivider, isDragButtonCell}}: GridNode<T>): ColumnSize | null | undefined => {
538
+ if (hideHeader) {
539
+ let width = DEFAULT_HIDE_HEADER_CELL_WIDTH[scale];
540
+ return showDivider ? width + 1 : width;
541
+ } else if (isSelectionCell) {
542
+ return SELECTION_CELL_DEFAULT_WIDTH[scale];
543
+ } else if (isDragButtonCell) {
544
+ return DRAG_BUTTON_CELL_DEFAULT_WIDTH[scale];
545
+ }
546
+ return 75;
547
+ }, [scale]);
548
+
549
+ let columnResizeState = useTableColumnResizeState({
550
+ tableWidth,
551
+ getDefaultWidth,
552
+ getDefaultMinWidth
553
+ }, tableState);
537
554
 
538
555
  let state = useVirtualizerState<object, ReactNode, ReactNode>({
539
556
  layout,
@@ -544,7 +561,10 @@ function TableVirtualizer(props) {
544
561
  bodyRef.current.scrollTop = rect.y;
545
562
  setScrollLeft(bodyRef.current, direction, rect.x);
546
563
  },
547
- persistedKeys: useMemo(() => focusedKey ? new Set([focusedKey]) : new Set(), [focusedKey])
564
+ persistedKeys: useMemo(() => focusedKey ? new Set([focusedKey]) : new Set(), [focusedKey]),
565
+ layoutOptions: useMemo(() => ({
566
+ columnWidths: columnResizeState.columnWidths
567
+ }), [columnResizeState.columnWidths])
548
568
  });
549
569
 
550
570
  let memoedVirtualizerProps = useMemo(() => ({
@@ -555,7 +575,11 @@ function TableVirtualizer(props) {
555
575
  }), [otherProps.tabIndex, focusedKey, isLoading, onLoadMore]);
556
576
 
557
577
  let {virtualizerProps, scrollViewProps: {onVisibleRectChange}} = useVirtualizer(memoedVirtualizerProps, state, domRef);
558
- let onVisibleRectChangeMemo = useMemo(() => chain(onVisibleRectChange, onVisibleRectChangeProp), [onVisibleRectChange, onVisibleRectChangeProp]);
578
+ let onVisibleRectChangeMemo = useCallback(rect => {
579
+ setTableWidth(rect.width);
580
+ onVisibleRectChange(rect);
581
+ onVisibleRectChangeProp(rect);
582
+ }, [onVisibleRectChange, onVisibleRectChangeProp]);
559
583
 
560
584
  // this effect runs whenever the contentSize changes, it doesn't matter what the content size is
561
585
  // only that it changes in a resize, and when that happens, we want to sync the body to the
@@ -575,7 +599,7 @@ function TableVirtualizer(props) {
575
599
  headerRef.current.scrollLeft = bodyRef.current.scrollLeft;
576
600
  }, [bodyRef, headerRef]);
577
601
 
578
- let resizerPosition = layout.getResizerPosition() - 2;
602
+ let resizerPosition = columnResizeState.resizingColumn != null ? layout.getLayoutInfo(columnResizeState.resizingColumn).rect.maxX - 2 : 0;
579
603
 
580
604
  let resizerAtEdge = resizerPosition > Math.max(state.virtualizer.contentSize.width, state.virtualizer.visibleRect.width) - 3;
581
605
  // this should be fine, every movement of the resizer causes a rerender
@@ -584,11 +608,11 @@ function TableVirtualizer(props) {
584
608
  let shouldHardCornerResizeCorner = resizerAtEdge && resizerInVisibleRegion;
585
609
 
586
610
  // minimize re-render caused on Resizers by memoing this
587
- let resizingColumnWidth = layout.getColumnWidth(layout.resizingColumn);
611
+ let resizingColumnWidth = columnResizeState.resizingColumn != null ? columnResizeState.getColumnWidth(columnResizeState.resizingColumn) : 0;
588
612
  let resizingColumn = useMemo(() => ({
589
613
  width: resizingColumnWidth,
590
- key: layout.resizingColumn
591
- }), [resizingColumnWidth, layout.resizingColumn]);
614
+ key: columnResizeState.resizingColumn
615
+ }), [resizingColumnWidth, columnResizeState.resizingColumn]);
592
616
  let mergedProps = mergeProps(
593
617
  otherProps,
594
618
  virtualizerProps,
@@ -598,7 +622,7 @@ function TableVirtualizer(props) {
598
622
  let firstColumn = collection.columns[0];
599
623
  let scrollPadding = 0;
600
624
  if (firstColumn.props.isSelectionCell || firstColumn.props.isDragButtonCell) {
601
- scrollPadding = layout.getColumnWidth(firstColumn.key);
625
+ scrollPadding = columnResizeState.getColumnWidth(firstColumn.key);
602
626
  }
603
627
 
604
628
  return (
@@ -618,7 +642,9 @@ function TableVirtualizer(props) {
618
642
  scrollPaddingInlineStart: scrollPadding
619
643
  }}
620
644
  ref={headerRef}>
621
- {state.visibleViews[0]}
645
+ <ResizeStateContext.Provider value={columnResizeState}>
646
+ {state.visibleViews[0]}
647
+ </ResizeStateContext.Provider>
622
648
  </div>
623
649
  <ScrollView
624
650
  className={
@@ -658,7 +684,7 @@ function TableVirtualizer(props) {
658
684
  {state.visibleViews[1]}
659
685
  <div
660
686
  className={classNames(styles, 'spectrum-Table-bodyResizeIndicator')}
661
- style={{[direction === 'ltr' ? 'left' : 'right']: `${resizerPosition}px`, height: `${Math.max(state.virtualizer.contentSize.height, state.virtualizer.visibleRect.height)}px`, display: layout.resizingColumn ? 'block' : 'none'}} />
687
+ style={{[direction === 'ltr' ? 'left' : 'right']: `${resizerPosition}px`, height: `${Math.max(state.virtualizer.contentSize.height, state.virtualizer.visibleRect.height)}px`, display: columnResizeState.resizingColumn ? 'block' : 'none'}} />
662
688
  </ScrollView>
663
689
  </div>
664
690
  </FocusScope>
@@ -793,7 +819,6 @@ function ResizableTableColumnHeader(props) {
793
819
  let resizingRef = useRef(null);
794
820
  let {
795
821
  state,
796
- layout,
797
822
  onResizeStart,
798
823
  onResize,
799
824
  onResizeEnd,
@@ -804,6 +829,7 @@ function ResizableTableColumnHeader(props) {
804
829
  headerMenuOpen,
805
830
  setHeaderMenuOpen
806
831
  } = useTableContext();
832
+ let columnResizeState = useContext(ResizeStateContext);
807
833
  let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/table');
808
834
  let {pressProps, isPressed} = usePress({isDisabled: isEmpty});
809
835
  let {columnHeaderProps} = useTableColumnHeader({
@@ -828,7 +854,7 @@ function ResizableTableColumnHeader(props) {
828
854
  state.sort(column.key, 'descending');
829
855
  break;
830
856
  case 'resize':
831
- layout.startResize(column.key);
857
+ columnResizeState.startResize(column.key);
832
858
  setIsInResizeMode(true);
833
859
  state.setKeyboardNavigationDisabled(true);
834
860
  break;
@@ -854,7 +880,7 @@ function ResizableTableColumnHeader(props) {
854
880
  // eslint-disable-next-line react-hooks/exhaustive-deps
855
881
  }, [allowsSorting]);
856
882
 
857
- let resizingColumn = layout.resizingColumn;
883
+ let resizingColumn = columnResizeState.resizingColumn;
858
884
  let showResizer = !isEmpty && ((headerRowHovered && getInteractionModality() !== 'keyboard') || resizingColumn != null);
859
885
  let alignment = 'start';
860
886
  let menuAlign = 'start' as 'start' | 'end';
@@ -950,7 +976,7 @@ function ResizableTableColumnHeader(props) {
950
976
  }
951
977
 
952
978
  function TableSelectAllCell({column}) {
953
- let ref = useRef();
979
+ let ref = useRef(undefined);
954
980
  let {state} = useTableContext();
955
981
  let isSingleSelectionMode = state.selectionManager.selectionMode === 'single';
956
982
  let {columnHeaderProps} = useTableColumnHeader({
@@ -997,7 +1023,7 @@ function TableSelectAllCell({column}) {
997
1023
  }
998
1024
 
999
1025
  function TableDragHeaderCell({column}) {
1000
- let ref = useRef();
1026
+ let ref = useRef(undefined);
1001
1027
  let {state} = useTableContext();
1002
1028
  let {columnHeaderProps} = useTableColumnHeader({
1003
1029
  node: column,
@@ -1077,7 +1103,7 @@ export function useTableRowContext() {
1077
1103
  }
1078
1104
 
1079
1105
  function TableRow({item, children, layoutInfo, parent, ...otherProps}) {
1080
- let ref = useRef();
1106
+ let ref = useRef(undefined);
1081
1107
  let {state, layout, dragAndDropHooks, isTableDraggable, isTableDroppable, dragState, dropState} = useTableContext();
1082
1108
  let isSelected = state.selectionManager.isSelected(item.key);
1083
1109
  let {rowProps, hasAction, allowsSelection} = useTableRow({
@@ -1120,7 +1146,7 @@ function TableRow({item, children, layoutInfo, parent, ...otherProps}) {
1120
1146
  let droppableItem: DroppableItemResult;
1121
1147
  let isDropTarget: boolean;
1122
1148
  let dropIndicator: DropIndicatorAria;
1123
- let dropIndicatorRef = useRef();
1149
+ let dropIndicatorRef = useRef(undefined);
1124
1150
  if (isTableDroppable) {
1125
1151
  let target = {type: 'item', key: item.key, dropPosition: 'on'} as DropTarget;
1126
1152
  isDropTarget = dropState.isDropTarget(target);
@@ -1128,7 +1154,7 @@ function TableRow({item, children, layoutInfo, parent, ...otherProps}) {
1128
1154
  dropIndicator = dragAndDropHooks.useDropIndicator({target}, dropState, dropIndicatorRef);
1129
1155
  }
1130
1156
 
1131
- let dragButtonRef = React.useRef();
1157
+ let dragButtonRef = React.useRef(undefined);
1132
1158
  let {buttonProps: dragButtonProps} = useButton({
1133
1159
  ...draggableItem?.dragButtonProps,
1134
1160
  elementType: 'div'
@@ -1209,7 +1235,7 @@ function TableRow({item, children, layoutInfo, parent, ...otherProps}) {
1209
1235
 
1210
1236
  function TableHeaderRow({item, children, layoutInfo, parent, ...props}) {
1211
1237
  let {state, headerMenuOpen} = useTableContext();
1212
- let ref = useRef();
1238
+ let ref = useRef(undefined);
1213
1239
  let {rowProps} = useTableHeaderRow({node: item, isVirtualized: true}, state, ref);
1214
1240
  let {hoverProps} = useHover({...props, isDisabled: headerMenuOpen});
1215
1241
  let style = useStyle(layoutInfo, parent);
@@ -1222,7 +1248,7 @@ function TableHeaderRow({item, children, layoutInfo, parent, ...props}) {
1222
1248
  }
1223
1249
 
1224
1250
  function TableDragCell({cell}) {
1225
- let ref = useRef();
1251
+ let ref = useRef(undefined);
1226
1252
  let {state, isTableDraggable} = useTableContext();
1227
1253
  let isDisabled = state.disabledKeys.has(cell.parentKey);
1228
1254
  let {gridCellProps} = useTableCell({
@@ -1257,7 +1283,7 @@ function TableDragCell({cell}) {
1257
1283
  }
1258
1284
 
1259
1285
  function TableCheckboxCell({cell}) {
1260
- let ref = useRef();
1286
+ let ref = useRef(undefined);
1261
1287
  let {state} = useTableContext();
1262
1288
  let isDisabled = state.disabledKeys.has(cell.parentKey);
1263
1289
  let {gridCellProps} = useTableCell({
@@ -1302,7 +1328,7 @@ function TableCell({cell}) {
1302
1328
  let {scale} = useProvider();
1303
1329
  let {state} = useTableContext();
1304
1330
  let isExpandableTable = 'expandedKeys' in state;
1305
- let ref = useRef();
1331
+ let ref = useRef(undefined);
1306
1332
  let columnProps = cell.column.props as SpectrumColumnProps<unknown>;
1307
1333
  let isDisabled = state.disabledKeys.has(cell.parentKey);
1308
1334
  let {gridCellProps} = useTableCell({
@@ -1404,7 +1430,7 @@ function ExpandableRowChevron({cell}) {
1404
1430
  // TODO: move some/all of the chevron button setup into a separate hook?
1405
1431
  let {direction} = useLocale();
1406
1432
  let {state} = useTableContext();
1407
- let expandButtonRef = useRef();
1433
+ let expandButtonRef = useRef(undefined);
1408
1434
  let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/table');
1409
1435
  let isExpanded;
1410
1436