@octanejs/aria 0.0.46 → 0.0.48
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} +243 -258
- 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} +128 -66
- package/src/grid/utils.ts +1 -0
- package/src/gridlist/{useGridListItem.ts → useGridListItem.tsx} +154 -112
- 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 +222 -0
- package/src/menu/{useSubmenuTrigger.ts → useSubmenuTrigger.tsx} +116 -151
- package/src/numberfield/{useNumberField.ts → useNumberField.tsx} +188 -238
- 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} +76 -83
- 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} +87 -79
- package/src/searchfield/useSearchField.tsx +152 -0
- package/src/select/useSelect.ts +20 -20
- package/src/selection/useSelectableCollection.tsx +755 -0
- package/src/selection/useSelectableItem.ts +48 -11
- package/src/slider/{useSliderThumb.ts → useSliderThumb.tsx} +120 -158
- package/src/spinbutton/useSpinButton.tsx +335 -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 +359 -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 +4 -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
|
@@ -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,21 @@ 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';
|
|
30
|
+
import { withSlot } from 'octane';
|
|
32
31
|
import { isFocusVisible } from '../interactions/useFocusVisible';
|
|
33
|
-
import {
|
|
34
|
-
import type { ListState } from '../stately/list/useListState';
|
|
32
|
+
import type { ListState } from '../upstream-exports/react-stately/useListState';
|
|
35
33
|
import { mergeProps } from '../utils/mergeProps';
|
|
36
34
|
import { scrollIntoViewport } from '../utils/scrollIntoView';
|
|
37
35
|
import { SelectableItemStates, useSelectableItem } from '../selection/useSelectableItem';
|
|
38
|
-
import type { TreeState } from '../stately/
|
|
36
|
+
import type { TreeState } from '../upstream-exports/react-stately/useTreeState';
|
|
39
37
|
import { useLocale } from '../i18n/I18nProvider';
|
|
40
38
|
import { useSlotId } from '../utils/useId';
|
|
41
39
|
import { useSyntheticLinkProps } from '../utils/openLink';
|
|
42
40
|
|
|
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
41
|
export interface AriaGridListItemOptions {
|
|
50
42
|
/**
|
|
51
43
|
* An object representing the list item. Contains all the relevant information that makes up the
|
|
@@ -58,6 +50,19 @@ export interface AriaGridListItemOptions {
|
|
|
58
50
|
shouldSelectOnPressUp?: boolean;
|
|
59
51
|
/** Whether this item has children, even if not loaded yet. */
|
|
60
52
|
hasChildItems?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Whether the row or its first focusable child element should be focused when the row is
|
|
55
|
+
* focused.
|
|
56
|
+
*
|
|
57
|
+
* @default 'row'
|
|
58
|
+
*/
|
|
59
|
+
focusMode?: 'child' | 'row';
|
|
60
|
+
/**
|
|
61
|
+
* Whether the row should support arrow key navigation even when the containing collection uses
|
|
62
|
+
* tab keyboard navigation. Allows users to navigate between rows with arrow keys while
|
|
63
|
+
* focus is on an interactive child element within the row.
|
|
64
|
+
*/
|
|
65
|
+
allowsArrowNavigation?: boolean;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
export interface GridListItemAria extends SelectableItemStates {
|
|
@@ -91,46 +96,58 @@ export function useGridListItem<T>(
|
|
|
91
96
|
props: AriaGridListItemOptions,
|
|
92
97
|
state: ListState<T> | TreeState<T>,
|
|
93
98
|
ref: RefObject<FocusableElement | null>,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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>;
|
|
99
|
+
_slot?: symbol,
|
|
100
|
+
): GridListItemAria {
|
|
101
|
+
if (_slot !== undefined) {
|
|
102
|
+
return withSlot(_slot, useGridListItem<T>, props, state, ref);
|
|
103
|
+
}
|
|
108
104
|
|
|
109
105
|
// Copied from useGridCell + some modifications to make it not so grid specific
|
|
110
|
-
let { node, isVirtualized } = props;
|
|
106
|
+
let { node, isVirtualized, focusMode = 'row', allowsArrowNavigation } = props;
|
|
111
107
|
|
|
112
108
|
// let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/gridlist');
|
|
113
|
-
let { direction } = useLocale(
|
|
109
|
+
let { direction } = useLocale();
|
|
114
110
|
let { onAction, linkBehavior, keyboardNavigationBehavior, shouldSelectOnPressUp } =
|
|
115
111
|
listMap.get(state)!;
|
|
116
|
-
let descriptionId = useSlotId(
|
|
112
|
+
let descriptionId = useSlotId();
|
|
117
113
|
|
|
118
114
|
// We need to track the key of the item at the time it was last focused so that we force
|
|
119
115
|
// 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
|
|
116
|
+
let keyWhenFocused = useRef<Key | null>(null);
|
|
121
117
|
let focus = () => {
|
|
118
|
+
if (ref.current === null) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (focusMode === 'child') {
|
|
123
|
+
// If focus is already on a focusable child within the row, early return so we don't shift focus
|
|
124
|
+
if (
|
|
125
|
+
isFocusWithin(ref.current) &&
|
|
126
|
+
ref.current !== getActiveElement(getOwnerDocument(ref.current))
|
|
127
|
+
) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let treeWalker = getFocusableTreeWalker(ref.current, { tabbable: true });
|
|
132
|
+
let focusable = treeWalker.firstChild() as FocusableElement;
|
|
133
|
+
if (focusable) {
|
|
134
|
+
focusSafely(focusable);
|
|
135
|
+
scrollIntoViewport(focusable, { containingElement: getScrollParent(focusable) });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
122
140
|
// Don't shift focus to the row if the active element is a element within the row already
|
|
123
141
|
// (e.g. clicking on a row button)
|
|
124
142
|
if (
|
|
125
|
-
|
|
126
|
-
(
|
|
127
|
-
!isFocusWithin(ref.current))
|
|
143
|
+
(keyWhenFocused.current != null && node.key !== keyWhenFocused.current) ||
|
|
144
|
+
!isFocusWithin(ref.current)
|
|
128
145
|
) {
|
|
129
146
|
focusSafely(ref.current);
|
|
130
147
|
}
|
|
131
148
|
};
|
|
132
149
|
|
|
133
|
-
let treeGridRowProps:
|
|
150
|
+
let treeGridRowProps: HTMLAttributes<HTMLElement> = {};
|
|
134
151
|
let hasChildRows = props.hasChildItems;
|
|
135
152
|
let hasLink = state.selectionManager.isLink(node.key);
|
|
136
153
|
if (node != null && 'expandedKeys' in state) {
|
|
@@ -175,27 +192,24 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
175
192
|
};
|
|
176
193
|
}
|
|
177
194
|
|
|
178
|
-
let { itemProps, ...itemStates } = useSelectableItem(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
onAction
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
},
|
|
192
|
-
subSlot(slot, 'selectableItem'),
|
|
193
|
-
);
|
|
195
|
+
let { itemProps, ...itemStates } = useSelectableItem({
|
|
196
|
+
selectionManager: state.selectionManager,
|
|
197
|
+
key: node.key,
|
|
198
|
+
ref,
|
|
199
|
+
isVirtualized,
|
|
200
|
+
shouldSelectOnPressUp: props.shouldSelectOnPressUp || shouldSelectOnPressUp,
|
|
201
|
+
onAction:
|
|
202
|
+
onAction || node.props?.onAction
|
|
203
|
+
? chain(node.props?.onAction, onAction ? () => onAction(node.key) : undefined)
|
|
204
|
+
: undefined,
|
|
205
|
+
focus,
|
|
206
|
+
linkBehavior,
|
|
207
|
+
});
|
|
194
208
|
|
|
195
|
-
let onKeyDownCapture = (e:
|
|
196
|
-
let activeElement = getActiveElement();
|
|
209
|
+
let onKeyDownCapture = (e: ReactKeyboardEvent) => {
|
|
210
|
+
let activeElement = getActiveElement(getOwnerDocument(ref.current));
|
|
197
211
|
if (
|
|
198
|
-
!nodeContains(e.currentTarget as
|
|
212
|
+
!nodeContains(e.currentTarget as Node, getEventTarget(e) as Element) ||
|
|
199
213
|
!ref.current ||
|
|
200
214
|
!activeElement
|
|
201
215
|
) {
|
|
@@ -206,7 +220,16 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
206
220
|
walker.currentNode = activeElement;
|
|
207
221
|
|
|
208
222
|
if (
|
|
209
|
-
handleTreeExpansionKeys(
|
|
223
|
+
handleTreeExpansionKeys(
|
|
224
|
+
e,
|
|
225
|
+
state,
|
|
226
|
+
node,
|
|
227
|
+
hasChildRows,
|
|
228
|
+
direction,
|
|
229
|
+
activeElement,
|
|
230
|
+
ref.current,
|
|
231
|
+
allowsArrowNavigation,
|
|
232
|
+
)
|
|
210
233
|
) {
|
|
211
234
|
return;
|
|
212
235
|
}
|
|
@@ -235,6 +258,7 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
235
258
|
} else {
|
|
236
259
|
walker.currentNode = ref.current;
|
|
237
260
|
let lastElement = last(walker);
|
|
261
|
+
// oxlint-disable-next-line max-depth
|
|
238
262
|
if (lastElement) {
|
|
239
263
|
focusSafely(lastElement);
|
|
240
264
|
scrollIntoViewport(lastElement, {
|
|
@@ -267,6 +291,7 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
267
291
|
} else {
|
|
268
292
|
walker.currentNode = ref.current;
|
|
269
293
|
let lastElement = last(walker);
|
|
294
|
+
// oxlint-disable-next-line max-depth
|
|
270
295
|
if (lastElement) {
|
|
271
296
|
focusSafely(lastElement);
|
|
272
297
|
scrollIntoViewport(lastElement, {
|
|
@@ -286,7 +311,6 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
286
311
|
if (!e.altKey && nodeContains(ref.current, getEventTarget(e) as Element)) {
|
|
287
312
|
e.stopPropagation();
|
|
288
313
|
e.preventDefault();
|
|
289
|
-
// octane adaptation: the native event IS `e.nativeEvent`.
|
|
290
314
|
ref.current.parentElement?.dispatchEvent(new KeyboardEvent(e.type, e));
|
|
291
315
|
}
|
|
292
316
|
break;
|
|
@@ -307,12 +331,33 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
307
331
|
}
|
|
308
332
|
return;
|
|
309
333
|
}
|
|
334
|
+
|
|
335
|
+
// if focus goes back to cell from child, make sure we don't refocus the cell if we are in focusMode=child
|
|
336
|
+
// since that would be a focus trap
|
|
337
|
+
if (
|
|
338
|
+
focusMode === 'child' &&
|
|
339
|
+
e.relatedTarget &&
|
|
340
|
+
nodeContains(ref.current, e.relatedTarget as Element)
|
|
341
|
+
) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// If the cell itself is focused, wait a frame so that focus finishes propagating
|
|
346
|
+
// up to the tree, and move focus to a focusable child if possible.
|
|
347
|
+
requestAnimationFrame(() => {
|
|
348
|
+
if (
|
|
349
|
+
focusMode === 'child' &&
|
|
350
|
+
getActiveElement(getOwnerDocument(ref.current)) === ref.current
|
|
351
|
+
) {
|
|
352
|
+
focus();
|
|
353
|
+
}
|
|
354
|
+
});
|
|
310
355
|
};
|
|
311
356
|
|
|
312
|
-
let onKeyDown = (e:
|
|
313
|
-
let activeElement = getActiveElement();
|
|
357
|
+
let onKeyDown = (e: ReactKeyboardEvent) => {
|
|
358
|
+
let activeElement = getActiveElement(getOwnerDocument(ref.current));
|
|
314
359
|
if (
|
|
315
|
-
!nodeContains(e.currentTarget as
|
|
360
|
+
!nodeContains(e.currentTarget as Node, getEventTarget(e) as Element) ||
|
|
316
361
|
!ref.current ||
|
|
317
362
|
!activeElement
|
|
318
363
|
) {
|
|
@@ -329,7 +374,16 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
329
374
|
}
|
|
330
375
|
|
|
331
376
|
if (
|
|
332
|
-
handleTreeExpansionKeys(
|
|
377
|
+
handleTreeExpansionKeys(
|
|
378
|
+
e,
|
|
379
|
+
state,
|
|
380
|
+
node,
|
|
381
|
+
hasChildRows,
|
|
382
|
+
direction,
|
|
383
|
+
activeElement,
|
|
384
|
+
ref.current,
|
|
385
|
+
allowsArrowNavigation,
|
|
386
|
+
)
|
|
333
387
|
) {
|
|
334
388
|
return;
|
|
335
389
|
}
|
|
@@ -364,56 +418,41 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
364
418
|
// });
|
|
365
419
|
// }
|
|
366
420
|
|
|
421
|
+
// oxlint-disable-next-line react/react-compiler
|
|
367
422
|
let rowProps: DOMAttributes = mergeProps(itemProps, linkProps, {
|
|
368
423
|
role: 'row',
|
|
369
|
-
onKeyDownCapture:
|
|
424
|
+
onKeyDownCapture:
|
|
425
|
+
keyboardNavigationBehavior === 'arrow' || allowsArrowNavigation
|
|
426
|
+
? onKeyDownCapture
|
|
427
|
+
: undefined,
|
|
370
428
|
onFocus,
|
|
371
429
|
// 'aria-label': [(node.textValue || undefined), rowAnnouncement].filter(Boolean).join(', '),
|
|
372
|
-
'aria-label':
|
|
430
|
+
'aria-label': node['aria-label'] || node.textValue || undefined,
|
|
373
431
|
'aria-selected': state.selectionManager.canSelectItem(node.key)
|
|
374
432
|
? state.selectionManager.isSelected(node.key)
|
|
375
433
|
: undefined,
|
|
376
434
|
'aria-disabled': state.selectionManager.isDisabled(node.key) || undefined,
|
|
377
435
|
'aria-labelledby':
|
|
378
|
-
descriptionId && (
|
|
436
|
+
descriptionId && (node['aria-label'] || node.textValue)
|
|
379
437
|
? `${getRowId(state, node.key)} ${descriptionId}`
|
|
380
438
|
: undefined,
|
|
381
439
|
id: getRowId(state, node.key),
|
|
382
440
|
});
|
|
383
441
|
|
|
384
|
-
|
|
385
|
-
|
|
442
|
+
if (focusMode === 'child' && allowsArrowNavigation && keyboardNavigationBehavior === 'tab') {
|
|
443
|
+
rowProps.tabIndex = -1;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Keep Space/Enter on a child control from reaching the row's usePress handler. With native
|
|
447
|
+
// events, stopPropagation() marks cancelBubble rather than exposing isPropagationStopped().
|
|
386
448
|
let baseOnKeyDown = rowProps.onKeyDown;
|
|
387
|
-
rowProps.onKeyDown = (e:
|
|
388
|
-
onKeyDown(e);
|
|
389
|
-
|
|
390
|
-
if (!(e as any).cancelBubble) {
|
|
449
|
+
rowProps.onKeyDown = (e: ReactKeyboardEvent<FocusableElement>) => {
|
|
450
|
+
onKeyDown(e as ReactKeyboardEvent);
|
|
451
|
+
if (!e.cancelBubble) {
|
|
391
452
|
baseOnKeyDown?.(e);
|
|
392
453
|
}
|
|
393
454
|
};
|
|
394
455
|
|
|
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
456
|
if (isVirtualized) {
|
|
418
457
|
let { collection } = state;
|
|
419
458
|
let nodes = [...collection];
|
|
@@ -431,6 +470,7 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
431
470
|
};
|
|
432
471
|
|
|
433
472
|
// TODO: should isExpanded and hasChildRows be a item state that gets returned by the hook?
|
|
473
|
+
// oxlint-disable react/react-compiler
|
|
434
474
|
return {
|
|
435
475
|
rowProps: { ...mergeProps(rowProps, treeGridRowProps) },
|
|
436
476
|
gridCellProps,
|
|
@@ -439,22 +479,24 @@ export function useGridListItem(...args: any[]): GridListItemAria {
|
|
|
439
479
|
},
|
|
440
480
|
...itemStates,
|
|
441
481
|
};
|
|
482
|
+
// oxlint-enable react/react-compiler
|
|
442
483
|
}
|
|
443
484
|
|
|
444
485
|
function handleTreeExpansionKeys<T>(
|
|
445
|
-
e:
|
|
486
|
+
e: ReactKeyboardEvent,
|
|
446
487
|
state: ListState<T> | TreeState<T>,
|
|
447
488
|
node: RSNode<unknown>,
|
|
448
489
|
hasChildRows: boolean | undefined,
|
|
449
|
-
direction:
|
|
490
|
+
direction: 'ltr' | 'rtl',
|
|
450
491
|
activeElement: Element | null,
|
|
451
492
|
rowRef: FocusableElement | null,
|
|
493
|
+
allowsArrowNavigation: boolean | undefined,
|
|
452
494
|
): boolean {
|
|
453
|
-
if (!('expandedKeys' in state) || activeElement !== rowRef) {
|
|
495
|
+
if (!('expandedKeys' in state) || (!allowsArrowNavigation && activeElement !== rowRef)) {
|
|
454
496
|
return false;
|
|
455
497
|
}
|
|
456
498
|
if (
|
|
457
|
-
e.key ===
|
|
499
|
+
e.key === EXPANSION_KEYS['expand'][direction] &&
|
|
458
500
|
state.selectionManager.focusedKey === node.key &&
|
|
459
501
|
hasChildRows &&
|
|
460
502
|
!state.expandedKeys.has(node.key)
|
|
@@ -463,7 +505,7 @@ function handleTreeExpansionKeys<T>(
|
|
|
463
505
|
e.stopPropagation();
|
|
464
506
|
return true;
|
|
465
507
|
} else if (
|
|
466
|
-
e.key ===
|
|
508
|
+
e.key === EXPANSION_KEYS['collapse'][direction] &&
|
|
467
509
|
state.selectionManager.focusedKey === node.key
|
|
468
510
|
) {
|
|
469
511
|
// 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
|
};
|