@react-aria/gridlist 3.14.4 → 3.15.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.
Files changed (40) hide show
  1. package/dist/import.mjs +2 -8
  2. package/dist/main.js +5 -11
  3. package/dist/main.js.map +1 -1
  4. package/dist/module.js +2 -8
  5. package/dist/module.js.map +1 -1
  6. package/dist/types/src/index.d.ts +3 -0
  7. package/package.json +15 -20
  8. package/src/index.ts +3 -7
  9. package/dist/types.d.ts +0 -135
  10. package/dist/types.d.ts.map +0 -1
  11. package/dist/useGridList.main.js +0 -83
  12. package/dist/useGridList.main.js.map +0 -1
  13. package/dist/useGridList.mjs +0 -78
  14. package/dist/useGridList.module.js +0 -78
  15. package/dist/useGridList.module.js.map +0 -1
  16. package/dist/useGridListItem.main.js +0 -305
  17. package/dist/useGridListItem.main.js.map +0 -1
  18. package/dist/useGridListItem.mjs +0 -300
  19. package/dist/useGridListItem.module.js +0 -300
  20. package/dist/useGridListItem.module.js.map +0 -1
  21. package/dist/useGridListSection.main.js +0 -43
  22. package/dist/useGridListSection.main.js.map +0 -1
  23. package/dist/useGridListSection.mjs +0 -38
  24. package/dist/useGridListSection.module.js +0 -38
  25. package/dist/useGridListSection.module.js.map +0 -1
  26. package/dist/useGridListSelectionCheckbox.main.js +0 -34
  27. package/dist/useGridListSelectionCheckbox.main.js.map +0 -1
  28. package/dist/useGridListSelectionCheckbox.mjs +0 -29
  29. package/dist/useGridListSelectionCheckbox.module.js +0 -29
  30. package/dist/useGridListSelectionCheckbox.module.js.map +0 -1
  31. package/dist/utils.main.js +0 -31
  32. package/dist/utils.main.js.map +0 -1
  33. package/dist/utils.mjs +0 -25
  34. package/dist/utils.module.js +0 -25
  35. package/dist/utils.module.js.map +0 -1
  36. package/src/useGridList.ts +0 -182
  37. package/src/useGridListItem.ts +0 -346
  38. package/src/useGridListSection.ts +0 -60
  39. package/src/useGridListSelectionCheckbox.ts +0 -32
  40. package/src/utils.ts +0 -43
package/dist/utils.mjs DELETED
@@ -1,25 +0,0 @@
1
- /*
2
- * Copyright 2020 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
- */ const $ce9b18daab526bbd$export$5b9bb410392e3991 = new WeakMap();
12
- function $ce9b18daab526bbd$export$f45c25170b9a99c2(state, key) {
13
- var _listMap_get;
14
- let { id: id } = (_listMap_get = $ce9b18daab526bbd$export$5b9bb410392e3991.get(state)) !== null && _listMap_get !== void 0 ? _listMap_get : {};
15
- if (!id) throw new Error('Unknown list');
16
- return `${id}-${$ce9b18daab526bbd$export$e0c709538cb8ae18(key)}`;
17
- }
18
- function $ce9b18daab526bbd$export$e0c709538cb8ae18(key) {
19
- if (typeof key === 'string') return key.replace(/\s*/g, '');
20
- return '' + key;
21
- }
22
-
23
-
24
- export {$ce9b18daab526bbd$export$5b9bb410392e3991 as listMap, $ce9b18daab526bbd$export$f45c25170b9a99c2 as getRowId, $ce9b18daab526bbd$export$e0c709538cb8ae18 as normalizeKey};
25
- //# sourceMappingURL=utils.module.js.map
@@ -1,25 +0,0 @@
1
- /*
2
- * Copyright 2020 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
- */ const $ce9b18daab526bbd$export$5b9bb410392e3991 = new WeakMap();
12
- function $ce9b18daab526bbd$export$f45c25170b9a99c2(state, key) {
13
- var _listMap_get;
14
- let { id: id } = (_listMap_get = $ce9b18daab526bbd$export$5b9bb410392e3991.get(state)) !== null && _listMap_get !== void 0 ? _listMap_get : {};
15
- if (!id) throw new Error('Unknown list');
16
- return `${id}-${$ce9b18daab526bbd$export$e0c709538cb8ae18(key)}`;
17
- }
18
- function $ce9b18daab526bbd$export$e0c709538cb8ae18(key) {
19
- if (typeof key === 'string') return key.replace(/\s*/g, '');
20
- return '' + key;
21
- }
22
-
23
-
24
- export {$ce9b18daab526bbd$export$5b9bb410392e3991 as listMap, $ce9b18daab526bbd$export$f45c25170b9a99c2 as getRowId, $ce9b18daab526bbd$export$e0c709538cb8ae18 as normalizeKey};
25
- //# sourceMappingURL=utils.module.js.map
@@ -1 +0,0 @@
1
- {"mappings":"AAAA;;;;;;;;;;CAUC,GAeM,MAAM,4CAAsD,IAAI;AAEhE,SAAS,0CAAY,KAAmB,EAAE,GAAQ;QAC5C;IAAX,IAAI,MAAC,EAAE,EAAC,GAAG,CAAA,eAAA,0CAAQ,GAAG,CAAC,oBAAZ,0BAAA,eAAsB,CAAC;IAClC,IAAI,CAAC,IACH,MAAM,IAAI,MAAM;IAGlB,OAAO,GAAG,GAAG,CAAC,EAAE,0CAAa,MAAM;AACrC;AAEO,SAAS,0CAAa,GAAQ;IACnC,IAAI,OAAO,QAAQ,UACjB,OAAO,IAAI,OAAO,CAAC,QAAQ;IAG7B,OAAO,KAAK;AACd","sources":["packages/@react-aria/gridlist/src/utils.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {Key} from '@react-types/shared';\nimport type {ListState} from '@react-stately/list';\n\ninterface ListMapShared {\n id: string,\n onAction?: (key: Key) => void,\n linkBehavior?: 'action' | 'selection' | 'override',\n keyboardNavigationBehavior: 'arrow' | 'tab',\n shouldSelectOnPressUp?: boolean\n}\n\n// Used to share:\n// id of the list and onAction between useList, useListItem, and useListSelectionCheckbox\nexport const listMap: WeakMap<ListState<unknown>, ListMapShared> = new WeakMap<ListState<unknown>, ListMapShared>();\n\nexport function getRowId<T>(state: ListState<T>, key: Key): string {\n let {id} = listMap.get(state) ?? {};\n if (!id) {\n throw new Error('Unknown list');\n }\n\n return `${id}-${normalizeKey(key)}`;\n}\n\nexport function normalizeKey(key: Key): string {\n if (typeof key === 'string') {\n return key.replace(/\\s*/g, '');\n }\n\n return '' + key;\n}\n"],"names":[],"version":3,"file":"utils.module.js.map"}
@@ -1,182 +0,0 @@
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 {
14
- AriaLabelingProps,
15
- CollectionBase,
16
- DisabledBehavior,
17
- DOMAttributes,
18
- DOMProps,
19
- FocusStrategy,
20
- Key,
21
- KeyboardDelegate,
22
- LayoutDelegate,
23
- MultipleSelection,
24
- RefObject
25
- } from '@react-types/shared';
26
- import {filterDOMProps, mergeProps, useId} from '@react-aria/utils';
27
- import {listMap} from './utils';
28
- import {ListState} from '@react-stately/list';
29
- import {useGridSelectionAnnouncement, useHighlightSelectionDescription} from '@react-aria/grid';
30
- import {useHasTabbableChild} from '@react-aria/focus';
31
- import {useSelectableList} from '@react-aria/selection';
32
-
33
- export interface GridListProps<T> extends CollectionBase<T>, MultipleSelection {
34
- /** Whether to auto focus the gridlist or an option. */
35
- autoFocus?: boolean | FocusStrategy,
36
- /**
37
- * Handler that is called when a user performs an action on an item. The exact user event depends on
38
- * the collection's `selectionBehavior` prop and the interaction modality.
39
- */
40
- onAction?: (key: Key) => void,
41
- /**
42
- * Whether `disabledKeys` applies to all interactions, or only selection.
43
- * @default "all"
44
- */
45
- disabledBehavior?: DisabledBehavior,
46
- /** Whether selection should occur on press up instead of press down. */
47
- shouldSelectOnPressUp?: boolean
48
- }
49
-
50
- export interface AriaGridListProps<T> extends GridListProps<T>, DOMProps, AriaLabelingProps {
51
- /**
52
- * Whether keyboard navigation to focusable elements within grid list items is
53
- * via the left/right arrow keys or the tab key.
54
- * @default 'arrow'
55
- */
56
- keyboardNavigationBehavior?: 'arrow' | 'tab',
57
- /**
58
- * Whether pressing the escape key should clear selection in the grid list or not.
59
- *
60
- * Most experiences should not modify this option as it eliminates a keyboard user's ability to
61
- * easily clear selection. Only use if the escape key is being handled externally or should not
62
- * trigger selection clearing contextually.
63
- * @default 'clearSelection'
64
- */
65
- escapeKeyBehavior?: 'clearSelection' | 'none'
66
- }
67
-
68
- export interface AriaGridListOptions<T> extends Omit<AriaGridListProps<T>, 'children'> {
69
- /** Whether the list uses virtual scrolling. */
70
- isVirtualized?: boolean,
71
- /**
72
- * Whether typeahead navigation is disabled.
73
- * @default false
74
- */
75
- disallowTypeAhead?: boolean,
76
- /**
77
- * An optional keyboard delegate implementation for type to select,
78
- * to override the default.
79
- */
80
- keyboardDelegate?: KeyboardDelegate,
81
- /**
82
- * A delegate object that provides layout information for items in the collection.
83
- * By default this uses the DOM, but this can be overridden to implement things like
84
- * virtualized scrolling.
85
- */
86
- layoutDelegate?: LayoutDelegate,
87
- /**
88
- * Whether focus should wrap around when the end/start is reached.
89
- * @default false
90
- */
91
- shouldFocusWrap?: boolean,
92
- /**
93
- * The behavior of links in the collection.
94
- * - 'action': link behaves like onAction.
95
- * - 'selection': link follows selection interactions (e.g. if URL drives selection).
96
- * - 'override': links override all other interactions (link items are not selectable).
97
- * @default 'action'
98
- */
99
- linkBehavior?: 'action' | 'selection' | 'override'
100
- }
101
-
102
- export interface GridListAria {
103
- /** Props for the grid element. */
104
- gridProps: DOMAttributes
105
- }
106
-
107
- /**
108
- * Provides the behavior and accessibility implementation for a list component with interactive children.
109
- * A grid list displays data in a single column and enables a user to navigate its contents via directional navigation keys.
110
- * @param props - Props for the list.
111
- * @param state - State for the list, as returned by `useListState`.
112
- * @param ref - The ref attached to the list element.
113
- */
114
- export function useGridList<T>(props: AriaGridListOptions<T>, state: ListState<T>, ref: RefObject<HTMLElement | null>): GridListAria {
115
- let {
116
- isVirtualized,
117
- keyboardDelegate,
118
- layoutDelegate,
119
- onAction,
120
- disallowTypeAhead,
121
- linkBehavior = 'action',
122
- keyboardNavigationBehavior = 'arrow',
123
- escapeKeyBehavior = 'clearSelection',
124
- shouldSelectOnPressUp
125
- } = props;
126
-
127
- if (!props['aria-label'] && !props['aria-labelledby']) {
128
- console.warn('An aria-label or aria-labelledby prop is required for accessibility.');
129
- }
130
-
131
- let {listProps} = useSelectableList({
132
- selectionManager: state.selectionManager,
133
- collection: state.collection,
134
- disabledKeys: state.disabledKeys,
135
- ref,
136
- keyboardDelegate,
137
- layoutDelegate,
138
- isVirtualized,
139
- selectOnFocus: state.selectionManager.selectionBehavior === 'replace',
140
- shouldFocusWrap: props.shouldFocusWrap,
141
- linkBehavior,
142
- disallowTypeAhead,
143
- autoFocus: props.autoFocus,
144
- escapeKeyBehavior
145
- });
146
-
147
- let id = useId(props.id);
148
- listMap.set(state, {id, onAction, linkBehavior, keyboardNavigationBehavior, shouldSelectOnPressUp});
149
-
150
- let descriptionProps = useHighlightSelectionDescription({
151
- selectionManager: state.selectionManager,
152
- hasItemActions: !!onAction
153
- });
154
-
155
- let hasTabbableChild = useHasTabbableChild(ref, {
156
- isDisabled: state.collection.size !== 0
157
- });
158
-
159
- let domProps = filterDOMProps(props, {labelable: true});
160
- let gridProps: DOMAttributes = mergeProps(
161
- domProps,
162
- {
163
- role: 'grid',
164
- id,
165
- 'aria-multiselectable': state.selectionManager.selectionMode === 'multiple' ? 'true' : undefined
166
- },
167
- // If collection is empty, make sure the grid is tabbable unless there is a child tabbable element.
168
- state.collection.size === 0 ? {tabIndex: hasTabbableChild ? -1 : 0} : listProps,
169
- descriptionProps
170
- );
171
-
172
- if (isVirtualized) {
173
- gridProps['aria-rowcount'] = state.collection.size;
174
- gridProps['aria-colcount'] = 1;
175
- }
176
-
177
- useGridSelectionAnnouncement({}, state);
178
-
179
- return {
180
- gridProps
181
- };
182
- }
@@ -1,346 +0,0 @@
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 {chain, getActiveElement, getEventTarget, getScrollParent, isFocusWithin, mergeProps, nodeContains, scrollIntoViewport, useSlotId, useSyntheticLinkProps} from '@react-aria/utils';
14
- import {Collection, DOMAttributes, FocusableElement, Key, RefObject, Node as RSNode} from '@react-types/shared';
15
- import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus';
16
- import {getRowId, listMap} from './utils';
17
- import {HTMLAttributes, KeyboardEvent as ReactKeyboardEvent, useRef} from 'react';
18
- import {isFocusVisible} from '@react-aria/interactions';
19
- import type {ListState} from '@react-stately/list';
20
- import {SelectableItemStates, useSelectableItem} from '@react-aria/selection';
21
- import type {TreeState} from '@react-stately/tree';
22
- import {useLocale} from '@react-aria/i18n';
23
-
24
- export interface AriaGridListItemOptions {
25
- /** An object representing the list item. Contains all the relevant information that makes up the list row. */
26
- node: RSNode<unknown>,
27
- /** Whether the list row is contained in a virtual scroller. */
28
- isVirtualized?: boolean,
29
- /** Whether selection should occur on press up instead of press down. */
30
- shouldSelectOnPressUp?: boolean,
31
- /** Whether this item has children, even if not loaded yet. */
32
- hasChildItems?: boolean
33
- }
34
-
35
- export interface GridListItemAria extends SelectableItemStates {
36
- /** Props for the list row element. */
37
- rowProps: DOMAttributes,
38
- /** Props for the grid cell element within the list row. */
39
- gridCellProps: DOMAttributes,
40
- /** Props for the list item description element, if any. */
41
- descriptionProps: DOMAttributes
42
- }
43
-
44
- const EXPANSION_KEYS = {
45
- 'expand': {
46
- ltr: 'ArrowRight',
47
- rtl: 'ArrowLeft'
48
- },
49
- 'collapse': {
50
- ltr: 'ArrowLeft',
51
- rtl: 'ArrowRight'
52
- }
53
- };
54
-
55
- /**
56
- * Provides the behavior and accessibility implementation for a row in a grid list.
57
- * @param props - Props for the row.
58
- * @param state - State of the parent list, as returned by `useListState`.
59
- * @param ref - The ref attached to the row element.
60
- */
61
- export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListState<T> | TreeState<T>, ref: RefObject<FocusableElement | null>): GridListItemAria {
62
- // Copied from useGridCell + some modifications to make it not so grid specific
63
- let {
64
- node,
65
- isVirtualized
66
- } = props;
67
-
68
- // let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/gridlist');
69
- let {direction} = useLocale();
70
- let {onAction, linkBehavior, keyboardNavigationBehavior, shouldSelectOnPressUp} = listMap.get(state)!;
71
- let descriptionId = useSlotId();
72
-
73
- // We need to track the key of the item at the time it was last focused so that we force
74
- // focus to go to the item when the DOM node is reused for a different item in a virtualizer.
75
- let keyWhenFocused = useRef<Key | null>(null);
76
- let focus = () => {
77
- // Don't shift focus to the row if the active element is a element within the row already
78
- // (e.g. clicking on a row button)
79
- if (
80
- ref.current !== null &&
81
- ((keyWhenFocused.current != null && node.key !== keyWhenFocused.current) ||
82
- !isFocusWithin(ref.current))
83
- ) {
84
- focusSafely(ref.current);
85
- }
86
- };
87
-
88
- let treeGridRowProps: HTMLAttributes<HTMLElement> = {};
89
- let hasChildRows = props.hasChildItems;
90
- let hasLink = state.selectionManager.isLink(node.key);
91
- if (node != null && 'expandedKeys' in state) {
92
- // TODO: ideally node.hasChildNodes would be a way to tell if a row has child nodes, but the row's contents make it so that value is always
93
- // true...
94
- let children = state.collection.getChildren?.(node.key);
95
- hasChildRows = hasChildRows || [...(children ?? [])].length > 1;
96
-
97
- if (onAction == null && !hasLink && state.selectionManager.selectionMode === 'none' && hasChildRows) {
98
- onAction = () => state.toggleKey(node.key);
99
- }
100
-
101
- let isExpanded = hasChildRows ? state.expandedKeys.has(node.key) : undefined;
102
- let setSize = 1;
103
- let index = node.index;
104
- if (node.level >= 0 && node?.parentKey != null) {
105
- let parent = state.collection.getItem(node.parentKey);
106
- if (parent) {
107
- // siblings must exist because our original node exists
108
- let siblings = getDirectChildren(parent, state.collection);
109
- setSize = [...siblings].filter(row => row.type === 'item').length;
110
- if (index > 0 && siblings[0].type !== 'item') {
111
- index -= 1; // subtract one for the parent item's content node
112
- }
113
- }
114
- } else {
115
- setSize = [...state.collection].filter(row => row.level === 0 && row.type === 'item').length;
116
- }
117
-
118
- treeGridRowProps = {
119
- 'aria-expanded': isExpanded,
120
- 'aria-level': node.level + 1,
121
- 'aria-posinset': index + 1,
122
- 'aria-setsize': setSize
123
- };
124
- }
125
-
126
- let {itemProps, ...itemStates} = useSelectableItem({
127
- selectionManager: state.selectionManager,
128
- key: node.key,
129
- ref,
130
- isVirtualized,
131
- shouldSelectOnPressUp: props.shouldSelectOnPressUp || shouldSelectOnPressUp,
132
- onAction: onAction || node.props?.onAction ? chain(node.props?.onAction, onAction ? () => onAction(node.key) : undefined) : undefined,
133
- focus,
134
- linkBehavior
135
- });
136
-
137
- let onKeyDownCapture = (e: ReactKeyboardEvent) => {
138
- let activeElement = getActiveElement();
139
- if (!nodeContains(e.currentTarget, getEventTarget(e) as Element) || !ref.current || !activeElement) {
140
- return;
141
- }
142
-
143
- let walker = getFocusableTreeWalker(ref.current);
144
- walker.currentNode = activeElement;
145
-
146
- if ('expandedKeys' in state && activeElement === ref.current) {
147
- if ((e.key === EXPANSION_KEYS['expand'][direction]) && state.selectionManager.focusedKey === node.key && hasChildRows && !state.expandedKeys.has(node.key)) {
148
- state.toggleKey(node.key);
149
- e.stopPropagation();
150
- return;
151
- } else if ((e.key === EXPANSION_KEYS['collapse'][direction]) && state.selectionManager.focusedKey === node.key && hasChildRows && state.expandedKeys.has(node.key)) {
152
- state.toggleKey(node.key);
153
- e.stopPropagation();
154
- return;
155
- }
156
- }
157
-
158
- switch (e.key) {
159
- case 'ArrowLeft': {
160
- if (keyboardNavigationBehavior === 'arrow') {
161
- // Find the next focusable element within the row.
162
- let focusable = direction === 'rtl'
163
- ? walker.nextNode() as FocusableElement
164
- : walker.previousNode() as FocusableElement;
165
-
166
- if (focusable) {
167
- e.preventDefault();
168
- e.stopPropagation();
169
- focusSafely(focusable);
170
- scrollIntoViewport(focusable, {containingElement: getScrollParent(ref.current)});
171
- } else {
172
- // If there is no next focusable child, then return focus back to the row
173
- e.preventDefault();
174
- e.stopPropagation();
175
- if (direction === 'rtl') {
176
- focusSafely(ref.current);
177
- scrollIntoViewport(ref.current, {containingElement: getScrollParent(ref.current)});
178
- } else {
179
- walker.currentNode = ref.current;
180
- let lastElement = last(walker);
181
- if (lastElement) {
182
- focusSafely(lastElement);
183
- scrollIntoViewport(lastElement, {containingElement: getScrollParent(ref.current)});
184
- }
185
- }
186
- }
187
- }
188
- break;
189
- }
190
- case 'ArrowRight': {
191
- if (keyboardNavigationBehavior === 'arrow') {
192
- let focusable = direction === 'rtl'
193
- ? walker.previousNode() as FocusableElement
194
- : walker.nextNode() as FocusableElement;
195
-
196
- if (focusable) {
197
- e.preventDefault();
198
- e.stopPropagation();
199
- focusSafely(focusable);
200
- scrollIntoViewport(focusable, {containingElement: getScrollParent(ref.current)});
201
- } else {
202
- e.preventDefault();
203
- e.stopPropagation();
204
- if (direction === 'ltr') {
205
- focusSafely(ref.current);
206
- scrollIntoViewport(ref.current, {containingElement: getScrollParent(ref.current)});
207
- } else {
208
- walker.currentNode = ref.current;
209
- let lastElement = last(walker);
210
- if (lastElement) {
211
- focusSafely(lastElement);
212
- scrollIntoViewport(lastElement, {containingElement: getScrollParent(ref.current)});
213
- }
214
- }
215
- }
216
- }
217
- break;
218
- }
219
- case 'ArrowUp':
220
- case 'ArrowDown':
221
- // Prevent this event from reaching row children, e.g. menu buttons. We want arrow keys to navigate
222
- // to the row above/below instead. We need to re-dispatch the event from a higher parent so it still
223
- // bubbles and gets handled by useSelectableCollection.
224
- if (!e.altKey && nodeContains(ref.current, getEventTarget(e) as Element)) {
225
- e.stopPropagation();
226
- e.preventDefault();
227
- ref.current.parentElement?.dispatchEvent(
228
- new KeyboardEvent(e.nativeEvent.type, e.nativeEvent)
229
- );
230
- }
231
- break;
232
- }
233
- };
234
-
235
- let onFocus = (e) => {
236
- keyWhenFocused.current = node.key;
237
- if (getEventTarget(e) !== ref.current) {
238
- // useSelectableItem only handles setting the focused key when
239
- // the focused element is the row itself. We also want to
240
- // set the focused key when a child element receives focus.
241
- // If focus is currently visible (e.g. the user is navigating with the keyboard),
242
- // then skip this. We want to restore focus to the previously focused row
243
- // in that case since the list should act like a single tab stop.
244
- if (!isFocusVisible()) {
245
- state.selectionManager.setFocusedKey(node.key);
246
- }
247
- return;
248
- }
249
- };
250
-
251
- let onKeyDown = (e) => {
252
- let activeElement = getActiveElement();
253
- if (!nodeContains(e.currentTarget, getEventTarget(e) as Element) || !ref.current || !activeElement) {
254
- return;
255
- }
256
-
257
- switch (e.key) {
258
- case 'Tab': {
259
- if (keyboardNavigationBehavior === 'tab') {
260
- // If there is another focusable element within this item, stop propagation so the tab key
261
- // is handled by the browser and not by useSelectableCollection (which would take us out of the list).
262
- let walker = getFocusableTreeWalker(ref.current, {tabbable: true});
263
- walker.currentNode = activeElement;
264
- let next = e.shiftKey ? walker.previousNode() : walker.nextNode();
265
-
266
- if (next) {
267
- e.stopPropagation();
268
- }
269
- }
270
- }
271
- }
272
- };
273
-
274
- let syntheticLinkProps = useSyntheticLinkProps(node.props);
275
- let linkProps = itemStates.hasAction ? syntheticLinkProps : {};
276
- // TODO: re-add when we get translations and fix this for iOS VO
277
- // let rowAnnouncement;
278
- // if (onAction) {
279
- // rowAnnouncement = stringFormatter.format('hasActionAnnouncement');
280
- // } else if (hasLink) {
281
- // rowAnnouncement = stringFormatter.format('hasLinkAnnouncement', {
282
- // link: node.props.href
283
- // });
284
- // }
285
-
286
- let rowProps: DOMAttributes = mergeProps(itemProps, linkProps, {
287
- role: 'row',
288
- onKeyDownCapture,
289
- onKeyDown,
290
- onFocus,
291
- // 'aria-label': [(node.textValue || undefined), rowAnnouncement].filter(Boolean).join(', '),
292
- 'aria-label': node['aria-label'] || node.textValue || undefined,
293
- 'aria-selected': state.selectionManager.canSelectItem(node.key) ? state.selectionManager.isSelected(node.key) : undefined,
294
- 'aria-disabled': state.selectionManager.isDisabled(node.key) || undefined,
295
- 'aria-labelledby': descriptionId && (node['aria-label'] || node.textValue) ? `${getRowId(state, node.key)} ${descriptionId}` : undefined,
296
- id: getRowId(state, node.key)
297
- });
298
-
299
- if (isVirtualized) {
300
- let {collection} = state;
301
- let nodes = [...collection];
302
- // TODO: refactor ListCollection to store an absolute index of a node's position?
303
- rowProps['aria-rowindex'] = nodes.find(node => node.type === 'section') ? [...collection.getKeys()].filter((key) => collection.getItem(key)?.type !== 'section').findIndex((key) => key === node.key) + 1 : node.index + 1;
304
- }
305
-
306
- let gridCellProps = {
307
- role: 'gridcell',
308
- 'aria-colindex': 1
309
- };
310
-
311
- // TODO: should isExpanded and hasChildRows be a item state that gets returned by the hook?
312
- return {
313
- rowProps: {...mergeProps(rowProps, treeGridRowProps)},
314
- gridCellProps,
315
- descriptionProps: {
316
- id: descriptionId
317
- },
318
- ...itemStates
319
- };
320
- }
321
-
322
- function last(walker: TreeWalker) {
323
- let next: FocusableElement | null = null;
324
- let last: FocusableElement | null = null;
325
- do {
326
- last = walker.lastChild() as FocusableElement | null;
327
- if (last) {
328
- next = last;
329
- }
330
- } while (last);
331
- return next;
332
- }
333
-
334
- function getDirectChildren<T>(parent: RSNode<T>, collection: Collection<RSNode<T>>) {
335
- // We can't assume that we can use firstChildKey because if a person builds a tree using hooks, they would not have access to that property (using type Node vs CollectionNode)
336
- // Instead, get all children and start at the first node (rather than just using firstChildKey) and only look at its siblings
337
- let children = collection.getChildren?.(parent.key);
338
- let childArray = children ? Array.from(children) : [];
339
- let node = childArray.length > 0 ? childArray[0] : null;
340
- let siblings: RSNode<T>[] = [];
341
- while (node) {
342
- siblings.push(node);
343
- node = node.nextKey != null ? collection.getItem(node.nextKey) : null;
344
- }
345
- return siblings;
346
- }
@@ -1,60 +0,0 @@
1
- /*
2
- * Copyright 2020 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 {DOMAttributes, RefObject} from '@react-types/shared';
14
- import type {ListState} from '@react-stately/list';
15
- import {useLabels, useSlotId} from '@react-aria/utils';
16
-
17
- export interface AriaGridListSectionProps {
18
- /** An accessibility label for the section. Required if `heading` is not present. */
19
- 'aria-label'?: string
20
- }
21
-
22
- export interface GridListSectionAria {
23
- /** Props for the wrapper list item. */
24
- rowProps: DOMAttributes,
25
-
26
- /** Props for the heading element, if any. */
27
- rowHeaderProps: DOMAttributes,
28
-
29
- /** Props for the grid's row group element. */
30
- rowGroupProps: DOMAttributes
31
- }
32
-
33
- /**
34
- * Provides the behavior and accessibility implementation for a section in a grid list.
35
- * See `useGridList` for more details about grid list.
36
- * @param props - Props for the section.
37
- */
38
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
39
- export function useGridListSection<T>(props: AriaGridListSectionProps, state: ListState<T>, ref: RefObject<HTMLElement | null>): GridListSectionAria {
40
- let {'aria-label': ariaLabel} = props;
41
- let headingId = useSlotId();
42
- let labelProps = useLabels({
43
- 'aria-label': ariaLabel,
44
- 'aria-labelledby': headingId
45
- });
46
-
47
- return {
48
- rowProps: {
49
- role: 'row'
50
- },
51
- rowHeaderProps: {
52
- id: headingId,
53
- role: 'rowheader'
54
- },
55
- rowGroupProps: {
56
- role: 'rowgroup',
57
- ...labelProps
58
- }
59
- };
60
- }
@@ -1,32 +0,0 @@
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 {AriaGridSelectionCheckboxProps, GridSelectionCheckboxAria, useGridSelectionCheckbox} from '@react-aria/grid';
14
- import {getRowId} from './utils';
15
- import type {ListState} from '@react-stately/list';
16
-
17
- /**
18
- * Provides the behavior and accessibility implementation for a selection checkbox in a grid list.
19
- * @param props - Props for the selection checkbox.
20
- * @param state - State of the list, as returned by `useListState`.
21
- */
22
- export function useGridListSelectionCheckbox<T>(props: AriaGridSelectionCheckboxProps, state: ListState<T>): GridSelectionCheckboxAria {
23
- let {key} = props;
24
- const {checkboxProps} = useGridSelectionCheckbox(props, state as any);
25
-
26
- return {
27
- checkboxProps: {
28
- ...checkboxProps,
29
- 'aria-labelledby': `${checkboxProps.id} ${getRowId(state, key)}`
30
- }
31
- };
32
- }