@react-aria/grid 3.0.0-beta.1 → 3.0.0-nightly-4980928d3-240906
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.
- package/dist/GridKeyboardDelegate.main.js +247 -0
- package/dist/GridKeyboardDelegate.main.js.map +1 -0
- package/dist/GridKeyboardDelegate.mjs +242 -0
- package/dist/GridKeyboardDelegate.module.js +242 -0
- package/dist/GridKeyboardDelegate.module.js.map +1 -0
- package/dist/ar-AE.main.js +15 -0
- package/dist/ar-AE.main.js.map +1 -0
- package/dist/ar-AE.mjs +17 -0
- package/dist/ar-AE.module.js +17 -0
- package/dist/ar-AE.module.js.map +1 -0
- package/dist/bg-BG.main.js +15 -0
- package/dist/bg-BG.main.js.map +1 -0
- package/dist/bg-BG.mjs +17 -0
- package/dist/bg-BG.module.js +17 -0
- package/dist/bg-BG.module.js.map +1 -0
- package/dist/cs-CZ.main.js +15 -0
- package/dist/cs-CZ.main.js.map +1 -0
- package/dist/cs-CZ.mjs +17 -0
- package/dist/cs-CZ.module.js +17 -0
- package/dist/cs-CZ.module.js.map +1 -0
- package/dist/da-DK.main.js +15 -0
- package/dist/da-DK.main.js.map +1 -0
- package/dist/da-DK.mjs +17 -0
- package/dist/da-DK.module.js +17 -0
- package/dist/da-DK.module.js.map +1 -0
- package/dist/de-DE.main.js +15 -0
- package/dist/de-DE.main.js.map +1 -0
- package/dist/de-DE.mjs +17 -0
- package/dist/de-DE.module.js +17 -0
- package/dist/de-DE.module.js.map +1 -0
- package/dist/el-GR.main.js +15 -0
- package/dist/el-GR.main.js.map +1 -0
- package/dist/el-GR.mjs +17 -0
- package/dist/el-GR.module.js +17 -0
- package/dist/el-GR.module.js.map +1 -0
- package/dist/en-US.main.js +15 -0
- package/dist/en-US.main.js.map +1 -0
- package/dist/en-US.mjs +17 -0
- package/dist/en-US.module.js +17 -0
- package/dist/en-US.module.js.map +1 -0
- package/dist/es-ES.main.js +15 -0
- package/dist/es-ES.main.js.map +1 -0
- package/dist/es-ES.mjs +17 -0
- package/dist/es-ES.module.js +17 -0
- package/dist/es-ES.module.js.map +1 -0
- package/dist/et-EE.main.js +15 -0
- package/dist/et-EE.main.js.map +1 -0
- package/dist/et-EE.mjs +17 -0
- package/dist/et-EE.module.js +17 -0
- package/dist/et-EE.module.js.map +1 -0
- package/dist/fi-FI.main.js +15 -0
- package/dist/fi-FI.main.js.map +1 -0
- package/dist/fi-FI.mjs +17 -0
- package/dist/fi-FI.module.js +17 -0
- package/dist/fi-FI.module.js.map +1 -0
- package/dist/fr-FR.main.js +15 -0
- package/dist/fr-FR.main.js.map +1 -0
- package/dist/fr-FR.mjs +17 -0
- package/dist/fr-FR.module.js +17 -0
- package/dist/fr-FR.module.js.map +1 -0
- package/dist/he-IL.main.js +15 -0
- package/dist/he-IL.main.js.map +1 -0
- package/dist/he-IL.mjs +17 -0
- package/dist/he-IL.module.js +17 -0
- package/dist/he-IL.module.js.map +1 -0
- package/dist/hr-HR.main.js +15 -0
- package/dist/hr-HR.main.js.map +1 -0
- package/dist/hr-HR.mjs +17 -0
- package/dist/hr-HR.module.js +17 -0
- package/dist/hr-HR.module.js.map +1 -0
- package/dist/hu-HU.main.js +15 -0
- package/dist/hu-HU.main.js.map +1 -0
- package/dist/hu-HU.mjs +17 -0
- package/dist/hu-HU.module.js +17 -0
- package/dist/hu-HU.module.js.map +1 -0
- package/dist/import.mjs +31 -0
- package/dist/intlStrings.main.js +108 -0
- package/dist/intlStrings.main.js.map +1 -0
- package/dist/intlStrings.mjs +110 -0
- package/dist/intlStrings.module.js +110 -0
- package/dist/intlStrings.module.js.map +1 -0
- package/dist/it-IT.main.js +15 -0
- package/dist/it-IT.main.js.map +1 -0
- package/dist/it-IT.mjs +17 -0
- package/dist/it-IT.module.js +17 -0
- package/dist/it-IT.module.js.map +1 -0
- package/dist/ja-JP.main.js +15 -0
- package/dist/ja-JP.main.js.map +1 -0
- package/dist/ja-JP.mjs +17 -0
- package/dist/ja-JP.module.js +17 -0
- package/dist/ja-JP.module.js.map +1 -0
- package/dist/ko-KR.main.js +15 -0
- package/dist/ko-KR.main.js.map +1 -0
- package/dist/ko-KR.mjs +17 -0
- package/dist/ko-KR.module.js +17 -0
- package/dist/ko-KR.module.js.map +1 -0
- package/dist/lt-LT.main.js +15 -0
- package/dist/lt-LT.main.js.map +1 -0
- package/dist/lt-LT.mjs +17 -0
- package/dist/lt-LT.module.js +17 -0
- package/dist/lt-LT.module.js.map +1 -0
- package/dist/lv-LV.main.js +15 -0
- package/dist/lv-LV.main.js.map +1 -0
- package/dist/lv-LV.mjs +17 -0
- package/dist/lv-LV.module.js +17 -0
- package/dist/lv-LV.module.js.map +1 -0
- package/dist/main.js +23 -823
- package/dist/main.js.map +1 -1
- package/dist/module.js +10 -773
- package/dist/module.js.map +1 -1
- package/dist/nb-NO.main.js +15 -0
- package/dist/nb-NO.main.js.map +1 -0
- package/dist/nb-NO.mjs +17 -0
- package/dist/nb-NO.module.js +17 -0
- package/dist/nb-NO.module.js.map +1 -0
- package/dist/nl-NL.main.js +15 -0
- package/dist/nl-NL.main.js.map +1 -0
- package/dist/nl-NL.mjs +17 -0
- package/dist/nl-NL.module.js +17 -0
- package/dist/nl-NL.module.js.map +1 -0
- package/dist/pl-PL.main.js +15 -0
- package/dist/pl-PL.main.js.map +1 -0
- package/dist/pl-PL.mjs +17 -0
- package/dist/pl-PL.module.js +17 -0
- package/dist/pl-PL.module.js.map +1 -0
- package/dist/pt-BR.main.js +15 -0
- package/dist/pt-BR.main.js.map +1 -0
- package/dist/pt-BR.mjs +17 -0
- package/dist/pt-BR.module.js +17 -0
- package/dist/pt-BR.module.js.map +1 -0
- package/dist/pt-PT.main.js +15 -0
- package/dist/pt-PT.main.js.map +1 -0
- package/dist/pt-PT.mjs +17 -0
- package/dist/pt-PT.module.js +17 -0
- package/dist/pt-PT.module.js.map +1 -0
- package/dist/ro-RO.main.js +15 -0
- package/dist/ro-RO.main.js.map +1 -0
- package/dist/ro-RO.mjs +17 -0
- package/dist/ro-RO.module.js +17 -0
- package/dist/ro-RO.module.js.map +1 -0
- package/dist/ru-RU.main.js +15 -0
- package/dist/ru-RU.main.js.map +1 -0
- package/dist/ru-RU.mjs +17 -0
- package/dist/ru-RU.module.js +17 -0
- package/dist/ru-RU.module.js.map +1 -0
- package/dist/sk-SK.main.js +15 -0
- package/dist/sk-SK.main.js.map +1 -0
- package/dist/sk-SK.mjs +17 -0
- package/dist/sk-SK.module.js +17 -0
- package/dist/sk-SK.module.js.map +1 -0
- package/dist/sl-SI.main.js +15 -0
- package/dist/sl-SI.main.js.map +1 -0
- package/dist/sl-SI.mjs +17 -0
- package/dist/sl-SI.module.js +17 -0
- package/dist/sl-SI.module.js.map +1 -0
- package/dist/sr-SP.main.js +15 -0
- package/dist/sr-SP.main.js.map +1 -0
- package/dist/sr-SP.mjs +17 -0
- package/dist/sr-SP.module.js +17 -0
- package/dist/sr-SP.module.js.map +1 -0
- package/dist/sv-SE.main.js +15 -0
- package/dist/sv-SE.main.js.map +1 -0
- package/dist/sv-SE.mjs +17 -0
- package/dist/sv-SE.module.js +17 -0
- package/dist/sv-SE.module.js.map +1 -0
- package/dist/tr-TR.main.js +15 -0
- package/dist/tr-TR.main.js.map +1 -0
- package/dist/tr-TR.mjs +17 -0
- package/dist/tr-TR.module.js +17 -0
- package/dist/tr-TR.module.js.map +1 -0
- package/dist/types.d.ts +114 -36
- package/dist/types.d.ts.map +1 -1
- package/dist/uk-UA.main.js +15 -0
- package/dist/uk-UA.main.js.map +1 -0
- package/dist/uk-UA.mjs +17 -0
- package/dist/uk-UA.module.js +17 -0
- package/dist/uk-UA.module.js.map +1 -0
- package/dist/useGrid.main.js +132 -0
- package/dist/useGrid.main.js.map +1 -0
- package/dist/useGrid.mjs +127 -0
- package/dist/useGrid.module.js +127 -0
- package/dist/useGrid.module.js.map +1 -0
- package/dist/useGridCell.main.js +220 -0
- package/dist/useGridCell.main.js.map +1 -0
- package/dist/useGridCell.mjs +215 -0
- package/dist/useGridCell.module.js +215 -0
- package/dist/useGridCell.module.js.map +1 -0
- package/dist/useGridRow.main.js +53 -0
- package/dist/useGridRow.main.js.map +1 -0
- package/dist/useGridRow.mjs +48 -0
- package/dist/useGridRow.module.js +48 -0
- package/dist/useGridRow.module.js.map +1 -0
- package/dist/useGridRowGroup.main.js +26 -0
- package/dist/useGridRowGroup.main.js.map +1 -0
- package/dist/useGridRowGroup.mjs +21 -0
- package/dist/useGridRowGroup.module.js +21 -0
- package/dist/useGridRowGroup.module.js.map +1 -0
- package/dist/useGridSelectionAnnouncement.main.js +94 -0
- package/dist/useGridSelectionAnnouncement.main.js.map +1 -0
- package/dist/useGridSelectionAnnouncement.mjs +89 -0
- package/dist/useGridSelectionAnnouncement.module.js +89 -0
- package/dist/useGridSelectionAnnouncement.module.js.map +1 -0
- package/dist/useGridSelectionCheckbox.main.js +39 -0
- package/dist/useGridSelectionCheckbox.main.js.map +1 -0
- package/dist/useGridSelectionCheckbox.mjs +34 -0
- package/dist/useGridSelectionCheckbox.module.js +34 -0
- package/dist/useGridSelectionCheckbox.module.js.map +1 -0
- package/dist/useHighlightSelectionDescription.main.js +55 -0
- package/dist/useHighlightSelectionDescription.main.js.map +1 -0
- package/dist/useHighlightSelectionDescription.mjs +50 -0
- package/dist/useHighlightSelectionDescription.module.js +50 -0
- package/dist/useHighlightSelectionDescription.module.js.map +1 -0
- package/dist/utils.main.js +20 -0
- package/dist/utils.main.js.map +1 -0
- package/dist/utils.mjs +15 -0
- package/dist/utils.module.js +15 -0
- package/dist/utils.module.js.map +1 -0
- package/dist/zh-CN.main.js +15 -0
- package/dist/zh-CN.main.js.map +1 -0
- package/dist/zh-CN.mjs +17 -0
- package/dist/zh-CN.module.js +17 -0
- package/dist/zh-CN.module.js.map +1 -0
- package/dist/zh-TW.main.js +15 -0
- package/dist/zh-TW.main.js.map +1 -0
- package/dist/zh-TW.mjs +17 -0
- package/dist/zh-TW.module.js +17 -0
- package/dist/zh-TW.module.js.map +1 -0
- package/package.json +23 -15
- package/src/GridKeyboardDelegate.ts +93 -83
- package/src/index.ts +17 -5
- package/src/useGrid.ts +77 -78
- package/src/useGridCell.ts +96 -46
- package/src/useGridRow.ts +32 -21
- package/src/useGridRowGroup.ts +3 -3
- package/src/useGridSelectionAnnouncement.ts +115 -0
- package/src/useGridSelectionCheckbox.ts +48 -0
- package/src/useHighlightSelectionDescription.ts +52 -0
- package/src/utils.ts +13 -3
- package/LICENSE +0 -201
package/src/useGridCell.ts
CHANGED
|
@@ -10,31 +10,39 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import {DOMAttributes, FocusableElement, RefObject} from '@react-types/shared';
|
|
13
14
|
import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
15
|
+
import {getScrollParent, mergeProps, scrollIntoViewport} from '@react-aria/utils';
|
|
16
|
+
import {GridCollection, GridNode} from '@react-types/grid';
|
|
17
|
+
import {gridMap} from './utils';
|
|
16
18
|
import {GridState} from '@react-stately/grid';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {mergeProps} from '@react-aria/utils';
|
|
20
|
-
import {Node as RSNode} from '@react-types/shared';
|
|
19
|
+
import {isFocusVisible} from '@react-aria/interactions';
|
|
20
|
+
import {KeyboardEvent as ReactKeyboardEvent, useRef} from 'react';
|
|
21
21
|
import {useLocale} from '@react-aria/i18n';
|
|
22
22
|
import {useSelectableItem} from '@react-aria/selection';
|
|
23
23
|
|
|
24
|
-
interface GridCellProps {
|
|
24
|
+
export interface GridCellProps {
|
|
25
25
|
/** An object representing the grid cell. Contains all the relevant information that makes up the grid cell. */
|
|
26
|
-
node:
|
|
26
|
+
node: GridNode<unknown>,
|
|
27
27
|
/** Whether the grid cell is contained in a virtual scroller. */
|
|
28
28
|
isVirtualized?: boolean,
|
|
29
29
|
/** Whether the cell or its first focusable child element should be focused when the grid cell is focused. */
|
|
30
30
|
focusMode?: 'child' | 'cell',
|
|
31
31
|
/** Whether selection should occur on press up instead of press down. */
|
|
32
|
-
shouldSelectOnPressUp?: boolean
|
|
32
|
+
shouldSelectOnPressUp?: boolean,
|
|
33
|
+
/**
|
|
34
|
+
* Handler that is called when a user performs an action on the cell.
|
|
35
|
+
* Please use onCellAction at the collection level instead.
|
|
36
|
+
* @deprecated
|
|
37
|
+
**/
|
|
38
|
+
onAction?: () => void
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
interface GridCellAria {
|
|
41
|
+
export interface GridCellAria {
|
|
36
42
|
/** Props for the grid cell element. */
|
|
37
|
-
gridCellProps:
|
|
43
|
+
gridCellProps: DOMAttributes,
|
|
44
|
+
/** Whether the cell is currently in a pressed state. */
|
|
45
|
+
isPressed: boolean
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
/**
|
|
@@ -42,51 +50,62 @@ interface GridCellAria {
|
|
|
42
50
|
* @param props - Props for the cell.
|
|
43
51
|
* @param state - State of the parent grid, as returned by `useGridState`.
|
|
44
52
|
*/
|
|
45
|
-
export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps, state: GridState<T, C>, ref: RefObject<
|
|
53
|
+
export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps, state: GridState<T, C>, ref: RefObject<FocusableElement | null>): GridCellAria {
|
|
46
54
|
let {
|
|
47
55
|
node,
|
|
48
56
|
isVirtualized,
|
|
49
57
|
focusMode = 'child',
|
|
50
|
-
shouldSelectOnPressUp
|
|
58
|
+
shouldSelectOnPressUp,
|
|
59
|
+
onAction
|
|
51
60
|
} = props;
|
|
52
61
|
|
|
53
62
|
let {direction} = useLocale();
|
|
54
|
-
let keyboardDelegate =
|
|
63
|
+
let {keyboardDelegate, actions: {onCellAction}} = gridMap.get(state);
|
|
64
|
+
|
|
65
|
+
// We need to track the key of the item at the time it was last focused so that we force
|
|
66
|
+
// focus to go to the item when the DOM node is reused for a different item in a virtualizer.
|
|
67
|
+
let keyWhenFocused = useRef(null);
|
|
55
68
|
|
|
56
69
|
// Handles focusing the cell. If there is a focusable child,
|
|
57
70
|
// it is focused, otherwise the cell itself is focused.
|
|
58
71
|
let focus = () => {
|
|
59
72
|
let treeWalker = getFocusableTreeWalker(ref.current);
|
|
60
73
|
if (focusMode === 'child') {
|
|
74
|
+
// If focus is already on a focusable child within the cell, early return so we don't shift focus
|
|
75
|
+
if (ref.current.contains(document.activeElement) && ref.current !== document.activeElement) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
61
79
|
let focusable = state.selectionManager.childFocusStrategy === 'last'
|
|
62
80
|
? last(treeWalker)
|
|
63
|
-
: treeWalker.firstChild() as
|
|
81
|
+
: treeWalker.firstChild() as FocusableElement;
|
|
64
82
|
if (focusable) {
|
|
65
83
|
focusSafely(focusable);
|
|
66
84
|
return;
|
|
67
85
|
}
|
|
68
86
|
}
|
|
69
87
|
|
|
70
|
-
if (
|
|
88
|
+
if (
|
|
89
|
+
(keyWhenFocused.current != null && node.key !== keyWhenFocused.current) ||
|
|
90
|
+
!ref.current.contains(document.activeElement)
|
|
91
|
+
) {
|
|
71
92
|
focusSafely(ref.current);
|
|
72
93
|
}
|
|
73
94
|
};
|
|
74
95
|
|
|
75
|
-
let {itemProps} = useSelectableItem({
|
|
96
|
+
let {itemProps, isPressed} = useSelectableItem({
|
|
76
97
|
selectionManager: state.selectionManager,
|
|
77
98
|
key: node.key,
|
|
78
99
|
ref,
|
|
79
100
|
isVirtualized,
|
|
80
101
|
focus,
|
|
81
|
-
shouldSelectOnPressUp
|
|
102
|
+
shouldSelectOnPressUp,
|
|
103
|
+
onAction: onCellAction ? () => onCellAction(node.key) : onAction,
|
|
104
|
+
isDisabled: state.collection.size === 0
|
|
82
105
|
});
|
|
83
106
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
let {pressProps} = usePress({...itemProps, isDisabled});
|
|
87
|
-
|
|
88
|
-
let onKeyDown = (e: ReactKeyboardEvent) => {
|
|
89
|
-
if (!e.currentTarget.contains(e.target as HTMLElement)) {
|
|
107
|
+
let onKeyDownCapture = (e: ReactKeyboardEvent) => {
|
|
108
|
+
if (!e.currentTarget.contains(e.target as Element) || state.isKeyboardNavigationDisabled) {
|
|
90
109
|
return;
|
|
91
110
|
}
|
|
92
111
|
|
|
@@ -97,18 +116,19 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
|
|
|
97
116
|
case 'ArrowLeft': {
|
|
98
117
|
// Find the next focusable element within the cell.
|
|
99
118
|
let focusable = direction === 'rtl'
|
|
100
|
-
? walker.nextNode() as
|
|
101
|
-
: walker.previousNode() as
|
|
119
|
+
? walker.nextNode() as FocusableElement
|
|
120
|
+
: walker.previousNode() as FocusableElement;
|
|
102
121
|
|
|
103
122
|
// Don't focus the cell itself if focusMode is "child"
|
|
104
123
|
if (focusMode === 'child' && focusable === ref.current) {
|
|
105
124
|
focusable = null;
|
|
106
125
|
}
|
|
107
126
|
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
e.stopPropagation();
|
|
108
129
|
if (focusable) {
|
|
109
|
-
e.preventDefault();
|
|
110
|
-
e.stopPropagation();
|
|
111
130
|
focusSafely(focusable);
|
|
131
|
+
scrollIntoViewport(focusable, {containingElement: getScrollParent(ref.current)});
|
|
112
132
|
} else {
|
|
113
133
|
// If there is no next focusable child, then move to the next cell to the left of this one.
|
|
114
134
|
// This will be handled by useSelectableCollection. However, if there is no cell to the left
|
|
@@ -117,20 +137,26 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
|
|
|
117
137
|
// child, depending on the focus mode.
|
|
118
138
|
let prev = keyboardDelegate.getKeyLeftOf(node.key);
|
|
119
139
|
if (prev !== node.key) {
|
|
140
|
+
// We prevent the capturing event from reaching children of the cell, e.g. pickers.
|
|
141
|
+
// We want arrow keys to navigate to the next cell instead. We need to re-dispatch
|
|
142
|
+
// the event from a higher parent so it still bubbles and gets handled by useSelectableCollection.
|
|
143
|
+
ref.current.parentElement.dispatchEvent(
|
|
144
|
+
new KeyboardEvent(e.nativeEvent.type, e.nativeEvent)
|
|
145
|
+
);
|
|
120
146
|
break;
|
|
121
147
|
}
|
|
122
148
|
|
|
123
|
-
e.preventDefault();
|
|
124
|
-
e.stopPropagation();
|
|
125
149
|
if (focusMode === 'cell' && direction === 'rtl') {
|
|
126
150
|
focusSafely(ref.current);
|
|
151
|
+
scrollIntoViewport(ref.current, {containingElement: getScrollParent(ref.current)});
|
|
127
152
|
} else {
|
|
128
153
|
walker.currentNode = ref.current;
|
|
129
154
|
focusable = direction === 'rtl'
|
|
130
|
-
? walker.firstChild() as
|
|
155
|
+
? walker.firstChild() as FocusableElement
|
|
131
156
|
: last(walker);
|
|
132
157
|
if (focusable) {
|
|
133
158
|
focusSafely(focusable);
|
|
159
|
+
scrollIntoViewport(focusable, {containingElement: getScrollParent(ref.current)});
|
|
134
160
|
}
|
|
135
161
|
}
|
|
136
162
|
}
|
|
@@ -138,34 +164,41 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
|
|
|
138
164
|
}
|
|
139
165
|
case 'ArrowRight': {
|
|
140
166
|
let focusable = direction === 'rtl'
|
|
141
|
-
? walker.previousNode() as
|
|
142
|
-
: walker.nextNode() as
|
|
167
|
+
? walker.previousNode() as FocusableElement
|
|
168
|
+
: walker.nextNode() as FocusableElement;
|
|
143
169
|
|
|
144
170
|
if (focusMode === 'child' && focusable === ref.current) {
|
|
145
171
|
focusable = null;
|
|
146
172
|
}
|
|
147
173
|
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
e.stopPropagation();
|
|
148
176
|
if (focusable) {
|
|
149
|
-
e.preventDefault();
|
|
150
|
-
e.stopPropagation();
|
|
151
177
|
focusSafely(focusable);
|
|
178
|
+
scrollIntoViewport(focusable, {containingElement: getScrollParent(ref.current)});
|
|
152
179
|
} else {
|
|
153
180
|
let next = keyboardDelegate.getKeyRightOf(node.key);
|
|
154
181
|
if (next !== node.key) {
|
|
182
|
+
// We prevent the capturing event from reaching children of the cell, e.g. pickers.
|
|
183
|
+
// We want arrow keys to navigate to the next cell instead. We need to re-dispatch
|
|
184
|
+
// the event from a higher parent so it still bubbles and gets handled by useSelectableCollection.
|
|
185
|
+
ref.current.parentElement.dispatchEvent(
|
|
186
|
+
new KeyboardEvent(e.nativeEvent.type, e.nativeEvent)
|
|
187
|
+
);
|
|
155
188
|
break;
|
|
156
189
|
}
|
|
157
190
|
|
|
158
|
-
e.preventDefault();
|
|
159
|
-
e.stopPropagation();
|
|
160
191
|
if (focusMode === 'cell' && direction === 'ltr') {
|
|
161
192
|
focusSafely(ref.current);
|
|
193
|
+
scrollIntoViewport(ref.current, {containingElement: getScrollParent(ref.current)});
|
|
162
194
|
} else {
|
|
163
195
|
walker.currentNode = ref.current;
|
|
164
196
|
focusable = direction === 'rtl'
|
|
165
197
|
? last(walker)
|
|
166
|
-
: walker.firstChild() as
|
|
198
|
+
: walker.firstChild() as FocusableElement;
|
|
167
199
|
if (focusable) {
|
|
168
200
|
focusSafely(focusable);
|
|
201
|
+
scrollIntoViewport(focusable, {containingElement: getScrollParent(ref.current)});
|
|
169
202
|
}
|
|
170
203
|
}
|
|
171
204
|
}
|
|
@@ -176,7 +209,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
|
|
|
176
209
|
// Prevent this event from reaching cell children, e.g. menu buttons. We want arrow keys to navigate
|
|
177
210
|
// to the cell above/below instead. We need to re-dispatch the event from a higher parent so it still
|
|
178
211
|
// bubbles and gets handled by useSelectableCollection.
|
|
179
|
-
if (!e.altKey && ref.current.contains(e.target as
|
|
212
|
+
if (!e.altKey && ref.current.contains(e.target as Element)) {
|
|
180
213
|
e.stopPropagation();
|
|
181
214
|
e.preventDefault();
|
|
182
215
|
ref.current.parentElement.dispatchEvent(
|
|
@@ -190,6 +223,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
|
|
|
190
223
|
// Grid cells can have focusable elements inside them. In this case, focus should
|
|
191
224
|
// be marshalled to that element rather than focusing the cell itself.
|
|
192
225
|
let onFocus = (e) => {
|
|
226
|
+
keyWhenFocused.current = node.key;
|
|
193
227
|
if (e.target !== ref.current) {
|
|
194
228
|
// useSelectableItem only handles setting the focused key when
|
|
195
229
|
// the focused element is the gridcell itself. We also want to
|
|
@@ -212,26 +246,42 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
|
|
|
212
246
|
});
|
|
213
247
|
};
|
|
214
248
|
|
|
215
|
-
let gridCellProps:
|
|
249
|
+
let gridCellProps: DOMAttributes = mergeProps(itemProps, {
|
|
216
250
|
role: 'gridcell',
|
|
217
|
-
onKeyDownCapture
|
|
251
|
+
onKeyDownCapture,
|
|
218
252
|
onFocus
|
|
219
253
|
});
|
|
220
254
|
|
|
221
255
|
if (isVirtualized) {
|
|
222
|
-
gridCellProps['aria-colindex'] = node.index + 1; // aria-colindex is 1-based
|
|
256
|
+
gridCellProps['aria-colindex'] = (node.colIndex ?? node.index) + 1; // aria-colindex is 1-based
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// When pressing with a pointer and cell selection is not enabled, usePress will be applied to the
|
|
260
|
+
// row rather than the cell. However, when the row is draggable, usePress cannot preventDefault
|
|
261
|
+
// on pointer down, so the browser will try to focus the cell which has a tabIndex applied.
|
|
262
|
+
// To avoid this, remove the tabIndex from the cell briefly on pointer down.
|
|
263
|
+
if (shouldSelectOnPressUp && gridCellProps.tabIndex != null && gridCellProps.onPointerDown == null) {
|
|
264
|
+
gridCellProps.onPointerDown = (e) => {
|
|
265
|
+
let el = e.currentTarget;
|
|
266
|
+
let tabindex = el.getAttribute('tabindex');
|
|
267
|
+
el.removeAttribute('tabindex');
|
|
268
|
+
requestAnimationFrame(() => {
|
|
269
|
+
el.setAttribute('tabindex', tabindex);
|
|
270
|
+
});
|
|
271
|
+
};
|
|
223
272
|
}
|
|
224
273
|
|
|
225
274
|
return {
|
|
226
|
-
gridCellProps
|
|
275
|
+
gridCellProps,
|
|
276
|
+
isPressed
|
|
227
277
|
};
|
|
228
278
|
}
|
|
229
279
|
|
|
230
280
|
function last(walker: TreeWalker) {
|
|
231
|
-
let next:
|
|
232
|
-
let last:
|
|
281
|
+
let next: FocusableElement;
|
|
282
|
+
let last: FocusableElement;
|
|
233
283
|
do {
|
|
234
|
-
last = walker.lastChild() as
|
|
284
|
+
last = walker.lastChild() as FocusableElement;
|
|
235
285
|
if (last) {
|
|
236
286
|
next = last;
|
|
237
287
|
}
|
package/src/useGridRow.ts
CHANGED
|
@@ -10,25 +10,33 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {chain} from '@react-aria/utils';
|
|
14
|
+
import {DOMAttributes, FocusableElement, RefObject} from '@react-types/shared';
|
|
15
|
+
import {GridCollection, GridNode} from '@react-types/grid';
|
|
16
|
+
import {gridMap} from './utils';
|
|
14
17
|
import {GridState} from '@react-stately/grid';
|
|
15
|
-
import {
|
|
16
|
-
import {Node} from '@react-types/shared';
|
|
17
|
-
import {usePress} from '@react-aria/interactions';
|
|
18
|
-
import {useSelectableItem} from '@react-aria/selection';
|
|
18
|
+
import {SelectableItemStates, useSelectableItem} from '@react-aria/selection';
|
|
19
19
|
|
|
20
20
|
export interface GridRowProps<T> {
|
|
21
21
|
/** An object representing the grid row. Contains all the relevant information that makes up the grid row. */
|
|
22
|
-
node:
|
|
22
|
+
node: GridNode<T>,
|
|
23
23
|
/** Whether the grid row is contained in a virtual scroller. */
|
|
24
24
|
isVirtualized?: boolean,
|
|
25
25
|
/** Whether selection should occur on press up instead of press down. */
|
|
26
|
-
shouldSelectOnPressUp?: boolean
|
|
26
|
+
shouldSelectOnPressUp?: boolean,
|
|
27
|
+
/**
|
|
28
|
+
* Handler that is called when a user performs an action on the row.
|
|
29
|
+
* Please use onCellAction at the collection level instead.
|
|
30
|
+
* @deprecated
|
|
31
|
+
**/
|
|
32
|
+
onAction?: () => void
|
|
27
33
|
}
|
|
28
34
|
|
|
29
|
-
export interface GridRowAria {
|
|
35
|
+
export interface GridRowAria extends SelectableItemStates {
|
|
30
36
|
/** Props for the grid row element. */
|
|
31
|
-
rowProps:
|
|
37
|
+
rowProps: DOMAttributes,
|
|
38
|
+
/** Whether the row is currently in a pressed state. */
|
|
39
|
+
isPressed: boolean
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
/**
|
|
@@ -36,31 +44,33 @@ export interface GridRowAria {
|
|
|
36
44
|
* @param props - Props for the row.
|
|
37
45
|
* @param state - State of the parent grid, as returned by `useGridState`.
|
|
38
46
|
*/
|
|
39
|
-
export function useGridRow<T, C extends GridCollection<T>, S extends GridState<T, C>>(props: GridRowProps<T>, state: S, ref: RefObject<
|
|
47
|
+
export function useGridRow<T, C extends GridCollection<T>, S extends GridState<T, C>>(props: GridRowProps<T>, state: S, ref: RefObject<FocusableElement | null>): GridRowAria {
|
|
40
48
|
let {
|
|
41
49
|
node,
|
|
42
50
|
isVirtualized,
|
|
43
|
-
shouldSelectOnPressUp
|
|
51
|
+
shouldSelectOnPressUp,
|
|
52
|
+
onAction
|
|
44
53
|
} = props;
|
|
45
54
|
|
|
46
|
-
let {
|
|
55
|
+
let {actions} = gridMap.get(state);
|
|
56
|
+
let onRowAction = actions.onRowAction ? () => actions.onRowAction(node.key) : onAction;
|
|
57
|
+
let {itemProps, ...states} = useSelectableItem({
|
|
47
58
|
selectionManager: state.selectionManager,
|
|
48
59
|
key: node.key,
|
|
49
60
|
ref,
|
|
50
61
|
isVirtualized,
|
|
51
|
-
shouldSelectOnPressUp
|
|
62
|
+
shouldSelectOnPressUp,
|
|
63
|
+
onAction: onRowAction || node?.props?.onAction ? chain(node?.props?.onAction, onRowAction) : undefined,
|
|
64
|
+
isDisabled: state.collection.size === 0
|
|
52
65
|
});
|
|
53
66
|
|
|
54
67
|
let isSelected = state.selectionManager.isSelected(node.key);
|
|
55
|
-
let isDisabled = state.disabledKeys.has(node.key);
|
|
56
68
|
|
|
57
|
-
|
|
58
|
-
let {pressProps} = usePress({...itemProps, isDisabled});
|
|
59
|
-
|
|
60
|
-
let rowProps: HTMLAttributes<HTMLElement> = {
|
|
69
|
+
let rowProps: DOMAttributes = {
|
|
61
70
|
role: 'row',
|
|
62
|
-
'aria-selected': isSelected,
|
|
63
|
-
|
|
71
|
+
'aria-selected': state.selectionManager.selectionMode !== 'none' ? isSelected : undefined,
|
|
72
|
+
'aria-disabled': states.isDisabled || undefined,
|
|
73
|
+
...itemProps
|
|
64
74
|
};
|
|
65
75
|
|
|
66
76
|
if (isVirtualized) {
|
|
@@ -68,6 +78,7 @@ export function useGridRow<T, C extends GridCollection<T>, S extends GridState<T
|
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
return {
|
|
71
|
-
rowProps
|
|
81
|
+
rowProps,
|
|
82
|
+
...states
|
|
72
83
|
};
|
|
73
84
|
}
|
package/src/useGridRowGroup.ts
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {DOMAttributes} from '@react-types/shared';
|
|
14
14
|
|
|
15
|
-
interface GridRowGroupAria {
|
|
15
|
+
export interface GridRowGroupAria {
|
|
16
16
|
/** Props for the row group element. */
|
|
17
|
-
rowGroupProps:
|
|
17
|
+
rowGroupProps: DOMAttributes
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2022 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {announce} from '@react-aria/live-announcer';
|
|
14
|
+
import {Collection, Key, Node, Selection} from '@react-types/shared';
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
import intlMessages from '../intl/*.json';
|
|
17
|
+
import {SelectionManager} from '@react-stately/selection';
|
|
18
|
+
import {useLocalizedStringFormatter} from '@react-aria/i18n';
|
|
19
|
+
import {useRef} from 'react';
|
|
20
|
+
import {useUpdateEffect} from '@react-aria/utils';
|
|
21
|
+
|
|
22
|
+
export interface GridSelectionAnnouncementProps {
|
|
23
|
+
/**
|
|
24
|
+
* A function that returns the text that should be announced by assistive technology when a row is added or removed from selection.
|
|
25
|
+
* @default (key) => state.collection.getItem(key)?.textValue
|
|
26
|
+
*/
|
|
27
|
+
getRowText?: (key: Key) => string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface GridSelectionState<T> {
|
|
31
|
+
/** A collection of items in the grid. */
|
|
32
|
+
collection: Collection<Node<T>>,
|
|
33
|
+
/** A set of items that are disabled. */
|
|
34
|
+
disabledKeys: Set<Key>,
|
|
35
|
+
/** A selection manager to read and update multiple selection state. */
|
|
36
|
+
selectionManager: SelectionManager
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function useGridSelectionAnnouncement<T>(props: GridSelectionAnnouncementProps, state: GridSelectionState<T>) {
|
|
40
|
+
let {
|
|
41
|
+
getRowText = (key) => state.collection.getTextValue?.(key) ?? state.collection.getItem(key)?.textValue
|
|
42
|
+
} = props;
|
|
43
|
+
let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/grid');
|
|
44
|
+
|
|
45
|
+
// Many screen readers do not announce when items in a grid are selected/deselected.
|
|
46
|
+
// We do this using an ARIA live region.
|
|
47
|
+
let selection = state.selectionManager.rawSelection;
|
|
48
|
+
let lastSelection = useRef(selection);
|
|
49
|
+
useUpdateEffect(() => {
|
|
50
|
+
if (!state.selectionManager.isFocused) {
|
|
51
|
+
lastSelection.current = selection;
|
|
52
|
+
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let addedKeys = diffSelection(selection, lastSelection.current);
|
|
57
|
+
let removedKeys = diffSelection(lastSelection.current, selection);
|
|
58
|
+
|
|
59
|
+
// If adding or removing a single row from the selection, announce the name of that item.
|
|
60
|
+
let isReplace = state.selectionManager.selectionBehavior === 'replace';
|
|
61
|
+
let messages = [];
|
|
62
|
+
|
|
63
|
+
if ((state.selectionManager.selectedKeys.size === 1 && isReplace)) {
|
|
64
|
+
if (state.collection.getItem(state.selectionManager.selectedKeys.keys().next().value)) {
|
|
65
|
+
let currentSelectionText = getRowText(state.selectionManager.selectedKeys.keys().next().value);
|
|
66
|
+
if (currentSelectionText) {
|
|
67
|
+
messages.push(stringFormatter.format('selectedItem', {item: currentSelectionText}));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else if (addedKeys.size === 1 && removedKeys.size === 0) {
|
|
71
|
+
let addedText = getRowText(addedKeys.keys().next().value);
|
|
72
|
+
if (addedText) {
|
|
73
|
+
messages.push(stringFormatter.format('selectedItem', {item: addedText}));
|
|
74
|
+
}
|
|
75
|
+
} else if (removedKeys.size === 1 && addedKeys.size === 0) {
|
|
76
|
+
if (state.collection.getItem(removedKeys.keys().next().value)) {
|
|
77
|
+
let removedText = getRowText(removedKeys.keys().next().value);
|
|
78
|
+
if (removedText) {
|
|
79
|
+
messages.push(stringFormatter.format('deselectedItem', {item: removedText}));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Announce how many items are selected, except when selecting the first item.
|
|
85
|
+
if (state.selectionManager.selectionMode === 'multiple') {
|
|
86
|
+
if (messages.length === 0 || selection === 'all' || selection.size > 1 || lastSelection.current === 'all' || lastSelection.current?.size > 1) {
|
|
87
|
+
messages.push(selection === 'all'
|
|
88
|
+
? stringFormatter.format('selectedAll')
|
|
89
|
+
: stringFormatter.format('selectedCount', {count: selection.size})
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (messages.length > 0) {
|
|
95
|
+
announce(messages.join(' '));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
lastSelection.current = selection;
|
|
99
|
+
}, [selection]);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function diffSelection(a: Selection, b: Selection): Set<Key> {
|
|
103
|
+
let res = new Set<Key>();
|
|
104
|
+
if (a === 'all' || b === 'all') {
|
|
105
|
+
return res;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (let key of a.keys()) {
|
|
109
|
+
if (!b.has(key)) {
|
|
110
|
+
res.add(key);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return res;
|
|
115
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {AriaCheckboxProps} from '@react-types/checkbox';
|
|
2
|
+
import {GridCollection} from '@react-types/grid';
|
|
3
|
+
import {GridState} from '@react-stately/grid';
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
import intlMessages from '../intl/*.json';
|
|
6
|
+
import {Key} from '@react-types/shared';
|
|
7
|
+
import {useId} from '@react-aria/utils';
|
|
8
|
+
import {useLocalizedStringFormatter} from '@react-aria/i18n';
|
|
9
|
+
|
|
10
|
+
export interface AriaGridSelectionCheckboxProps {
|
|
11
|
+
/** A unique key for the checkbox. */
|
|
12
|
+
key: Key
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface GridSelectionCheckboxAria {
|
|
16
|
+
/** Props for the row selection checkbox element. */
|
|
17
|
+
checkboxProps: AriaCheckboxProps
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Provides the behavior and accessibility implementation for a selection checkbox in a grid.
|
|
23
|
+
* @param props - Props for the selection checkbox.
|
|
24
|
+
* @param state - State of the grid, as returned by `useGridState`.
|
|
25
|
+
*/
|
|
26
|
+
export function useGridSelectionCheckbox<T, C extends GridCollection<T>>(props: AriaGridSelectionCheckboxProps, state: GridState<T, C>): GridSelectionCheckboxAria {
|
|
27
|
+
let {key} = props;
|
|
28
|
+
|
|
29
|
+
let manager = state.selectionManager;
|
|
30
|
+
let checkboxId = useId();
|
|
31
|
+
let isDisabled = !state.selectionManager.canSelectItem(key);
|
|
32
|
+
let isSelected = state.selectionManager.isSelected(key);
|
|
33
|
+
|
|
34
|
+
// Checkbox should always toggle selection, regardless of selectionBehavior.
|
|
35
|
+
let onChange = () => manager.toggleSelection(key);
|
|
36
|
+
|
|
37
|
+
const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/grid');
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
checkboxProps: {
|
|
41
|
+
id: checkboxId,
|
|
42
|
+
'aria-label': stringFormatter.format('select'),
|
|
43
|
+
isSelected,
|
|
44
|
+
isDisabled,
|
|
45
|
+
onChange
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {AriaLabelingProps} from '@react-types/shared';
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
import intlMessages from '../intl/*.json';
|
|
16
|
+
import {MultipleSelectionManager} from '@react-stately/selection';
|
|
17
|
+
import {useDescription} from '@react-aria/utils';
|
|
18
|
+
import {useInteractionModality} from '@react-aria/interactions';
|
|
19
|
+
import {useLocalizedStringFormatter} from '@react-aria/i18n';
|
|
20
|
+
import {useMemo} from 'react';
|
|
21
|
+
|
|
22
|
+
export interface HighlightSelectionDescriptionProps {
|
|
23
|
+
selectionManager: MultipleSelectionManager,
|
|
24
|
+
hasItemActions?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Computes the description for a grid selectable collection.
|
|
29
|
+
* @param props
|
|
30
|
+
*/
|
|
31
|
+
export function useHighlightSelectionDescription(props: HighlightSelectionDescriptionProps): AriaLabelingProps {
|
|
32
|
+
let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/grid');
|
|
33
|
+
let modality = useInteractionModality();
|
|
34
|
+
// null is the default if the user hasn't interacted with the table at all yet or the rest of the page
|
|
35
|
+
let shouldLongPress = (modality === 'pointer' || modality === 'virtual' || modality == null)
|
|
36
|
+
&& typeof window !== 'undefined' && 'ontouchstart' in window;
|
|
37
|
+
|
|
38
|
+
let interactionDescription = useMemo(() => {
|
|
39
|
+
let selectionMode = props.selectionManager.selectionMode;
|
|
40
|
+
let selectionBehavior = props.selectionManager.selectionBehavior;
|
|
41
|
+
|
|
42
|
+
let message = undefined;
|
|
43
|
+
if (shouldLongPress) {
|
|
44
|
+
message = stringFormatter.format('longPressToSelect');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return selectionBehavior === 'replace' && selectionMode !== 'none' && props.hasItemActions ? message : undefined;
|
|
48
|
+
}, [props.selectionManager.selectionMode, props.selectionManager.selectionBehavior, props.hasItemActions, stringFormatter, shouldLongPress]);
|
|
49
|
+
|
|
50
|
+
let descriptionProps = useDescription(interactionDescription);
|
|
51
|
+
return descriptionProps;
|
|
52
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -12,7 +12,17 @@
|
|
|
12
12
|
|
|
13
13
|
import type {GridCollection} from '@react-types/grid';
|
|
14
14
|
import type {GridState} from '@react-stately/grid';
|
|
15
|
-
import type {KeyboardDelegate} from '@react-types/shared';
|
|
15
|
+
import type {Key, KeyboardDelegate} from '@react-types/shared';
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
interface GridMapShared {
|
|
18
|
+
keyboardDelegate: KeyboardDelegate,
|
|
19
|
+
actions: {
|
|
20
|
+
onRowAction: (key: Key) => void,
|
|
21
|
+
onCellAction: (key: Key) => void
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Used to share:
|
|
26
|
+
// keyboard delegate between useGrid and useGridCell
|
|
27
|
+
// onRowAction/onCellAction across hooks
|
|
28
|
+
export const gridMap = new WeakMap<GridState<unknown, GridCollection<unknown>>, GridMapShared>();
|