@react-aria/grid 3.1.1-nightly.3022 → 3.2.0
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/main.js +188 -89
- package/dist/main.js.map +1 -1
- package/dist/module.js +182 -85
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +14 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +14 -13
- package/src/useGrid.ts +39 -15
- package/src/useGridCell.ts +8 -4
- package/src/useGridRow.ts +8 -2
- package/src/useHighlightSelectionDescription.ts +51 -0
- package/src/utils.ts +13 -2
package/src/useGrid.ts
CHANGED
|
@@ -15,12 +15,13 @@ import {AriaLabelingProps, DOMProps, KeyboardDelegate, Selection} from '@react-t
|
|
|
15
15
|
import {filterDOMProps, mergeProps, useId, useUpdateEffect} from '@react-aria/utils';
|
|
16
16
|
import {GridCollection} from '@react-types/grid';
|
|
17
17
|
import {GridKeyboardDelegate} from './GridKeyboardDelegate';
|
|
18
|
-
import {
|
|
18
|
+
import {gridMap} from './utils';
|
|
19
19
|
import {GridState} from '@react-stately/grid';
|
|
20
20
|
import {HTMLAttributes, Key, RefObject, useMemo, useRef} from 'react';
|
|
21
21
|
// @ts-ignore
|
|
22
22
|
import intlMessages from '../intl/*.json';
|
|
23
23
|
import {useCollator, useLocale, useMessageFormatter} from '@react-aria/i18n';
|
|
24
|
+
import {useHighlightSelectionDescription} from './useHighlightSelectionDescription';
|
|
24
25
|
import {useSelectableCollection} from '@react-aria/selection';
|
|
25
26
|
|
|
26
27
|
export interface GridProps extends DOMProps, AriaLabelingProps {
|
|
@@ -44,7 +45,11 @@ export interface GridProps extends DOMProps, AriaLabelingProps {
|
|
|
44
45
|
/**
|
|
45
46
|
* The ref attached to the scrollable body. Used to provided automatic scrolling on item focus for non-virtualized grids.
|
|
46
47
|
*/
|
|
47
|
-
scrollRef?: RefObject<HTMLElement
|
|
48
|
+
scrollRef?: RefObject<HTMLElement>,
|
|
49
|
+
/** Handler that is called when a user performs an action on the row. */
|
|
50
|
+
onRowAction?: (key: Key) => void,
|
|
51
|
+
/** Handler that is called when a user performs an action on the cell. */
|
|
52
|
+
onCellAction?: (key: Key) => void
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
export interface GridAria {
|
|
@@ -65,7 +70,9 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
|
|
|
65
70
|
keyboardDelegate,
|
|
66
71
|
focusMode,
|
|
67
72
|
getRowText = (key) => state.collection.getItem(key)?.textValue,
|
|
68
|
-
scrollRef
|
|
73
|
+
scrollRef,
|
|
74
|
+
onRowAction,
|
|
75
|
+
onCellAction
|
|
69
76
|
} = props;
|
|
70
77
|
let formatMessage = useMessageFormatter(intlMessages);
|
|
71
78
|
|
|
@@ -85,6 +92,7 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
|
|
|
85
92
|
collator,
|
|
86
93
|
focusMode
|
|
87
94
|
}), [keyboardDelegate, state.collection, state.disabledKeys, ref, direction, collator, focusMode]);
|
|
95
|
+
|
|
88
96
|
let {collectionProps} = useSelectableCollection({
|
|
89
97
|
ref,
|
|
90
98
|
selectionManager: state.selectionManager,
|
|
@@ -94,16 +102,25 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
|
|
|
94
102
|
});
|
|
95
103
|
|
|
96
104
|
let id = useId();
|
|
97
|
-
|
|
105
|
+
gridMap.set(state, {keyboardDelegate: delegate, actions: {onRowAction, onCellAction}});
|
|
98
106
|
|
|
99
|
-
let
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
id,
|
|
103
|
-
'aria-multiselectable': state.selectionManager.selectionMode === 'multiple' ? 'true' : undefined,
|
|
104
|
-
...collectionProps
|
|
107
|
+
let descriptionProps = useHighlightSelectionDescription({
|
|
108
|
+
selectionManager: state.selectionManager,
|
|
109
|
+
hasItemActions: !!(onRowAction || onCellAction)
|
|
105
110
|
});
|
|
106
111
|
|
|
112
|
+
let domProps = filterDOMProps(props, {labelable: true});
|
|
113
|
+
let gridProps: HTMLAttributes<HTMLElement> = mergeProps(
|
|
114
|
+
domProps,
|
|
115
|
+
{
|
|
116
|
+
role: 'grid',
|
|
117
|
+
id,
|
|
118
|
+
'aria-multiselectable': state.selectionManager.selectionMode === 'multiple' ? 'true' : undefined
|
|
119
|
+
},
|
|
120
|
+
collectionProps,
|
|
121
|
+
descriptionProps
|
|
122
|
+
);
|
|
123
|
+
|
|
107
124
|
if (isVirtualized) {
|
|
108
125
|
gridProps['aria-rowcount'] = state.collection.size;
|
|
109
126
|
gridProps['aria-colcount'] = state.collection.columnCount;
|
|
@@ -114,9 +131,7 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
|
|
|
114
131
|
let selection = state.selectionManager.rawSelection;
|
|
115
132
|
let lastSelection = useRef(selection);
|
|
116
133
|
useUpdateEffect(() => {
|
|
117
|
-
|
|
118
|
-
// every time the user presses the arrow keys.
|
|
119
|
-
if (!state.selectionManager.isFocused || state.selectionManager.selectionBehavior === 'replace') {
|
|
134
|
+
if (!state.selectionManager.isFocused) {
|
|
120
135
|
lastSelection.current = selection;
|
|
121
136
|
|
|
122
137
|
return;
|
|
@@ -126,8 +141,17 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
|
|
|
126
141
|
let removedKeys = diffSelection(lastSelection.current, selection);
|
|
127
142
|
|
|
128
143
|
// If adding or removing a single row from the selection, announce the name of that item.
|
|
144
|
+
let isReplace = state.selectionManager.selectionBehavior === 'replace';
|
|
129
145
|
let messages = [];
|
|
130
|
-
|
|
146
|
+
|
|
147
|
+
if ((state.selectionManager.selectedKeys.size === 1 && isReplace)) {
|
|
148
|
+
if (state.collection.getItem(state.selectionManager.selectedKeys.keys().next().value)) {
|
|
149
|
+
let currentSelectionText = getRowText(state.selectionManager.selectedKeys.keys().next().value);
|
|
150
|
+
if (currentSelectionText) {
|
|
151
|
+
messages.push(formatMessage('selectedItem', {item: currentSelectionText}));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else if (addedKeys.size === 1 && removedKeys.size === 0) {
|
|
131
155
|
let addedText = getRowText(addedKeys.keys().next().value);
|
|
132
156
|
if (addedText) {
|
|
133
157
|
messages.push(formatMessage('selectedItem', {item: addedText}));
|
|
@@ -143,7 +167,7 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
|
|
|
143
167
|
|
|
144
168
|
// Announce how many items are selected, except when selecting the first item.
|
|
145
169
|
if (state.selectionManager.selectionMode === 'multiple') {
|
|
146
|
-
if (messages.length === 0 || selection === 'all' || selection.size > 1 || lastSelection.current === 'all' || lastSelection.current
|
|
170
|
+
if (messages.length === 0 || selection === 'all' || selection.size > 1 || lastSelection.current === 'all' || lastSelection.current?.size > 1) {
|
|
147
171
|
messages.push(selection === 'all'
|
|
148
172
|
? formatMessage('selectedAll')
|
|
149
173
|
: formatMessage('selectedCount', {count: selection.size})
|
package/src/useGridCell.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus';
|
|
14
14
|
import {GridCollection} from '@react-types/grid';
|
|
15
|
-
import {
|
|
15
|
+
import {gridMap} from './utils';
|
|
16
16
|
import {GridState} from '@react-stately/grid';
|
|
17
17
|
import {HTMLAttributes, KeyboardEvent as ReactKeyboardEvent, RefObject} from 'react';
|
|
18
18
|
import {isFocusVisible} from '@react-aria/interactions';
|
|
@@ -30,7 +30,11 @@ interface GridCellProps {
|
|
|
30
30
|
focusMode?: 'child' | 'cell',
|
|
31
31
|
/** Whether selection should occur on press up instead of press down. */
|
|
32
32
|
shouldSelectOnPressUp?: boolean,
|
|
33
|
-
/**
|
|
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
|
+
**/
|
|
34
38
|
onAction?: () => void
|
|
35
39
|
}
|
|
36
40
|
|
|
@@ -56,7 +60,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
|
|
|
56
60
|
} = props;
|
|
57
61
|
|
|
58
62
|
let {direction} = useLocale();
|
|
59
|
-
let keyboardDelegate =
|
|
63
|
+
let {keyboardDelegate, actions: {onCellAction}} = gridMap.get(state);
|
|
60
64
|
|
|
61
65
|
// Handles focusing the cell. If there is a focusable child,
|
|
62
66
|
// it is focused, otherwise the cell itself is focused.
|
|
@@ -84,7 +88,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
|
|
|
84
88
|
isVirtualized,
|
|
85
89
|
focus,
|
|
86
90
|
shouldSelectOnPressUp,
|
|
87
|
-
onAction
|
|
91
|
+
onAction: onCellAction ? () => onCellAction(node.key) : onAction
|
|
88
92
|
});
|
|
89
93
|
|
|
90
94
|
let onKeyDown = (e: ReactKeyboardEvent) => {
|
package/src/useGridRow.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import {GridCollection} from '@react-types/grid';
|
|
14
|
+
import {gridMap} from './utils';
|
|
14
15
|
import {GridState} from '@react-stately/grid';
|
|
15
16
|
import {HTMLAttributes, RefObject} from 'react';
|
|
16
17
|
import {Node} from '@react-types/shared';
|
|
@@ -23,7 +24,11 @@ export interface GridRowProps<T> {
|
|
|
23
24
|
isVirtualized?: boolean,
|
|
24
25
|
/** Whether selection should occur on press up instead of press down. */
|
|
25
26
|
shouldSelectOnPressUp?: boolean,
|
|
26
|
-
/**
|
|
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
|
+
**/
|
|
27
32
|
onAction?: () => void
|
|
28
33
|
}
|
|
29
34
|
|
|
@@ -47,13 +52,14 @@ export function useGridRow<T, C extends GridCollection<T>, S extends GridState<T
|
|
|
47
52
|
onAction
|
|
48
53
|
} = props;
|
|
49
54
|
|
|
55
|
+
let {actions: {onRowAction}} = gridMap.get(state);
|
|
50
56
|
let {itemProps, isPressed} = useSelectableItem({
|
|
51
57
|
selectionManager: state.selectionManager,
|
|
52
58
|
key: node.key,
|
|
53
59
|
ref,
|
|
54
60
|
isVirtualized,
|
|
55
61
|
shouldSelectOnPressUp,
|
|
56
|
-
onAction
|
|
62
|
+
onAction: onRowAction ? () => onRowAction(node.key) : onAction
|
|
57
63
|
});
|
|
58
64
|
|
|
59
65
|
let isSelected = state.selectionManager.isSelected(node.key);
|
|
@@ -0,0 +1,51 @@
|
|
|
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 {useMemo} from 'react';
|
|
20
|
+
import {useMessageFormatter} from '@react-aria/i18n';
|
|
21
|
+
|
|
22
|
+
interface UseHighlightSelectionDescriptionProps {
|
|
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: UseHighlightSelectionDescriptionProps): AriaLabelingProps {
|
|
32
|
+
let formatMessage = useMessageFormatter(intlMessages);
|
|
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) && 'ontouchstart' in window;
|
|
36
|
+
|
|
37
|
+
let interactionDescription = useMemo(() => {
|
|
38
|
+
let selectionMode = props.selectionManager.selectionMode;
|
|
39
|
+
let selectionBehavior = props.selectionManager.selectionBehavior;
|
|
40
|
+
|
|
41
|
+
let message = undefined;
|
|
42
|
+
if (shouldLongPress) {
|
|
43
|
+
message = formatMessage('longPressToSelect');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return selectionBehavior === 'replace' && selectionMode !== 'none' && props.hasItemActions ? message : undefined;
|
|
47
|
+
}, [props.selectionManager.selectionMode, props.selectionManager.selectionBehavior, props.hasItemActions, formatMessage, shouldLongPress]);
|
|
48
|
+
|
|
49
|
+
let descriptionProps = useDescription(interactionDescription);
|
|
50
|
+
return descriptionProps;
|
|
51
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -12,7 +12,18 @@
|
|
|
12
12
|
|
|
13
13
|
import type {GridCollection} from '@react-types/grid';
|
|
14
14
|
import type {GridState} from '@react-stately/grid';
|
|
15
|
+
import {Key} from 'react';
|
|
15
16
|
import type {KeyboardDelegate} from '@react-types/shared';
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
interface GridMapShared {
|
|
19
|
+
keyboardDelegate: KeyboardDelegate,
|
|
20
|
+
actions: {
|
|
21
|
+
onRowAction: (key: Key) => void,
|
|
22
|
+
onCellAction: (key: Key) => void
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Used to share:
|
|
27
|
+
// keyboard delegate between useGrid and useGridCell
|
|
28
|
+
// onRowAction/onCellAction across hooks
|
|
29
|
+
export const gridMap = new WeakMap<GridState<unknown, GridCollection<unknown>>, GridMapShared>();
|