@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.
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} +243 -258
  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} +128 -66
  37. package/src/grid/utils.ts +1 -0
  38. package/src/gridlist/{useGridListItem.ts → useGridListItem.tsx} +154 -112
  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 +222 -0
  123. package/src/menu/{useSubmenuTrigger.ts → useSubmenuTrigger.tsx} +116 -151
  124. package/src/numberfield/{useNumberField.ts → useNumberField.tsx} +188 -238
  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} +76 -83
  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} +87 -79
  133. package/src/searchfield/useSearchField.tsx +152 -0
  134. package/src/select/useSelect.ts +20 -20
  135. package/src/selection/useSelectableCollection.tsx +755 -0
  136. package/src/selection/useSelectableItem.ts +48 -11
  137. package/src/slider/{useSliderThumb.ts → useSliderThumb.tsx} +120 -158
  138. package/src/spinbutton/useSpinButton.tsx +335 -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 +359 -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 +4 -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,22 @@
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 { withSlot } from 'octane';
17
+ import { AriaMenuOptions } from './useMenu';
18
+ import type { AriaPopoverProps } from '../overlays/usePopover';
19
+ import { FocusableElement, FocusStrategy, Node, PressEvent, RefObject } from '@react-types/shared';
18
20
  import { focusWithoutScrolling } from '../utils/focusWithoutScrolling';
19
21
  import {
20
22
  getActiveElement,
@@ -22,17 +24,16 @@ import {
22
24
  isFocusWithin,
23
25
  nodeContains,
24
26
  } 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';
27
+ import type { OverlayProps } from '../overlays/Overlay';
28
+ import type { SubmenuTriggerState } from '../upstream-exports/react-stately/useMenuTriggerState';
29
+ import { useCallback, useRef } from '../compat/react';
28
30
  import { useEvent } from '../utils/useEvent';
29
31
  import { useId } from '../utils/useId';
32
+ import { useKeyboard } from '../interactions/useKeyboard';
30
33
  import { useLayoutEffect } from '../utils/useLayoutEffect';
31
34
  import { useLocale } from '../i18n/I18nProvider';
32
35
  import { useSafelyMouseToSubmenu } from './useSafelyMouseToSubmenu';
33
36
 
34
- import { S, splitSlot, subSlot } from '../internal';
35
-
36
37
  export interface AriaSubmenuTriggerProps {
37
38
  /**
38
39
  * An object representing the submenu trigger menu item. Contains all the relevant information
@@ -40,7 +41,7 @@ export interface AriaSubmenuTriggerProps {
40
41
  *
41
42
  * @deprecated
42
43
  */
43
- node?: any;
44
+ node?: Node<unknown>;
44
45
  /** Whether the submenu trigger is disabled. */
45
46
  isDisabled?: boolean;
46
47
  /** The type of the contents that the submenu trigger opens. */
@@ -74,18 +75,9 @@ export interface SubmenuTriggerAria<T> {
74
75
  submenuTriggerProps: SubmenuTriggerProps;
75
76
  /** Props for the submenu controlled by the submenu trigger menu item. */
76
77
  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
- };
78
+ /** Props for the submenu's popover container. */
79
+ popoverProps: Pick<AriaPopoverProps, 'isNonModal' | 'shouldCloseOnInteractOutside'> &
80
+ Pick<OverlayProps, 'disableFocusManagement'>;
89
81
  }
90
82
 
91
83
  /**
@@ -100,20 +92,11 @@ export function useSubmenuTrigger<T>(
100
92
  props: AriaSubmenuTriggerProps,
101
93
  state: SubmenuTriggerState,
102
94
  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>;
95
+ _slot?: symbol,
96
+ ): SubmenuTriggerAria<T> {
97
+ if (_slot !== undefined) {
98
+ return withSlot(_slot, useSubmenuTrigger<T>, props, state, ref);
99
+ }
117
100
 
118
101
  let {
119
102
  parentMenuRef,
@@ -123,23 +106,16 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
123
106
  delay = 200,
124
107
  shouldUseVirtualFocus,
125
108
  } = 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
- );
109
+ let submenuTriggerId = useId();
110
+ let overlayId = useId();
111
+ let { direction } = useLocale();
112
+ let openTimeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
113
+ let cancelOpenTimeout = useCallback(() => {
114
+ if (openTimeout.current) {
115
+ clearTimeout(openTimeout.current);
116
+ openTimeout.current = undefined;
117
+ }
118
+ }, [openTimeout]);
143
119
 
144
120
  let onSubmenuOpen = useCallback(
145
121
  (focusStrategy?: FocusStrategy) => {
@@ -147,74 +123,70 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
147
123
  state.open(focusStrategy);
148
124
  },
149
125
  [state, cancelOpenTimeout],
150
- subSlot(slot, 'open'),
151
126
  );
152
127
 
153
- let onSubmenuClose = useCallback(
154
- () => {
155
- cancelOpenTimeout();
156
- state.close();
157
- },
158
- [state, cancelOpenTimeout],
159
- subSlot(slot, 'close'),
160
- );
161
-
162
- useLayoutEffect(
163
- () => {
164
- return () => {
165
- cancelOpenTimeout();
166
- };
167
- },
168
- [cancelOpenTimeout],
169
- subSlot(slot, 'teardown'),
170
- );
128
+ let onSubmenuClose = useCallback(() => {
129
+ cancelOpenTimeout();
130
+ state.close();
131
+ }, [state, cancelOpenTimeout]);
171
132
 
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
- }
133
+ useLayoutEffect(() => {
134
+ return () => {
135
+ cancelOpenTimeout();
136
+ };
137
+ }, [cancelOpenTimeout]);
178
138
 
179
- switch (e.key) {
180
- case 'ArrowLeft':
139
+ let { keyboardProps } = useKeyboard({
140
+ shortcuts: {
141
+ ArrowLeft: (e) => {
142
+ // If focus is not within the menu, assume virtual focus is being used.
143
+ // This means some other input element is also within the popover, so we shouldn't close the menu.
144
+ if (!isFocusWithin(e.currentTarget as Element)) {
145
+ return false;
146
+ }
181
147
  if (
182
148
  direction === 'ltr' &&
183
- nodeContains(e.currentTarget as Element, getEventTarget(e) as Element)
149
+ nodeContains(e.currentTarget as globalThis.Node, getEventTarget(e) as Element)
184
150
  ) {
185
- e.preventDefault();
186
- e.stopPropagation();
187
151
  onSubmenuClose();
188
152
  if (!shouldUseVirtualFocus && ref.current) {
189
153
  focusWithoutScrolling(ref.current);
190
154
  }
155
+ return;
156
+ }
157
+ return false;
158
+ },
159
+ ArrowRight: (e) => {
160
+ if (!isFocusWithin(e.currentTarget as Element)) {
161
+ return false;
191
162
  }
192
- break;
193
- case 'ArrowRight':
194
163
  if (
195
164
  direction === 'rtl' &&
196
- nodeContains(e.currentTarget as Element, getEventTarget(e) as Element)
165
+ nodeContains(e.currentTarget as globalThis.Node, getEventTarget(e) as Element)
197
166
  ) {
198
- e.preventDefault();
199
- e.stopPropagation();
200
167
  onSubmenuClose();
201
168
  if (!shouldUseVirtualFocus && ref.current) {
202
169
  focusWithoutScrolling(ref.current);
203
170
  }
171
+ return;
172
+ }
173
+ return false;
174
+ },
175
+ Escape: (e) => {
176
+ if (!isFocusWithin(e.currentTarget as Element)) {
177
+ return false;
204
178
  }
205
- break;
206
- case 'Escape':
207
- // TODO: can remove this when we fix collection event leaks
208
179
  if (nodeContains(submenuRef.current, getEventTarget(e) as Element)) {
209
- e.stopPropagation();
210
180
  onSubmenuClose();
211
181
  if (!shouldUseVirtualFocus && ref.current) {
212
182
  focusWithoutScrolling(ref.current);
213
183
  }
184
+ return;
214
185
  }
215
- break;
216
- }
217
- };
186
+ return false;
187
+ },
188
+ },
189
+ });
218
190
 
219
191
  let submenuProps = {
220
192
  id: overlayId,
@@ -223,16 +195,15 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
223
195
  ...(type === 'menu' && {
224
196
  onClose: state.closeAll,
225
197
  autoFocus: state.focusStrategy ?? undefined,
226
- onKeyDown: submenuKeyDown,
198
+ ...keyboardProps,
227
199
  }),
228
200
  };
229
201
 
230
- let submenuTriggerKeyDown = (e: BaseEvent<KeyboardEvent>) => {
231
- switch (e.key) {
232
- case 'ArrowRight':
202
+ let { keyboardProps: submenuTriggerKeyboardProps } = useKeyboard({
203
+ shortcuts: {
204
+ ArrowRight: () => {
233
205
  if (!isDisabled) {
234
206
  if (direction === 'ltr') {
235
- e.preventDefault();
236
207
  if (!state.isOpen) {
237
208
  onSubmenuOpen('first');
238
209
  }
@@ -240,18 +211,19 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
240
211
  if (type === 'menu' && !!submenuRef?.current && getActiveElement() === ref?.current) {
241
212
  focusWithoutScrolling(submenuRef.current);
242
213
  }
214
+ return;
243
215
  } else if (state.isOpen) {
244
216
  onSubmenuClose();
217
+ return;
245
218
  } else {
246
- e.continuePropagation();
219
+ return false;
247
220
  }
248
221
  }
249
-
250
- break;
251
- case 'ArrowLeft':
222
+ return false;
223
+ },
224
+ ArrowLeft: () => {
252
225
  if (!isDisabled) {
253
226
  if (direction === 'rtl') {
254
- e.preventDefault();
255
227
  if (!state.isOpen) {
256
228
  onSubmenuOpen('first');
257
229
  }
@@ -259,18 +231,18 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
259
231
  if (type === 'menu' && !!submenuRef?.current && getActiveElement() === ref?.current) {
260
232
  focusWithoutScrolling(submenuRef.current);
261
233
  }
234
+ return;
262
235
  } else if (state.isOpen) {
263
236
  onSubmenuClose();
237
+ return;
264
238
  } else {
265
- e.continuePropagation();
239
+ return false;
266
240
  }
267
241
  }
268
- break;
269
- default:
270
- e.continuePropagation();
271
- break;
272
- }
273
- };
242
+ return false;
243
+ },
244
+ },
245
+ });
274
246
 
275
247
  let onPressStart = (e: PressEvent) => {
276
248
  if (!isDisabled && (e.pointerType === 'virtual' || e.pointerType === 'keyboard')) {
@@ -301,22 +273,17 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
301
273
  }
302
274
  };
303
275
 
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
- );
276
+ useEvent(parentMenuRef, 'focusin', (e) => {
277
+ // If we detect focus moved to a different item in the same menu that the currently open submenu trigger is in
278
+ // then close the submenu. This is for a case where the user hovers a root menu item when multiple submenus are open
279
+ if (
280
+ state.isOpen &&
281
+ nodeContains(parentMenuRef.current, getEventTarget(e) as HTMLElement) &&
282
+ getEventTarget(e) !== ref.current
283
+ ) {
284
+ onSubmenuClose();
285
+ }
286
+ });
320
287
 
321
288
  let shouldCloseOnInteractOutside = (target: Element) => {
322
289
  if (target !== ref.current) {
@@ -326,18 +293,17 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
326
293
  return false;
327
294
  };
328
295
 
329
- useSafelyMouseToSubmenu(
330
- {
331
- menuRef: parentMenuRef,
332
- submenuRef,
333
- isOpen: state.isOpen,
334
- isDisabled: isDisabled,
335
- },
336
- subSlot(slot, 'safeMouse'),
337
- );
296
+ useSafelyMouseToSubmenu({
297
+ menuRef: parentMenuRef,
298
+ submenuRef,
299
+ isOpen: state.isOpen,
300
+ isDisabled: isDisabled,
301
+ });
338
302
 
339
303
  return {
340
304
  submenuTriggerProps: {
305
+ onKeyDown: submenuTriggerKeyboardProps.onKeyDown,
306
+ onKeyUp: submenuTriggerKeyboardProps.onKeyUp,
341
307
  id: submenuTriggerId,
342
308
  'aria-controls': state.isOpen ? overlayId : undefined,
343
309
  'aria-haspopup': !isDisabled ? type : undefined,
@@ -345,7 +311,6 @@ export function useSubmenuTrigger(...args: any[]): SubmenuTriggerAria<any> {
345
311
  onPressStart,
346
312
  onPress,
347
313
  onHoverChange,
348
- onKeyDown: submenuTriggerKeyDown,
349
314
  isOpen: state.isOpen,
350
315
  },
351
316
  submenuProps,