@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
@@ -0,0 +1,266 @@
1
+ /** @jsxImportSource octane */
2
+ // Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/src/tooltip/usePreviewTrigger.ts).
3
+ /*
4
+ * Copyright 2026 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 {
16
+ KeyboardEvent as AriaKeyboardEvent,
17
+ DOMAttributes,
18
+ FocusableElement,
19
+ RefObject,
20
+ } from '@react-types/shared';
21
+ import { AriaPopoverProps } from '../overlays/usePopover';
22
+ import { focusWithoutScrolling } from '../utils/focusWithoutScrolling';
23
+ import { getActiveElement } from '../utils/shadowdom/DOMFunctions';
24
+ import { getFocusableTreeWalker } from '../focus/FocusScope';
25
+ import {
26
+ getInteractionModality,
27
+ useFocusVisibleListener,
28
+ useInteractionModality,
29
+ } from '../interactions/useFocusVisible';
30
+ import { getOwnerDocument } from '../utils/domHelpers';
31
+ import intlMessages from '../intl/previewtrigger/index';
32
+ import { mergeProps } from '../utils/mergeProps';
33
+ import { nodeContains } from '../utils/shadowdom/DOMFunctions';
34
+ import {
35
+ TooltipTriggerProps,
36
+ TooltipTriggerState,
37
+ } from '../upstream-exports/react-stately/useTooltipTriggerState';
38
+ import { useEffect, useRef } from '../compat/react';
39
+ import { useEffectEvent } from '../utils/useEffectEvent';
40
+ import { useEvent } from '../utils/useEvent';
41
+ import { useHover } from '../interactions/useHover';
42
+ import { useId } from '../utils/useId';
43
+ import { useLocalizedStringFormatter } from '../i18n/useLocalizedStringFormatter';
44
+ import { useLongPress } from '../interactions/useLongPress';
45
+ import { useSafeArea } from './useSafeArea';
46
+
47
+ export interface AriaPreviewTriggerProps extends Omit<
48
+ TooltipTriggerProps,
49
+ 'trigger' | 'shouldCloseOnPress'
50
+ > {}
51
+
52
+ export interface AriaPreviewTriggerOptions extends AriaPreviewTriggerProps {
53
+ /** A ref to the trigger element (e.g. a Link). */
54
+ triggerRef: RefObject<FocusableElement | null>;
55
+ /** A ref to the popover element. */
56
+ popoverRef: RefObject<Element | null>;
57
+ }
58
+
59
+ export interface PreviewTriggerTriggerAria {
60
+ /**
61
+ * Props for the trigger element (e.g. a Link).
62
+ */
63
+ triggerProps: DOMAttributes;
64
+ /**
65
+ * Props for the popover overlay element.
66
+ */
67
+ popoverProps: Omit<AriaPopoverProps, 'triggerRef' | 'popoverRef'> & DOMAttributes;
68
+ }
69
+
70
+ /**
71
+ * Provides the behavior and accessibility implementation for a preview trigger.
72
+ * A preview trigger displays a popover on hover, focus, or long press. Unlike a
73
+ * tooltip, the popover may contain interactive content.
74
+ */
75
+ export function usePreviewTrigger(
76
+ props: AriaPreviewTriggerOptions,
77
+ state: TooltipTriggerState,
78
+ ): PreviewTriggerTriggerAria {
79
+ let { triggerRef, popoverRef, isDisabled } = props;
80
+ let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/link-preview');
81
+ let popoverId = useId();
82
+
83
+ // Suppresses the next focus from reopening the preview (e.g. when restoring focus on Escape).
84
+ let ignoreFocus = useRef(false);
85
+ // When opened via long press, move focus into the popover once it opens so touch screen readers
86
+ // (e.g. VoiceOver) move their virtual cursor into the preview.
87
+ let shouldFocusOnOpen = useRef(false);
88
+ // Whether the pointer is currently within the safe area (the trigger, the popover, or the region
89
+ // between them). Tracked by useSafeArea below so the preview stays open while the pointer travels
90
+ // from the link to the popover, even with closeDelay of 0.
91
+ let pointerInSafeArea = useRef(false);
92
+
93
+ let isFocusVisible = useRef(false);
94
+ useFocusVisibleListener((visible) => {
95
+ isFocusVisible.current = visible;
96
+ }, []);
97
+
98
+ // Cancel a pending close and keep the preview open.
99
+ let keepOpen = useEffectEvent(() => state.open(true));
100
+
101
+ // Close the preview unless something is still keeping it open: the pointer is within the safe
102
+ // area, or focus is within the trigger or popover. During focus transitions the active element
103
+ // may briefly be the body; the popover's focusin handler re-opens in that case (focus moving in).
104
+ let checkClose = useEffectEvent(() => {
105
+ if (pointerInSafeArea.current) {
106
+ return;
107
+ }
108
+ let active = triggerRef.current ? getActiveElement(getOwnerDocument(triggerRef.current)) : null;
109
+ if (
110
+ isFocusVisible.current &&
111
+ ((triggerRef.current && nodeContains(triggerRef.current, active)) ||
112
+ (popoverRef.current && nodeContains(popoverRef.current, active)))
113
+ ) {
114
+ return;
115
+ }
116
+ state.close();
117
+ });
118
+
119
+ useEffect(() => {
120
+ let popover = popoverRef.current;
121
+ if (!state.isOpen || !popover || !shouldFocusOnOpen.current) {
122
+ return;
123
+ }
124
+
125
+ // When opened via long press, move focus to the popover itself so touch screen readers move
126
+ // their virtual cursor into the preview.
127
+ shouldFocusOnOpen.current = false;
128
+ focusWithoutScrolling(popover as FocusableElement);
129
+ }, [state.isOpen, popoverRef]);
130
+
131
+ let onHoverStart = () => {
132
+ // Match useTooltipTrigger: only treat as hovered when the modality is actually a pointer.
133
+ if (getInteractionModality() === 'pointer') {
134
+ pointerInSafeArea.current = true;
135
+ state.open();
136
+ }
137
+ };
138
+
139
+ let onHoverEnd = () => {
140
+ // Before the preview opens, cancel a pending warmup if the pointer leaves the trigger. Once
141
+ // open, the safe-area polygon (useSafeArea) governs closing as the pointer moves to the popover.
142
+ if (!state.isOpen) {
143
+ pointerInSafeArea.current = false;
144
+ state.close();
145
+ }
146
+ };
147
+
148
+ let onTriggerFocus = (e: FocusEvent) => {
149
+ if (ignoreFocus.current) {
150
+ ignoreFocus.current = false;
151
+ return;
152
+ }
153
+
154
+ // Prevent browser focusing the link on long press when focus is already in the popover.
155
+ if (state.isOpen && e.relatedTarget === popoverRef.current) {
156
+ focusWithoutScrolling(popoverRef.current as FocusableElement);
157
+ return;
158
+ }
159
+
160
+ if (isFocusVisible.current) {
161
+ // Open after the warmup delay on keyboard focus, not immediately like a tooltip. This way
162
+ // tabbing quickly through the page doesn't open previews (and add their tab stops); the
163
+ // delay ensures the user is actually interested in the link's details.
164
+ state.open();
165
+ }
166
+ };
167
+
168
+ useEvent(triggerRef, 'react-aria-focus-scope-restore', (e) => {
169
+ e.preventDefault();
170
+ ignoreFocus.current = true;
171
+ triggerRef.current?.focus();
172
+ });
173
+
174
+ // Move focus from the link into the preview when the user presses Tab while it is open.
175
+ // Tabbing back out of the popover is handled by the popover's own FocusScope.
176
+ let onTriggerKeyDown = (e: AriaKeyboardEvent) => {
177
+ if (e.key === 'Tab' && !e.shiftKey && state.isOpen && popoverRef.current) {
178
+ let walker = getFocusableTreeWalker(popoverRef.current, { tabbable: true });
179
+ let first = walker.nextNode() as FocusableElement | null;
180
+ if (first) {
181
+ e.preventDefault();
182
+ first.focus();
183
+ }
184
+ } else if (e.key === 'Escape') {
185
+ e.preventDefault();
186
+ state.close(true);
187
+ }
188
+ };
189
+
190
+ let { hoverProps } = useHover({ isDisabled, onHoverStart, onHoverEnd });
191
+ let focusableProps = {
192
+ onFocus: onTriggerFocus,
193
+ onBlur: checkClose,
194
+ onKeyDown: onTriggerKeyDown,
195
+ };
196
+
197
+ // Only describe the long press interaction when the user is actually using touch, otherwise it
198
+ // is confusing (e.g. a screen reader announcing "long press" while navigating with a keyboard).
199
+ // null is the default before the user has interacted with anything.
200
+ let modality = useInteractionModality();
201
+ let shouldLongPress =
202
+ (modality === 'pointer' || modality === 'virtual' || modality == null) &&
203
+ typeof window !== 'undefined' &&
204
+ 'ontouchstart' in window;
205
+
206
+ // Open the preview on long press on touch devices, since there is no hover. Move focus into the
207
+ // popover once it opens so touch screen readers (e.g. VoiceOver) move their virtual cursor in.
208
+ let { longPressProps } = useLongPress({
209
+ isDisabled,
210
+ pointerType: 'touch',
211
+ accessibilityDescription: shouldLongPress
212
+ ? stringFormatter.format('longPressMessage')
213
+ : undefined,
214
+ onLongPress() {
215
+ shouldFocusOnOpen.current = true;
216
+ state.open(true);
217
+ },
218
+ });
219
+
220
+ // Keep the preview open while the pointer is anywhere within the safe area connecting the link
221
+ // and the popover, so moving the pointer between them (even diagonally) doesn't close it. This
222
+ // works for any popover placement and even when closeDelay is 0.
223
+ useSafeArea({
224
+ triggerRef,
225
+ overlayRef: popoverRef,
226
+ isOpen: state.isOpen,
227
+ isDisabled,
228
+ onSafeAreaChange: (isInSafeArea) => {
229
+ if (isInSafeArea === pointerInSafeArea.current) {
230
+ return;
231
+ }
232
+ pointerInSafeArea.current = isInSafeArea;
233
+ if (isInSafeArea) {
234
+ keepOpen();
235
+ } else {
236
+ checkClose();
237
+ }
238
+ },
239
+ });
240
+
241
+ // oxlint-disable-next-line react/react-compiler
242
+ let triggerProps = mergeProps(focusableProps, hoverProps, longPressProps);
243
+ let describedBy = [triggerProps['aria-describedby'], state.isOpen ? popoverId : null]
244
+ .filter(Boolean)
245
+ .join(' ');
246
+
247
+ return {
248
+ triggerProps: {
249
+ ...triggerProps,
250
+ 'aria-haspopup': 'dialog',
251
+ 'aria-expanded': state.isOpen,
252
+ 'aria-controls': state.isOpen ? popoverId : undefined,
253
+ 'aria-describedby': describedBy || undefined,
254
+ style: {
255
+ WebkitTouchCallout: 'none',
256
+ // @ts-ignore
257
+ WebkitUserDrag: 'none',
258
+ },
259
+ },
260
+ popoverProps: {
261
+ id: popoverId,
262
+ onFocusWithin: keepOpen,
263
+ onBlurWithin: checkClose,
264
+ },
265
+ };
266
+ }
@@ -0,0 +1,171 @@
1
+ /** @jsxImportSource octane */
2
+ // Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/src/tooltip/useSafeArea.ts).
3
+ /*
4
+ * Copyright 2026 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 { getOwnerDocument, getOwnerWindow } from '../utils/domHelpers';
16
+ import { RefObject } from '@react-types/shared';
17
+ import { useEffect } from '../compat/react';
18
+ import { useEffectEvent } from '../utils/useEffectEvent';
19
+
20
+ interface Point {
21
+ x: number;
22
+ y: number;
23
+ }
24
+
25
+ interface SafeAreaOptions {
26
+ /** Ref for the trigger element. */
27
+ triggerRef: RefObject<Element | null>;
28
+ /** Ref for the overlay element. */
29
+ overlayRef: RefObject<Element | null>;
30
+ /** Whether the overlay is open. */
31
+ isOpen: boolean;
32
+ /** Whether this feature is disabled. */
33
+ isDisabled?: boolean;
34
+ /**
35
+ * Called on pointer move (and when the pointer leaves the document) with whether the pointer is
36
+ * currently within the "safe area" — the trigger, the overlay, or the region between them. This
37
+ * lets an overlay stay open while the pointer travels from the trigger to the overlay, even
38
+ * diagonally and regardless of placement.
39
+ */
40
+ onSafeAreaChange: (isInSafeArea: boolean) => void;
41
+ }
42
+
43
+ // A small amount of padding (in pixels) added around the trigger and overlay so the safe area is
44
+ // slightly forgiving of sub-pixel jitter and the gap between the elements.
45
+ const PADDING = 8;
46
+
47
+ /**
48
+ * Tracks whether the pointer is within a "safe area" connecting a trigger and its overlay, so the
49
+ * overlay can stay open while the pointer moves between them. The safe area is the union of the
50
+ * trigger rect, the overlay rect, and the convex hull connecting the two (a polygon), which works
51
+ * for any placement of the overlay relative to the trigger.
52
+ */
53
+ export function useSafeArea(options: SafeAreaOptions): void {
54
+ let { triggerRef, overlayRef, isOpen, isDisabled } = options;
55
+ let onSafeAreaChange = useEffectEvent(options.onSafeAreaChange);
56
+
57
+ useEffect(() => {
58
+ let trigger = triggerRef.current;
59
+ if (isDisabled || !isOpen || !trigger) {
60
+ return;
61
+ }
62
+
63
+ let onPointerMove = (e: PointerEvent) => {
64
+ if (e.pointerType === 'touch') {
65
+ return;
66
+ }
67
+ let point = { x: e.clientX, y: e.clientY };
68
+ let triggerRect = trigger!.getBoundingClientRect();
69
+ let overlayRect = overlayRef.current?.getBoundingClientRect();
70
+ onSafeAreaChange(isPointInSafeArea(point, triggerRect, overlayRect));
71
+ };
72
+
73
+ // If the pointer leaves the document entirely, it is no longer in the safe area.
74
+ let onPointerLeave = () => onSafeAreaChange(false);
75
+
76
+ let win = getOwnerWindow(trigger);
77
+ let doc = getOwnerDocument(trigger);
78
+ win.addEventListener('pointermove', onPointerMove);
79
+ doc.documentElement.addEventListener('pointerleave', onPointerLeave);
80
+ return () => {
81
+ win.removeEventListener('pointermove', onPointerMove);
82
+ doc.documentElement.removeEventListener('pointerleave', onPointerLeave);
83
+ };
84
+ }, [isDisabled, isOpen, triggerRef, overlayRef]);
85
+ }
86
+
87
+ function isPointInSafeArea(point: Point, triggerRect: DOMRect, overlayRect?: DOMRect): boolean {
88
+ if (rectContains(triggerRect, point)) {
89
+ return true;
90
+ }
91
+ if (!overlayRect) {
92
+ return false;
93
+ }
94
+ if (rectContains(overlayRect, point)) {
95
+ return true;
96
+ }
97
+ // Otherwise, check whether the point is within the convex hull connecting the two rects.
98
+ let hull = convexHull([...rectCorners(triggerRect), ...rectCorners(overlayRect)]);
99
+ return hull.length >= 3 && isPointInPolygon(point, hull);
100
+ }
101
+
102
+ function rectContains(rect: DOMRect, point: Point): boolean {
103
+ return (
104
+ point.x >= rect.left - PADDING &&
105
+ point.x <= rect.right + PADDING &&
106
+ point.y >= rect.top - PADDING &&
107
+ point.y <= rect.bottom + PADDING
108
+ );
109
+ }
110
+
111
+ function rectCorners(rect: DOMRect): Point[] {
112
+ let left = rect.left - PADDING;
113
+ let right = rect.right + PADDING;
114
+ let top = rect.top - PADDING;
115
+ let bottom = rect.bottom + PADDING;
116
+ return [
117
+ { x: left, y: top },
118
+ { x: right, y: top },
119
+ { x: right, y: bottom },
120
+ { x: left, y: bottom },
121
+ ];
122
+ }
123
+
124
+ // Computes the convex hull of a set of points using the monotone chain algorithm.
125
+ function convexHull(points: Point[]): Point[] {
126
+ let sorted = points.slice().sort((a, b) => a.x - b.x || a.y - b.y);
127
+ if (sorted.length < 3) {
128
+ return sorted;
129
+ }
130
+
131
+ let cross = (o: Point, a: Point, b: Point) =>
132
+ (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
133
+
134
+ let lower: Point[] = [];
135
+ for (let p of sorted) {
136
+ while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
137
+ lower.pop();
138
+ }
139
+ lower.push(p);
140
+ }
141
+
142
+ let upper: Point[] = [];
143
+ for (let i = sorted.length - 1; i >= 0; i--) {
144
+ let p = sorted[i];
145
+ while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
146
+ upper.pop();
147
+ }
148
+ upper.push(p);
149
+ }
150
+
151
+ lower.pop();
152
+ upper.pop();
153
+ return lower.concat(upper);
154
+ }
155
+
156
+ // Ray casting point-in-polygon test.
157
+ function isPointInPolygon(point: Point, polygon: Point[]): boolean {
158
+ let { x, y } = point;
159
+ let inside = false;
160
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
161
+ let xi = polygon[i].x;
162
+ let yi = polygon[i].y;
163
+ let xj = polygon[j].x;
164
+ let yj = polygon[j].y;
165
+ let intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
166
+ if (intersect) {
167
+ inside = !inside;
168
+ }
169
+ }
170
+ return inside;
171
+ }
@@ -0,0 +1,3 @@
1
+ /** @jsxImportSource octane */
2
+ // Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/exports/mergeRefs.ts).
3
+ export { mergeRefs } from '../../utils/mergeRefs';
@@ -0,0 +1,18 @@
1
+ /** @jsxImportSource octane */
2
+ // Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/exports/usePreviewTrigger.ts).
3
+ /*
4
+ * Copyright 2026 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
+ export { usePreviewTrigger } from '../../tooltip/usePreviewTrigger';
15
+ export type {
16
+ AriaPreviewTriggerProps,
17
+ PreviewTriggerTriggerAria,
18
+ } from '../../tooltip/usePreviewTrigger';
@@ -0,0 +1,22 @@
1
+ /** @jsxImportSource octane */
2
+ // Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/exports/useTokenField.ts).
3
+ /*
4
+ * Copyright 2026 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
13
+ */
14
+
15
+ export {
16
+ useTokenField,
17
+ tokenFieldPositionToDOMRange,
18
+ setTokenFieldSelection,
19
+ } from '../../tokenfield/useTokenField';
20
+ export { useToken } from '../../tokenfield/useToken';
21
+ export type { TokenFieldAria, AriaTokenFieldProps } from '../../tokenfield/useTokenField';
22
+ export type { TokenAria, TokenProps } from '../../tokenfield/useToken';
@@ -0,0 +1,8 @@
1
+ /** @jsxImportSource octane */
2
+ export {
3
+ getChildNodes,
4
+ getFirstItem,
5
+ getLastItem,
6
+ getNthItem,
7
+ compareNodeOrder,
8
+ } from '../../../../stately/collections/getChildNodes';
@@ -0,0 +1,2 @@
1
+ /** @jsxImportSource octane */
2
+ export { getItemCount } from '../../../../stately/collections/getItemCount';
@@ -0,0 +1,7 @@
1
+ /** @jsxImportSource octane */
2
+ export {
3
+ type GridNode,
4
+ GridCollection,
5
+ type IGridCollection,
6
+ type GridRow,
7
+ } from '../../../../stately/grid/GridCollection';
@@ -0,0 +1,6 @@
1
+ /** @jsxImportSource octane */
2
+ export {
3
+ useGridState,
4
+ type GridState,
5
+ type GridStateOptions,
6
+ } from '../../../../stately/grid/useGridState';
@@ -0,0 +1,2 @@
1
+ /** @jsxImportSource octane */
2
+ export { clamp, snapValueToStep, toFixedNumber } from '../../../../stately/utils/number';
@@ -0,0 +1,23 @@
1
+ /** @jsxImportSource octane */
2
+ /*
3
+ * Copyright 2020 Adobe. All rights reserved.
4
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License. You may obtain a copy
6
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software distributed under
9
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
10
+ * OF ANY KIND, either express or implied. See the License for the specific language
11
+ * governing permissions and limitations under the License.
12
+ */
13
+
14
+ export { useComboBoxState } from '../../stately/combobox/useComboBoxState';
15
+
16
+ export type {
17
+ ComboBoxProps,
18
+ ComboBoxStateOptions,
19
+ ComboBoxState,
20
+ ComboBoxValidationValue,
21
+ SelectionMode,
22
+ MenuTriggerAction,
23
+ } from '../../stately/combobox/useComboBoxState';
@@ -0,0 +1,15 @@
1
+ /** @jsxImportSource octane */
2
+ /*
3
+ * Copyright 2020 Adobe. All rights reserved.
4
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License. You may obtain a copy
6
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software distributed under
9
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
10
+ * OF ANY KIND, either express or implied. See the License for the specific language
11
+ * governing permissions and limitations under the License.
12
+ */
13
+
14
+ export type { ListProps, ListState } from '../../stately/list/useListState';
15
+ export { useListState, UNSTABLE_useFilteredListState } from '../../stately/list/useListState';
@@ -0,0 +1,26 @@
1
+ /** @jsxImportSource octane */
2
+ /*
3
+ * Copyright 2020 Adobe. All rights reserved.
4
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License. You may obtain a copy
6
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software distributed under
9
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
10
+ * OF ANY KIND, either express or implied. See the License for the specific language
11
+ * governing permissions and limitations under the License.
12
+ */
13
+
14
+ export { useMenuTriggerState } from '../../stately/menu/useMenuTriggerState';
15
+ export { useSubmenuTriggerState } from '../../stately/menu/useSubmenuTriggerState';
16
+
17
+ export type {
18
+ MenuTriggerProps,
19
+ MenuTriggerState,
20
+ RootMenuTriggerState,
21
+ MenuTriggerType,
22
+ } from '../../stately/menu/useMenuTriggerState';
23
+ export type {
24
+ SubmenuTriggerProps,
25
+ SubmenuTriggerState,
26
+ } from '../../stately/menu/useSubmenuTriggerState';
@@ -0,0 +1,21 @@
1
+ /** @jsxImportSource octane */
2
+ /*
3
+ * Copyright 2020 Adobe. All rights reserved.
4
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License. You may obtain a copy
6
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software distributed under
9
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
10
+ * OF ANY KIND, either express or implied. See the License for the specific language
11
+ * governing permissions and limitations under the License.
12
+ */
13
+
14
+ export type { MultipleSelectionStateProps } from '../../stately/selection/useMultipleSelectionState';
15
+ export { useMultipleSelectionState } from '../../stately/selection/useMultipleSelectionState';
16
+ export {
17
+ type MultipleSelectionManager,
18
+ type MultipleSelectionState,
19
+ type FocusState,
20
+ } from '../../stately/selection/types';
21
+ export type { DisabledBehavior, Selection, SelectionBehavior } from '@react-types/shared';
@@ -0,0 +1,20 @@
1
+ /** @jsxImportSource octane */
2
+ /*
3
+ * Copyright 2020 Adobe. All rights reserved.
4
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License. You may obtain a copy
6
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software distributed under
9
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
10
+ * OF ANY KIND, either express or implied. See the License for the specific language
11
+ * governing permissions and limitations under the License.
12
+ */
13
+
14
+ export { useNumberFieldState } from '../../stately/numberfield/useNumberFieldState';
15
+
16
+ export type { NumberFieldStateOptions } from '../../stately/numberfield/useNumberFieldState';
17
+ export type {
18
+ NumberFieldProps,
19
+ NumberFieldState,
20
+ } from '../../stately/numberfield/useNumberFieldState';
@@ -0,0 +1,20 @@
1
+ /** @jsxImportSource octane */
2
+ // Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-stately/exports/useOverlayTriggerState.ts).
3
+ /*
4
+ * Copyright 2020 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
+ export { useOverlayTriggerState } from '../../stately/overlays/useOverlayTriggerState';
16
+
17
+ export type {
18
+ OverlayTriggerProps,
19
+ OverlayTriggerState,
20
+ } from '../../stately/overlays/useOverlayTriggerState';
@@ -0,0 +1,16 @@
1
+ /** @jsxImportSource octane */
2
+ /*
3
+ * Copyright 2020 Adobe. All rights reserved.
4
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License. You may obtain a copy
6
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software distributed under
9
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
10
+ * OF ANY KIND, either express or implied. See the License for the specific language
11
+ * governing permissions and limitations under the License.
12
+ */
13
+
14
+ export { useRadioGroupState } from '../../stately/radio/useRadioGroupState';
15
+
16
+ export type { RadioGroupProps, RadioGroupState } from '../../stately/radio/useRadioGroupState';