@astryxdesign/core 0.7.7-canary.fc68123 → 0.7.7

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 (240) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +1 -4
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +6 -36
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheetPanel.js +4 -23
  7. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  8. package/dist/BottomSheet/useSheetGestures.js +1 -3
  9. package/dist/Button/Button.d.ts.map +1 -1
  10. package/dist/Button/Button.js +1 -17
  11. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  12. package/dist/CheckboxInput/CheckboxInput.js +2 -13
  13. package/dist/ComplexSelector/ComplexSelector.d.ts +1 -4
  14. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  15. package/dist/ComplexSelector/ComplexSelector.js +4 -14
  16. package/dist/ContextMenu/ContextMenu.js +2 -2
  17. package/dist/Dialog/Dialog.d.ts.map +1 -1
  18. package/dist/Dialog/Dialog.js +31 -7
  19. package/dist/Field/Field.js +2 -2
  20. package/dist/HoverCard/HoverCard.d.ts +3 -4
  21. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  22. package/dist/HoverCard/HoverCard.js +2 -27
  23. package/dist/HoverCard/useHoverCard.d.ts +4 -5
  24. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  25. package/dist/HoverCard/useHoverCard.js +31 -17
  26. package/dist/Icon/Icon.d.ts +1 -5
  27. package/dist/Icon/Icon.d.ts.map +1 -1
  28. package/dist/Icon/Icon.js +2 -5
  29. package/dist/Layer/usePressPin.d.ts +5 -17
  30. package/dist/Layer/usePressPin.d.ts.map +1 -1
  31. package/dist/Layer/usePressPin.js +12 -62
  32. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  33. package/dist/Lightbox/Lightbox.js +0 -18
  34. package/dist/PowerSearch/PowerSearch.d.ts +3 -36
  35. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  36. package/dist/PowerSearch/PowerSearch.js +127 -433
  37. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +1 -32
  38. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  39. package/dist/PowerSearch/PowerSearchEditPopover.js +6 -17
  40. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -38
  41. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  42. package/dist/PowerSearch/PowerSearchValueEditor.js +61 -170
  43. package/dist/PowerSearch/formatFilterValue.d.ts +0 -7
  44. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  45. package/dist/PowerSearch/formatFilterValue.js +1 -11
  46. package/dist/RadioList/RadioList.d.ts.map +1 -1
  47. package/dist/RadioList/RadioList.js +2 -3
  48. package/dist/RadioList/RadioListItem.d.ts +1 -1
  49. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  50. package/dist/RadioList/RadioListItem.js +3 -5
  51. package/dist/Resizable/ResizeHandle.d.ts +1 -2
  52. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  53. package/dist/Resizable/ResizeHandle.js +77 -99
  54. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -1
  55. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +3 -3
  56. package/dist/Slider/sliderTouchController.js +2 -2
  57. package/dist/Spinner/Spinner.d.ts +1 -1
  58. package/dist/Spinner/Spinner.d.ts.map +1 -1
  59. package/dist/Spinner/Spinner.js +58 -60
  60. package/dist/Switch/useSwitchTouch.js +4 -4
  61. package/dist/Table/BaseTable.d.ts.map +1 -1
  62. package/dist/Table/BaseTable.js +8 -29
  63. package/dist/TextInput/TextInput.d.ts +2 -2
  64. package/dist/TextInput/TextInput.d.ts.map +1 -1
  65. package/dist/TextInput/TextInput.js +7 -10
  66. package/dist/Toast/Toast.d.ts +1 -11
  67. package/dist/Toast/Toast.d.ts.map +1 -1
  68. package/dist/Toast/Toast.js +15 -25
  69. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  70. package/dist/Toast/ToastViewport.js +34 -94
  71. package/dist/Toast/types.d.ts +3 -9
  72. package/dist/Toast/types.d.ts.map +1 -1
  73. package/dist/Tooltip/useTooltip.d.ts +1 -19
  74. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  75. package/dist/Tooltip/useTooltip.js +53 -34
  76. package/dist/Typeahead/TypeaheadItem.d.ts +1 -2
  77. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  78. package/dist/astryx.css +6 -35
  79. package/dist/gestures/PressTracker.d.ts.map +1 -1
  80. package/dist/gestures/PressTracker.js +3 -4
  81. package/dist/hooks/actionGroupTracker.d.ts +1 -4
  82. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  83. package/dist/hooks/actionGroupTracker.js +3 -13
  84. package/dist/hooks/index.d.ts +0 -1
  85. package/dist/hooks/index.d.ts.map +1 -1
  86. package/dist/hooks/index.js +0 -1
  87. package/dist/hooks/useAttachment.d.ts +1 -1
  88. package/dist/hooks/useAttachment.js +1 -1
  89. package/dist/hooks/useInputStatusIcon.d.ts +1 -1
  90. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  91. package/dist/hooks/useInputStatusIcon.js +2 -2
  92. package/dist/hooks/useLongPress.d.ts +2 -6
  93. package/dist/hooks/useLongPress.d.ts.map +1 -1
  94. package/dist/hooks/useLongPress.js +15 -95
  95. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.d.ts.map +1 -1
  96. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.js +0 -6
  97. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  98. package/dist/i18n/generated-locales/en.generated.js +0 -30
  99. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  100. package/dist/i18n/generated-locales/pseudo.generated.js +0 -30
  101. package/dist/index.d.ts +0 -1
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +0 -4
  104. package/dist/motion/animatedLayout.d.ts.map +1 -1
  105. package/dist/motion/animatedLayout.js +6 -5
  106. package/dist/motion/animator.d.ts +14 -14
  107. package/dist/motion/animator.d.ts.map +1 -1
  108. package/dist/motion/animator.js +18 -41
  109. package/dist/utils/dateParser.d.ts.map +1 -1
  110. package/dist/utils/dateParser.js +0 -12
  111. package/dist/utils/focusReturn.d.ts +0 -15
  112. package/dist/utils/focusReturn.d.ts.map +1 -1
  113. package/dist/utils/focusReturn.js +0 -28
  114. package/dist/utils/interactionOverlay.stylex.d.ts +2 -3
  115. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  116. package/dist/utils/interactionOverlay.stylex.js +2 -3
  117. package/dist/utils/pressFeedback.d.ts.map +1 -1
  118. package/dist/utils/pressFeedback.js +6 -19
  119. package/locales/en.json +0 -120
  120. package/locales/pseudo.json +0 -90
  121. package/package.json +5 -10
  122. package/src/AlertDialog/AlertDialog.doc.mjs +2 -2
  123. package/src/AlertDialog/AlertDialog.tsx +5 -37
  124. package/src/BottomSheet/BottomSheetPanel.tsx +3 -37
  125. package/src/BottomSheet/useSheetGestures.ts +1 -3
  126. package/src/Button/Button.tsx +4 -26
  127. package/src/CheckboxInput/CheckboxInput.doc.mjs +0 -18
  128. package/src/CheckboxInput/CheckboxInput.tsx +9 -36
  129. package/src/ComplexSelector/ComplexSelector.tsx +5 -20
  130. package/src/ContextMenu/ContextMenu.tsx +2 -2
  131. package/src/Dialog/Dialog.tsx +31 -10
  132. package/src/Field/Field.tsx +2 -2
  133. package/src/HoverCard/HoverCard.doc.mjs +2 -4
  134. package/src/HoverCard/HoverCard.tsx +4 -35
  135. package/src/HoverCard/useHoverCard.doc.mjs +36 -178
  136. package/src/HoverCard/useHoverCard.tsx +52 -46
  137. package/src/Icon/Icon.doc.mjs +8 -49
  138. package/src/Icon/Icon.tsx +7 -15
  139. package/src/Layer/usePressPin.ts +15 -81
  140. package/src/Lightbox/Lightbox.tsx +0 -18
  141. package/src/PowerSearch/PowerSearch.doc.mjs +5 -54
  142. package/src/PowerSearch/PowerSearch.tsx +155 -609
  143. package/src/PowerSearch/PowerSearchEditPopover.tsx +6 -28
  144. package/src/PowerSearch/PowerSearchValueEditor.tsx +38 -203
  145. package/src/PowerSearch/formatFilterValue.ts +1 -18
  146. package/src/RadioList/RadioList.tsx +2 -3
  147. package/src/RadioList/RadioListItem.tsx +3 -5
  148. package/src/Resizable/Resizable.doc.mjs +4 -5
  149. package/src/Resizable/ResizeHandle.tsx +109 -167
  150. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +3 -6
  151. package/src/Slider/sliderTouchController.ts +2 -2
  152. package/src/Spinner/Spinner.tsx +63 -77
  153. package/src/Switch/useSwitchTouch.ts +4 -4
  154. package/src/Table/BaseTable.tsx +12 -46
  155. package/src/TextInput/TextInput.tsx +10 -11
  156. package/src/Toast/Toast.doc.mjs +3 -4
  157. package/src/Toast/Toast.tsx +32 -52
  158. package/src/Toast/ToastViewport.tsx +35 -121
  159. package/src/Toast/types.ts +3 -9
  160. package/src/Tooltip/useTooltip.doc.mjs +0 -24
  161. package/src/Tooltip/useTooltip.tsx +56 -61
  162. package/src/Typeahead/TypeaheadItem.doc.mjs +3 -6
  163. package/src/Typeahead/TypeaheadItem.tsx +1 -2
  164. package/src/gestures/PressTracker.ts +3 -4
  165. package/src/gestures/README.md +1 -7
  166. package/src/hooks/actionGroupTracker.ts +5 -20
  167. package/src/hooks/index.ts +0 -2
  168. package/src/hooks/useAttachment.ts +1 -1
  169. package/src/hooks/useInputStatusIcon.tsx +2 -3
  170. package/src/hooks/useLongPress.doc.mjs +2 -2
  171. package/src/hooks/useLongPress.ts +15 -136
  172. package/src/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.ts +0 -6
  173. package/src/i18n/generated-locales/en.generated.ts +0 -41
  174. package/src/i18n/generated-locales/pseudo.generated.ts +0 -42
  175. package/src/index.ts +0 -13
  176. package/src/motion/README.md +5 -9
  177. package/src/motion/animatedLayout.ts +4 -5
  178. package/src/motion/animator.ts +29 -61
  179. package/src/utils/dateParser.ts +0 -16
  180. package/src/utils/focusReturn.ts +0 -30
  181. package/src/utils/interactionOverlay.stylex.ts +2 -3
  182. package/src/utils/pressFeedback.ts +6 -19
  183. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +0 -100
  184. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +0 -1
  185. package/dist/PowerSearch/PowerSearchBottomSheet.js +0 -1913
  186. package/dist/PowerSearch/formatTokenValue.d.ts +0 -28
  187. package/dist/PowerSearch/formatTokenValue.d.ts.map +0 -1
  188. package/dist/PowerSearch/formatTokenValue.js +0 -160
  189. package/dist/PowerSearch/powerSearchSheetUtils.d.ts +0 -65
  190. package/dist/PowerSearch/powerSearchSheetUtils.d.ts.map +0 -1
  191. package/dist/PowerSearch/powerSearchSheetUtils.js +0 -168
  192. package/dist/animator/createAnimator.d.ts +0 -98
  193. package/dist/animator/createAnimator.d.ts.map +0 -1
  194. package/dist/animator/createAnimator.js +0 -259
  195. package/dist/hooks/pressGroupContext.d.ts +0 -6
  196. package/dist/hooks/pressGroupContext.d.ts.map +0 -1
  197. package/dist/hooks/pressGroupContext.js +0 -20
  198. package/dist/hooks/useActionGroupTracking.d.ts +0 -53
  199. package/dist/hooks/useActionGroupTracking.d.ts.map +0 -1
  200. package/dist/hooks/useActionGroupTracking.js +0 -174
  201. package/dist/hooks/useAnimator.d.ts +0 -41
  202. package/dist/hooks/useAnimator.d.ts.map +0 -1
  203. package/dist/hooks/useAnimator.js +0 -70
  204. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts +0 -8
  205. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +0 -1
  206. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +0 -42
  207. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts +0 -8
  208. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts.map +0 -1
  209. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.js +0 -14
  210. package/dist/internal/indicatorPress.stylex.d.ts +0 -20
  211. package/dist/internal/indicatorPress.stylex.d.ts.map +0 -1
  212. package/dist/internal/indicatorPress.stylex.js +0 -26
  213. package/dist/motion/progress.d.ts +0 -88
  214. package/dist/motion/progress.d.ts.map +0 -1
  215. package/dist/motion/progress.js +0 -128
  216. package/dist/motion/uikit/alert.d.ts +0 -41
  217. package/dist/motion/uikit/alert.d.ts.map +0 -1
  218. package/dist/motion/uikit/alert.js +0 -43
  219. package/dist/privateAttributes/pressGroupItem.stylex.d.ts +0 -15
  220. package/dist/privateAttributes/pressGroupItem.stylex.d.ts.map +0 -1
  221. package/dist/privateAttributes/pressGroupItem.stylex.js +0 -23
  222. package/dist/utils/triggerFocus.d.ts +0 -36
  223. package/dist/utils/triggerFocus.d.ts.map +0 -1
  224. package/dist/utils/triggerFocus.js +0 -65
  225. package/src/PowerSearch/PowerSearchBottomSheet.tsx +0 -2515
  226. package/src/PowerSearch/formatTokenValue.ts +0 -215
  227. package/src/PowerSearch/powerSearchSheetUtils.ts +0 -224
  228. package/src/animator/createAnimator.doc.mjs +0 -153
  229. package/src/animator/createAnimator.ts +0 -324
  230. package/src/hooks/pressGroupContext.ts +0 -21
  231. package/src/hooks/useActionGroupTracking.ts +0 -224
  232. package/src/hooks/useAnimator.doc.mjs +0 -109
  233. package/src/hooks/useAnimator.ts +0 -67
  234. package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +0 -43
  235. package/src/i18n/generated-english/PowerSearch/formatTokenValue.generated.ts +0 -15
  236. package/src/internal/indicatorPress.stylex.ts +0 -24
  237. package/src/motion/progress.ts +0 -155
  238. package/src/motion/uikit/alert.ts +0 -43
  239. package/src/privateAttributes/pressGroupItem.stylex.ts +0 -23
  240. package/src/utils/triggerFocus.ts +0 -80
@@ -13,14 +13,13 @@
13
13
  * SYNC: When placement, stacking, hosting, focus, announcement, or safe-area
14
14
  * behavior changes, update ToastViewport.test.tsx, Toast.doc.mjs, and Toast.stories.tsx.
15
15
  */
16
- import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
16
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
17
17
  import { createPortal } from 'react-dom';
18
18
  import * as stylex from '@stylexjs/stylex';
19
19
  import "../theme/tokens.stylex.js";
20
20
  import { spacingVars, durationVars, easeVars } from "../theme/tokens.stylex.js";
21
21
  import { mergeProps } from "../utils/index.js";
22
22
  import { getNodeText } from "../utils/getNodeText.js";
23
- import { moveFocusLeftBehind, noteFocusWithin } from "../utils/focusReturn.js";
24
23
  import { INTERACTIVE_SELECTORS } from "../hooks/useClickableContainer.js";
25
24
  import { useAnnounce } from "../hooks/useAnnounce.js";
26
25
  import { layerTextReset } from "../Layer/layerTextReset.stylex.js";
@@ -123,8 +122,7 @@ function ToastRow({
123
122
  isExiting,
124
123
  isReversed,
125
124
  onExited,
126
- onDismiss,
127
- isLiveRegion
125
+ onDismiss
128
126
  }) {
129
127
  const o = entry.options;
130
128
  const type = o.type ?? 'info';
@@ -196,16 +194,12 @@ function ToastRow({
196
194
  isExiting: isExiting,
197
195
  gestureDirection: isReversed ? -1 : 1,
198
196
  onDismiss: reason => onDismiss(entry.id, reason),
199
- renderContent: o.renderContent,
200
- timerKey: entry.id,
201
- isLiveRegion: isLiveRegion
197
+ renderContent: o.renderContent
202
198
  })
203
199
  })
204
200
  });
205
201
  }
206
202
  ToastRow.displayName = 'ToastRow';
207
- const hasActiveModalOutlet = () => getActiveModalOutlet() != null;
208
- const hasNoModalOutlet = () => false;
209
203
 
210
204
  /**
211
205
  * Container that renders and manages toast notifications. Place at the root
@@ -266,11 +260,6 @@ export function ToastViewport({
266
260
  // The next toast id that should receive focus once the dismissed toast has
267
261
  // unmounted, or 'restore' to fall back to the previously-focused element.
268
262
  const pendingFocusRef = useRef(null);
269
- // The id of a toast whose overwrite updated a row that held focus. If the
270
- // update removed the focused control, the commit hands focus off.
271
- const overwriteFocusRef = useRef(null);
272
- // Where focus was inside a toast whose dismissal hands focus off.
273
- const dismissFocusNoteRef = useRef(null);
274
263
 
275
264
  // Collect a focusable control within a toast node, if any.
276
265
  // Reuses the canonical INTERACTIVE_SELECTORS list (native controls plus
@@ -291,9 +280,13 @@ export function ToastViewport({
291
280
  return null;
292
281
  }, []);
293
282
 
294
- // Announce toasts through the persistent singleton live regions. They are
295
- // mounted empty and only mutated afterwards, which screen readers announce
296
- // (see useAnnounce.ts). The announcement happens in addToast — the imperative
283
+ // Announce toasts through the persistent singleton live regions. Each
284
+ // <Toast> also renders its own role="status"/"alert" region, but that region
285
+ // is "born with content" — mounted together with its text — which many
286
+ // screen readers do not announce (see useAnnounce.ts); the singleton regions
287
+ // are mounted empty and only mutated afterwards, so they are what actually
288
+ // guarantees the announcement (the per-toast markup is kept for browse-mode
289
+ // discoverability). The announcement happens in addToast — the imperative
297
290
  // dispatch path invoked once per useToast() call from an event handler,
298
291
  // never from render — so each toast is announced exactly once by
299
292
  // construction, independent of the React render lifecycle (StrictMode
@@ -301,12 +294,6 @@ export function ToastViewport({
301
294
  // re-renders never re-announce). It is client-only (addToast never runs
302
295
  // during SSR), so it is SSR-safe.
303
296
  const announce = useAnnounce();
304
- // An open native modal hides the body-level channel above from assistive
305
- // technology, so while the viewport is hosted in one, each card announces
306
- // itself through its own live region instead. Outside a modal the cards
307
- // carry none, so a card updated in place is announced once.
308
- const hasModalOutlet = useSyncExternalStore(subscribeModalOutlets, hasActiveModalOutlet, hasNoModalOutlet);
309
- const isModalHosted = isTopLayer && hasModalOutlet;
310
297
  const addToast = useCallback(entry => {
311
298
  const {
312
299
  uniqueID,
@@ -339,26 +326,7 @@ export function ToastViewport({
339
326
  if (overwritten && renderedToastsRef.current.includes(overwritten)) {
340
327
  replacedRef.current.set(overwritten.id, overwritten);
341
328
  }
342
- // An overwrite of a toast that is not leaving keeps its row, so the card
343
- // updates in place and focus inside it stays. A leaving namesake
344
- // is replaced by a new row, which enters again.
345
- const keepsRow = overwritten != null && !exitingIdsRef.current.has(overwritten.id);
346
- const placed = keepsRow ? {
347
- ...entry,
348
- rowKey: overwritten.rowKey ?? overwritten.id
349
- } : entry;
350
- if (keepsRow) {
351
- // If the update removes the focused control, the commit hands focus
352
- // off as a dismissal does.
353
- const note = noteFocusWithin(viewportRef.current?.querySelector(`[data-toast-id="${overwritten.id}"]`));
354
- if (note != null) {
355
- overwriteFocusRef.current = {
356
- id: placed.id,
357
- note
358
- };
359
- }
360
- }
361
- const projected = overwritten ? committed.map(t => t === overwritten ? placed : t) : [...committed, placed];
329
+ const projected = overwritten ? committed.map(t => t === overwritten ? entry : t) : [...committed, entry];
362
330
  // `maxVisible={0}` shows every toast (FR7): nothing leaves the window.
363
331
  const firstVisible = maxVisible > 0 ? projected.length - maxVisible : 0;
364
332
  const cutIds = new Set(projected.filter((t, i) => i < firstVisible && exitingIdsRef.current.has(t.id)).map(t => t.id));
@@ -381,7 +349,7 @@ export function ToastViewport({
381
349
  }
382
350
  }
383
351
  }
384
- pending.set(entry.id, placed);
352
+ pending.set(entry.id, entry);
385
353
  // An overwrite swaps a new entry, with a new id, into the replaced
386
354
  // toast's place, so the old row leaves the DOM without ever being
387
355
  // dismissed. Its settled id is dropped by the prune below, which covers
@@ -392,7 +360,7 @@ export function ToastViewport({
392
360
  // replaced namesake as leaving since, which must not turn the
393
361
  // overwrite into an append. An ignored collision already returned.
394
362
  const kept = prev.filter(t => !cutIds.has(t.id));
395
- return overwritten && kept.includes(overwritten) ? kept.map(t => t === overwritten ? placed : t) : [...kept, placed];
363
+ return overwritten && kept.includes(overwritten) ? kept.map(t => t === overwritten ? entry : t) : [...kept, entry];
396
364
  });
397
365
  }, [announce, maxVisible]);
398
366
  const removeToast = useCallback((id, reason) => {
@@ -415,15 +383,9 @@ export function ToastViewport({
415
383
  // focused before the user entered the viewport) rather than <body>.
416
384
  const el = viewportRef.current;
417
385
  const active = document.activeElement;
418
- // A toast an overwrite replaced in place no longer owns its row: the
419
- // replacement does, so dismissing the replaced toast leaves the row, and
420
- // focus in it, to the replacement.
421
- const rowKey = entry.rowKey ?? entry.id;
422
- const isReplacedInPlace = !toastsRef.current.some(t => t.id === id) && toastsRef.current.some(t => t.rowKey === rowKey);
423
- const dismissedNode = isReplacedInPlace ? null : el?.querySelector(`[data-toast-id="${id}"]`) ?? null;
386
+ const dismissedNode = el?.querySelector(`[data-toast-id="${id}"]`) ?? null;
424
387
  if (dismissedNode && active instanceof Node && dismissedNode.contains(active)) {
425
388
  focusHandoffIdRef.current = id;
426
- dismissFocusNoteRef.current = noteFocusWithin(dismissedNode);
427
389
  // Pick the toast to receive focus while the DOM is still intact: the
428
390
  // nearest one after it, then before it, that shows once it is gone
429
391
  // (FR10), else restore. The list may already run ahead of the render
@@ -479,31 +441,6 @@ export function ToastViewport({
479
441
  setToasts(prev => prev.filter(t => t.id !== id));
480
442
  }, []);
481
443
 
482
- // Moves focus to a control in the target toast, else in another toast that
483
- // shows (newest first), else back to the element focused before focus
484
- // entered the viewport, so a handoff never drops focus to <body>.
485
- const handOffFocus = useCallback((note, targetId) => {
486
- const el = viewportRef.current;
487
- const rowFor = id => el?.querySelector(`[data-toast-id="${id}"]`) ?? null;
488
- const shownIds = toastsRef.current.slice(maxVisibleRef.current > 0 ? -maxVisibleRef.current : 0).map(t => t.id).reverse();
489
- const candidates = targetId == null ? [] : [targetId, ...shownIds.filter(id => id !== targetId)];
490
- let target = null;
491
- for (const id of candidates) {
492
- target = getFocusable(rowFor(id));
493
- if (target != null) {
494
- break;
495
- }
496
- }
497
- const prev = prevFocusRef.current;
498
- const isReturning = target == null && prev != null && prev.isConnected;
499
- if (target == null) {
500
- target = isReturning ? prev : el;
501
- }
502
- if (moveFocusLeftBehind(note, target) && isReturning) {
503
- prevFocusRef.current = null;
504
- }
505
- }, [getFocusable]);
506
-
507
444
  // After a dismissed toast unmounts, hand focus off so it never falls to
508
445
  // <body>. Runs in a layout effect once the toast list no longer contains
509
446
  // the dismissed toast.
@@ -519,21 +456,25 @@ export function ToastViewport({
519
456
  }
520
457
  focusHandoffIdRef.current = null;
521
458
  pendingFocusRef.current = null;
522
- const note = dismissFocusNoteRef.current;
523
- dismissFocusNoteRef.current = null;
524
- handOffFocus(note, target === 'restore' ? null : target);
525
- }, [toasts, handOffFocus]);
526
-
527
- // After an overwrite updated a row that held focus: focus stays on the same
528
- // control, or, if the update removed it, moves as a dismissal hands it off.
529
- useLayoutEffect(() => {
530
- const overwrite = overwriteFocusRef.current;
531
- if (overwrite == null) {
532
- return;
459
+ const el = viewportRef.current;
460
+ if (target !== 'restore' && el) {
461
+ const nextNode = el.querySelector(`[data-toast-id="${target}"]`);
462
+ const focusable = getFocusable(nextNode) ?? nextNode;
463
+ if (focusable) {
464
+ focusable.focus();
465
+ return;
466
+ }
467
+ }
468
+ // No remaining toast to receive focus — restore the previously-focused
469
+ // element if it's still connected, else fall back to the container.
470
+ const prev = prevFocusRef.current;
471
+ prevFocusRef.current = null;
472
+ if (prev && prev.isConnected) {
473
+ prev.focus();
474
+ } else if (el) {
475
+ el.focus();
533
476
  }
534
- overwriteFocusRef.current = null;
535
- handOffFocus(overwrite.note, overwrite.id);
536
- }, [toasts, handOffFocus]);
477
+ }, [toasts, getFocusable]);
537
478
  const findByUniqueID = useCallback(uid => {
538
479
  return toastsRef.current.find(t => t.options.uniqueID === uid);
539
480
  }, []);
@@ -722,9 +663,8 @@ export function ToastViewport({
722
663
  isExiting: exitingIds.has(entry.id),
723
664
  isReversed: isReversed,
724
665
  onExited: handleExited,
725
- onDismiss: removeToast,
726
- isLiveRegion: isModalHosted
727
- }, entry.rowKey ?? entry.id))
666
+ onDismiss: removeToast
667
+ }, entry.id))
728
668
  })
729
669
  });
730
670
  return /*#__PURE__*/_jsxs(ToastContext, {
@@ -31,10 +31,9 @@ export interface ToastOptions {
31
31
  /**
32
32
  * Replaces the content of this toast's card with your own layout.
33
33
  *
34
- * Astryx keeps the card and its auto-hide behavior (inside a
35
- * `ToastViewport` the viewport announces the toast, and while a modal hosts
36
- * the viewport the card announces itself), then hands the
37
- * renderer this toast's content, resolved settings and `dismiss` callback. The renderer owns every control inside its layout; call
34
+ * Astryx keeps the card, live-region role and auto-hide behavior, then hands
35
+ * the renderer this toast's content, resolved settings and `dismiss`
36
+ * callback. The renderer owns every control inside its layout; call
38
37
  * `dismiss` from the control that should close the toast. Astryx does not
39
38
  * inject a fallback control into custom content.
40
39
  *
@@ -86,10 +85,5 @@ export interface ToastEntry {
86
85
  id: string;
87
86
  options: ToastOptions;
88
87
  createdAt: number;
89
- /**
90
- * The row's identity in the stack. A uniqueID overwrite keeps the replaced
91
- * toast's, so the card updates in place and focus inside it stays.
92
- */
93
- rowKey?: string;
94
88
  }
95
89
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/Toast/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAErC,gDAAgD;AAChD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;AAEhF,mEAAmE;AACnE,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE5D,wCAAwC;AACxC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnD,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAC;IAErC,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C,gDAAgD;IAChD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC/C;AAED,oDAAoD;AACpD,MAAM,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC;AAExC,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,yDAAyD;IACzD,IAAI,EAAE,SAAS,CAAC;IAChB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,4EAA4E;IAC5E,IAAI,EAAE,SAAS,CAAC;IAChB,8CAA8C;IAC9C,UAAU,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,KAAK,EAAE,uBAAuB,KAC3B,SAAS,CAAC;AAEf,oDAAoD;AACpD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,cAAc,CAAC;AAEpE,iDAAiD;AACjD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/Toast/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAErC,gDAAgD;AAChD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;AAEhF,mEAAmE;AACnE,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE5D,wCAAwC;AACxC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnD,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAC;IAErC,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C,gDAAgD;IAChD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC/C;AAED,oDAAoD;AACpD,MAAM,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC;AAExC,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,yDAAyD;IACzD,IAAI,EAAE,SAAS,CAAC;IAChB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,4EAA4E;IAC5E,IAAI,EAAE,SAAS,CAAC;IAChB,8CAA8C;IAC9C,UAAU,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,KAAK,EAAE,uBAAuB,KAC3B,SAAS,CAAC;AAEf,oDAAoD;AACpD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,cAAc,CAAC;AAEpE,iDAAiD;AACjD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * @file useTooltip.tsx
3
3
  * @input Uses useLayer, usePressPin, React hooks
4
- * @output Exports useTooltip hook with stable trigger refs for hover/focus/tap tooltips
5
- * and imperative show() and hide() for an app-built toggle trigger,
4
+ * @output Exports useTooltip hook with stable trigger refs for hover/focus/tap tooltips,
6
5
  * and the module-only useTooltipWithTriggerBehavior that InfoTip's press-to-pin uses
7
6
  * @position Layer hook; builds on useLayer for tooltip behavior
8
7
  *
@@ -134,23 +133,6 @@ export interface TooltipReturn {
134
133
  * silent no-op.
135
134
  */
136
135
  renderTooltip: (children: ReactNode, props?: Omit<ContextRenderProps, 'positioning'>) => ReactNode;
137
- /**
138
- * Opens the tooltip now and keeps it open until `hide()`, Escape, a press
139
- * outside, or focus leaving its trigger, as a pinned tooltip does. Its text
140
- * is announced once. Reports through `onShow`. Under a controlled `isOpen`,
141
- * it calls `onShow` and does not change `isOpen`. Does nothing while
142
- * `isEnabled` is false.
143
- *
144
- * Use `show()` and `hide()` to build a toggle-style trigger: your own button
145
- * calls them, sets `aria-describedby` and `aria-controls` to `describedBy`
146
- * and `aria-expanded` (true while shown), and keeps its own accessible name.
147
- */
148
- show: () => void;
149
- /**
150
- * Closes the tooltip now. Reports through `onHide`. Under a controlled
151
- * `isOpen`, it calls `onHide` and does not change `isOpen`.
152
- */
153
- hide: () => void;
154
136
  }
155
137
  /**
156
138
  * Hook for tooltip behavior with hover/focus triggers.
@@ -1 +1 @@
1
- {"version":3,"file":"useTooltip.d.ts","sourceRoot":"","sources":["../../src/Tooltip/useTooltip.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,OAAO,CAAC;AAGf,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAKhE,OAAO,EAAc,KAAK,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AA0C5E;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,GAAG,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAE9B;;;OAGG;IACH,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAEtC;;;;OAIG;IACH,cAAc,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAEzC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;OAOG;IACH,aAAa,EAAE,CACb,QAAQ,EAAE,SAAS,EACnB,KAAK,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,KAC5C,SAAS,CAAC;IAEf;;;;;;;;;;OAUG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;;OAGG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,wBAAgB,UAAU,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CAOtE;AAED;;;GAGG;AACH,MAAM,WAAW,iCAAkC,SAAQ,cAAc;IACvE;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,EAAE,oBAAoB,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,iCAAsC,GAC9C,aAAa,CA2df"}
1
+ {"version":3,"file":"useTooltip.d.ts","sourceRoot":"","sources":["../../src/Tooltip/useTooltip.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAKhE,OAAO,EAAc,KAAK,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AA0C5E;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,GAAG,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAE9B;;;OAGG;IACH,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAEtC;;;;OAIG;IACH,cAAc,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAEzC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;OAOG;IACH,aAAa,EAAE,CACb,QAAQ,EAAE,SAAS,EACnB,KAAK,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,KAC5C,SAAS,CAAC;CAChB;AAyBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,wBAAgB,UAAU,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CAOtE;AAED;;;GAGG;AACH,MAAM,WAAW,iCAAkC,SAAQ,cAAc;IACvE;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,EAAE,oBAAoB,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,iCAAsC,GAC9C,aAAa,CAqdf"}
@@ -5,16 +5,14 @@
5
5
  /**
6
6
  * @file useTooltip.tsx
7
7
  * @input Uses useLayer, usePressPin, React hooks
8
- * @output Exports useTooltip hook with stable trigger refs for hover/focus/tap tooltips
9
- * and imperative show() and hide() for an app-built toggle trigger,
8
+ * @output Exports useTooltip hook with stable trigger refs for hover/focus/tap tooltips,
10
9
  * and the module-only useTooltipWithTriggerBehavior that InfoTip's press-to-pin uses
11
10
  * @position Layer hook; builds on useLayer for tooltip behavior
12
11
  *
13
12
  * SYNC: When modified, update:
14
13
  * - /packages/core/src/Tooltip/index.ts
15
14
  */
16
- import { useCallback, useEffect, useRef, useState } from 'react';
17
- import { createTriggerFocusGate } from "../utils/triggerFocus.js";
15
+ import { useCallback, useEffect, useRef } from 'react';
18
16
  import * as stylex from '@stylexjs/stylex';
19
17
  import { useLayerInternal } from "../Layer/useLayer.js";
20
18
  import { useAnnounceRenderedText } from "../hooks/useAnnounceRenderedText.js";
@@ -54,6 +52,28 @@ const styles = {
54
52
  * Touch trigger behavior for tooltips
55
53
  */
56
54
 
55
+ /**
56
+ * Check if an element is naturally focusable
57
+ */
58
+ function isFocusable(element) {
59
+ // Elements with explicit tabindex
60
+ if (element.hasAttribute('tabindex')) {
61
+ return element.tabIndex >= 0;
62
+ }
63
+
64
+ // Naturally focusable elements
65
+ const focusableTags = ['A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'];
66
+ if (focusableTags.includes(element.tagName)) {
67
+ return !element.disabled;
68
+ }
69
+
70
+ // Elements with contenteditable
71
+ if (element.isContentEditable) {
72
+ return true;
73
+ }
74
+ return false;
75
+ }
76
+
57
77
  /**
58
78
  * Hook for tooltip behavior with hover/focus triggers.
59
79
  *
@@ -167,9 +187,6 @@ export function useTooltipWithTriggerBehavior(options = {}) {
167
187
  isHeldOpen: isPressHeldOpen,
168
188
  attach: attachPressPin,
169
189
  unpin: unpinPress,
170
- hold: holdPress,
171
- release: releasePress,
172
- isAppHeld,
173
190
  isPinShown,
174
191
  touch: {
175
192
  isTouchPointerRef,
@@ -272,11 +289,25 @@ export function useTooltipWithTriggerBehavior(options = {}) {
272
289
  scheduleHide();
273
290
  }, [isTouchPointerRef, layerId, scheduleHide]);
274
291
 
275
- // Under `auto`, whether focus counts is decided when it arrives, and a
276
- // focus that counted still hides on blur (utils/triggerFocus.ts).
277
- const [focusGate] = useState(createTriggerFocusGate);
292
+ // Under `auto`, whether focus counts is decided when it arrives, not when
293
+ // the listeners attach: a roving-tabindex member that was `tabindex="-1"`
294
+ // then can be the tab stop now. A trigger that receives focus is focusable
295
+ // by definition; focus inside it counts only while the trigger is
296
+ // focusable too, so a plain wrapper still ignores its children's focus.
297
+ const isFocusForTrigger = useCallback(e => {
298
+ if (focusTrigger === 'always') {
299
+ return true;
300
+ }
301
+ const triggerEl = e.currentTarget;
302
+ return triggerEl != null && (e.target === triggerEl || isFocusable(triggerEl));
303
+ }, [focusTrigger]);
304
+
305
+ // Whether the trigger's current focus counted when it arrived, so its blur
306
+ // hides the tooltip even if the trigger has since left the tab order.
307
+ const focusCountedRef = useRef(false);
278
308
  const handleFocusIn = useCallback(e => {
279
- if (!focusGate.focusIn(e, focusTrigger) || !isEnabled) {
309
+ focusCountedRef.current = isFocusForTrigger(e);
310
+ if (!isEnabled || !focusCountedRef.current) {
280
311
  return;
281
312
  }
282
313
  // A tap focuses the trigger it activates, and `:focus-visible` does not
@@ -295,17 +326,18 @@ export function useTooltipWithTriggerBehavior(options = {}) {
295
326
  }
296
327
  clearTimeouts();
297
328
  showLayer();
298
- }, [focusGate, focusTrigger, isEnabled, isTouchInteraction, clearTimeouts, showLayer]);
329
+ }, [isEnabled, isFocusForTrigger, isTouchInteraction, clearTimeouts, showLayer]);
299
330
 
300
331
  // A pinned tooltip's focus-leave close is the pin's own: it listens on the
301
332
  // trigger whatever `focusTrigger` is. This schedules the unpinned hide,
302
333
  // which a held tooltip skips.
303
334
  const handleFocusOut = useCallback(e => {
304
- if (!focusGate.focusOut(e, focusTrigger)) {
335
+ if (!focusCountedRef.current && !isFocusForTrigger(e)) {
305
336
  return;
306
337
  }
338
+ focusCountedRef.current = false;
307
339
  scheduleHide();
308
- }, [focusGate, focusTrigger, scheduleHide]);
340
+ }, [isFocusForTrigger, scheduleHide]);
309
341
 
310
342
  // Pressing the trigger hides its own tooltip: once the control is activated
311
343
  // the hint has served its purpose, and a tooltip lingering over a
@@ -315,10 +347,9 @@ export function useTooltipWithTriggerBehavior(options = {}) {
315
347
  // A touch press is a different gesture (it may be the only way to open the
316
348
  // tooltip at all), so the touch path answers it first.
317
349
  const handlePointerDown = useCallback(event => {
318
- // A toggle trigger's press pins or closes through its click. The app's
319
- // hide() closes a held tooltip. Neither path hides here or waits for a
320
- // tap verdict.
321
- if (isToggleTrigger(triggerRef.current) || isAppHeld()) {
350
+ // A press on a toggle trigger pins or closes through its click. It
351
+ // neither hides the preview here nor waits for a tap verdict.
352
+ if (isToggleTrigger(triggerRef.current)) {
322
353
  return;
323
354
  }
324
355
  if (handleTouchPointerDown(event)) {
@@ -329,7 +360,7 @@ export function useTooltipWithTriggerBehavior(options = {}) {
329
360
  }
330
361
  clearTimeouts();
331
362
  hideLayer();
332
- }, [isToggleTrigger, isAppHeld, handleTouchPointerDown, isOpen, clearTimeouts, hideLayer]);
363
+ }, [isToggleTrigger, handleTouchPointerDown, isOpen, clearTimeouts, hideLayer]);
333
364
 
334
365
  // Interaction ref that handles event listeners only
335
366
  const interactionRef = useCallback(el => {
@@ -353,7 +384,7 @@ export function useTooltipWithTriggerBehavior(options = {}) {
353
384
  el.addEventListener('pointerdown', handlePointerDown);
354
385
 
355
386
  // Focus listeners unless focus never opens the tooltip; `auto` decides
356
- // per event (utils/triggerFocus.ts).
387
+ // per event (isFocusForTrigger).
357
388
  if (focusTrigger !== 'never') {
358
389
  el.addEventListener('focusin', handleFocusIn);
359
390
  el.addEventListener('focusout', handleFocusOut);
@@ -394,11 +425,9 @@ export function useTooltipWithTriggerBehavior(options = {}) {
394
425
  showLayer();
395
426
  } else {
396
427
  clearTimeouts();
397
- // A pin or app hold ends when the consumer closes the tooltip.
398
- unpinPress();
399
428
  hideLayer();
400
429
  }
401
- }, [isOpen, clearTimeouts, showLayer, hideLayer, unpinPress]);
430
+ }, [isOpen, clearTimeouts, showLayer, hideLayer]);
402
431
 
403
432
  // Dismiss on Escape (WCAG 1.4.13 — dismissible) through the shared layer
404
433
  // stack. A visible tip is the top-most layer, so it takes the press and
@@ -478,22 +507,12 @@ export function useTooltipWithTriggerBehavior(options = {}) {
478
507
  children: children
479
508
  }), renderProps);
480
509
  }, [renderLayer, placement, alignment, popoverXstyle, cancelHide, scheduleHide, contentRefs]);
481
- const show = useCallback(() => {
482
- clearTimeouts();
483
- holdPress();
484
- }, [clearTimeouts, holdPress]);
485
- const hide = useCallback(() => {
486
- clearTimeouts();
487
- releasePress();
488
- }, [clearTimeouts, releasePress]);
489
510
  return {
490
511
  ref,
491
512
  positionRef: layerRef,
492
513
  interactionRef,
493
514
  anchorId: layerAnchorId,
494
515
  describedBy: layerId,
495
- renderTooltip,
496
- show,
497
- hide
516
+ renderTooltip
498
517
  };
499
518
  }
@@ -31,8 +31,7 @@ export interface TypeaheadItemProps<T extends SearchableItem = SearchableItem> e
31
31
  */
32
32
  isDisabled?: boolean;
33
33
  /**
34
- * This prop has no effect. Set `item.auxiliaryData.group` on search-source
35
- * items to group results in Typeahead or BaseTypeahead.
34
+ * Group label for grouping items visually.
36
35
  */
37
36
  group?: string;
38
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TypeaheadItem.d.ts","sourceRoot":"","sources":["../../src/Typeahead/TypeaheadItem.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAQ5C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAQ5C,MAAM,WAAW,kBAAkB,CACjC,CAAC,SAAS,cAAc,GAAG,cAAc,CACzC,SAAQ,SAAS,CAAC,cAAc,CAAC;IACjC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,IAAI,EAAE,CAAC,CAAC;IAER;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA6CD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,cAAc,EAAE,EACtD,GAAG,EACH,IAAI,EACJ,IAAI,EACJ,WAAW,EACX,UAAkB,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,kBAAkB,CAAC,CAAC,CAAC,qBAyBvB;yBApCe,aAAa"}
1
+ {"version":3,"file":"TypeaheadItem.d.ts","sourceRoot":"","sources":["../../src/Typeahead/TypeaheadItem.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAQ5C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAQ5C,MAAM,WAAW,kBAAkB,CACjC,CAAC,SAAS,cAAc,GAAG,cAAc,CACzC,SAAQ,SAAS,CAAC,cAAc,CAAC;IACjC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,IAAI,EAAE,CAAC,CAAC;IAER;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA6CD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,cAAc,EAAE,EACtD,GAAG,EACH,IAAI,EACJ,IAAI,EACJ,WAAW,EACX,UAAkB,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,kBAAkB,CAAC,CAAC,CAAC,qBAyBvB;yBApCe,aAAa"}