react-aria-components 1.6.0 → 1.7.1

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.
Files changed (228) hide show
  1. package/README.md +1 -1
  2. package/dist/Autocomplete.main.js +23 -12
  3. package/dist/Autocomplete.main.js.map +1 -1
  4. package/dist/Autocomplete.mjs +23 -12
  5. package/dist/Autocomplete.module.js +23 -12
  6. package/dist/Autocomplete.module.js.map +1 -1
  7. package/dist/Checkbox.main.js +1 -1
  8. package/dist/Checkbox.main.js.map +1 -1
  9. package/dist/Checkbox.mjs +1 -1
  10. package/dist/Checkbox.module.js +1 -1
  11. package/dist/Checkbox.module.js.map +1 -1
  12. package/dist/Collection.main.js.map +1 -1
  13. package/dist/Collection.module.js.map +1 -1
  14. package/dist/ColorArea.main.js +4 -4
  15. package/dist/ColorArea.main.js.map +1 -1
  16. package/dist/ColorArea.mjs +2 -2
  17. package/dist/ColorArea.module.js +2 -2
  18. package/dist/ColorArea.module.js.map +1 -1
  19. package/dist/ColorField.main.js +8 -10
  20. package/dist/ColorField.main.js.map +1 -1
  21. package/dist/ColorField.mjs +4 -6
  22. package/dist/ColorField.module.js +4 -6
  23. package/dist/ColorField.module.js.map +1 -1
  24. package/dist/ColorPicker.main.js +2 -2
  25. package/dist/ColorPicker.main.js.map +1 -1
  26. package/dist/ColorPicker.mjs +1 -1
  27. package/dist/ColorPicker.module.js +1 -1
  28. package/dist/ColorPicker.module.js.map +1 -1
  29. package/dist/ColorSlider.main.js +5 -7
  30. package/dist/ColorSlider.main.js.map +1 -1
  31. package/dist/ColorSlider.mjs +3 -5
  32. package/dist/ColorSlider.module.js +3 -5
  33. package/dist/ColorSlider.module.js.map +1 -1
  34. package/dist/ColorSwatch.main.js +2 -2
  35. package/dist/ColorSwatch.main.js.map +1 -1
  36. package/dist/ColorSwatch.mjs +1 -1
  37. package/dist/ColorSwatch.module.js +1 -1
  38. package/dist/ColorSwatch.module.js.map +1 -1
  39. package/dist/ColorSwatchPicker.main.js +3 -3
  40. package/dist/ColorSwatchPicker.main.js.map +1 -1
  41. package/dist/ColorSwatchPicker.mjs +1 -1
  42. package/dist/ColorSwatchPicker.module.js +1 -1
  43. package/dist/ColorSwatchPicker.module.js.map +1 -1
  44. package/dist/ColorThumb.main.js +2 -6
  45. package/dist/ColorThumb.main.js.map +1 -1
  46. package/dist/ColorThumb.mjs +1 -5
  47. package/dist/ColorThumb.module.js +1 -5
  48. package/dist/ColorThumb.module.js.map +1 -1
  49. package/dist/ColorWheel.main.js +4 -4
  50. package/dist/ColorWheel.main.js.map +1 -1
  51. package/dist/ColorWheel.mjs +2 -2
  52. package/dist/ColorWheel.module.js +2 -2
  53. package/dist/ColorWheel.module.js.map +1 -1
  54. package/dist/ComboBox.main.js +1 -1
  55. package/dist/ComboBox.main.js.map +1 -1
  56. package/dist/ComboBox.mjs +1 -1
  57. package/dist/ComboBox.module.js +1 -1
  58. package/dist/ComboBox.module.js.map +1 -1
  59. package/dist/DateField.main.js +4 -3
  60. package/dist/DateField.main.js.map +1 -1
  61. package/dist/DateField.mjs +4 -3
  62. package/dist/DateField.module.js +4 -3
  63. package/dist/DateField.module.js.map +1 -1
  64. package/dist/DatePicker.main.js +2 -2
  65. package/dist/DatePicker.main.js.map +1 -1
  66. package/dist/DatePicker.mjs +2 -2
  67. package/dist/DatePicker.module.js +2 -2
  68. package/dist/DatePicker.module.js.map +1 -1
  69. package/dist/Dialog.main.js +2 -1
  70. package/dist/Dialog.main.js.map +1 -1
  71. package/dist/Dialog.mjs +2 -1
  72. package/dist/Dialog.module.js +2 -1
  73. package/dist/Dialog.module.js.map +1 -1
  74. package/dist/Disclosure.main.js +5 -7
  75. package/dist/Disclosure.main.js.map +1 -1
  76. package/dist/Disclosure.mjs +2 -4
  77. package/dist/Disclosure.module.js +2 -4
  78. package/dist/Disclosure.module.js.map +1 -1
  79. package/dist/DropZone.main.js +1 -3
  80. package/dist/DropZone.main.js.map +1 -1
  81. package/dist/DropZone.mjs +1 -3
  82. package/dist/DropZone.module.js +1 -3
  83. package/dist/DropZone.module.js.map +1 -1
  84. package/dist/GridList.main.js.map +1 -1
  85. package/dist/GridList.module.js.map +1 -1
  86. package/dist/Group.main.js.map +1 -1
  87. package/dist/Group.module.js.map +1 -1
  88. package/dist/ListBox.main.js +17 -16
  89. package/dist/ListBox.main.js.map +1 -1
  90. package/dist/ListBox.mjs +19 -18
  91. package/dist/ListBox.module.js +19 -18
  92. package/dist/ListBox.module.js.map +1 -1
  93. package/dist/Menu.main.js +39 -24
  94. package/dist/Menu.main.js.map +1 -1
  95. package/dist/Menu.mjs +40 -25
  96. package/dist/Menu.module.js +40 -25
  97. package/dist/Menu.module.js.map +1 -1
  98. package/dist/Meter.main.js +1 -1
  99. package/dist/Meter.main.js.map +1 -1
  100. package/dist/Meter.mjs +1 -1
  101. package/dist/Meter.module.js +1 -1
  102. package/dist/Meter.module.js.map +1 -1
  103. package/dist/NumberField.main.js +4 -2
  104. package/dist/NumberField.main.js.map +1 -1
  105. package/dist/NumberField.mjs +4 -2
  106. package/dist/NumberField.module.js +4 -2
  107. package/dist/NumberField.module.js.map +1 -1
  108. package/dist/Popover.main.js +67 -15
  109. package/dist/Popover.main.js.map +1 -1
  110. package/dist/Popover.mjs +69 -17
  111. package/dist/Popover.module.js +69 -17
  112. package/dist/Popover.module.js.map +1 -1
  113. package/dist/ProgressBar.main.js +1 -1
  114. package/dist/ProgressBar.main.js.map +1 -1
  115. package/dist/ProgressBar.mjs +1 -1
  116. package/dist/ProgressBar.module.js +1 -1
  117. package/dist/ProgressBar.module.js.map +1 -1
  118. package/dist/RadioGroup.main.js +1 -1
  119. package/dist/RadioGroup.main.js.map +1 -1
  120. package/dist/RadioGroup.mjs +1 -1
  121. package/dist/RadioGroup.module.js +1 -1
  122. package/dist/RadioGroup.module.js.map +1 -1
  123. package/dist/SearchField.main.js +4 -2
  124. package/dist/SearchField.main.js.map +1 -1
  125. package/dist/SearchField.mjs +5 -3
  126. package/dist/SearchField.module.js +5 -3
  127. package/dist/SearchField.module.js.map +1 -1
  128. package/dist/Select.main.js +3 -2
  129. package/dist/Select.main.js.map +1 -1
  130. package/dist/Select.mjs +3 -2
  131. package/dist/Select.module.js +3 -2
  132. package/dist/Select.module.js.map +1 -1
  133. package/dist/Slider.main.js +2 -2
  134. package/dist/Slider.main.js.map +1 -1
  135. package/dist/Slider.mjs +2 -2
  136. package/dist/Slider.module.js +2 -2
  137. package/dist/Slider.module.js.map +1 -1
  138. package/dist/Table.main.js +15 -6
  139. package/dist/Table.main.js.map +1 -1
  140. package/dist/Table.mjs +15 -6
  141. package/dist/Table.module.js +15 -6
  142. package/dist/Table.module.js.map +1 -1
  143. package/dist/TableLayout.main.js.map +1 -1
  144. package/dist/TableLayout.module.js.map +1 -1
  145. package/dist/Tabs.main.js +3 -2
  146. package/dist/Tabs.main.js.map +1 -1
  147. package/dist/Tabs.mjs +4 -3
  148. package/dist/Tabs.module.js +4 -3
  149. package/dist/Tabs.module.js.map +1 -1
  150. package/dist/TagGroup.main.js +1 -1
  151. package/dist/TagGroup.main.js.map +1 -1
  152. package/dist/TagGroup.mjs +1 -1
  153. package/dist/TagGroup.module.js +1 -1
  154. package/dist/TagGroup.module.js.map +1 -1
  155. package/dist/TextField.main.js +4 -2
  156. package/dist/TextField.main.js.map +1 -1
  157. package/dist/TextField.mjs +5 -3
  158. package/dist/TextField.module.js +5 -3
  159. package/dist/TextField.module.js.map +1 -1
  160. package/dist/Toast.main.js +148 -0
  161. package/dist/Toast.main.js.map +1 -0
  162. package/dist/Toast.mjs +137 -0
  163. package/dist/Toast.module.js +137 -0
  164. package/dist/Toast.module.js.map +1 -0
  165. package/dist/Tree.main.js +38 -34
  166. package/dist/Tree.main.js.map +1 -1
  167. package/dist/Tree.mjs +34 -30
  168. package/dist/Tree.module.js +34 -30
  169. package/dist/Tree.module.js.map +1 -1
  170. package/dist/Virtualizer.main.js +4 -1
  171. package/dist/Virtualizer.main.js.map +1 -1
  172. package/dist/Virtualizer.mjs +4 -1
  173. package/dist/Virtualizer.module.js +4 -1
  174. package/dist/Virtualizer.module.js.map +1 -1
  175. package/dist/import.mjs +9 -7
  176. package/dist/main.js +31 -17
  177. package/dist/main.js.map +1 -1
  178. package/dist/module.js +9 -7
  179. package/dist/module.js.map +1 -1
  180. package/dist/types.d.ts +159 -59
  181. package/dist/types.d.ts.map +1 -1
  182. package/dist/utils.main.js +9 -7
  183. package/dist/utils.main.js.map +1 -1
  184. package/dist/utils.mjs +9 -7
  185. package/dist/utils.module.js +9 -7
  186. package/dist/utils.module.js.map +1 -1
  187. package/i18n/index.js +1 -1
  188. package/i18n/index.mjs +1 -1
  189. package/package.json +25 -30
  190. package/src/Autocomplete.tsx +19 -15
  191. package/src/Checkbox.tsx +3 -1
  192. package/src/Collection.tsx +2 -2
  193. package/src/ColorArea.tsx +2 -3
  194. package/src/ColorField.tsx +8 -6
  195. package/src/ColorPicker.tsx +1 -2
  196. package/src/ColorSlider.tsx +5 -5
  197. package/src/ColorSwatch.tsx +2 -2
  198. package/src/ColorSwatchPicker.tsx +1 -1
  199. package/src/ColorThumb.tsx +2 -4
  200. package/src/ColorWheel.tsx +2 -2
  201. package/src/ComboBox.tsx +3 -1
  202. package/src/DateField.tsx +8 -3
  203. package/src/DatePicker.tsx +6 -2
  204. package/src/Dialog.tsx +6 -2
  205. package/src/Disclosure.tsx +2 -3
  206. package/src/DropZone.tsx +1 -2
  207. package/src/GridList.tsx +5 -0
  208. package/src/Group.tsx +1 -0
  209. package/src/ListBox.tsx +11 -9
  210. package/src/Menu.tsx +53 -25
  211. package/src/Meter.tsx +3 -1
  212. package/src/NumberField.tsx +11 -2
  213. package/src/Popover.tsx +77 -25
  214. package/src/ProgressBar.tsx +3 -1
  215. package/src/RadioGroup.tsx +3 -1
  216. package/src/SearchField.tsx +6 -4
  217. package/src/Select.tsx +5 -2
  218. package/src/Slider.tsx +6 -2
  219. package/src/Table.tsx +35 -15
  220. package/src/TableLayout.ts +1 -1
  221. package/src/Tabs.tsx +6 -3
  222. package/src/TagGroup.tsx +3 -1
  223. package/src/TextField.tsx +6 -4
  224. package/src/Toast.tsx +184 -0
  225. package/src/Tree.tsx +67 -49
  226. package/src/Virtualizer.tsx +18 -3
  227. package/src/index.ts +16 -16
  228. package/src/utils.tsx +8 -10
package/src/Menu.tsx CHANGED
@@ -10,11 +10,11 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {AriaMenuProps, FocusScope, mergeProps, useFocusRing, useMenu, useMenuItem, useMenuSection, useMenuTrigger} from 'react-aria';
13
+ import {AriaMenuProps, FocusScope, mergeProps, useHover, useMenu, useMenuItem, useMenuSection, useMenuTrigger, useSubmenuTrigger} from 'react-aria';
14
14
  import {BaseCollection, Collection, CollectionBuilder, createBranchComponent, createLeafComponent} from '@react-aria/collections';
15
- import {MenuTriggerProps as BaseMenuTriggerProps, Collection as ICollection, Node, TreeState, useMenuTriggerState, useTreeState} from 'react-stately';
15
+ import {MenuTriggerProps as BaseMenuTriggerProps, Collection as ICollection, Node, RootMenuTriggerState, TreeState, useMenuTriggerState, useSubmenuTriggerState, useTreeState} from 'react-stately';
16
16
  import {CollectionProps, CollectionRendererContext, ItemRenderProps, SectionContext, SectionProps, usePersistedKeys} from './Collection';
17
- import {ContextValue, Provider, RenderProps, ScrollableProps, SlotProps, StyleProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
17
+ import {ContextValue, DEFAULT_SLOT, Provider, RenderProps, ScrollableProps, SlotProps, StyleRenderProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
18
18
  import {filterDOMProps, mergeRefs, useObjectRef, useResizeObserver} from '@react-aria/utils';
19
19
  import {FocusStrategy, forwardRefType, HoverEvents, Key, LinkDOMProps, MultipleSelection} from '@react-types/shared';
20
20
  import {HeaderContext} from './Header';
@@ -22,7 +22,7 @@ import {KeyboardContext} from './Keyboard';
22
22
  import {MultipleSelectionState, SelectionManager, useMultipleSelectionState} from '@react-stately/selection';
23
23
  import {OverlayTriggerStateContext} from './Dialog';
24
24
  import {PopoverContext} from './Popover';
25
- import {PressResponder, useHover} from '@react-aria/interactions';
25
+ import {PressResponder} from '@react-aria/interactions';
26
26
  import React, {
27
27
  createContext,
28
28
  ForwardedRef,
@@ -36,11 +36,9 @@ import React, {
36
36
  useRef,
37
37
  useState
38
38
  } from 'react';
39
- import {RootMenuTriggerState, useSubmenuTriggerState} from '@react-stately/menu';
40
39
  import {SeparatorContext} from './Separator';
41
40
  import {TextContext} from './Text';
42
41
  import {UNSTABLE_InternalAutocompleteContext} from './Autocomplete';
43
- import {useSubmenuTrigger} from '@react-aria/menu';
44
42
 
45
43
  export const MenuContext = createContext<ContextValue<MenuProps<any>, HTMLDivElement>>(null);
46
44
  export const MenuStateContext = createContext<TreeState<any> | null>(null);
@@ -70,7 +68,6 @@ export function MenuTrigger(props: MenuTriggerProps) {
70
68
  ref: ref,
71
69
  onResize: onResize
72
70
  });
73
-
74
71
  let scrollRef = useRef(null);
75
72
 
76
73
  return (
@@ -84,7 +81,8 @@ export function MenuTrigger(props: MenuTriggerProps) {
84
81
  triggerRef: ref,
85
82
  scrollRef,
86
83
  placement: 'bottom start',
87
- style: {'--trigger-width': buttonWidth} as React.CSSProperties
84
+ style: {'--trigger-width': buttonWidth} as React.CSSProperties,
85
+ 'aria-labelledby': menuProps['aria-labelledby']
88
86
  }]
89
87
  ]}>
90
88
  <PressResponder {...menuTriggerProps} ref={ref} isPressed={state.isOpen}>
@@ -106,7 +104,7 @@ export interface SubmenuTriggerProps {
106
104
  delay?: number
107
105
  }
108
106
 
109
- const SubmenuTriggerContext = createContext<{parentMenuRef: RefObject<HTMLElement | null>} | null>(null);
107
+ const SubmenuTriggerContext = createContext<{parentMenuRef: RefObject<HTMLElement | null>, shouldUseVirtualFocus?: boolean} | null>(null);
110
108
 
111
109
  /**
112
110
  * A submenu trigger is used to wrap a submenu's trigger item and the submenu itself.
@@ -120,11 +118,12 @@ export const SubmenuTrigger = /*#__PURE__*/ createBranchComponent('submenutrigg
120
118
  let submenuTriggerState = useSubmenuTriggerState({triggerKey: item.key}, rootMenuTriggerState);
121
119
  let submenuRef = useRef<HTMLDivElement>(null);
122
120
  let itemRef = useObjectRef(ref);
123
- let {parentMenuRef} = useContext(SubmenuTriggerContext)!;
121
+ let {parentMenuRef, shouldUseVirtualFocus} = useContext(SubmenuTriggerContext)!;
124
122
  let {submenuTriggerProps, submenuProps, popoverProps} = useSubmenuTrigger({
125
123
  parentMenuRef,
126
124
  submenuRef,
127
- delay: props.delay
125
+ delay: props.delay,
126
+ shouldUseVirtualFocus
128
127
  }, submenuTriggerState, itemRef);
129
128
 
130
129
  return (
@@ -138,9 +137,7 @@ export const SubmenuTrigger = /*#__PURE__*/ createBranchComponent('submenutrigg
138
137
  trigger: 'SubmenuTrigger',
139
138
  triggerRef: itemRef,
140
139
  placement: 'end top',
141
- // Prevent parent popover from hiding submenu.
142
- // @ts-ignore
143
- 'data-react-aria-top-layer': true,
140
+ 'aria-labelledby': submenuProps['aria-labelledby'],
144
141
  ...popoverProps
145
142
  }]
146
143
  ]}>
@@ -150,7 +147,18 @@ export const SubmenuTrigger = /*#__PURE__*/ createBranchComponent('submenutrigg
150
147
  );
151
148
  }, props => props.children[0]);
152
149
 
153
- export interface MenuProps<T> extends Omit<AriaMenuProps<T>, 'children'>, CollectionProps<T>, StyleProps, SlotProps, ScrollableProps<HTMLDivElement> {}
150
+ export interface MenuRenderProps {
151
+ /**
152
+ * Whether the menu has no items and should display its empty state.
153
+ * @selector [data-empty]
154
+ */
155
+ isEmpty: boolean
156
+ }
157
+
158
+ export interface MenuProps<T> extends Omit<AriaMenuProps<T>, 'children'>, CollectionProps<T>, StyleRenderProps<MenuRenderProps>, SlotProps, ScrollableProps<HTMLDivElement> {
159
+ /** Provides content to display when there are no items in the list. */
160
+ renderEmptyState?: () => ReactNode
161
+ }
154
162
 
155
163
  /**
156
164
  * A menu displays a list of actions or options that a user can choose.
@@ -161,7 +169,7 @@ export const Menu = /*#__PURE__*/ (forwardRef as forwardRefType)(function Menu<T
161
169
  // Delay rendering the actual menu until we have the collection so that auto focus works properly.
162
170
  return (
163
171
  <CollectionBuilder content={<Collection {...props} />}>
164
- {collection => collection.size > 0 && <MenuInner props={props} collection={collection} menuRef={ref} />}
172
+ {collection => <MenuInner props={props} collection={collection} menuRef={ref} />}
165
173
  </CollectionBuilder>
166
174
  );
167
175
  });
@@ -173,10 +181,10 @@ interface MenuInnerProps<T> {
173
181
  }
174
182
 
175
183
  function MenuInner<T extends object>({props, collection, menuRef: ref}: MenuInnerProps<T>) {
176
- let {filterFn, collectionProps: autocompleteMenuProps, collectionRef} = useContext(UNSTABLE_InternalAutocompleteContext) || {};
184
+ let {filter, collectionProps: autocompleteMenuProps, collectionRef} = useContext(UNSTABLE_InternalAutocompleteContext) || {};
177
185
  // Memoed so that useAutocomplete callback ref is properly only called once on mount and not everytime a rerender happens
178
186
  ref = useObjectRef(useMemo(() => mergeRefs(ref, collectionRef !== undefined ? collectionRef as RefObject<HTMLDivElement> : null), [collectionRef, ref]));
179
- let filteredCollection = useMemo(() => filterFn ? collection.filter(filterFn) : collection, [collection, filterFn]);
187
+ let filteredCollection = useMemo(() => filter ? collection.UNSTABLE_filter(filter) : collection, [collection, filter]);
180
188
  let state = useTreeState({
181
189
  ...props,
182
190
  collection: filteredCollection as ICollection<Node<object>>,
@@ -189,9 +197,22 @@ function MenuInner<T extends object>({props, collection, menuRef: ref}: MenuInne
189
197
  defaultClassName: 'react-aria-Menu',
190
198
  className: props.className,
191
199
  style: props.style,
192
- values: {}
200
+ values: {
201
+ isEmpty: state.collection.size === 0
202
+ }
193
203
  });
194
204
 
205
+ let emptyState: ReactElement | null = null;
206
+ if (state.collection.size === 0 && props.renderEmptyState) {
207
+ emptyState = (
208
+ <div
209
+ role="menuitem"
210
+ style={{display: 'contents'}}>
211
+ {props.renderEmptyState()}
212
+ </div>
213
+ );
214
+ }
215
+
195
216
  return (
196
217
  <FocusScope>
197
218
  <div
@@ -200,21 +221,28 @@ function MenuInner<T extends object>({props, collection, menuRef: ref}: MenuInne
200
221
  {...renderProps}
201
222
  ref={ref}
202
223
  slot={props.slot || undefined}
224
+ data-empty={state.collection.size === 0 || undefined}
203
225
  onScroll={props.onScroll}>
204
226
  <Provider
205
227
  values={[
206
228
  [MenuStateContext, state],
207
229
  [SeparatorContext, {elementType: 'div'}],
208
230
  [SectionContext, {name: 'MenuSection', render: MenuSectionInner}],
209
- [SubmenuTriggerContext, {parentMenuRef: ref}],
231
+ [SubmenuTriggerContext, {parentMenuRef: ref, shouldUseVirtualFocus: autocompleteMenuProps?.shouldUseVirtualFocus}],
210
232
  [MenuItemContext, null],
211
- [SelectionManagerContext, state.selectionManager]
233
+ [UNSTABLE_InternalAutocompleteContext, null],
234
+ [SelectionManagerContext, state.selectionManager],
235
+ /* Ensure root MenuTriggerState is defined, in case Menu is rendered outside a MenuTrigger. */
236
+ /* We assume the context can never change between defined and undefined. */
237
+ /* eslint-disable-next-line react-hooks/rules-of-hooks */
238
+ [RootMenuTriggerStateContext, triggerState ?? useMenuTriggerState({})]
212
239
  ]}>
213
240
  <CollectionRoot
214
241
  collection={state.collection}
215
242
  persistedKeys={usePersistedKeys(state.selectionManager.focusedKey)}
216
243
  scrollRef={ref} />
217
244
  </Provider>
245
+ {emptyState}
218
246
  </div>
219
247
  </FocusScope>
220
248
  );
@@ -343,7 +371,6 @@ export const MenuItem = /*#__PURE__*/ createLeafComponent('item', function MenuI
343
371
  selectionManager
344
372
  }, state, ref);
345
373
 
346
- let {isFocusVisible, focusProps} = useFocusRing();
347
374
  let {hoverProps, isHovered} = useHover({
348
375
  isDisabled: states.isDisabled
349
376
  });
@@ -355,7 +382,7 @@ export const MenuItem = /*#__PURE__*/ createLeafComponent('item', function MenuI
355
382
  values: {
356
383
  ...states,
357
384
  isHovered,
358
- isFocusVisible,
385
+ isFocusVisible: states.isFocusVisible,
359
386
  selectionMode: selectionManager.selectionMode,
360
387
  selectionBehavior: selectionManager.selectionBehavior,
361
388
  hasSubmenu: !!props['aria-haspopup'],
@@ -367,13 +394,13 @@ export const MenuItem = /*#__PURE__*/ createLeafComponent('item', function MenuI
367
394
 
368
395
  return (
369
396
  <ElementType
370
- {...mergeProps(menuItemProps, focusProps, hoverProps)}
397
+ {...mergeProps(menuItemProps, hoverProps)}
371
398
  {...renderProps}
372
399
  ref={ref}
373
400
  data-disabled={states.isDisabled || undefined}
374
401
  data-hovered={isHovered || undefined}
375
402
  data-focused={states.isFocused || undefined}
376
- data-focus-visible={isFocusVisible || undefined}
403
+ data-focus-visible={states.isFocusVisible || undefined}
377
404
  data-pressed={states.isPressed || undefined}
378
405
  data-selected={states.isSelected || undefined}
379
406
  data-selection-mode={selectionManager.selectionMode === 'none' ? undefined : selectionManager.selectionMode}
@@ -383,6 +410,7 @@ export const MenuItem = /*#__PURE__*/ createLeafComponent('item', function MenuI
383
410
  values={[
384
411
  [TextContext, {
385
412
  slots: {
413
+ [DEFAULT_SLOT]: labelProps,
386
414
  label: labelProps,
387
415
  description: descriptionProps
388
416
  }
package/src/Meter.tsx CHANGED
@@ -45,7 +45,9 @@ export const Meter = /*#__PURE__*/ (forwardRef as forwardRefType)(function Meter
45
45
  } = props;
46
46
  value = clamp(value, minValue, maxValue);
47
47
 
48
- let [labelRef, label] = useSlot();
48
+ let [labelRef, label] = useSlot(
49
+ !props['aria-label'] && !props['aria-labelledby']
50
+ );
49
51
  let {
50
52
  meterProps,
51
53
  labelProps
@@ -35,6 +35,11 @@ export interface NumberFieldRenderProps {
35
35
  * @selector [data-invalid]
36
36
  */
37
37
  isInvalid: boolean,
38
+ /**
39
+ * Whether the number field is required.
40
+ * @selector [data-required]
41
+ */
42
+ isRequired: boolean,
38
43
  /**
39
44
  * State of the number field.
40
45
  */
@@ -61,7 +66,9 @@ export const NumberField = /*#__PURE__*/ (forwardRef as forwardRefType)(function
61
66
  });
62
67
 
63
68
  let inputRef = useRef<HTMLInputElement>(null);
64
- let [labelRef, label] = useSlot();
69
+ let [labelRef, label] = useSlot(
70
+ !props['aria-label'] && !props['aria-labelledby']
71
+ );
65
72
  let {
66
73
  labelProps,
67
74
  groupProps,
@@ -82,7 +89,8 @@ export const NumberField = /*#__PURE__*/ (forwardRef as forwardRefType)(function
82
89
  values: {
83
90
  state,
84
91
  isDisabled: props.isDisabled || false,
85
- isInvalid: validation.isInvalid || false
92
+ isInvalid: validation.isInvalid || false,
93
+ isRequired: props.isRequired || false
86
94
  },
87
95
  defaultClassName: 'react-aria-NumberField'
88
96
  });
@@ -117,6 +125,7 @@ export const NumberField = /*#__PURE__*/ (forwardRef as forwardRefType)(function
117
125
  ref={ref}
118
126
  slot={props.slot || undefined}
119
127
  data-disabled={props.isDisabled || undefined}
128
+ data-required={props.isRequired || undefined}
120
129
  data-invalid={validation.isInvalid || undefined} />
121
130
  {props.name && <input type="hidden" name={props.name} value={isNaN(state.numberValue) ? '' : state.numberValue} />}
122
131
  </Provider>
package/src/Popover.tsx CHANGED
@@ -10,17 +10,18 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {AriaPopoverProps, DismissButton, Overlay, PlacementAxis, PositionProps, usePopover} from 'react-aria';
13
+ import {AriaLabelingProps, forwardRefType, RefObject} from '@react-types/shared';
14
+ import {AriaPopoverProps, DismissButton, Overlay, PlacementAxis, PositionProps, useLocale, usePopover} from 'react-aria';
14
15
  import {ContextValue, RenderProps, SlotProps, useContextProps, useRenderProps} from './utils';
15
16
  import {filterDOMProps, mergeProps, useEnterAnimation, useExitAnimation, useLayoutEffect} from '@react-aria/utils';
16
- import {forwardRefType, RefObject} from '@react-types/shared';
17
+ import {focusSafely} from '@react-aria/interactions';
17
18
  import {OverlayArrowContext} from './OverlayArrow';
18
19
  import {OverlayTriggerProps, OverlayTriggerState, useOverlayTriggerState} from 'react-stately';
19
20
  import {OverlayTriggerStateContext} from './Dialog';
20
- import React, {createContext, ForwardedRef, forwardRef, useContext, useRef, useState} from 'react';
21
+ import React, {createContext, ForwardedRef, forwardRef, useContext, useEffect, useRef, useState} from 'react';
21
22
  import {useIsHidden} from '@react-aria/collections';
22
23
 
23
- export interface PopoverProps extends Omit<PositionProps, 'isOpen'>, Omit<AriaPopoverProps, 'popoverRef' | 'triggerRef' | 'offset' | 'arrowSize'>, OverlayTriggerProps, RenderProps<PopoverRenderProps>, SlotProps {
24
+ export interface PopoverProps extends Omit<PositionProps, 'isOpen'>, Omit<AriaPopoverProps, 'popoverRef' | 'triggerRef' | 'groupRef' | 'offset' | 'arrowSize'>, OverlayTriggerProps, RenderProps<PopoverRenderProps>, SlotProps, AriaLabelingProps {
24
25
  /**
25
26
  * The name of the component that triggered the popover. This is reflected on the element
26
27
  * as the `data-trigger` attribute, and can be used to provide specific
@@ -80,6 +81,9 @@ export interface PopoverRenderProps {
80
81
 
81
82
  export const PopoverContext = createContext<ContextValue<PopoverProps, HTMLElement>>(null);
82
83
 
84
+ // Stores a ref for the portal container for a group of popovers (e.g. submenus).
85
+ const PopoverGroupContext = createContext<RefObject<Element | null> | null>(null);
86
+
83
87
  /**
84
88
  * A popover is an overlay element positioned relative to a trigger.
85
89
  */
@@ -90,6 +94,7 @@ export const Popover = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pop
90
94
  let state = props.isOpen != null || props.defaultOpen != null || !contextState ? localState : contextState;
91
95
  let isExiting = useExitAnimation(ref, state.isOpen) || props.isExiting || false;
92
96
  let isHidden = useIsHidden();
97
+ let {direction} = useLocale();
93
98
 
94
99
  // If we are in a hidden tree, we still need to preserve our children.
95
100
  if (isHidden) {
@@ -117,7 +122,8 @@ export const Popover = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pop
117
122
  triggerRef={props.triggerRef!}
118
123
  state={state}
119
124
  popoverRef={ref}
120
- isExiting={isExiting} />
125
+ isExiting={isExiting}
126
+ dir={direction} />
121
127
  );
122
128
  });
123
129
 
@@ -126,7 +132,8 @@ interface PopoverInnerProps extends AriaPopoverProps, RenderProps<PopoverRenderP
126
132
  isEntering?: boolean,
127
133
  isExiting: boolean,
128
134
  UNSTABLE_portalContainer?: Element,
129
- trigger?: string
135
+ trigger?: string,
136
+ dir?: 'ltr' | 'rtl'
130
137
  }
131
138
 
132
139
  function PopoverInner({state, isExiting, UNSTABLE_portalContainer, ...props}: PopoverInnerProps) {
@@ -134,6 +141,9 @@ function PopoverInner({state, isExiting, UNSTABLE_portalContainer, ...props}: Po
134
141
  // Referenced from: packages/@react-spectrum/tooltip/src/TooltipTrigger.tsx
135
142
  let arrowRef = useRef<HTMLDivElement>(null);
136
143
  let [arrowWidth, setArrowWidth] = useState(0);
144
+ let containerRef = useRef<HTMLDivElement | null>(null);
145
+ let groupCtx = useContext(PopoverGroupContext);
146
+ let isSubPopover = groupCtx && props.trigger === 'SubmenuTrigger';
137
147
  useLayoutEffect(() => {
138
148
  if (arrowRef.current && state.isOpen) {
139
149
  setArrowWidth(arrowRef.current.getBoundingClientRect().width);
@@ -143,7 +153,10 @@ function PopoverInner({state, isExiting, UNSTABLE_portalContainer, ...props}: Po
143
153
  let {popoverProps, underlayProps, arrowProps, placement} = usePopover({
144
154
  ...props,
145
155
  offset: props.offset ?? 8,
146
- arrowSize: arrowWidth
156
+ arrowSize: arrowWidth,
157
+ // If this is a submenu/subdialog, use the root popover's container
158
+ // to detect outside interaction and add aria-hidden.
159
+ groupRef: isSubPopover ? groupCtx! : containerRef
147
160
  }, state);
148
161
 
149
162
  let ref = props.popoverRef as RefObject<HTMLDivElement | null>;
@@ -159,27 +172,66 @@ function PopoverInner({state, isExiting, UNSTABLE_portalContainer, ...props}: Po
159
172
  }
160
173
  });
161
174
 
175
+ // Automatically render Popover with role=dialog except when isNonModal is true,
176
+ // or a dialog is already nested inside the popover.
177
+ let shouldBeDialog = !props.isNonModal || props.trigger === 'SubmenuTrigger';
178
+ let [isDialog, setDialog] = useState(false);
179
+ useLayoutEffect(() => {
180
+ if (ref.current) {
181
+ setDialog(shouldBeDialog && !ref.current.querySelector('[role=dialog]'));
182
+ }
183
+ }, [ref, shouldBeDialog]);
184
+
185
+ // Focus the popover itself on mount, unless a child element is already focused.
186
+ useEffect(() => {
187
+ if (isDialog && ref.current && !ref.current.contains(document.activeElement)) {
188
+ focusSafely(ref.current);
189
+ }
190
+ }, [isDialog, ref]);
191
+
162
192
  let style = {...popoverProps.style, ...renderProps.style};
193
+ let overlay = (
194
+ <div
195
+ {...mergeProps(filterDOMProps(props as any), popoverProps)}
196
+ {...renderProps}
197
+ role={isDialog ? 'dialog' : undefined}
198
+ tabIndex={isDialog ? -1 : undefined}
199
+ aria-label={props['aria-label']}
200
+ aria-labelledby={props['aria-labelledby']}
201
+ ref={ref}
202
+ slot={props.slot || undefined}
203
+ style={style}
204
+ dir={props.dir}
205
+ data-trigger={props.trigger}
206
+ data-placement={placement}
207
+ data-entering={isEntering || undefined}
208
+ data-exiting={isExiting || undefined}>
209
+ {!props.isNonModal && <DismissButton onDismiss={state.close} />}
210
+ <OverlayArrowContext.Provider value={{...arrowProps, placement, ref: arrowRef}}>
211
+ {renderProps.children}
212
+ </OverlayArrowContext.Provider>
213
+ <DismissButton onDismiss={state.close} />
214
+ </div>
215
+ );
216
+
217
+ // If this is a root popover, render an extra div to act as the portal container for submenus/subdialogs.
218
+ if (!isSubPopover) {
219
+ return (
220
+ <Overlay {...props} shouldContainFocus={isDialog} isExiting={isExiting} portalContainer={UNSTABLE_portalContainer}>
221
+ {!props.isNonModal && state.isOpen && <div data-testid="underlay" {...underlayProps} style={{position: 'fixed', inset: 0}} />}
222
+ <div ref={containerRef} style={{display: 'contents'}}>
223
+ <PopoverGroupContext.Provider value={containerRef}>
224
+ {overlay}
225
+ </PopoverGroupContext.Provider>
226
+ </div>
227
+ </Overlay>
228
+ );
229
+ }
163
230
 
231
+ // Submenus/subdialogs are mounted into the root popover's container.
164
232
  return (
165
- <Overlay {...props} isExiting={isExiting} portalContainer={UNSTABLE_portalContainer}>
166
- {!props.isNonModal && state.isOpen && <div data-testid="underlay" {...underlayProps} style={{position: 'fixed', inset: 0}} />}
167
- <div
168
- {...mergeProps(filterDOMProps(props as any), popoverProps)}
169
- {...renderProps}
170
- ref={ref}
171
- slot={props.slot || undefined}
172
- style={style}
173
- data-trigger={props.trigger}
174
- data-placement={placement}
175
- data-entering={isEntering || undefined}
176
- data-exiting={isExiting || undefined}>
177
- {!props.isNonModal && <DismissButton onDismiss={state.close} />}
178
- <OverlayArrowContext.Provider value={{...arrowProps, placement, ref: arrowRef}}>
179
- {renderProps.children}
180
- </OverlayArrowContext.Provider>
181
- <DismissButton onDismiss={state.close} />
182
- </div>
233
+ <Overlay {...props} shouldContainFocus={isDialog} isExiting={isExiting} portalContainer={UNSTABLE_portalContainer ?? groupCtx?.current ?? undefined}>
234
+ {overlay}
183
235
  </Overlay>
184
236
  );
185
237
  }
@@ -51,7 +51,9 @@ export const ProgressBar = forwardRef(function ProgressBar(props: ProgressBarPro
51
51
  } = props;
52
52
  value = clamp(value, minValue, maxValue);
53
53
 
54
- let [labelRef, label] = useSlot();
54
+ let [labelRef, label] = useSlot(
55
+ !props['aria-label'] && !props['aria-labelledby']
56
+ );
55
57
  let {
56
58
  progressBarProps,
57
59
  labelProps
@@ -125,7 +125,9 @@ export const RadioGroup = /*#__PURE__*/ (forwardRef as forwardRefType)(function
125
125
  validationBehavior
126
126
  });
127
127
 
128
- let [labelRef, label] = useSlot();
128
+ let [labelRef, label] = useSlot(
129
+ !props['aria-label'] && !props['aria-labelledby']
130
+ );
129
131
  let {radioGroupProps, labelProps, descriptionProps, errorMessageProps, ...validation} = useRadioGroup({
130
132
  ...props,
131
133
  label,
@@ -13,14 +13,14 @@
13
13
  import {AriaSearchFieldProps, useSearchField} from 'react-aria';
14
14
  import {ButtonContext} from './Button';
15
15
  import {ContextValue, Provider, RACValidation, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
16
+ import {createHideableComponent} from '@react-aria/collections';
16
17
  import {FieldErrorContext} from './FieldError';
17
18
  import {filterDOMProps, mergeProps} from '@react-aria/utils';
18
19
  import {FormContext} from './Form';
19
- import {forwardRefType} from '@react-types/shared';
20
20
  import {GroupContext} from './Group';
21
21
  import {InputContext} from './Input';
22
22
  import {LabelContext} from './Label';
23
- import React, {createContext, ForwardedRef, forwardRef, useRef} from 'react';
23
+ import React, {createContext, ForwardedRef, useRef} from 'react';
24
24
  import {SearchFieldState, useSearchFieldState} from 'react-stately';
25
25
  import {TextContext} from './Text';
26
26
 
@@ -53,13 +53,15 @@ export const SearchFieldContext = createContext<ContextValue<SearchFieldProps, H
53
53
  /**
54
54
  * A search field allows a user to enter and clear a search query.
55
55
  */
56
- export const SearchField = /*#__PURE__*/ (forwardRef as forwardRefType)(function SearchField(props: SearchFieldProps, ref: ForwardedRef<HTMLDivElement>) {
56
+ export const SearchField = /*#__PURE__*/ createHideableComponent(function SearchField(props: SearchFieldProps, ref: ForwardedRef<HTMLDivElement>) {
57
57
  [props, ref] = useContextProps(props, ref, SearchFieldContext);
58
58
  let {validationBehavior: formValidationBehavior} = useSlottedContext(FormContext) || {};
59
59
  let validationBehavior = props.validationBehavior ?? formValidationBehavior ?? 'native';
60
60
  let inputRef = useRef<HTMLInputElement>(null);
61
61
  let [inputContextProps, mergedInputRef] = useContextProps({}, inputRef, InputContext);
62
- let [labelRef, label] = useSlot();
62
+ let [labelRef, label] = useSlot(
63
+ !props['aria-label'] && !props['aria-labelledby']
64
+ );
63
65
  let state = useSearchFieldState({
64
66
  ...props,
65
67
  validationBehavior
package/src/Select.tsx CHANGED
@@ -114,7 +114,9 @@ function SelectInner<T extends object>({props, selectRef: ref, collection}: Sele
114
114
 
115
115
  // Get props for child elements from useSelect
116
116
  let buttonRef = useRef<HTMLButtonElement>(null);
117
- let [labelRef, label] = useSlot();
117
+ let [labelRef, label] = useSlot(
118
+ !props['aria-label'] && !props['aria-labelledby']
119
+ );
118
120
  let {
119
121
  labelProps,
120
122
  triggerProps,
@@ -177,7 +179,8 @@ function SelectInner<T extends object>({props, selectRef: ref, collection}: Sele
177
179
  triggerRef: buttonRef,
178
180
  scrollRef,
179
181
  placement: 'bottom start',
180
- style: {'--trigger-width': buttonWidth} as React.CSSProperties
182
+ style: {'--trigger-width': buttonWidth} as React.CSSProperties,
183
+ 'aria-labelledby': menuProps['aria-labelledby']
181
184
  }],
182
185
  [ListBoxContext, {...menuProps, ref: scrollRef}],
183
186
  [ListStateContext, state],
package/src/Slider.tsx CHANGED
@@ -55,7 +55,9 @@ export const Slider = /*#__PURE__*/ (forwardRef as forwardRefType)(function Slid
55
55
  let trackRef = useRef<HTMLDivElement>(null);
56
56
  let numberFormatter = useNumberFormatter(props.formatOptions);
57
57
  let state = useSliderState({...props, numberFormatter});
58
- let [labelRef, label] = useSlot();
58
+ let [labelRef, label] = useSlot(
59
+ !props['aria-label'] && !props['aria-labelledby']
60
+ );
59
61
  let {
60
62
  groupProps,
61
63
  trackProps,
@@ -221,7 +223,9 @@ export const SliderThumb = /*#__PURE__*/ (forwardRef as forwardRefType)(function
221
223
  let {index = 0} = props;
222
224
  let defaultInputRef = useRef<HTMLInputElement>(null);
223
225
  let inputRef = userInputRef || defaultInputRef;
224
- let [labelRef, label] = useSlot();
226
+ let [labelRef, label] = useSlot(
227
+ !props['aria-label'] && !props['aria-labelledby']
228
+ );
225
229
  let {thumbProps, inputProps, labelProps, isDragging, isFocused, isDisabled} = useSliderThumb({
226
230
  ...props,
227
231
  index,
package/src/Table.tsx CHANGED
@@ -41,8 +41,21 @@ class TableCollection<T> extends BaseCollection<T> implements ITableCollection<T
41
41
 
42
42
  commit(firstKey: Key, lastKey: Key, isSSR = false) {
43
43
  this.updateColumns(isSSR);
44
+
45
+ this.rows = [];
46
+ for (let row of this.getChildren(this.body.key)) {
47
+ let lastChildKey = (row as CollectionNode<T>).lastChildKey;
48
+ if (lastChildKey != null) {
49
+ let lastCell = this.getItem(lastChildKey) as GridNode<T>;
50
+ let numberOfCellsInRow = (lastCell.colIndex ?? lastCell.index) + (lastCell.colSpan ?? 1);
51
+ if (numberOfCellsInRow !== this.columns.length && !isSSR) {
52
+ throw new Error(`Cell count must match column count. Found ${numberOfCellsInRow} cells and ${this.columns.length} columns.`);
53
+ }
54
+ }
55
+ this.rows.push(row);
56
+ }
57
+
44
58
  super.commit(firstKey, lastKey, isSSR);
45
- this.rows = [...this.getChildren(this.body.key)];
46
59
  }
47
60
 
48
61
  private updateColumns(isSSR: boolean) {
@@ -526,11 +539,11 @@ export interface TableHeaderRenderProps {
526
539
 
527
540
  export interface TableHeaderProps<T> extends StyleRenderProps<TableHeaderRenderProps>, HoverEvents {
528
541
  /** A list of table columns. */
529
- columns?: T[],
542
+ columns?: Iterable<T>,
530
543
  /** A list of `Column(s)` or a function. If the latter, a list of columns must be provided using the `columns` prop. */
531
544
  children?: ReactNode | ((item: T) => ReactElement),
532
545
  /** Values that should invalidate the column cache when using dynamic collections. */
533
- dependencies?: any[]
546
+ dependencies?: ReadonlyArray<any>
534
547
  }
535
548
 
536
549
  /**
@@ -737,7 +750,6 @@ export const Column = /*#__PURE__*/ createLeafComponent('column', (props: Column
737
750
  {...mergeProps(filterDOMProps(props as any), columnHeaderProps, focusProps, hoverProps)}
738
751
  {...renderProps}
739
752
  style={style}
740
- colSpan={column.colspan}
741
753
  ref={ref}
742
754
  data-hovered={isHovered || undefined}
743
755
  data-focused={isFocused || undefined}
@@ -976,12 +988,12 @@ export const TableBody = /*#__PURE__*/ createBranchComponent('tablebody', <T ext
976
988
 
977
989
  export interface RowRenderProps extends ItemRenderProps {
978
990
  /** Whether the row's children have keyboard focus. */
979
- isFocusVisibleWithin: boolean
991
+ isFocusVisibleWithin: boolean,
992
+ /** The unique id of the row. */
993
+ id?: Key
980
994
  }
981
995
 
982
996
  export interface RowProps<T> extends StyleRenderProps<RowRenderProps>, LinkDOMProps, HoverEvents {
983
- /** The unique id of the row. */
984
- id?: Key,
985
997
  /** A list of columns used when dynamically rendering cells. */
986
998
  columns?: Iterable<T>,
987
999
  /** The cells within the row. Supports static items or a function for dynamic rendering. */
@@ -989,7 +1001,7 @@ export interface RowProps<T> extends StyleRenderProps<RowRenderProps>, LinkDOMPr
989
1001
  /** The object value that this row represents. When using dynamic collections, this is set automatically. */
990
1002
  value?: T,
991
1003
  /** Values that should invalidate the cell cache when using dynamic collections. */
992
- dependencies?: any[],
1004
+ dependencies?: ReadonlyArray<any>,
993
1005
  /** A string representation of the row's contents, used for features like typeahead. */
994
1006
  textValue?: string,
995
1007
  /** Whether the row is disabled. */
@@ -998,7 +1010,9 @@ export interface RowProps<T> extends StyleRenderProps<RowRenderProps>, LinkDOMPr
998
1010
  * Handler that is called when a user performs an action on the row. The exact user event depends on
999
1011
  * the collection's `selectionBehavior` prop and the interaction modality.
1000
1012
  */
1001
- onAction?: () => void
1013
+ onAction?: () => void,
1014
+ /** The unique id of the row. */
1015
+ id?: Key
1002
1016
  }
1003
1017
 
1004
1018
  /**
@@ -1075,7 +1089,8 @@ export const Row = /*#__PURE__*/ createBranchComponent(
1075
1089
  selectionBehavior: state.selectionManager.selectionBehavior,
1076
1090
  isDragging,
1077
1091
  isDropTarget: dropIndicator?.isDropTarget,
1078
- isFocusVisibleWithin
1092
+ isFocusVisibleWithin,
1093
+ id: item.key
1079
1094
  }
1080
1095
  });
1081
1096
 
@@ -1166,14 +1181,18 @@ export interface CellRenderProps {
1166
1181
  * Whether the cell is currently hovered with a mouse.
1167
1182
  * @selector [data-hovered]
1168
1183
  */
1169
- isHovered: boolean
1184
+ isHovered: boolean,
1185
+ /**
1186
+ * The unique id of the cell.
1187
+ **/
1188
+ id?: Key
1170
1189
  }
1171
1190
 
1172
1191
  export interface CellProps extends RenderProps<CellRenderProps> {
1173
- /** The unique id of the cell. */
1174
- id?: Key,
1175
1192
  /** A string representation of the cell's contents, used for features like typeahead. */
1176
- textValue?: string
1193
+ textValue?: string,
1194
+ /** Indicates how many columns the data cell spans. */
1195
+ colSpan?: number
1177
1196
  }
1178
1197
 
1179
1198
  /**
@@ -1203,7 +1222,8 @@ export const Cell = /*#__PURE__*/ createLeafComponent('cell', (props: CellProps,
1203
1222
  isFocused,
1204
1223
  isFocusVisible,
1205
1224
  isPressed,
1206
- isHovered
1225
+ isHovered,
1226
+ id: cell.key
1207
1227
  }
1208
1228
  });
1209
1229