@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.
Files changed (201) hide show
  1. package/README.md +7 -6
  2. package/UPSTREAM.md +58 -69
  3. package/package.json +11 -11
  4. package/src/calendar/useCalendarGrid.tsx +64 -62
  5. package/src/collections/Document.ts +3 -0
  6. package/src/color/useColorArea.tsx +95 -69
  7. package/src/color/useColorWheel.tsx +38 -37
  8. package/src/combobox/{useComboBox.ts → useComboBox.tsx} +239 -259
  9. package/src/components/ComboBox.ts +35 -2
  10. package/src/components/Dialog.ts +12 -1
  11. package/src/components/DropZone.tsx +3 -2
  12. package/src/components/FileTrigger.tsx +1 -0
  13. package/src/components/GridList.ts +11 -2
  14. package/src/components/ListBox.ts +1 -1
  15. package/src/components/Menu.ts +1 -0
  16. package/src/components/Meter.ts +3 -1
  17. package/src/components/Popover.ts +20 -8
  18. package/src/components/PreviewTrigger.tsx +101 -0
  19. package/src/components/ProgressBar.ts +10 -2
  20. package/src/components/Select.ts +1 -1
  21. package/src/components/Table.ts +26 -15
  22. package/src/components/TokenField.tsx +395 -0
  23. package/src/components/Tooltip.ts +4 -6
  24. package/src/components/Tree.ts +14 -7
  25. package/src/components/Virtualizer.tsx +30 -14
  26. package/src/components/index.ts +22 -0
  27. package/src/components/utils.ts +1 -1
  28. package/src/datepicker/useDateField.tsx +2 -11
  29. package/src/datepicker/useDatePickerGroup.tsx +44 -33
  30. package/src/datepicker/useDateSegment.tsx +31 -30
  31. package/src/dialog/useDialog.ts +8 -1
  32. package/src/dnd/DragManager.tsx +5 -1
  33. package/src/dnd/useDrag.tsx +2 -1
  34. package/src/focus/FocusScope.ts +2 -2
  35. package/src/grid/useGrid.ts +3 -0
  36. package/src/grid/{useGridCell.ts → useGridCell.tsx} +121 -64
  37. package/src/grid/utils.ts +1 -0
  38. package/src/gridlist/{useGridListItem.ts → useGridListItem.tsx} +152 -111
  39. package/src/index.ts +22 -0
  40. package/src/interactions/createEventHandler.ts +11 -8
  41. package/src/interactions/createKeyboardShortcutHandler.tsx +218 -0
  42. package/src/interactions/textSelection.ts +3 -3
  43. package/src/interactions/useContextMenu.tsx +125 -0
  44. package/src/interactions/useFocusVisible.ts +4 -4
  45. package/src/interactions/useKeyboard.ts +50 -2
  46. package/src/interactions/useLongPress.ts +33 -22
  47. package/src/interactions/useMove.ts +6 -6
  48. package/src/interactions/usePress.ts +8 -0
  49. package/src/interactions/utils.ts +10 -10
  50. package/src/intl/combobox/fi-FI.json +2 -2
  51. package/src/intl/previewtrigger/ar-AE.json +3 -0
  52. package/src/intl/previewtrigger/bg-BG.json +3 -0
  53. package/src/intl/previewtrigger/cs-CZ.json +3 -0
  54. package/src/intl/previewtrigger/da-DK.json +3 -0
  55. package/src/intl/previewtrigger/de-DE.json +3 -0
  56. package/src/intl/previewtrigger/el-GR.json +3 -0
  57. package/src/intl/previewtrigger/en-US.json +3 -0
  58. package/src/intl/previewtrigger/es-ES.json +3 -0
  59. package/src/intl/previewtrigger/et-EE.json +3 -0
  60. package/src/intl/previewtrigger/fi-FI.json +3 -0
  61. package/src/intl/previewtrigger/fr-FR.json +3 -0
  62. package/src/intl/previewtrigger/he-IL.json +3 -0
  63. package/src/intl/previewtrigger/hr-HR.json +3 -0
  64. package/src/intl/previewtrigger/hu-HU.json +3 -0
  65. package/src/intl/previewtrigger/index.ts +73 -0
  66. package/src/intl/previewtrigger/it-IT.json +3 -0
  67. package/src/intl/previewtrigger/ja-JP.json +3 -0
  68. package/src/intl/previewtrigger/ko-KR.json +3 -0
  69. package/src/intl/previewtrigger/lt-LT.json +3 -0
  70. package/src/intl/previewtrigger/lv-LV.json +3 -0
  71. package/src/intl/previewtrigger/nb-NO.json +3 -0
  72. package/src/intl/previewtrigger/nl-NL.json +3 -0
  73. package/src/intl/previewtrigger/pl-PL.json +3 -0
  74. package/src/intl/previewtrigger/pt-BR.json +3 -0
  75. package/src/intl/previewtrigger/pt-PT.json +3 -0
  76. package/src/intl/previewtrigger/ro-RO.json +3 -0
  77. package/src/intl/previewtrigger/ru-RU.json +3 -0
  78. package/src/intl/previewtrigger/sk-SK.json +3 -0
  79. package/src/intl/previewtrigger/sl-SI.json +3 -0
  80. package/src/intl/previewtrigger/sr-SP.json +3 -0
  81. package/src/intl/previewtrigger/sv-SE.json +3 -0
  82. package/src/intl/previewtrigger/tr-TR.json +3 -0
  83. package/src/intl/previewtrigger/uk-UA.json +3 -0
  84. package/src/intl/previewtrigger/zh-CN.json +3 -0
  85. package/src/intl/previewtrigger/zh-TW.json +3 -0
  86. package/src/intl/table/ar-AE.json +3 -3
  87. package/src/intl/table/bg-BG.json +3 -3
  88. package/src/intl/table/cs-CZ.json +3 -3
  89. package/src/intl/table/da-DK.json +3 -3
  90. package/src/intl/table/de-DE.json +3 -3
  91. package/src/intl/table/el-GR.json +3 -3
  92. package/src/intl/table/es-ES.json +3 -3
  93. package/src/intl/table/et-EE.json +3 -3
  94. package/src/intl/table/fi-FI.json +3 -3
  95. package/src/intl/table/fr-FR.json +3 -3
  96. package/src/intl/table/he-IL.json +3 -3
  97. package/src/intl/table/hr-HR.json +3 -3
  98. package/src/intl/table/hu-HU.json +3 -3
  99. package/src/intl/table/it-IT.json +3 -3
  100. package/src/intl/table/ja-JP.json +3 -3
  101. package/src/intl/table/ko-KR.json +3 -3
  102. package/src/intl/table/lt-LT.json +3 -3
  103. package/src/intl/table/lv-LV.json +3 -3
  104. package/src/intl/table/nb-NO.json +3 -3
  105. package/src/intl/table/nl-NL.json +3 -3
  106. package/src/intl/table/pl-PL.json +3 -3
  107. package/src/intl/table/pt-BR.json +3 -3
  108. package/src/intl/table/pt-PT.json +3 -3
  109. package/src/intl/table/ro-RO.json +3 -3
  110. package/src/intl/table/ru-RU.json +3 -3
  111. package/src/intl/table/sk-SK.json +3 -3
  112. package/src/intl/table/sl-SI.json +3 -3
  113. package/src/intl/table/sr-SP.json +3 -3
  114. package/src/intl/table/sv-SE.json +3 -3
  115. package/src/intl/table/tr-TR.json +3 -3
  116. package/src/intl/table/uk-UA.json +3 -3
  117. package/src/intl/table/zh-CN.json +4 -4
  118. package/src/intl/table/zh-TW.json +3 -3
  119. package/src/intl/tree/es-ES.json +1 -1
  120. package/src/link/useLink.ts +2 -0
  121. package/src/menu/useMenuItem.ts +33 -36
  122. package/src/menu/useMenuTrigger.tsx +217 -0
  123. package/src/menu/{useSubmenuTrigger.ts → useSubmenuTrigger.tsx} +112 -152
  124. package/src/numberfield/{useNumberField.ts → useNumberField.tsx} +185 -239
  125. package/src/overlays/Overlay.ts +5 -1
  126. package/src/overlays/ariaHideOutside.ts +6 -8
  127. package/src/overlays/useCloseOnScroll.ts +7 -5
  128. package/src/overlays/{useOverlay.ts → useOverlay.tsx} +72 -84
  129. package/src/overlays/usePopover.ts +10 -5
  130. package/src/overlays/usePreventScroll.ts +4 -4
  131. package/src/progress/useProgressBar.ts +2 -1
  132. package/src/radio/{useRadioGroup.ts → useRadioGroup.tsx} +83 -80
  133. package/src/searchfield/useSearchField.tsx +147 -0
  134. package/src/select/useSelect.ts +20 -20
  135. package/src/selection/useSelectableCollection.tsx +751 -0
  136. package/src/selection/useSelectableItem.ts +48 -11
  137. package/src/slider/{useSliderThumb.ts → useSliderThumb.tsx} +116 -159
  138. package/src/spinbutton/useSpinButton.tsx +330 -0
  139. package/src/stately/combobox/useComboBoxState.ts +1 -1
  140. package/src/stately/grid/useGridState.ts +9 -10
  141. package/src/stately/index.ts +11 -0
  142. package/src/stately/menu/useMenuTriggerState.ts +1 -1
  143. package/src/stately/menu/useSubmenuTriggerState.ts +2 -0
  144. package/src/stately/overlays/useOverlayTriggerState.ts +12 -1
  145. package/src/stately/radio/useRadioGroupState.ts +5 -1
  146. package/src/stately/table/TableUtils.ts +15 -1
  147. package/src/stately/table/useTableState.ts +7 -0
  148. package/src/stately/tokenfield/TokenFieldValue.tsx +440 -0
  149. package/src/stately/tokenfield/useTokenFieldState.tsx +57 -0
  150. package/src/stately/tooltip/useTooltipTriggerState.ts +16 -7
  151. package/src/stately/virtualizer/Layout.tsx +2 -0
  152. package/src/stately/virtualizer/ScrollAnchor.tsx +338 -0
  153. package/src/stately/virtualizer/Virtualizer.tsx +52 -1
  154. package/src/stately/virtualizer/types.tsx +2 -0
  155. package/src/table/useTableCell.ts +12 -0
  156. package/src/table/useTableColumnHeader.ts +8 -1
  157. package/src/table/useTableColumnResize.tsx +354 -0
  158. package/src/tag/useTag.ts +22 -15
  159. package/src/textfield/useTextField.ts +2 -0
  160. package/src/tokenfield/useToken.tsx +64 -0
  161. package/src/tokenfield/useTokenField.tsx +849 -0
  162. package/src/tooltip/usePreviewTrigger.tsx +266 -0
  163. package/src/tooltip/useSafeArea.tsx +171 -0
  164. package/src/upstream-exports/react-aria/mergeRefs.tsx +3 -0
  165. package/src/upstream-exports/react-aria/usePreviewTrigger.tsx +18 -0
  166. package/src/upstream-exports/react-aria/useTokenField.tsx +22 -0
  167. package/src/upstream-exports/react-stately/private/collections/getChildNodes.tsx +8 -0
  168. package/src/upstream-exports/react-stately/private/collections/getItemCount.tsx +2 -0
  169. package/src/upstream-exports/react-stately/private/grid/GridCollection.tsx +7 -0
  170. package/src/upstream-exports/react-stately/private/grid/useGridState.tsx +6 -0
  171. package/src/upstream-exports/react-stately/private/utils/number.tsx +2 -0
  172. package/src/upstream-exports/react-stately/useComboBoxState.tsx +23 -0
  173. package/src/upstream-exports/react-stately/useListState.tsx +15 -0
  174. package/src/upstream-exports/react-stately/useMenuTriggerState.tsx +26 -0
  175. package/src/upstream-exports/react-stately/useMultipleSelectionState.tsx +21 -0
  176. package/src/upstream-exports/react-stately/useNumberFieldState.tsx +20 -0
  177. package/src/upstream-exports/react-stately/useOverlayTriggerState.tsx +20 -0
  178. package/src/upstream-exports/react-stately/useRadioGroupState.tsx +16 -0
  179. package/src/upstream-exports/react-stately/useSearchFieldState.tsx +19 -0
  180. package/src/upstream-exports/react-stately/useSliderState.tsx +20 -0
  181. package/src/upstream-exports/react-stately/useTableState.tsx +45 -0
  182. package/src/upstream-exports/react-stately/useTokenFieldState.tsx +25 -0
  183. package/src/upstream-exports/react-stately/useTooltipTriggerState.tsx +20 -0
  184. package/src/upstream-exports/react-stately/useTreeState.tsx +14 -0
  185. package/src/utils/domHelpers.ts +74 -17
  186. package/src/utils/isVirtualEvent.ts +2 -1
  187. package/src/utils/openLink.ts +8 -3
  188. package/src/utils/platform.ts +7 -3
  189. package/src/utils/scrollIntoView.ts +4 -4
  190. package/src/utils/shadowdom/DOMFunctions.ts +42 -2
  191. package/src/utils/useId.ts +11 -2
  192. package/src/utils/useLoadMoreSentinel.ts +8 -3
  193. package/src/utils/useViewportSize.ts +3 -3
  194. package/src/virtualizer/ScrollView.tsx +13 -4
  195. package/src/virtualizer/VirtualizerItem.tsx +3 -1
  196. package/src/virtualizer/useVirtualizerItem.tsx +43 -3
  197. package/src/menu/useMenuTrigger.ts +0 -189
  198. package/src/searchfield/useSearchField.ts +0 -156
  199. package/src/selection/useSelectableCollection.ts +0 -750
  200. package/src/spinbutton/useSpinButton.ts +0 -380
  201. package/src/table/useTableColumnResize.ts +0 -374
@@ -1,20 +1,21 @@
1
- // Ported from react-aria (source: .react-spectrum/packages/react-aria/src/menu/useSubmenuTrigger.ts).
2
- // octane adaptations:
3
- // - Handlers receive NATIVE events: `submenuKeyDown` gets the native KeyboardEvent through
4
- // the menu's merged onKeyDown chain; `submenuTriggerKeyDown` gets the ported
5
- // BaseEvent-wrapped KeyboardEvent (it flows through useMenuItem's useKeyboard, which
6
- // supplies `continuePropagation`).
7
- // - The popover container types (`AriaPopoverProps` from overlays/usePopover and
8
- // `OverlayProps` from overlays/Overlay) are NOT yet ported (overlay positioning/modal
9
- // machinery is out of scope); `SubmenuTriggerAria.popoverProps` is a local structural
10
- // equivalent of upstream's picked fields.
11
- // - `SubmenuTriggerState` type from the ported stately menu state.
12
- // - Public-hook slot threading (splitSlot/subSlot) per the binding convention; explicit
13
- // dependency arrays are kept verbatim.
14
- import type { AriaMenuItemProps } from './useMenuItem';
15
- import type { AriaMenuOptions } from './useMenu';
16
- import type { BaseEvent } from '../interactions/createEventHandler';
17
- import type { FocusableElement, FocusStrategy, RefObject } from '@react-types/shared';
1
+ /** @jsxImportSource octane */
2
+ // Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/src/menu/useSubmenuTrigger.ts).
3
+ /*
4
+ * Copyright 2023 Adobe. All rights reserved.
5
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License. You may obtain a copy
7
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software distributed under
10
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
11
+ * OF ANY KIND, either express or implied. See the License for the specific language
12
+ * governing permissions and limitations under the License.
13
+ */
14
+
15
+ import { AriaMenuItemProps } from './useMenuItem';
16
+ import { AriaMenuOptions } from './useMenu';
17
+ import type { AriaPopoverProps } from '../overlays/usePopover';
18
+ import { FocusableElement, FocusStrategy, Node, PressEvent, RefObject } from '@react-types/shared';
18
19
  import { focusWithoutScrolling } from '../utils/focusWithoutScrolling';
19
20
  import {
20
21
  getActiveElement,
@@ -22,17 +23,16 @@ import {
22
23
  isFocusWithin,
23
24
  nodeContains,
24
25
  } from '../utils/shadowdom/DOMFunctions';
25
- import type { PressEvent } from '@react-types/shared';
26
- import type { SubmenuTriggerState } from '../stately/menu/useSubmenuTriggerState';
27
- import { useCallback, useRef } from 'octane';
26
+ import type { OverlayProps } from '../overlays/Overlay';
27
+ import type { SubmenuTriggerState } from '../upstream-exports/react-stately/useMenuTriggerState';
28
+ import { useCallback, useRef } from '../compat/react';
28
29
  import { useEvent } from '../utils/useEvent';
29
30
  import { useId } from '../utils/useId';
31
+ import { useKeyboard } from '../interactions/useKeyboard';
30
32
  import { useLayoutEffect } from '../utils/useLayoutEffect';
31
33
  import { useLocale } from '../i18n/I18nProvider';
32
34
  import { useSafelyMouseToSubmenu } from './useSafelyMouseToSubmenu';
33
35
 
34
- import { S, splitSlot, subSlot } from '../internal';
35
-
36
36
  export interface AriaSubmenuTriggerProps {
37
37
  /**
38
38
  * An object representing the submenu trigger menu item. Contains all the relevant information
@@ -40,7 +40,7 @@ export interface AriaSubmenuTriggerProps {
40
40
  *
41
41
  * @deprecated
42
42
  */
43
- node?: any;
43
+ node?: Node<unknown>;
44
44
  /** Whether the submenu trigger is disabled. */
45
45
  isDisabled?: boolean;
46
46
  /** The type of the contents that the submenu trigger opens. */
@@ -74,18 +74,9 @@ export interface SubmenuTriggerAria<T> {
74
74
  submenuTriggerProps: SubmenuTriggerProps;
75
75
  /** Props for the submenu controlled by the submenu trigger menu item. */
76
76
  submenuProps: SubmenuProps<T>;
77
- /**
78
- * Props for the submenu's popover container.
79
- *
80
- * octane adaptation: a structural equivalent of upstream's
81
- * `Pick<AriaPopoverProps, 'isNonModal' | 'shouldCloseOnInteractOutside'> &
82
- * Pick<OverlayProps, 'disableFocusManagement'>` (the overlays area is not yet ported).
83
- */
84
- popoverProps: {
85
- isNonModal?: boolean;
86
- shouldCloseOnInteractOutside?: (element: Element) => boolean;
87
- disableFocusManagement?: boolean;
88
- };
77
+ /** Props for the submenu's popover container. */
78
+ popoverProps: Pick<AriaPopoverProps, 'isNonModal' | 'shouldCloseOnInteractOutside'> &
79
+ Pick<OverlayProps, 'disableFocusManagement'>;
89
80
  }
90
81
 
91
82
  /**
@@ -100,21 +91,8 @@ export function useSubmenuTrigger<T>(
100
91
  props: AriaSubmenuTriggerProps,
101
92
  state: SubmenuTriggerState,
102
93
  ref: RefObject<FocusableElement | null>,
103
- ): SubmenuTriggerAria<T>;
104
- // Slot-threading form: sibling ported hooks pass their derived sub-slot as the trailing arg.
105
- export function useSubmenuTrigger<T>(
106
- props: AriaSubmenuTriggerProps,
107
- state: SubmenuTriggerState,
108
- ref: RefObject<FocusableElement | null>,
109
- slot: symbol | undefined,
110
- ): SubmenuTriggerAria<T>;
111
- export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
112
- const [user, slotArg] = splitSlot(args);
113
- const slot = slotArg ?? S('useSubmenuTrigger');
114
- const props = user[0] as AriaSubmenuTriggerProps;
115
- const state = user[1] as SubmenuTriggerState;
116
- const ref = user[2] as RefObject<FocusableElement | null>;
117
-
94
+ _slot?: symbol,
95
+ ): SubmenuTriggerAria<T> {
118
96
  let {
119
97
  parentMenuRef,
120
98
  submenuRef,
@@ -123,23 +101,16 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
123
101
  delay = 200,
124
102
  shouldUseVirtualFocus,
125
103
  } = props;
126
- let submenuTriggerId = useId(subSlot(slot, 'triggerId'));
127
- let overlayId = useId(subSlot(slot, 'overlayId'));
128
- let { direction } = useLocale(subSlot(slot, 'locale'));
129
- let openTimeout = useRef<ReturnType<typeof setTimeout> | undefined>(
130
- undefined,
131
- subSlot(slot, 'openTimeout'),
132
- );
133
- let cancelOpenTimeout = useCallback(
134
- () => {
135
- if (openTimeout.current) {
136
- clearTimeout(openTimeout.current);
137
- openTimeout.current = undefined;
138
- }
139
- },
140
- [openTimeout],
141
- subSlot(slot, 'cancelOpen'),
142
- );
104
+ let submenuTriggerId = useId();
105
+ let overlayId = useId();
106
+ let { direction } = useLocale();
107
+ let openTimeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
108
+ let cancelOpenTimeout = useCallback(() => {
109
+ if (openTimeout.current) {
110
+ clearTimeout(openTimeout.current);
111
+ openTimeout.current = undefined;
112
+ }
113
+ }, [openTimeout]);
143
114
 
144
115
  let onSubmenuOpen = useCallback(
145
116
  (focusStrategy?: FocusStrategy) => {
@@ -147,74 +118,70 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
147
118
  state.open(focusStrategy);
148
119
  },
149
120
  [state, cancelOpenTimeout],
150
- subSlot(slot, 'open'),
151
121
  );
152
122
 
153
- let onSubmenuClose = useCallback(
154
- () => {
155
- cancelOpenTimeout();
156
- state.close();
157
- },
158
- [state, cancelOpenTimeout],
159
- subSlot(slot, 'close'),
160
- );
123
+ let onSubmenuClose = useCallback(() => {
124
+ cancelOpenTimeout();
125
+ state.close();
126
+ }, [state, cancelOpenTimeout]);
161
127
 
162
- useLayoutEffect(
163
- () => {
164
- return () => {
165
- cancelOpenTimeout();
166
- };
167
- },
168
- [cancelOpenTimeout],
169
- subSlot(slot, 'teardown'),
170
- );
171
-
172
- let submenuKeyDown = (e: KeyboardEvent) => {
173
- // If focus is not within the menu, assume virtual focus is being used.
174
- // This means some other input element is also within the popover, so we shouldn't close the menu.
175
- if (!isFocusWithin(e.currentTarget as Element)) {
176
- return;
177
- }
128
+ useLayoutEffect(() => {
129
+ return () => {
130
+ cancelOpenTimeout();
131
+ };
132
+ }, [cancelOpenTimeout]);
178
133
 
179
- switch (e.key) {
180
- case 'ArrowLeft':
134
+ let { keyboardProps } = useKeyboard({
135
+ shortcuts: {
136
+ ArrowLeft: (e) => {
137
+ // If focus is not within the menu, assume virtual focus is being used.
138
+ // This means some other input element is also within the popover, so we shouldn't close the menu.
139
+ if (!isFocusWithin(e.currentTarget as Element)) {
140
+ return false;
141
+ }
181
142
  if (
182
143
  direction === 'ltr' &&
183
- nodeContains(e.currentTarget as Element, getEventTarget(e) as Element)
144
+ nodeContains(e.currentTarget as globalThis.Node, getEventTarget(e) as Element)
184
145
  ) {
185
- e.preventDefault();
186
- e.stopPropagation();
187
146
  onSubmenuClose();
188
147
  if (!shouldUseVirtualFocus && ref.current) {
189
148
  focusWithoutScrolling(ref.current);
190
149
  }
150
+ return;
151
+ }
152
+ return false;
153
+ },
154
+ ArrowRight: (e) => {
155
+ if (!isFocusWithin(e.currentTarget as Element)) {
156
+ return false;
191
157
  }
192
- break;
193
- case 'ArrowRight':
194
158
  if (
195
159
  direction === 'rtl' &&
196
- nodeContains(e.currentTarget as Element, getEventTarget(e) as Element)
160
+ nodeContains(e.currentTarget as globalThis.Node, getEventTarget(e) as Element)
197
161
  ) {
198
- e.preventDefault();
199
- e.stopPropagation();
200
162
  onSubmenuClose();
201
163
  if (!shouldUseVirtualFocus && ref.current) {
202
164
  focusWithoutScrolling(ref.current);
203
165
  }
166
+ return;
167
+ }
168
+ return false;
169
+ },
170
+ Escape: (e) => {
171
+ if (!isFocusWithin(e.currentTarget as Element)) {
172
+ return false;
204
173
  }
205
- break;
206
- case 'Escape':
207
- // TODO: can remove this when we fix collection event leaks
208
174
  if (nodeContains(submenuRef.current, getEventTarget(e) as Element)) {
209
- e.stopPropagation();
210
175
  onSubmenuClose();
211
176
  if (!shouldUseVirtualFocus && ref.current) {
212
177
  focusWithoutScrolling(ref.current);
213
178
  }
179
+ return;
214
180
  }
215
- break;
216
- }
217
- };
181
+ return false;
182
+ },
183
+ },
184
+ });
218
185
 
219
186
  let submenuProps = {
220
187
  id: overlayId,
@@ -223,16 +190,15 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
223
190
  ...(type === 'menu' && {
224
191
  onClose: state.closeAll,
225
192
  autoFocus: state.focusStrategy ?? undefined,
226
- onKeyDown: submenuKeyDown,
193
+ ...keyboardProps,
227
194
  }),
228
195
  };
229
196
 
230
- let submenuTriggerKeyDown = (e: BaseEvent<KeyboardEvent>) => {
231
- switch (e.key) {
232
- case 'ArrowRight':
197
+ let { keyboardProps: submenuTriggerKeyboardProps } = useKeyboard({
198
+ shortcuts: {
199
+ ArrowRight: () => {
233
200
  if (!isDisabled) {
234
201
  if (direction === 'ltr') {
235
- e.preventDefault();
236
202
  if (!state.isOpen) {
237
203
  onSubmenuOpen('first');
238
204
  }
@@ -240,18 +206,19 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
240
206
  if (type === 'menu' && !!submenuRef?.current && getActiveElement() === ref?.current) {
241
207
  focusWithoutScrolling(submenuRef.current);
242
208
  }
209
+ return;
243
210
  } else if (state.isOpen) {
244
211
  onSubmenuClose();
212
+ return;
245
213
  } else {
246
- e.continuePropagation();
214
+ return false;
247
215
  }
248
216
  }
249
-
250
- break;
251
- case 'ArrowLeft':
217
+ return false;
218
+ },
219
+ ArrowLeft: () => {
252
220
  if (!isDisabled) {
253
221
  if (direction === 'rtl') {
254
- e.preventDefault();
255
222
  if (!state.isOpen) {
256
223
  onSubmenuOpen('first');
257
224
  }
@@ -259,18 +226,18 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
259
226
  if (type === 'menu' && !!submenuRef?.current && getActiveElement() === ref?.current) {
260
227
  focusWithoutScrolling(submenuRef.current);
261
228
  }
229
+ return;
262
230
  } else if (state.isOpen) {
263
231
  onSubmenuClose();
232
+ return;
264
233
  } else {
265
- e.continuePropagation();
234
+ return false;
266
235
  }
267
236
  }
268
- break;
269
- default:
270
- e.continuePropagation();
271
- break;
272
- }
273
- };
237
+ return false;
238
+ },
239
+ },
240
+ });
274
241
 
275
242
  let onPressStart = (e: PressEvent) => {
276
243
  if (!isDisabled && (e.pointerType === 'virtual' || e.pointerType === 'keyboard')) {
@@ -301,22 +268,17 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
301
268
  }
302
269
  };
303
270
 
304
- useEvent(
305
- parentMenuRef,
306
- 'focusin',
307
- (e) => {
308
- // If we detect focus moved to a different item in the same menu that the currently open submenu trigger is in
309
- // then close the submenu. This is for a case where the user hovers a root menu item when multiple submenus are open
310
- if (
311
- state.isOpen &&
312
- nodeContains(parentMenuRef.current, getEventTarget(e) as HTMLElement) &&
313
- getEventTarget(e) !== ref.current
314
- ) {
315
- onSubmenuClose();
316
- }
317
- },
318
- subSlot(slot, 'focusin'),
319
- );
271
+ useEvent(parentMenuRef, 'focusin', (e) => {
272
+ // If we detect focus moved to a different item in the same menu that the currently open submenu trigger is in
273
+ // then close the submenu. This is for a case where the user hovers a root menu item when multiple submenus are open
274
+ if (
275
+ state.isOpen &&
276
+ nodeContains(parentMenuRef.current, getEventTarget(e) as HTMLElement) &&
277
+ getEventTarget(e) !== ref.current
278
+ ) {
279
+ onSubmenuClose();
280
+ }
281
+ });
320
282
 
321
283
  let shouldCloseOnInteractOutside = (target: Element) => {
322
284
  if (target !== ref.current) {
@@ -326,18 +288,17 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
326
288
  return false;
327
289
  };
328
290
 
329
- useSafelyMouseToSubmenu(
330
- {
331
- menuRef: parentMenuRef,
332
- submenuRef,
333
- isOpen: state.isOpen,
334
- isDisabled: isDisabled,
335
- },
336
- subSlot(slot, 'safeMouse'),
337
- );
291
+ useSafelyMouseToSubmenu({
292
+ menuRef: parentMenuRef,
293
+ submenuRef,
294
+ isOpen: state.isOpen,
295
+ isDisabled: isDisabled,
296
+ });
338
297
 
339
298
  return {
340
299
  submenuTriggerProps: {
300
+ onKeyDown: submenuTriggerKeyboardProps.onKeyDown,
301
+ onKeyUp: submenuTriggerKeyboardProps.onKeyUp,
341
302
  id: submenuTriggerId,
342
303
  'aria-controls': state.isOpen ? overlayId : undefined,
343
304
  'aria-haspopup': !isDisabled ? type : undefined,
@@ -345,7 +306,6 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
345
306
  onPressStart,
346
307
  onPress,
347
308
  onHoverChange,
348
- onKeyDown: submenuTriggerKeyDown,
349
309
  isOpen: state.isOpen,
350
310
  },
351
311
  submenuProps,