@octanejs/aria 0.0.46 → 0.0.47
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/README.md +7 -6
- package/UPSTREAM.md +58 -69
- package/package.json +11 -11
- package/src/calendar/useCalendarGrid.tsx +64 -62
- package/src/collections/Document.ts +3 -0
- package/src/color/useColorArea.tsx +95 -69
- package/src/color/useColorWheel.tsx +38 -37
- package/src/combobox/{useComboBox.ts → useComboBox.tsx} +239 -259
- package/src/components/ComboBox.ts +35 -2
- package/src/components/Dialog.ts +12 -1
- package/src/components/DropZone.tsx +3 -2
- package/src/components/FileTrigger.tsx +1 -0
- package/src/components/GridList.ts +11 -2
- package/src/components/ListBox.ts +1 -1
- package/src/components/Menu.ts +1 -0
- package/src/components/Meter.ts +3 -1
- package/src/components/Popover.ts +20 -8
- package/src/components/PreviewTrigger.tsx +101 -0
- package/src/components/ProgressBar.ts +10 -2
- package/src/components/Select.ts +1 -1
- package/src/components/Table.ts +26 -15
- package/src/components/TokenField.tsx +395 -0
- package/src/components/Tooltip.ts +4 -6
- package/src/components/Tree.ts +14 -7
- package/src/components/Virtualizer.tsx +30 -14
- package/src/components/index.ts +22 -0
- package/src/components/utils.ts +1 -1
- package/src/datepicker/useDateField.tsx +2 -11
- package/src/datepicker/useDatePickerGroup.tsx +44 -33
- package/src/datepicker/useDateSegment.tsx +31 -30
- package/src/dialog/useDialog.ts +8 -1
- package/src/dnd/DragManager.tsx +5 -1
- package/src/dnd/useDrag.tsx +2 -1
- package/src/focus/FocusScope.ts +2 -2
- package/src/grid/useGrid.ts +3 -0
- package/src/grid/{useGridCell.ts → useGridCell.tsx} +121 -64
- package/src/grid/utils.ts +1 -0
- package/src/gridlist/{useGridListItem.ts → useGridListItem.tsx} +152 -111
- package/src/index.ts +22 -0
- package/src/interactions/createEventHandler.ts +11 -8
- package/src/interactions/createKeyboardShortcutHandler.tsx +218 -0
- package/src/interactions/textSelection.ts +3 -3
- package/src/interactions/useContextMenu.tsx +125 -0
- package/src/interactions/useFocusVisible.ts +4 -4
- package/src/interactions/useKeyboard.ts +50 -2
- package/src/interactions/useLongPress.ts +33 -22
- package/src/interactions/useMove.ts +6 -6
- package/src/interactions/usePress.ts +8 -0
- package/src/interactions/utils.ts +10 -10
- package/src/intl/combobox/fi-FI.json +2 -2
- package/src/intl/previewtrigger/ar-AE.json +3 -0
- package/src/intl/previewtrigger/bg-BG.json +3 -0
- package/src/intl/previewtrigger/cs-CZ.json +3 -0
- package/src/intl/previewtrigger/da-DK.json +3 -0
- package/src/intl/previewtrigger/de-DE.json +3 -0
- package/src/intl/previewtrigger/el-GR.json +3 -0
- package/src/intl/previewtrigger/en-US.json +3 -0
- package/src/intl/previewtrigger/es-ES.json +3 -0
- package/src/intl/previewtrigger/et-EE.json +3 -0
- package/src/intl/previewtrigger/fi-FI.json +3 -0
- package/src/intl/previewtrigger/fr-FR.json +3 -0
- package/src/intl/previewtrigger/he-IL.json +3 -0
- package/src/intl/previewtrigger/hr-HR.json +3 -0
- package/src/intl/previewtrigger/hu-HU.json +3 -0
- package/src/intl/previewtrigger/index.ts +73 -0
- package/src/intl/previewtrigger/it-IT.json +3 -0
- package/src/intl/previewtrigger/ja-JP.json +3 -0
- package/src/intl/previewtrigger/ko-KR.json +3 -0
- package/src/intl/previewtrigger/lt-LT.json +3 -0
- package/src/intl/previewtrigger/lv-LV.json +3 -0
- package/src/intl/previewtrigger/nb-NO.json +3 -0
- package/src/intl/previewtrigger/nl-NL.json +3 -0
- package/src/intl/previewtrigger/pl-PL.json +3 -0
- package/src/intl/previewtrigger/pt-BR.json +3 -0
- package/src/intl/previewtrigger/pt-PT.json +3 -0
- package/src/intl/previewtrigger/ro-RO.json +3 -0
- package/src/intl/previewtrigger/ru-RU.json +3 -0
- package/src/intl/previewtrigger/sk-SK.json +3 -0
- package/src/intl/previewtrigger/sl-SI.json +3 -0
- package/src/intl/previewtrigger/sr-SP.json +3 -0
- package/src/intl/previewtrigger/sv-SE.json +3 -0
- package/src/intl/previewtrigger/tr-TR.json +3 -0
- package/src/intl/previewtrigger/uk-UA.json +3 -0
- package/src/intl/previewtrigger/zh-CN.json +3 -0
- package/src/intl/previewtrigger/zh-TW.json +3 -0
- package/src/intl/table/ar-AE.json +3 -3
- package/src/intl/table/bg-BG.json +3 -3
- package/src/intl/table/cs-CZ.json +3 -3
- package/src/intl/table/da-DK.json +3 -3
- package/src/intl/table/de-DE.json +3 -3
- package/src/intl/table/el-GR.json +3 -3
- package/src/intl/table/es-ES.json +3 -3
- package/src/intl/table/et-EE.json +3 -3
- package/src/intl/table/fi-FI.json +3 -3
- package/src/intl/table/fr-FR.json +3 -3
- package/src/intl/table/he-IL.json +3 -3
- package/src/intl/table/hr-HR.json +3 -3
- package/src/intl/table/hu-HU.json +3 -3
- package/src/intl/table/it-IT.json +3 -3
- package/src/intl/table/ja-JP.json +3 -3
- package/src/intl/table/ko-KR.json +3 -3
- package/src/intl/table/lt-LT.json +3 -3
- package/src/intl/table/lv-LV.json +3 -3
- package/src/intl/table/nb-NO.json +3 -3
- package/src/intl/table/nl-NL.json +3 -3
- package/src/intl/table/pl-PL.json +3 -3
- package/src/intl/table/pt-BR.json +3 -3
- package/src/intl/table/pt-PT.json +3 -3
- package/src/intl/table/ro-RO.json +3 -3
- package/src/intl/table/ru-RU.json +3 -3
- package/src/intl/table/sk-SK.json +3 -3
- package/src/intl/table/sl-SI.json +3 -3
- package/src/intl/table/sr-SP.json +3 -3
- package/src/intl/table/sv-SE.json +3 -3
- package/src/intl/table/tr-TR.json +3 -3
- package/src/intl/table/uk-UA.json +3 -3
- package/src/intl/table/zh-CN.json +4 -4
- package/src/intl/table/zh-TW.json +3 -3
- package/src/intl/tree/es-ES.json +1 -1
- package/src/link/useLink.ts +2 -0
- package/src/menu/useMenuItem.ts +33 -36
- package/src/menu/useMenuTrigger.tsx +217 -0
- package/src/menu/{useSubmenuTrigger.ts → useSubmenuTrigger.tsx} +112 -152
- package/src/numberfield/{useNumberField.ts → useNumberField.tsx} +185 -239
- package/src/overlays/Overlay.ts +5 -1
- package/src/overlays/ariaHideOutside.ts +6 -8
- package/src/overlays/useCloseOnScroll.ts +7 -5
- package/src/overlays/{useOverlay.ts → useOverlay.tsx} +72 -84
- package/src/overlays/usePopover.ts +10 -5
- package/src/overlays/usePreventScroll.ts +4 -4
- package/src/progress/useProgressBar.ts +2 -1
- package/src/radio/{useRadioGroup.ts → useRadioGroup.tsx} +83 -80
- package/src/searchfield/useSearchField.tsx +147 -0
- package/src/select/useSelect.ts +20 -20
- package/src/selection/useSelectableCollection.tsx +751 -0
- package/src/selection/useSelectableItem.ts +48 -11
- package/src/slider/{useSliderThumb.ts → useSliderThumb.tsx} +116 -159
- package/src/spinbutton/useSpinButton.tsx +330 -0
- package/src/stately/combobox/useComboBoxState.ts +1 -1
- package/src/stately/grid/useGridState.ts +9 -10
- package/src/stately/index.ts +11 -0
- package/src/stately/menu/useMenuTriggerState.ts +1 -1
- package/src/stately/menu/useSubmenuTriggerState.ts +2 -0
- package/src/stately/overlays/useOverlayTriggerState.ts +12 -1
- package/src/stately/radio/useRadioGroupState.ts +5 -1
- package/src/stately/table/TableUtils.ts +15 -1
- package/src/stately/table/useTableState.ts +7 -0
- package/src/stately/tokenfield/TokenFieldValue.tsx +440 -0
- package/src/stately/tokenfield/useTokenFieldState.tsx +57 -0
- package/src/stately/tooltip/useTooltipTriggerState.ts +16 -7
- package/src/stately/virtualizer/Layout.tsx +2 -0
- package/src/stately/virtualizer/ScrollAnchor.tsx +338 -0
- package/src/stately/virtualizer/Virtualizer.tsx +52 -1
- package/src/stately/virtualizer/types.tsx +2 -0
- package/src/table/useTableCell.ts +12 -0
- package/src/table/useTableColumnHeader.ts +8 -1
- package/src/table/useTableColumnResize.tsx +354 -0
- package/src/tag/useTag.ts +22 -15
- package/src/textfield/useTextField.ts +2 -0
- package/src/tokenfield/useToken.tsx +64 -0
- package/src/tokenfield/useTokenField.tsx +849 -0
- package/src/tooltip/usePreviewTrigger.tsx +266 -0
- package/src/tooltip/useSafeArea.tsx +171 -0
- package/src/upstream-exports/react-aria/mergeRefs.tsx +3 -0
- package/src/upstream-exports/react-aria/usePreviewTrigger.tsx +18 -0
- package/src/upstream-exports/react-aria/useTokenField.tsx +22 -0
- package/src/upstream-exports/react-stately/private/collections/getChildNodes.tsx +8 -0
- package/src/upstream-exports/react-stately/private/collections/getItemCount.tsx +2 -0
- package/src/upstream-exports/react-stately/private/grid/GridCollection.tsx +7 -0
- package/src/upstream-exports/react-stately/private/grid/useGridState.tsx +6 -0
- package/src/upstream-exports/react-stately/private/utils/number.tsx +2 -0
- package/src/upstream-exports/react-stately/useComboBoxState.tsx +23 -0
- package/src/upstream-exports/react-stately/useListState.tsx +15 -0
- package/src/upstream-exports/react-stately/useMenuTriggerState.tsx +26 -0
- package/src/upstream-exports/react-stately/useMultipleSelectionState.tsx +21 -0
- package/src/upstream-exports/react-stately/useNumberFieldState.tsx +20 -0
- package/src/upstream-exports/react-stately/useOverlayTriggerState.tsx +20 -0
- package/src/upstream-exports/react-stately/useRadioGroupState.tsx +16 -0
- package/src/upstream-exports/react-stately/useSearchFieldState.tsx +19 -0
- package/src/upstream-exports/react-stately/useSliderState.tsx +20 -0
- package/src/upstream-exports/react-stately/useTableState.tsx +45 -0
- package/src/upstream-exports/react-stately/useTokenFieldState.tsx +25 -0
- package/src/upstream-exports/react-stately/useTooltipTriggerState.tsx +20 -0
- package/src/upstream-exports/react-stately/useTreeState.tsx +14 -0
- package/src/utils/domHelpers.ts +74 -17
- package/src/utils/isVirtualEvent.ts +2 -1
- package/src/utils/openLink.ts +8 -3
- package/src/utils/platform.ts +7 -3
- package/src/utils/scrollIntoView.ts +4 -4
- package/src/utils/shadowdom/DOMFunctions.ts +42 -2
- package/src/utils/useId.ts +11 -2
- package/src/utils/useLoadMoreSentinel.ts +8 -3
- package/src/utils/useViewportSize.ts +3 -3
- package/src/virtualizer/ScrollView.tsx +13 -4
- package/src/virtualizer/VirtualizerItem.tsx +3 -1
- package/src/virtualizer/useVirtualizerItem.tsx +43 -3
- package/src/menu/useMenuTrigger.ts +0 -189
- package/src/searchfield/useSearchField.ts +0 -156
- package/src/selection/useSelectableCollection.ts +0 -750
- package/src/spinbutton/useSpinButton.ts +0 -380
- package/src/table/useTableColumnResize.ts +0 -374
package/src/grid/utils.ts
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
/** @jsxImportSource octane */
|
|
2
|
+
type DOMAttributes = Record<string, any>;
|
|
3
|
+
// Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/src/gridlist/useGridListItem.ts).
|
|
4
|
+
/*
|
|
5
|
+
* Copyright 2022 Adobe. All rights reserved.
|
|
6
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
8
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
11
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
12
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
13
|
+
* governing permissions and limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
13
16
|
import { chain } from '../utils/chain';
|
|
14
|
-
import
|
|
15
|
-
Collection,
|
|
16
|
-
FocusableElement,
|
|
17
|
-
Key,
|
|
18
|
-
RefObject,
|
|
19
|
-
Node as RSNode,
|
|
20
|
-
} from '@react-types/shared';
|
|
17
|
+
import { Collection, FocusableElement, Key, RefObject, Node as RSNode } from '@react-types/shared';
|
|
21
18
|
import { focusSafely } from '../interactions/focusSafely';
|
|
22
19
|
import {
|
|
23
20
|
getActiveElement,
|
|
@@ -26,26 +23,20 @@ import {
|
|
|
26
23
|
nodeContains,
|
|
27
24
|
} from '../utils/shadowdom/DOMFunctions';
|
|
28
25
|
import { getFocusableTreeWalker } from '../focus/FocusScope';
|
|
26
|
+
import { getOwnerDocument } from '../utils/domHelpers';
|
|
29
27
|
import { getRowId, listMap } from './utils';
|
|
30
28
|
import { getScrollParent } from '../utils/getScrollParent';
|
|
31
|
-
import { useRef } from '
|
|
29
|
+
import { HTMLAttributes, KeyboardEvent as ReactKeyboardEvent, useRef } from '../compat/react';
|
|
32
30
|
import { isFocusVisible } from '../interactions/useFocusVisible';
|
|
33
|
-
import {
|
|
34
|
-
import type { ListState } from '../stately/list/useListState';
|
|
31
|
+
import type { ListState } from '../upstream-exports/react-stately/useListState';
|
|
35
32
|
import { mergeProps } from '../utils/mergeProps';
|
|
36
33
|
import { scrollIntoViewport } from '../utils/scrollIntoView';
|
|
37
34
|
import { SelectableItemStates, useSelectableItem } from '../selection/useSelectableItem';
|
|
38
|
-
import type { TreeState } from '../stately/
|
|
35
|
+
import type { TreeState } from '../upstream-exports/react-stately/useTreeState';
|
|
39
36
|
import { useLocale } from '../i18n/I18nProvider';
|
|
40
37
|
import { useSlotId } from '../utils/useId';
|
|
41
38
|
import { useSyntheticLinkProps } from '../utils/openLink';
|
|
42
39
|
|
|
43
|
-
import { S, splitSlot, subSlot } from '../internal';
|
|
44
|
-
|
|
45
|
-
// octane adaptation: minimal structural DOMAttributes (upstream's drags React's synthetic
|
|
46
|
-
// handler types along).
|
|
47
|
-
type DOMAttributes = Record<string, any>;
|
|
48
|
-
|
|
49
40
|
export interface AriaGridListItemOptions {
|
|
50
41
|
/**
|
|
51
42
|
* An object representing the list item. Contains all the relevant information that makes up the
|
|
@@ -58,6 +49,19 @@ export interface AriaGridListItemOptions {
|
|
|
58
49
|
shouldSelectOnPressUp?: boolean;
|
|
59
50
|
/** Whether this item has children, even if not loaded yet. */
|
|
60
51
|
hasChildItems?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether the row or its first focusable child element should be focused when the row is
|
|
54
|
+
* focused.
|
|
55
|
+
*
|
|
56
|
+
* @default 'row'
|
|
57
|
+
*/
|
|
58
|
+
focusMode?: 'child' | 'row';
|
|
59
|
+
/**
|
|
60
|
+
* Whether the row should support arrow key navigation even when the containing collection uses
|
|
61
|
+
* tab keyboard navigation. Allows users to navigate between rows with arrow keys while
|
|
62
|
+
* focus is on an interactive child element within the row.
|
|
63
|
+
*/
|
|
64
|
+
allowsArrowNavigation?: boolean;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
export interface GridListItemAria extends SelectableItemStates {
|
|
@@ -91,46 +95,54 @@ export function useGridListItem<T>(
|
|
|
91
95
|
props: AriaGridListItemOptions,
|
|
92
96
|
state: ListState<T> | TreeState<T>,
|
|
93
97
|
ref: RefObject<FocusableElement | null>,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
export function useGridListItem<T>(
|
|
97
|
-
props: AriaGridListItemOptions,
|
|
98
|
-
state: ListState<T> | TreeState<T>,
|
|
99
|
-
ref: RefObject<FocusableElement | null>,
|
|
100
|
-
slot: symbol | undefined,
|
|
101
|
-
): GridListItemAria;
|
|
102
|
-
export function useGridListItem(...args: any[]): GridListItemAria {
|
|
103
|
-
const [user, slotArg] = splitSlot(args);
|
|
104
|
-
const slot = slotArg ?? S('useGridListItem');
|
|
105
|
-
const props = user[0] as AriaGridListItemOptions;
|
|
106
|
-
const state = user[1] as ListState<any> | TreeState<any>;
|
|
107
|
-
const ref = user[2] as RefObject<FocusableElement | null>;
|
|
108
|
-
|
|
98
|
+
_slot?: symbol,
|
|
99
|
+
): GridListItemAria {
|
|
109
100
|
// Copied from useGridCell + some modifications to make it not so grid specific
|
|
110
|
-
let { node, isVirtualized } = props;
|
|
101
|
+
let { node, isVirtualized, focusMode = 'row', allowsArrowNavigation } = props;
|
|
111
102
|
|
|
112
103
|
// let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/gridlist');
|
|
113
|
-
let { direction } = useLocale(
|
|
104
|
+
let { direction } = useLocale();
|
|
114
105
|
let { onAction, linkBehavior, keyboardNavigationBehavior, shouldSelectOnPressUp } =
|
|
115
106
|
listMap.get(state)!;
|
|
116
|
-
let descriptionId = useSlotId(
|
|
107
|
+
let descriptionId = useSlotId();
|
|
117
108
|
|
|
118
109
|
// We need to track the key of the item at the time it was last focused so that we force
|
|
119
110
|
// focus to go to the item when the DOM node is reused for a different item in a virtualizer.
|
|
120
|
-
let keyWhenFocused = useRef<Key | null>(null
|
|
111
|
+
let keyWhenFocused = useRef<Key | null>(null);
|
|
121
112
|
let focus = () => {
|
|
113
|
+
if (ref.current === null) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (focusMode === 'child') {
|
|
118
|
+
// If focus is already on a focusable child within the row, early return so we don't shift focus
|
|
119
|
+
if (
|
|
120
|
+
isFocusWithin(ref.current) &&
|
|
121
|
+
ref.current !== getActiveElement(getOwnerDocument(ref.current))
|
|
122
|
+
) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let treeWalker = getFocusableTreeWalker(ref.current, { tabbable: true });
|
|
127
|
+
let focusable = treeWalker.firstChild() as FocusableElement;
|
|
128
|
+
if (focusable) {
|
|
129
|
+
focusSafely(focusable);
|
|
130
|
+
scrollIntoViewport(focusable, { containingElement: getScrollParent(focusable) });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
122
135
|
// Don't shift focus to the row if the active element is a element within the row already
|
|
123
136
|
// (e.g. clicking on a row button)
|
|
124
137
|
if (
|
|
125
|
-
|
|
126
|
-
(
|
|
127
|
-
!isFocusWithin(ref.current))
|
|
138
|
+
(keyWhenFocused.current != null && node.key !== keyWhenFocused.current) ||
|
|
139
|
+
!isFocusWithin(ref.current)
|
|
128
140
|
) {
|
|
129
141
|
focusSafely(ref.current);
|
|
130
142
|
}
|
|
131
143
|
};
|
|
132
144
|
|
|
133
|
-
let treeGridRowProps:
|
|
145
|
+
let treeGridRowProps: HTMLAttributes<HTMLElement> = {};
|
|
134
146
|
let hasChildRows = props.hasChildItems;
|
|
135
147
|
let hasLink = state.selectionManager.isLink(node.key);
|
|
136
148
|
if (node != null && 'expandedKeys' in state) {
|
|
@@ -175,27 +187,24 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
175
187
|
};
|
|
176
188
|
}
|
|
177
189
|
|
|
178
|
-
let { itemProps, ...itemStates } = useSelectableItem(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
onAction
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
},
|
|
192
|
-
subSlot(slot, 'selectableItem'),
|
|
193
|
-
);
|
|
190
|
+
let { itemProps, ...itemStates } = useSelectableItem({
|
|
191
|
+
selectionManager: state.selectionManager,
|
|
192
|
+
key: node.key,
|
|
193
|
+
ref,
|
|
194
|
+
isVirtualized,
|
|
195
|
+
shouldSelectOnPressUp: props.shouldSelectOnPressUp || shouldSelectOnPressUp,
|
|
196
|
+
onAction:
|
|
197
|
+
onAction || node.props?.onAction
|
|
198
|
+
? chain(node.props?.onAction, onAction ? () => onAction(node.key) : undefined)
|
|
199
|
+
: undefined,
|
|
200
|
+
focus,
|
|
201
|
+
linkBehavior,
|
|
202
|
+
});
|
|
194
203
|
|
|
195
|
-
let onKeyDownCapture = (e:
|
|
196
|
-
let activeElement = getActiveElement();
|
|
204
|
+
let onKeyDownCapture = (e: ReactKeyboardEvent) => {
|
|
205
|
+
let activeElement = getActiveElement(getOwnerDocument(ref.current));
|
|
197
206
|
if (
|
|
198
|
-
!nodeContains(e.currentTarget as
|
|
207
|
+
!nodeContains(e.currentTarget as Node, getEventTarget(e) as Element) ||
|
|
199
208
|
!ref.current ||
|
|
200
209
|
!activeElement
|
|
201
210
|
) {
|
|
@@ -206,7 +215,16 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
206
215
|
walker.currentNode = activeElement;
|
|
207
216
|
|
|
208
217
|
if (
|
|
209
|
-
handleTreeExpansionKeys(
|
|
218
|
+
handleTreeExpansionKeys(
|
|
219
|
+
e,
|
|
220
|
+
state,
|
|
221
|
+
node,
|
|
222
|
+
hasChildRows,
|
|
223
|
+
direction,
|
|
224
|
+
activeElement,
|
|
225
|
+
ref.current,
|
|
226
|
+
allowsArrowNavigation,
|
|
227
|
+
)
|
|
210
228
|
) {
|
|
211
229
|
return;
|
|
212
230
|
}
|
|
@@ -235,6 +253,7 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
235
253
|
} else {
|
|
236
254
|
walker.currentNode = ref.current;
|
|
237
255
|
let lastElement = last(walker);
|
|
256
|
+
// oxlint-disable-next-line max-depth
|
|
238
257
|
if (lastElement) {
|
|
239
258
|
focusSafely(lastElement);
|
|
240
259
|
scrollIntoViewport(lastElement, {
|
|
@@ -267,6 +286,7 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
267
286
|
} else {
|
|
268
287
|
walker.currentNode = ref.current;
|
|
269
288
|
let lastElement = last(walker);
|
|
289
|
+
// oxlint-disable-next-line max-depth
|
|
270
290
|
if (lastElement) {
|
|
271
291
|
focusSafely(lastElement);
|
|
272
292
|
scrollIntoViewport(lastElement, {
|
|
@@ -286,7 +306,6 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
286
306
|
if (!e.altKey && nodeContains(ref.current, getEventTarget(e) as Element)) {
|
|
287
307
|
e.stopPropagation();
|
|
288
308
|
e.preventDefault();
|
|
289
|
-
// octane adaptation: the native event IS `e.nativeEvent`.
|
|
290
309
|
ref.current.parentElement?.dispatchEvent(new KeyboardEvent(e.type, e));
|
|
291
310
|
}
|
|
292
311
|
break;
|
|
@@ -307,12 +326,33 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
307
326
|
}
|
|
308
327
|
return;
|
|
309
328
|
}
|
|
329
|
+
|
|
330
|
+
// if focus goes back to cell from child, make sure we don't refocus the cell if we are in focusMode=child
|
|
331
|
+
// since that would be a focus trap
|
|
332
|
+
if (
|
|
333
|
+
focusMode === 'child' &&
|
|
334
|
+
e.relatedTarget &&
|
|
335
|
+
nodeContains(ref.current, e.relatedTarget as Element)
|
|
336
|
+
) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// If the cell itself is focused, wait a frame so that focus finishes propagating
|
|
341
|
+
// up to the tree, and move focus to a focusable child if possible.
|
|
342
|
+
requestAnimationFrame(() => {
|
|
343
|
+
if (
|
|
344
|
+
focusMode === 'child' &&
|
|
345
|
+
getActiveElement(getOwnerDocument(ref.current)) === ref.current
|
|
346
|
+
) {
|
|
347
|
+
focus();
|
|
348
|
+
}
|
|
349
|
+
});
|
|
310
350
|
};
|
|
311
351
|
|
|
312
|
-
let onKeyDown = (e:
|
|
313
|
-
let activeElement = getActiveElement();
|
|
352
|
+
let onKeyDown = (e: ReactKeyboardEvent) => {
|
|
353
|
+
let activeElement = getActiveElement(getOwnerDocument(ref.current));
|
|
314
354
|
if (
|
|
315
|
-
!nodeContains(e.currentTarget as
|
|
355
|
+
!nodeContains(e.currentTarget as Node, getEventTarget(e) as Element) ||
|
|
316
356
|
!ref.current ||
|
|
317
357
|
!activeElement
|
|
318
358
|
) {
|
|
@@ -329,7 +369,16 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
329
369
|
}
|
|
330
370
|
|
|
331
371
|
if (
|
|
332
|
-
handleTreeExpansionKeys(
|
|
372
|
+
handleTreeExpansionKeys(
|
|
373
|
+
e,
|
|
374
|
+
state,
|
|
375
|
+
node,
|
|
376
|
+
hasChildRows,
|
|
377
|
+
direction,
|
|
378
|
+
activeElement,
|
|
379
|
+
ref.current,
|
|
380
|
+
allowsArrowNavigation,
|
|
381
|
+
)
|
|
333
382
|
) {
|
|
334
383
|
return;
|
|
335
384
|
}
|
|
@@ -364,56 +413,45 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
364
413
|
// });
|
|
365
414
|
// }
|
|
366
415
|
|
|
416
|
+
// oxlint-disable-next-line react/react-compiler
|
|
367
417
|
let rowProps: DOMAttributes = mergeProps(itemProps, linkProps, {
|
|
368
418
|
role: 'row',
|
|
369
|
-
onKeyDownCapture:
|
|
419
|
+
onKeyDownCapture:
|
|
420
|
+
keyboardNavigationBehavior === 'arrow' || allowsArrowNavigation
|
|
421
|
+
? onKeyDownCapture
|
|
422
|
+
: undefined,
|
|
370
423
|
onFocus,
|
|
371
424
|
// 'aria-label': [(node.textValue || undefined), rowAnnouncement].filter(Boolean).join(', '),
|
|
372
|
-
'aria-label':
|
|
425
|
+
'aria-label': node['aria-label'] || node.textValue || undefined,
|
|
373
426
|
'aria-selected': state.selectionManager.canSelectItem(node.key)
|
|
374
427
|
? state.selectionManager.isSelected(node.key)
|
|
375
428
|
: undefined,
|
|
376
429
|
'aria-disabled': state.selectionManager.isDisabled(node.key) || undefined,
|
|
377
430
|
'aria-labelledby':
|
|
378
|
-
descriptionId && (
|
|
431
|
+
descriptionId && (node['aria-label'] || node.textValue)
|
|
379
432
|
? `${getRowId(state, node.key)} ${descriptionId}`
|
|
380
433
|
: undefined,
|
|
381
434
|
id: getRowId(state, node.key),
|
|
382
435
|
});
|
|
383
436
|
|
|
437
|
+
if (focusMode === 'child' && allowsArrowNavigation && keyboardNavigationBehavior === 'tab') {
|
|
438
|
+
rowProps.tabIndex = -1;
|
|
439
|
+
}
|
|
440
|
+
|
|
384
441
|
// we need to guard against space/enter triggering selection/row link via usePress (from itemProps) so check if propagation
|
|
385
442
|
// is stopped. this also fixes space not working in a textfield in a tree parent row
|
|
386
443
|
let baseOnKeyDown = rowProps.onKeyDown;
|
|
387
|
-
rowProps.onKeyDown = (e:
|
|
388
|
-
onKeyDown(e);
|
|
389
|
-
|
|
390
|
-
|
|
444
|
+
rowProps.onKeyDown = (e: ReactKeyboardEvent<FocusableElement>) => {
|
|
445
|
+
onKeyDown(e as ReactKeyboardEvent);
|
|
446
|
+
if (!(
|
|
447
|
+
'isPropagationStopped' in e &&
|
|
448
|
+
typeof e.isPropagationStopped === 'function' &&
|
|
449
|
+
e.isPropagationStopped()
|
|
450
|
+
)) {
|
|
391
451
|
baseOnKeyDown?.(e);
|
|
392
452
|
}
|
|
393
453
|
};
|
|
394
454
|
|
|
395
|
-
// guard against presses triggering row selecition when they happen on elements within the row
|
|
396
|
-
// am currently assuming if it is tabbable it is interactive, but maybe can use a different kind of check
|
|
397
|
-
let baseOnPointerDown = rowProps.onPointerDown;
|
|
398
|
-
rowProps.onPointerDown = (e: PointerEvent) => {
|
|
399
|
-
let target = getEventTarget(e) as Element | null;
|
|
400
|
-
if (target && target !== ref.current && isTabbable(target)) {
|
|
401
|
-
e.stopPropagation();
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
baseOnPointerDown?.(e);
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
let baseOnMouseDown = rowProps.onMouseDown;
|
|
408
|
-
rowProps.onMouseDown = (e: MouseEvent) => {
|
|
409
|
-
let target = getEventTarget(e) as Element | null;
|
|
410
|
-
if (target && target !== ref.current && isTabbable(target)) {
|
|
411
|
-
e.stopPropagation();
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
baseOnMouseDown?.(e);
|
|
415
|
-
};
|
|
416
|
-
|
|
417
455
|
if (isVirtualized) {
|
|
418
456
|
let { collection } = state;
|
|
419
457
|
let nodes = [...collection];
|
|
@@ -431,6 +469,7 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
431
469
|
};
|
|
432
470
|
|
|
433
471
|
// TODO: should isExpanded and hasChildRows be a item state that gets returned by the hook?
|
|
472
|
+
// oxlint-disable react/react-compiler
|
|
434
473
|
return {
|
|
435
474
|
rowProps: { ...mergeProps(rowProps, treeGridRowProps) },
|
|
436
475
|
gridCellProps,
|
|
@@ -439,22 +478,24 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
439
478
|
},
|
|
440
479
|
...itemStates,
|
|
441
480
|
};
|
|
481
|
+
// oxlint-enable react/react-compiler
|
|
442
482
|
}
|
|
443
483
|
|
|
444
484
|
function handleTreeExpansionKeys<T>(
|
|
445
|
-
e:
|
|
485
|
+
e: ReactKeyboardEvent,
|
|
446
486
|
state: ListState<T> | TreeState<T>,
|
|
447
487
|
node: RSNode<unknown>,
|
|
448
488
|
hasChildRows: boolean | undefined,
|
|
449
|
-
direction:
|
|
489
|
+
direction: 'ltr' | 'rtl',
|
|
450
490
|
activeElement: Element | null,
|
|
451
491
|
rowRef: FocusableElement | null,
|
|
492
|
+
allowsArrowNavigation: boolean | undefined,
|
|
452
493
|
): boolean {
|
|
453
|
-
if (!('expandedKeys' in state) || activeElement !== rowRef) {
|
|
494
|
+
if (!('expandedKeys' in state) || (!allowsArrowNavigation && activeElement !== rowRef)) {
|
|
454
495
|
return false;
|
|
455
496
|
}
|
|
456
497
|
if (
|
|
457
|
-
e.key ===
|
|
498
|
+
e.key === EXPANSION_KEYS['expand'][direction] &&
|
|
458
499
|
state.selectionManager.focusedKey === node.key &&
|
|
459
500
|
hasChildRows &&
|
|
460
501
|
!state.expandedKeys.has(node.key)
|
|
@@ -463,7 +504,7 @@ function handleTreeExpansionKeys<T>(
|
|
|
463
504
|
e.stopPropagation();
|
|
464
505
|
return true;
|
|
465
506
|
} else if (
|
|
466
|
-
e.key ===
|
|
507
|
+
e.key === EXPANSION_KEYS['collapse'][direction] &&
|
|
467
508
|
state.selectionManager.focusedKey === node.key
|
|
468
509
|
) {
|
|
469
510
|
// If item is collapsible, collapse it; else move to parent
|
package/src/index.ts
CHANGED
|
@@ -359,3 +359,25 @@ export type {
|
|
|
359
359
|
} from './table/useTableColumnResize';
|
|
360
360
|
export type { GridAria, GridProps } from './grid/useGrid';
|
|
361
361
|
export type { GridRowAria, GridRowProps } from './grid/useGridRow';
|
|
362
|
+
|
|
363
|
+
export { useContextMenu } from './interactions/useContextMenu';
|
|
364
|
+
export type {
|
|
365
|
+
ContextMenuProps,
|
|
366
|
+
ContextMenuAria,
|
|
367
|
+
ContextMenuEvent,
|
|
368
|
+
} from './interactions/useContextMenu';
|
|
369
|
+
export type { KeyboardShortcutBindings } from './interactions/createKeyboardShortcutHandler';
|
|
370
|
+
export { usePreviewTrigger } from './tooltip/usePreviewTrigger';
|
|
371
|
+
export type {
|
|
372
|
+
AriaPreviewTriggerProps,
|
|
373
|
+
PreviewTriggerTriggerAria,
|
|
374
|
+
} from './tooltip/usePreviewTrigger';
|
|
375
|
+
export {
|
|
376
|
+
useTokenField,
|
|
377
|
+
tokenFieldPositionToDOMRange,
|
|
378
|
+
setTokenFieldSelection,
|
|
379
|
+
getSelection,
|
|
380
|
+
} from './tokenfield/useTokenField';
|
|
381
|
+
export type { AriaTokenFieldProps, TokenFieldAria } from './tokenfield/useTokenField';
|
|
382
|
+
export { useToken } from './tokenfield/useToken';
|
|
383
|
+
export type { TokenProps, TokenAria } from './tokenfield/useToken';
|
|
@@ -20,6 +20,8 @@ export type BaseEvent<T extends Event> = T & {
|
|
|
20
20
|
*/
|
|
21
21
|
stopPropagation(): void;
|
|
22
22
|
continuePropagation(): void;
|
|
23
|
+
isDefaultPrevented(): boolean;
|
|
24
|
+
isPropagationStopped(): boolean;
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
const hasOwn = Object.prototype.hasOwnProperty;
|
|
@@ -35,14 +37,8 @@ export function createEventHandler<T extends Event>(
|
|
|
35
37
|
return undefined;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
// NB: the flag deliberately lives on the WRAPPER closure, not per dispatch — after a
|
|
39
|
-
// handler calls continuePropagation(), a later event on the same wrapper instance does
|
|
40
|
-
// not re-arm stop-by-default. That is upstream's exact (and admittedly surprising)
|
|
41
|
-
// contract at the pinned version; the differential KeyLatch fixture pins octane to
|
|
42
|
-
// React's observable behavior across consecutive dispatches, so an upstream semantics
|
|
43
|
-
// change will surface at the next pin bump rather than silently diverging here.
|
|
44
|
-
let shouldStopPropagation = true;
|
|
45
40
|
return (e: T) => {
|
|
41
|
+
let shouldStopPropagation = true;
|
|
46
42
|
const overrides: Record<PropertyKey, any> = {
|
|
47
43
|
preventDefault() {
|
|
48
44
|
e.preventDefault();
|
|
@@ -87,7 +83,14 @@ export function createEventHandler<T extends Event>(
|
|
|
87
83
|
|
|
88
84
|
handler(event);
|
|
89
85
|
|
|
90
|
-
if (
|
|
86
|
+
if (
|
|
87
|
+
shouldStopPropagation &&
|
|
88
|
+
!(
|
|
89
|
+
'isPropagationStopped' in e &&
|
|
90
|
+
typeof e.isPropagationStopped === 'function' &&
|
|
91
|
+
e.isPropagationStopped()
|
|
92
|
+
)
|
|
93
|
+
) {
|
|
91
94
|
e.stopPropagation();
|
|
92
95
|
}
|
|
93
96
|
};
|