@astryxdesign/core 0.7.7-canary.c0929c8 → 0.7.7-canary.c19ca12

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 (472) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +4 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +36 -6
  4. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts +13 -0
  5. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts.map +1 -0
  6. package/dist/AppShell/useCloseMobileDrawerOnActivate.js +45 -0
  7. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheet.js +79 -9
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  10. package/dist/BottomSheet/BottomSheetPanel.js +23 -4
  11. package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
  12. package/dist/BottomSheet/bottomSheetStack.js +5 -4
  13. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  14. package/dist/BottomSheet/useSheetGestures.js +3 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +2 -1
  16. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  17. package/dist/Breadcrumbs/BreadcrumbItem.js +7 -3
  18. package/dist/Button/Button.d.ts.map +1 -1
  19. package/dist/Button/Button.js +47 -3
  20. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  21. package/dist/CheckboxInput/CheckboxInput.js +13 -2
  22. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  23. package/dist/CheckboxList/CheckboxList.js +7 -1
  24. package/dist/CheckboxList/CheckboxListContext.d.ts +6 -0
  25. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  26. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  27. package/dist/CheckboxList/CheckboxListItem.js +10 -2
  28. package/dist/ComplexSelector/ComplexSelector.d.ts +4 -1
  29. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  30. package/dist/ComplexSelector/ComplexSelector.js +14 -4
  31. package/dist/ContextMenu/ContextMenu.js +2 -2
  32. package/dist/DateInput/DateInput.d.ts.map +1 -1
  33. package/dist/DateInput/DateInput.js +5 -2
  34. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  35. package/dist/DateInput/MonthScroller.js +13 -27
  36. package/dist/DateInput/Wheel.d.ts.map +1 -1
  37. package/dist/DateInput/Wheel.js +22 -41
  38. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  39. package/dist/DateTimeInput/DateTimeInput.js +5 -2
  40. package/dist/Dialog/Dialog.d.ts.map +1 -1
  41. package/dist/Dialog/Dialog.js +23 -45
  42. package/dist/Drawer/Drawer.d.ts.map +1 -1
  43. package/dist/Drawer/Drawer.js +15 -11
  44. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  45. package/dist/Drawer/useDrawerDialogPresence.js +5 -1
  46. package/dist/Field/Field.js +2 -2
  47. package/dist/Field/PanelSearchInput.d.ts +9 -1
  48. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  49. package/dist/Field/PanelSearchInput.js +3 -1
  50. package/dist/HoverCard/HoverCard.d.ts +4 -3
  51. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  52. package/dist/HoverCard/HoverCard.js +27 -2
  53. package/dist/HoverCard/useHoverCard.d.ts +5 -4
  54. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  55. package/dist/HoverCard/useHoverCard.js +17 -31
  56. package/dist/Icon/Icon.d.ts +5 -1
  57. package/dist/Icon/Icon.d.ts.map +1 -1
  58. package/dist/Icon/Icon.js +5 -2
  59. package/dist/InfoTip/InfoTip.d.ts +63 -0
  60. package/dist/InfoTip/InfoTip.d.ts.map +1 -0
  61. package/dist/InfoTip/InfoTip.js +126 -0
  62. package/dist/InfoTip/index.d.ts +10 -0
  63. package/dist/InfoTip/index.d.ts.map +1 -0
  64. package/dist/InfoTip/index.js +12 -0
  65. package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
  66. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  67. package/dist/Layer/layerAnimations.stylex.js +25 -20
  68. package/dist/Layer/useBackdropPress.d.ts +27 -0
  69. package/dist/Layer/useBackdropPress.d.ts.map +1 -0
  70. package/dist/Layer/useBackdropPress.js +53 -0
  71. package/dist/Layer/usePressPin.d.ts +20 -10
  72. package/dist/Layer/usePressPin.d.ts.map +1 -1
  73. package/dist/Layer/usePressPin.js +66 -18
  74. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  75. package/dist/Lightbox/Lightbox.js +18 -0
  76. package/dist/MobileNav/MobileNav.js +0 -1
  77. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  78. package/dist/MultiSelector/hooks.js +2 -4
  79. package/dist/Outline/Outline.d.ts +2 -4
  80. package/dist/Outline/Outline.d.ts.map +1 -1
  81. package/dist/Outline/Outline.js +4 -52
  82. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  83. package/dist/Outline/useOutlineFromMarkdown.js +1 -0
  84. package/dist/Outline/useScrollSpy.js +3 -3
  85. package/dist/PowerSearch/PowerSearch.d.ts +36 -3
  86. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  87. package/dist/PowerSearch/PowerSearch.js +571 -302
  88. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +100 -0
  89. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -0
  90. package/dist/PowerSearch/PowerSearchBottomSheet.js +1913 -0
  91. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +32 -1
  92. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  93. package/dist/PowerSearch/PowerSearchEditPopover.js +17 -6
  94. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +38 -3
  95. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  96. package/dist/PowerSearch/PowerSearchValueEditor.js +170 -61
  97. package/dist/PowerSearch/formatFilterValue.d.ts +7 -0
  98. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  99. package/dist/PowerSearch/formatFilterValue.js +11 -1
  100. package/dist/PowerSearch/formatTokenValue.d.ts +28 -0
  101. package/dist/PowerSearch/formatTokenValue.d.ts.map +1 -0
  102. package/dist/PowerSearch/formatTokenValue.js +160 -0
  103. package/dist/PowerSearch/powerSearchSheetUtils.d.ts +65 -0
  104. package/dist/PowerSearch/powerSearchSheetUtils.d.ts.map +1 -0
  105. package/dist/PowerSearch/powerSearchSheetUtils.js +168 -0
  106. package/dist/RadioList/RadioList.d.ts.map +1 -1
  107. package/dist/RadioList/RadioList.js +8 -2
  108. package/dist/RadioList/RadioListItem.d.ts +1 -1
  109. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  110. package/dist/RadioList/RadioListItem.js +5 -3
  111. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  112. package/dist/Resizable/ResizeHandle.js +118 -260
  113. package/dist/Resizable/resizeGesture.d.ts +91 -0
  114. package/dist/Resizable/resizeGesture.d.ts.map +1 -0
  115. package/dist/Resizable/resizeGesture.js +251 -0
  116. package/dist/SegmentedControl/SegmentedControl.d.ts +3 -1
  117. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  118. package/dist/SegmentedControl/SegmentedControl.js +115 -8
  119. package/dist/SegmentedControl/SegmentedControlContext.d.ts +6 -0
  120. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  121. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -0
  122. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  123. package/dist/SegmentedControl/SegmentedControlItem.js +8 -1
  124. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts +357 -0
  125. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -0
  126. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +1349 -0
  127. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts +70 -0
  128. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts.map +1 -0
  129. package/dist/SegmentedControl/useSegmentedTouchTracking.js +264 -0
  130. package/dist/Selector/Selector.d.ts.map +1 -1
  131. package/dist/Selector/Selector.js +96 -67
  132. package/dist/Selector/optionShape.d.ts +24 -0
  133. package/dist/Selector/optionShape.d.ts.map +1 -0
  134. package/dist/Selector/optionShape.js +48 -0
  135. package/dist/Selector/selectableOptionEntries.d.ts +20 -0
  136. package/dist/Selector/selectableOptionEntries.d.ts.map +1 -0
  137. package/dist/Selector/selectableOptionEntries.js +38 -0
  138. package/dist/Selector/utils.d.ts +2 -17
  139. package/dist/Selector/utils.d.ts.map +1 -1
  140. package/dist/Selector/utils.js +3 -51
  141. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  142. package/dist/SideNav/SideNavItem.js +3 -9
  143. package/dist/Slider/sliderTouchController.js +2 -2
  144. package/dist/Spinner/Spinner.d.ts +1 -1
  145. package/dist/Spinner/Spinner.d.ts.map +1 -1
  146. package/dist/Spinner/Spinner.js +60 -58
  147. package/dist/Switch/switchTouch.d.ts +0 -7
  148. package/dist/Switch/switchTouch.d.ts.map +1 -1
  149. package/dist/Switch/switchTouch.js +2 -10
  150. package/dist/Switch/useSwitchTouch.d.ts +2 -11
  151. package/dist/Switch/useSwitchTouch.d.ts.map +1 -1
  152. package/dist/Switch/useSwitchTouch.js +35 -93
  153. package/dist/Table/BaseTable.d.ts.map +1 -1
  154. package/dist/Table/BaseTable.js +29 -8
  155. package/dist/TextInput/TextInput.d.ts +2 -2
  156. package/dist/TextInput/TextInput.d.ts.map +1 -1
  157. package/dist/TextInput/TextInput.js +10 -7
  158. package/dist/Toast/Toast.d.ts +11 -1
  159. package/dist/Toast/Toast.d.ts.map +1 -1
  160. package/dist/Toast/Toast.js +30 -15
  161. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  162. package/dist/Toast/ToastViewport.js +94 -34
  163. package/dist/Toast/types.d.ts +9 -3
  164. package/dist/Toast/types.d.ts.map +1 -1
  165. package/dist/Token/Token.d.ts +2 -2
  166. package/dist/Token/Token.d.ts.map +1 -1
  167. package/dist/Token/Token.js +13 -4
  168. package/dist/Token/TokenNamingContext.d.ts +14 -0
  169. package/dist/Token/TokenNamingContext.d.ts.map +1 -0
  170. package/dist/Token/TokenNamingContext.js +23 -0
  171. package/dist/Tokenizer/Tokenizer.d.ts +46 -59
  172. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  173. package/dist/Tokenizer/Tokenizer.js +40 -57
  174. package/dist/Tokenizer/TokenizerBottomSheet.d.ts +25 -28
  175. package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
  176. package/dist/Tokenizer/TokenizerBottomSheet.js +157 -54
  177. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts +3 -0
  178. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -0
  179. package/dist/Tokenizer/TokenizerItemRemovableContext.js +19 -0
  180. package/dist/Tooltip/Tooltip.d.ts +25 -30
  181. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  182. package/dist/Tooltip/Tooltip.js +24 -5
  183. package/dist/Tooltip/index.d.ts +1 -1
  184. package/dist/Tooltip/index.d.ts.map +1 -1
  185. package/dist/Tooltip/useTooltip.d.ts +45 -28
  186. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  187. package/dist/Tooltip/useTooltip.js +52 -59
  188. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  189. package/dist/TopNav/TopNavItem.js +4 -7
  190. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  191. package/dist/TopNav/TopNavMegaMenuItem.js +4 -7
  192. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  193. package/dist/TopNav/TopNavMenu.js +4 -7
  194. package/dist/Typeahead/TypeaheadItem.d.ts +2 -1
  195. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  196. package/dist/animator/createAnimator.d.ts +98 -0
  197. package/dist/animator/createAnimator.d.ts.map +1 -0
  198. package/dist/animator/createAnimator.js +259 -0
  199. package/dist/astryx.css +96 -7
  200. package/dist/gestures/GestureHost.d.ts +27 -1
  201. package/dist/gestures/GestureHost.d.ts.map +1 -1
  202. package/dist/gestures/GestureHost.js +157 -21
  203. package/dist/gestures/PressTracker.d.ts.map +1 -1
  204. package/dist/gestures/PressTracker.js +4 -3
  205. package/dist/hooks/actionGroupTracker.d.ts +7 -4
  206. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  207. package/dist/hooks/actionGroupTracker.js +19 -56
  208. package/dist/hooks/index.d.ts +1 -0
  209. package/dist/hooks/index.d.ts.map +1 -1
  210. package/dist/hooks/index.js +1 -0
  211. package/dist/hooks/pressGroupContext.d.ts +6 -0
  212. package/dist/hooks/pressGroupContext.d.ts.map +1 -0
  213. package/dist/hooks/pressGroupContext.js +20 -0
  214. package/dist/hooks/useActionGroupTracking.d.ts +53 -0
  215. package/dist/hooks/useActionGroupTracking.d.ts.map +1 -0
  216. package/dist/hooks/useActionGroupTracking.js +174 -0
  217. package/dist/hooks/useAnimator.d.ts +41 -0
  218. package/dist/hooks/useAnimator.d.ts.map +1 -0
  219. package/dist/hooks/useAnimator.js +70 -0
  220. package/dist/hooks/useAttachment.d.ts +1 -1
  221. package/dist/hooks/useAttachment.js +1 -1
  222. package/dist/hooks/useHighlightedOptionScroll.d.ts +11 -1
  223. package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
  224. package/dist/hooks/useHighlightedOptionScroll.js +112 -11
  225. package/dist/hooks/useInputStatusIcon.d.ts +1 -1
  226. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  227. package/dist/hooks/useInputStatusIcon.js +2 -2
  228. package/dist/hooks/useLongPress.d.ts +6 -2
  229. package/dist/hooks/useLongPress.d.ts.map +1 -1
  230. package/dist/hooks/useLongPress.js +95 -15
  231. package/dist/hooks/useMediaQuery.d.ts +2 -1
  232. package/dist/hooks/useMediaQuery.d.ts.map +1 -1
  233. package/dist/hooks/useMediaQuery.js +7 -4
  234. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  235. package/dist/hooks/useMenuPress.js +4 -2
  236. package/dist/i18n/generated-english/InfoTip/InfoTip.generated.d.ts +8 -0
  237. package/dist/i18n/generated-english/InfoTip/InfoTip.generated.d.ts.map +1 -0
  238. package/dist/i18n/generated-english/InfoTip/InfoTip.generated.js +12 -0
  239. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.d.ts.map +1 -1
  240. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.js +1 -0
  241. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts +8 -0
  242. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +1 -0
  243. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +42 -0
  244. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.d.ts.map +1 -1
  245. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.js +6 -0
  246. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts +8 -0
  247. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts.map +1 -0
  248. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.js +14 -0
  249. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.d.ts.map +1 -1
  250. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.js +7 -1
  251. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  252. package/dist/i18n/generated-locales/en.generated.js +33 -1
  253. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  254. package/dist/i18n/generated-locales/pseudo.generated.js +33 -1
  255. package/dist/index.d.ts +2 -0
  256. package/dist/index.d.ts.map +1 -1
  257. package/dist/index.js +5 -0
  258. package/dist/internal/SheetConfirmation.d.ts +11 -0
  259. package/dist/internal/SheetConfirmation.d.ts.map +1 -0
  260. package/dist/internal/SheetConfirmation.js +92 -0
  261. package/dist/internal/indicatorPress.stylex.d.ts +20 -0
  262. package/dist/internal/indicatorPress.stylex.d.ts.map +1 -0
  263. package/dist/internal/indicatorPress.stylex.js +26 -0
  264. package/dist/internal/sheetDraft.d.ts +35 -0
  265. package/dist/internal/sheetDraft.d.ts.map +1 -0
  266. package/dist/internal/sheetDraft.js +65 -0
  267. package/dist/internal/touch-sheet.d.ts +21 -0
  268. package/dist/internal/touch-sheet.d.ts.map +1 -0
  269. package/dist/internal/touch-sheet.js +19 -0
  270. package/dist/internal/useDismissSearchOnTouchDrag.d.ts +24 -0
  271. package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
  272. package/dist/internal/useDismissSearchOnTouchDrag.js +56 -0
  273. package/dist/motion/animator.d.ts +14 -14
  274. package/dist/motion/animator.d.ts.map +1 -1
  275. package/dist/motion/animator.js +41 -18
  276. package/dist/motion/reducedMotion.d.ts +24 -0
  277. package/dist/motion/reducedMotion.d.ts.map +1 -1
  278. package/dist/motion/reducedMotion.js +78 -1
  279. package/dist/motion/roles.d.ts +7 -1
  280. package/dist/motion/roles.d.ts.map +1 -1
  281. package/dist/motion/roles.js +20 -1
  282. package/dist/motion/uikit/alert.d.ts +41 -0
  283. package/dist/motion/uikit/alert.d.ts.map +1 -0
  284. package/dist/motion/uikit/alert.js +43 -0
  285. package/dist/motion/uikit/index.d.ts +6 -5
  286. package/dist/motion/uikit/index.d.ts.map +1 -1
  287. package/dist/motion/uikit/index.js +6 -5
  288. package/dist/motion/uikit/segmented.d.ts +55 -0
  289. package/dist/motion/uikit/segmented.d.ts.map +1 -0
  290. package/dist/motion/uikit/segmented.js +77 -0
  291. package/dist/motion/uikit/springs.d.ts +0 -18
  292. package/dist/motion/uikit/springs.d.ts.map +1 -1
  293. package/dist/motion/uikit/springs.js +5 -21
  294. package/dist/privateAttributes/pressGroupItem.stylex.d.ts +15 -0
  295. package/dist/privateAttributes/pressGroupItem.stylex.d.ts.map +1 -0
  296. package/dist/privateAttributes/pressGroupItem.stylex.js +23 -0
  297. package/dist/privateAttributes/segmentTitle.stylex.d.ts +5 -0
  298. package/dist/privateAttributes/segmentTitle.stylex.d.ts.map +1 -0
  299. package/dist/privateAttributes/segmentTitle.stylex.js +22 -0
  300. package/dist/utils/browserClickWait.d.ts +35 -0
  301. package/dist/utils/browserClickWait.d.ts.map +1 -0
  302. package/dist/utils/browserClickWait.js +123 -0
  303. package/dist/utils/clickGuard.d.ts +14 -1
  304. package/dist/utils/clickGuard.d.ts.map +1 -1
  305. package/dist/utils/clickGuard.js +46 -1
  306. package/dist/utils/dateParser.d.ts.map +1 -1
  307. package/dist/utils/dateParser.js +12 -0
  308. package/dist/utils/focusReturn.d.ts +15 -0
  309. package/dist/utils/focusReturn.d.ts.map +1 -1
  310. package/dist/utils/focusReturn.js +28 -0
  311. package/dist/utils/interactionOverlay.stylex.d.ts +3 -2
  312. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  313. package/dist/utils/interactionOverlay.stylex.js +3 -2
  314. package/dist/utils/pressFeedback.d.ts.map +1 -1
  315. package/dist/utils/pressFeedback.js +19 -6
  316. package/dist/utils/triggerFocus.d.ts +36 -0
  317. package/dist/utils/triggerFocus.d.ts.map +1 -0
  318. package/dist/utils/triggerFocus.js +65 -0
  319. package/locales/en.json +128 -0
  320. package/locales/pseudo.json +96 -0
  321. package/package.json +18 -8
  322. package/src/AlertDialog/AlertDialog.doc.mjs +2 -2
  323. package/src/AlertDialog/AlertDialog.tsx +37 -5
  324. package/src/AppShell/useCloseMobileDrawerOnActivate.ts +52 -0
  325. package/src/BottomSheet/BottomSheet.tsx +95 -10
  326. package/src/BottomSheet/BottomSheetPanel.tsx +40 -5
  327. package/src/BottomSheet/bottomSheetStack.ts +8 -4
  328. package/src/BottomSheet/useSheetGestures.ts +3 -1
  329. package/src/Breadcrumbs/BreadcrumbItem.tsx +13 -3
  330. package/src/Button/Button.tsx +63 -5
  331. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  332. package/src/CheckboxInput/CheckboxInput.tsx +36 -9
  333. package/src/CheckboxList/CheckboxList.doc.mjs +4 -2
  334. package/src/CheckboxList/CheckboxList.tsx +6 -0
  335. package/src/CheckboxList/CheckboxListContext.tsx +6 -0
  336. package/src/CheckboxList/CheckboxListItem.tsx +11 -2
  337. package/src/ComplexSelector/ComplexSelector.tsx +20 -5
  338. package/src/ContextMenu/ContextMenu.tsx +2 -2
  339. package/src/DateInput/DateInput.tsx +8 -2
  340. package/src/DateInput/MonthScroller.tsx +18 -22
  341. package/src/DateInput/Wheel.tsx +30 -32
  342. package/src/DateTimeInput/DateTimeInput.tsx +8 -2
  343. package/src/Dialog/Dialog.tsx +54 -45
  344. package/src/Drawer/Drawer.tsx +28 -11
  345. package/src/Drawer/useDrawerDialogPresence.ts +5 -1
  346. package/src/Field/Field.tsx +2 -2
  347. package/src/Field/PanelSearchInput.tsx +11 -0
  348. package/src/HoverCard/HoverCard.doc.mjs +4 -2
  349. package/src/HoverCard/HoverCard.tsx +35 -4
  350. package/src/HoverCard/useHoverCard.doc.mjs +178 -36
  351. package/src/HoverCard/useHoverCard.tsx +46 -52
  352. package/src/Icon/Icon.doc.mjs +49 -8
  353. package/src/Icon/Icon.tsx +15 -7
  354. package/src/InfoTip/InfoTip.doc.mjs +192 -0
  355. package/src/InfoTip/InfoTip.tsx +186 -0
  356. package/src/InfoTip/index.ts +14 -0
  357. package/src/Layer/layerAnimations.stylex.ts +24 -16
  358. package/src/Layer/useBackdropPress.ts +81 -0
  359. package/src/Layer/usePressPin.ts +86 -26
  360. package/src/Lightbox/Lightbox.tsx +18 -0
  361. package/src/MobileNav/MobileNav.tsx +1 -1
  362. package/src/MultiSelector/hooks.ts +2 -4
  363. package/src/Outline/Outline.doc.mjs +177 -51
  364. package/src/Outline/Outline.tsx +5 -57
  365. package/src/Outline/useOutlineFromMarkdown.doc.mjs +157 -0
  366. package/src/Outline/useOutlineFromMarkdown.ts +1 -0
  367. package/src/Outline/useScrollSpy.ts +3 -3
  368. package/src/PowerSearch/PowerSearch.doc.mjs +55 -6
  369. package/src/PowerSearch/PowerSearch.tsx +798 -324
  370. package/src/PowerSearch/PowerSearchBottomSheet.tsx +2515 -0
  371. package/src/PowerSearch/PowerSearchEditPopover.tsx +28 -6
  372. package/src/PowerSearch/PowerSearchValueEditor.tsx +203 -38
  373. package/src/PowerSearch/formatFilterValue.ts +18 -1
  374. package/src/PowerSearch/formatTokenValue.ts +215 -0
  375. package/src/PowerSearch/powerSearchSheetUtils.ts +224 -0
  376. package/src/RadioList/RadioList.doc.mjs +4 -2
  377. package/src/RadioList/RadioList.tsx +7 -2
  378. package/src/RadioList/RadioListItem.tsx +5 -3
  379. package/src/Resizable/ResizeHandle.tsx +133 -328
  380. package/src/Resizable/resizeGesture.ts +368 -0
  381. package/src/SegmentedControl/SegmentedControl.doc.mjs +3 -3
  382. package/src/SegmentedControl/SegmentedControl.tsx +179 -5
  383. package/src/SegmentedControl/SegmentedControlContext.ts +6 -0
  384. package/src/SegmentedControl/SegmentedControlItem.tsx +12 -1
  385. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +1640 -0
  386. package/src/SegmentedControl/useSegmentedTouchTracking.ts +308 -0
  387. package/src/Selector/Selector.tsx +136 -89
  388. package/src/Selector/optionShape.ts +64 -0
  389. package/src/Selector/selectableOptionEntries.ts +39 -0
  390. package/src/Selector/utils.ts +9 -68
  391. package/src/SideNav/SideNavItem.tsx +3 -7
  392. package/src/Slider/sliderTouchController.ts +2 -2
  393. package/src/Spinner/Spinner.tsx +77 -63
  394. package/src/Switch/switchTouch.ts +2 -10
  395. package/src/Switch/useSwitchTouch.ts +38 -104
  396. package/src/Table/BaseTable.tsx +46 -12
  397. package/src/TextInput/TextInput.tsx +11 -10
  398. package/src/Toast/Toast.doc.mjs +4 -3
  399. package/src/Toast/Toast.tsx +57 -32
  400. package/src/Toast/ToastViewport.tsx +121 -35
  401. package/src/Toast/types.ts +9 -3
  402. package/src/Token/Token.tsx +15 -4
  403. package/src/Token/TokenNamingContext.ts +28 -0
  404. package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
  405. package/src/Tokenizer/Tokenizer.tsx +89 -85
  406. package/src/Tokenizer/TokenizerBottomSheet.tsx +219 -93
  407. package/src/Tokenizer/TokenizerItemRemovableContext.ts +24 -0
  408. package/src/Tooltip/Tooltip.doc.mjs +123 -54
  409. package/src/Tooltip/Tooltip.tsx +36 -39
  410. package/src/Tooltip/index.ts +0 -1
  411. package/src/Tooltip/useTooltip.doc.mjs +171 -33
  412. package/src/Tooltip/useTooltip.tsx +98 -86
  413. package/src/TopNav/TopNavItem.tsx +6 -5
  414. package/src/TopNav/TopNavMegaMenuItem.tsx +6 -5
  415. package/src/TopNav/TopNavMenu.tsx +6 -5
  416. package/src/Typeahead/TypeaheadItem.doc.mjs +6 -3
  417. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  418. package/src/animator/createAnimator.doc.mjs +153 -0
  419. package/src/animator/createAnimator.ts +324 -0
  420. package/src/gestures/GestureHost.ts +198 -20
  421. package/src/gestures/PressTracker.ts +4 -3
  422. package/src/gestures/README.md +7 -1
  423. package/src/hooks/actionGroupTracker.ts +32 -61
  424. package/src/hooks/index.ts +2 -0
  425. package/src/hooks/pressGroupContext.ts +21 -0
  426. package/src/hooks/useActionGroupTracking.ts +224 -0
  427. package/src/hooks/useAnimator.doc.mjs +109 -0
  428. package/src/hooks/useAnimator.ts +67 -0
  429. package/src/hooks/useAttachment.ts +1 -1
  430. package/src/hooks/useHighlightedOptionScroll.ts +123 -15
  431. package/src/hooks/useInputStatusIcon.tsx +3 -2
  432. package/src/hooks/useLongPress.doc.mjs +2 -2
  433. package/src/hooks/useLongPress.ts +136 -15
  434. package/src/hooks/useMediaQuery.doc.mjs +2 -2
  435. package/src/hooks/useMediaQuery.ts +13 -4
  436. package/src/hooks/useMenuPress.ts +8 -2
  437. package/src/i18n/generated-english/InfoTip/InfoTip.generated.ts +13 -0
  438. package/src/i18n/generated-english/PowerSearch/PowerSearch.generated.ts +1 -0
  439. package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +43 -0
  440. package/src/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.ts +6 -0
  441. package/src/i18n/generated-english/PowerSearch/formatTokenValue.generated.ts +15 -0
  442. package/src/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.ts +7 -1
  443. package/src/i18n/generated-locales/en.generated.ts +43 -0
  444. package/src/i18n/generated-locales/pseudo.generated.ts +44 -0
  445. package/src/index.ts +14 -0
  446. package/src/internal/SheetConfirmation.tsx +101 -0
  447. package/src/internal/indicatorPress.stylex.ts +24 -0
  448. package/src/internal/sheetDraft.ts +80 -0
  449. package/src/internal/touch-sheet.ts +21 -0
  450. package/src/internal/useDismissSearchOnTouchDrag.ts +69 -0
  451. package/src/motion/MEASUREMENTS.md +7 -0
  452. package/src/motion/README.md +9 -5
  453. package/src/motion/animator.ts +61 -29
  454. package/src/motion/reducedMotion.ts +79 -1
  455. package/src/motion/roles.ts +20 -1
  456. package/src/motion/uikit/alert.ts +43 -0
  457. package/src/motion/uikit/index.ts +5 -5
  458. package/src/motion/uikit/segmented.ts +74 -0
  459. package/src/motion/uikit/springs.ts +5 -21
  460. package/src/privateAttributes/pressGroupItem.stylex.ts +23 -0
  461. package/src/privateAttributes/segmentTitle.stylex.ts +22 -0
  462. package/src/utils/browserClickWait.ts +133 -0
  463. package/src/utils/clickGuard.ts +64 -1
  464. package/src/utils/dateParser.ts +16 -0
  465. package/src/utils/focusReturn.ts +30 -0
  466. package/src/utils/interactionOverlay.stylex.ts +3 -2
  467. package/src/utils/pressFeedback.ts +19 -6
  468. package/src/utils/triggerFocus.ts +80 -0
  469. package/dist/internal/tokenizer-presentation.d.ts +0 -21
  470. package/dist/internal/tokenizer-presentation.d.ts.map +0 -1
  471. package/dist/internal/tokenizer-presentation.js +0 -21
  472. package/src/internal/tokenizer-presentation.ts +0 -25
@@ -14,7 +14,14 @@
14
14
  * - /packages/cli/assets/templates/blocks/components/Table/ (showcase blocks)
15
15
  */
16
16
 
17
- import {memo, useRef, type ReactElement, type ReactNode, type Ref} from 'react';
17
+ import {
18
+ memo,
19
+ useId,
20
+ useRef,
21
+ type ReactElement,
22
+ type ReactNode,
23
+ type Ref,
24
+ } from 'react';
18
25
  import {isRenderable} from '../utils/isRenderable';
19
26
  import * as stylex from '@stylexjs/stylex';
20
27
  import {spacingVars} from '../theme/tokens.stylex';
@@ -423,6 +430,7 @@ function BaseTableInner<T extends Record<string, unknown>>({
423
430
  } satisfies TableRenderProps);
424
431
 
425
432
  // --- Plugin pipeline: header cells ---
433
+ const headerLabelIdBase = useId();
426
434
  const headerCells = resolvedColumns.map((col, columnIndex) => {
427
435
  const headerContent = col.header ?? col.key;
428
436
 
@@ -477,28 +485,54 @@ function BaseTableInner<T extends Record<string, unknown>>({
477
485
  const hasSlots =
478
486
  before != null || after != null || overlay != null || below != null;
479
487
 
480
- const headerInner = hasSlots ? (
488
+ const labelRow = isRenderable(after) ? (
489
+ <div {...stylex.props(styles.headerLabelRow)}>
490
+ {resolvedContent}
491
+ {after}
492
+ </div>
493
+ ) : (
494
+ resolvedContent
495
+ );
496
+
497
+ // The overlay is a control of its own. Keep its name and value out of the
498
+ // header's name.
499
+ const namesOwnHeader =
500
+ isRenderable(overlay) &&
501
+ mergedHtmlProps['aria-label'] == null &&
502
+ mergedHtmlProps['aria-labelledby'] == null;
503
+ const labelId = `${headerLabelIdBase}-label-${columnIndex}`;
504
+ const belowId = `${headerLabelIdBase}-below-${columnIndex}`;
505
+ const hasBelow = isRenderable(below);
506
+
507
+ const headerInner = !hasSlots ? (
508
+ resolvedContent
509
+ ) : namesOwnHeader ? (
510
+ <>
511
+ {/* Inline, so the cell's text-overflow still reaches the label. */}
512
+ <span id={labelId}>
513
+ {before}
514
+ {labelRow}
515
+ </span>
516
+ {overlay}
517
+ {hasBelow && <div id={belowId}>{below}</div>}
518
+ </>
519
+ ) : (
481
520
  <>
482
521
  {before}
483
- {isRenderable(after) ? (
484
- <div {...stylex.props(styles.headerLabelRow)}>
485
- {resolvedContent}
486
- {after}
487
- </div>
488
- ) : (
489
- resolvedContent
490
- )}
522
+ {labelRow}
491
523
  {overlay}
492
524
  {below}
493
525
  </>
494
- ) : (
495
- resolvedContent
496
526
  );
527
+ const headerNameProps = namesOwnHeader
528
+ ? {'aria-labelledby': hasBelow ? `${labelId} ${belowId}` : labelId}
529
+ : {};
497
530
 
498
531
  return (
499
532
  <HeaderCellComponent
500
533
  key={col.key}
501
534
  {...mergedHtmlProps}
535
+ {...headerNameProps}
502
536
  {...headerTitleProp}
503
537
  contextMenuActions={cellRenderProps.contextMenuActions}
504
538
  xstyle={cellRenderProps.xstyle}>
@@ -35,21 +35,22 @@ import {
35
35
  typographyVars,
36
36
  typeScaleVars,
37
37
  } from '../theme/tokens.stylex';
38
+ import {Field} from '../Field/Field';
39
+ import {InputClearButton} from '../Field/InputClearButton';
38
40
  import {
39
- Field,
40
- InputClearButton,
41
- type InputStatus,
42
41
  inputWrapperStyles,
43
42
  inputStatusBorderStyles,
44
43
  inputStatusHoverShadowStyles,
45
44
  inputStatusFocusWithinStyles,
46
- } from '../Field';
47
- import {renderIconSlot, type IconType} from '../Icon';
45
+ } from '../Field/inputStyles.stylex';
46
+ import type {InputStatus} from '../Field/types';
47
+ import {renderIconSlot, type IconType} from '../Icon/Icon';
48
48
  import {isRenderable} from '../utils/isRenderable';
49
- import {Spinner} from '../Spinner';
50
- import {useTooltip} from '../Tooltip';
51
- import {VisuallyHidden} from '../VisuallyHidden';
52
- import {getInputARIA, isImeKeyEvent} from '../utils';
49
+ import {Spinner} from '../Spinner/Spinner';
50
+ import {useTooltip} from '../Tooltip/useTooltip';
51
+ import {VisuallyHidden} from '../VisuallyHidden/VisuallyHidden';
52
+ import {getInputARIA} from '../utils/inputAria';
53
+ import {isImeKeyEvent} from '../utils/ime';
53
54
  import {
54
55
  textEntryTraitAttributes,
55
56
  type TextEntryTraitProps,
@@ -109,7 +110,7 @@ export type {
109
110
  InputStatus as TextInputStatus,
110
111
  InputStatusType as TextInputStatusType,
111
112
  } from '../Field';
112
- import {mergeProps} from '../utils';
113
+ import {mergeProps} from '../utils/mergeProps';
113
114
  import {useSize} from '../SizeContext/SizeContext';
114
115
  import {useInputContainer} from '../hooks/useInputContainer';
115
116
  import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
@@ -89,7 +89,7 @@ export const docs = {
89
89
  name: 'renderContent',
90
90
  type: '(toast: ToastContentRenderProps) => ReactNode',
91
91
  description:
92
- "Replaces the content of this toast's card with your own layout. Astryx keeps the card, its astryx-toast theme target, the live-region role and auto-hide behavior, then hands the renderer the message, endContent, resolved toast settings and a dismiss callback. The custom renderer owns every control in its layout: compose the control you want and call dismiss from it. Astryx does not inject a fallback close into custom content. Per-toast: an app shares one layout by wrapping useToast and passing it on every call, while a toast raised by library code that never passes it renders as an ordinary Astryx toast. The argument is {body, endContent, type, isAutoHide, autoHideDuration, dismiss}, where type is 'info' | 'error'.",
92
+ "Replaces the content of this toast's card with your own layout. Astryx keeps the card, its astryx-toast theme target and auto-hide behavior (inside a ToastViewport the viewport announces the toast, and while a modal hosts the viewport the card announces itself), then hands the renderer the message, endContent, resolved toast settings and a dismiss callback. The custom renderer owns every control in its layout: compose the control you want and call dismiss from it. Astryx does not inject a fallback close into custom content. Per-toast: an app shares one layout by wrapping useToast and passing it on every call, while a toast raised by library code that never passes it renders as an ordinary Astryx toast. The argument is {body, endContent, type, isAutoHide, autoHideDuration, dismiss}, where type is 'info' | 'error'.",
93
93
  },
94
94
  ],
95
95
  theming: {
@@ -211,7 +211,7 @@ export const docsZh = {
211
211
  collisionBehavior: '当已存在相同 uniqueID 的 toast 时的行为。',
212
212
  onHide: '当 toast 被移除时触发的回调。',
213
213
  renderContent:
214
- "用你自己的布局替换该 toast 卡片内部的内容。Astryx 保留卡片本身、astryx-toast 主题目标、实时区域角色和自动关闭行为,并把消息、endContent、解析后的 toast 设置和 dismiss 回调交给渲染函数。自定义渲染函数完全拥有布局中的控件:请组合所需的控件并通过它调用 dismiss。Astryx 不会向自定义内容注入后备关闭按钮。按 toast 单独设置:应用可通过封装 useToast 并在每次调用时传入来共享同一套布局;而由库代码发起、从不传入该参数的 toast 会渲染为普通的 Astryx toast。传入参数为 {body, endContent, type, isAutoHide, autoHideDuration, dismiss},其中 type 为 'info' | 'error'。",
214
+ "用你自己的布局替换该 toast 卡片内部的内容。Astryx 保留卡片本身、astryx-toast 主题目标和自动关闭行为(在 ToastViewport 中由视口播报该 toast;当模态框承载视口时由卡片自行播报),并把消息、endContent、解析后的 toast 设置和 dismiss 回调交给渲染函数。自定义渲染函数完全拥有布局中的控件:请组合所需的控件并通过它调用 dismiss。Astryx 不会向自定义内容注入后备关闭按钮。按 toast 单独设置:应用可通过封装 useToast 并在每次调用时传入来共享同一套布局;而由库代码发起、从不传入该参数的 toast 会渲染为普通的 Astryx toast。传入参数为 {body, endContent, type, isAutoHide, autoHideDuration, dismiss},其中 type 为 'info' | 'error'。",
215
215
  },
216
216
  usage: {
217
217
  description:
@@ -328,6 +328,7 @@ export const docsDense = {
328
328
  uniqueID: 'unique id for dedup',
329
329
  collisionBehavior: 'behavior when matching uniqueID exists',
330
330
  onHide: 'callback when toast removed',
331
- renderContent: 'custom inner layout; receives content, resolved settings + dismiss callback; no injected fallback control',
331
+ renderContent:
332
+ 'custom inner layout; receives content, resolved settings + dismiss callback; no injected fallback control',
332
333
  },
333
334
  };
@@ -154,6 +154,16 @@ export interface ToastProps {
154
154
 
155
155
  interface ToastSurfaceProps extends ToastProps {
156
156
  gestureDirection: ToastGestureDirection;
157
+ /**
158
+ * Changes when a uniqueID overwrite updates this card in place: the new
159
+ * toast's auto-hide timer starts over.
160
+ */
161
+ timerKey?: string;
162
+ /**
163
+ * False when the viewport announces the toast: the card then carries no
164
+ * live region of its own, so an update in place is announced once.
165
+ */
166
+ isLiveRegion?: boolean;
157
167
  }
158
168
 
159
169
  /**
@@ -193,6 +203,8 @@ export function ToastSurface({
193
203
  onDismiss,
194
204
  renderContent,
195
205
  gestureDirection,
206
+ timerKey,
207
+ isLiveRegion = true,
196
208
  }: ToastSurfaceProps) {
197
209
  const t = useComponentTranslator();
198
210
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -226,34 +238,28 @@ export function ToastSurface({
226
238
  }, remainingRef.current);
227
239
  }, [isAutoHide]);
228
240
 
229
- const pauseTimer = useCallback(
230
- (source: PauseSource) => {
231
- if (!isAutoHide) {
232
- return;
233
- }
234
- const sources = pauseSourcesRef.current;
235
- const wasRunning = sources.size === 0;
236
- sources.add(source);
237
- if (!wasRunning) {
238
- return;
239
- }
240
- if (timerRef.current) {
241
- clearTimeout(timerRef.current);
242
- timerRef.current = null;
243
- }
244
- if (startTimeRef.current != null) {
245
- const elapsed = Date.now() - startTimeRef.current;
246
- remainingRef.current = Math.max(remainingRef.current - elapsed, 1000);
247
- }
248
- },
249
- [isAutoHide],
250
- );
241
+ // Pointer, focus and gesture holds are tracked whatever isAutoHide is: an
242
+ // overwrite can turn a toast that does not auto-hide into one that does
243
+ // while a hold is in place. The window hold leaves with its listeners.
244
+ const pauseTimer = useCallback((source: PauseSource) => {
245
+ const sources = pauseSourcesRef.current;
246
+ const wasRunning = sources.size === 0;
247
+ sources.add(source);
248
+ if (!wasRunning) {
249
+ return;
250
+ }
251
+ if (timerRef.current) {
252
+ clearTimeout(timerRef.current);
253
+ timerRef.current = null;
254
+ }
255
+ if (startTimeRef.current != null) {
256
+ const elapsed = Date.now() - startTimeRef.current;
257
+ remainingRef.current = Math.max(remainingRef.current - elapsed, 1000);
258
+ }
259
+ }, []);
251
260
 
252
261
  const resumeTimer = useCallback(
253
262
  (source: PauseSource) => {
254
- if (!isAutoHide) {
255
- return;
256
- }
257
263
  const sources = pauseSourcesRef.current;
258
264
  if (!sources.delete(source)) {
259
265
  return;
@@ -271,20 +277,31 @@ export function ToastSurface({
271
277
  }
272
278
  startTimer();
273
279
  },
274
- [isAutoHide, startTimer],
280
+ [startTimer],
275
281
  );
276
282
 
277
283
  useEffect(() => {
278
284
  remainingRef.current = autoHideDuration;
285
+ // An update in place can remove the focused control, which fires no blur:
286
+ // a focus hold whose focus has left the card is stale.
287
+ const root = cardRootRef.current?.current;
288
+ if (
289
+ pauseSourcesRef.current.has('focus') &&
290
+ root != null &&
291
+ !root.contains(root.ownerDocument.activeElement)
292
+ ) {
293
+ pauseSourcesRef.current.delete('focus');
294
+ }
279
295
  startTimer();
280
296
  return () => {
281
297
  if (timerRef.current) {
282
298
  clearTimeout(timerRef.current);
283
299
  }
284
300
  };
285
- // startTimer's identity is stable per isAutoHide, so this runs on mount
286
- // and on a genuine duration change — not on unrelated viewport renders.
287
- }, [autoHideDuration, startTimer]);
301
+ // startTimer's identity is stable per isAutoHide, so this runs on mount,
302
+ // on a genuine duration change and on an overwrite (timerKey), not on
303
+ // unrelated viewport renders.
304
+ }, [autoHideDuration, startTimer, timerKey]);
288
305
 
289
306
  // Pause the auto-hide timer while the window is not focused, so a toast
290
307
  // doesn't silently expire while the user is in another window or tab.
@@ -292,13 +309,21 @@ export function ToastSurface({
292
309
  if (!isAutoHide) {
293
310
  return;
294
311
  }
312
+ const pauseSources = pauseSourcesRef.current;
295
313
  const pauseForWindow = () => pauseTimer('window');
296
314
  const resumeForWindow = () => resumeTimer('window');
297
315
  window.addEventListener('blur', pauseForWindow);
298
316
  window.addEventListener('focus', resumeForWindow);
317
+ // A toast raised while the window is already blurred gets no blur event,
318
+ // so it starts held, and the window's next focus resumes it.
319
+ if (!document.hasFocus()) {
320
+ pauseForWindow();
321
+ }
299
322
  return () => {
300
323
  window.removeEventListener('blur', pauseForWindow);
301
324
  window.removeEventListener('focus', resumeForWindow);
325
+ // With the listeners gone, no focus event would clear the window hold.
326
+ pauseSources.delete('window');
302
327
  };
303
328
  }, [isAutoHide, pauseTimer, resumeTimer]);
304
329
 
@@ -363,9 +388,9 @@ export function ToastSurface({
363
388
  return (
364
389
  <div
365
390
  ref={rootRef}
366
- role={isError ? 'alert' : 'status'}
367
- aria-live={isError ? 'assertive' : 'polite'}
368
- aria-atomic="true"
391
+ role={isLiveRegion ? (isError ? 'alert' : 'status') : undefined}
392
+ aria-live={isLiveRegion ? (isError ? 'assertive' : 'polite') : undefined}
393
+ aria-atomic={isLiveRegion ? 'true' : undefined}
369
394
  onMouseEnter={pauseForPointer}
370
395
  onMouseLeave={resumeForPointer}
371
396
  onFocusCapture={pauseForFocus}
@@ -21,6 +21,7 @@ import {
21
21
  useMemo,
22
22
  useRef,
23
23
  useState,
24
+ useSyncExternalStore,
24
25
  } from 'react';
25
26
  import type {ReactNode} from 'react';
26
27
  import {createPortal} from 'react-dom';
@@ -28,6 +29,11 @@ import * as stylex from '@stylexjs/stylex';
28
29
  import {spacingVars, durationVars, easeVars} from '../theme/tokens.stylex';
29
30
  import {mergeProps} from '../utils';
30
31
  import {getNodeText} from '../utils/getNodeText';
32
+ import {
33
+ moveFocusLeftBehind,
34
+ noteFocusWithin,
35
+ type FocusWithin,
36
+ } from '../utils/focusReturn';
31
37
  import {INTERACTIVE_SELECTORS} from '../hooks/useClickableContainer';
32
38
  import {useAnnounce} from '../hooks/useAnnounce';
33
39
  import {layerTextReset} from '../Layer/layerTextReset.stylex';
@@ -224,6 +230,8 @@ interface ToastRowProps {
224
230
  isReversed: boolean;
225
231
  onExited: (id: string) => void;
226
232
  onDismiss: (id: string, reason: ToastDismissReason) => void;
233
+ /** Whether the card carries its own live region (see ToastViewport). */
234
+ isLiveRegion: boolean;
227
235
  }
228
236
 
229
237
  /**
@@ -246,6 +254,7 @@ function ToastRow({
246
254
  isReversed,
247
255
  onExited,
248
256
  onDismiss,
257
+ isLiveRegion,
249
258
  }: ToastRowProps) {
250
259
  const o = entry.options;
251
260
  const type = o.type ?? 'info';
@@ -296,6 +305,8 @@ function ToastRow({
296
305
  gestureDirection={isReversed ? -1 : 1}
297
306
  onDismiss={reason => onDismiss(entry.id, reason)}
298
307
  renderContent={o.renderContent}
308
+ timerKey={entry.id}
309
+ isLiveRegion={isLiveRegion}
299
310
  />
300
311
  </div>
301
312
  </div>
@@ -303,6 +314,9 @@ function ToastRow({
303
314
  }
304
315
  ToastRow.displayName = 'ToastRow';
305
316
 
317
+ const hasActiveModalOutlet = (): boolean => getActiveModalOutlet() != null;
318
+ const hasNoModalOutlet = (): boolean => false;
319
+
306
320
  /**
307
321
  * Container that renders and manages toast notifications. Place at the root
308
322
  * of your app to enable useToast(). Toasts stack with enter/exit
@@ -363,6 +377,13 @@ export function ToastViewport({
363
377
  // The next toast id that should receive focus once the dismissed toast has
364
378
  // unmounted, or 'restore' to fall back to the previously-focused element.
365
379
  const pendingFocusRef = useRef<string | 'restore' | null>(null);
380
+ // The id of a toast whose overwrite updated a row that held focus. If the
381
+ // update removed the focused control, the commit hands focus off.
382
+ const overwriteFocusRef = useRef<{id: string; note: FocusWithin} | null>(
383
+ null,
384
+ );
385
+ // Where focus was inside a toast whose dismissal hands focus off.
386
+ const dismissFocusNoteRef = useRef<FocusWithin | null>(null);
366
387
 
367
388
  // Collect a focusable control within a toast node, if any.
368
389
  // Reuses the canonical INTERACTIVE_SELECTORS list (native controls plus
@@ -392,13 +413,9 @@ export function ToastViewport({
392
413
  [],
393
414
  );
394
415
 
395
- // Announce toasts through the persistent singleton live regions. Each
396
- // <Toast> also renders its own role="status"/"alert" region, but that region
397
- // is "born with content" — mounted together with its text — which many
398
- // screen readers do not announce (see useAnnounce.ts); the singleton regions
399
- // are mounted empty and only mutated afterwards, so they are what actually
400
- // guarantees the announcement (the per-toast markup is kept for browse-mode
401
- // discoverability). The announcement happens in addToast — the imperative
416
+ // Announce toasts through the persistent singleton live regions. They are
417
+ // mounted empty and only mutated afterwards, which screen readers announce
418
+ // (see useAnnounce.ts). The announcement happens in addToast — the imperative
402
419
  // dispatch path invoked once per useToast() call from an event handler,
403
420
  // never from render — so each toast is announced exactly once by
404
421
  // construction, independent of the React render lifecycle (StrictMode
@@ -406,6 +423,16 @@ export function ToastViewport({
406
423
  // re-renders never re-announce). It is client-only (addToast never runs
407
424
  // during SSR), so it is SSR-safe.
408
425
  const announce = useAnnounce();
426
+ // An open native modal hides the body-level channel above from assistive
427
+ // technology, so while the viewport is hosted in one, each card announces
428
+ // itself through its own live region instead. Outside a modal the cards
429
+ // carry none, so a card updated in place is announced once.
430
+ const hasModalOutlet = useSyncExternalStore(
431
+ subscribeModalOutlets,
432
+ hasActiveModalOutlet,
433
+ hasNoModalOutlet,
434
+ );
435
+ const isModalHosted = isTopLayer && hasModalOutlet;
409
436
 
410
437
  const addToast = useCallback(
411
438
  (entry: ToastEntry) => {
@@ -441,9 +468,29 @@ export function ToastViewport({
441
468
  if (overwritten && renderedToastsRef.current.includes(overwritten)) {
442
469
  replacedRef.current.set(overwritten.id, overwritten);
443
470
  }
471
+ // An overwrite of a toast that is not leaving keeps its row, so the card
472
+ // updates in place and focus inside it stays. A leaving namesake
473
+ // is replaced by a new row, which enters again.
474
+ const keepsRow =
475
+ overwritten != null && !exitingIdsRef.current.has(overwritten.id);
476
+ const placed: ToastEntry = keepsRow
477
+ ? {...entry, rowKey: overwritten.rowKey ?? overwritten.id}
478
+ : entry;
479
+ if (keepsRow) {
480
+ // If the update removes the focused control, the commit hands focus
481
+ // off as a dismissal does.
482
+ const note = noteFocusWithin(
483
+ viewportRef.current?.querySelector<HTMLElement>(
484
+ `[data-toast-id="${overwritten.id}"]`,
485
+ ),
486
+ );
487
+ if (note != null) {
488
+ overwriteFocusRef.current = {id: placed.id, note};
489
+ }
490
+ }
444
491
  const projected = overwritten
445
- ? committed.map(t => (t === overwritten ? entry : t))
446
- : [...committed, entry];
492
+ ? committed.map(t => (t === overwritten ? placed : t))
493
+ : [...committed, placed];
447
494
  // `maxVisible={0}` shows every toast (FR7): nothing leaves the window.
448
495
  const firstVisible = maxVisible > 0 ? projected.length - maxVisible : 0;
449
496
  const cutIds = new Set(
@@ -470,7 +517,7 @@ export function ToastViewport({
470
517
  }
471
518
  }
472
519
  }
473
- pending.set(entry.id, entry);
520
+ pending.set(entry.id, placed);
474
521
  // An overwrite swaps a new entry, with a new id, into the replaced
475
522
  // toast's place, so the old row leaves the DOM without ever being
476
523
  // dismissed. Its settled id is dropped by the prune below, which covers
@@ -482,8 +529,8 @@ export function ToastViewport({
482
529
  // overwrite into an append. An ignored collision already returned.
483
530
  const kept = prev.filter(t => !cutIds.has(t.id));
484
531
  return overwritten && kept.includes(overwritten)
485
- ? kept.map(t => (t === overwritten ? entry : t))
486
- : [...kept, entry];
532
+ ? kept.map(t => (t === overwritten ? placed : t))
533
+ : [...kept, placed];
487
534
  });
488
535
  },
489
536
  [announce, maxVisible],
@@ -512,14 +559,23 @@ export function ToastViewport({
512
559
  // focused before the user entered the viewport) rather than <body>.
513
560
  const el = viewportRef.current;
514
561
  const active = document.activeElement;
515
- const dismissedNode =
516
- el?.querySelector<HTMLElement>(`[data-toast-id="${id}"]`) ?? null;
562
+ // A toast an overwrite replaced in place no longer owns its row: the
563
+ // replacement does, so dismissing the replaced toast leaves the row, and
564
+ // focus in it, to the replacement.
565
+ const rowKey = entry.rowKey ?? entry.id;
566
+ const isReplacedInPlace =
567
+ !toastsRef.current.some(t => t.id === id) &&
568
+ toastsRef.current.some(t => t.rowKey === rowKey);
569
+ const dismissedNode = isReplacedInPlace
570
+ ? null
571
+ : (el?.querySelector<HTMLElement>(`[data-toast-id="${id}"]`) ?? null);
517
572
  if (
518
573
  dismissedNode &&
519
574
  active instanceof Node &&
520
575
  dismissedNode.contains(active)
521
576
  ) {
522
577
  focusHandoffIdRef.current = id;
578
+ dismissFocusNoteRef.current = noteFocusWithin(dismissedNode);
523
579
  // Pick the toast to receive focus while the DOM is still intact: the
524
580
  // nearest one after it, then before it, that shows once it is gone
525
581
  // (FR10), else restore. The list may already run ahead of the render
@@ -587,6 +643,41 @@ export function ToastViewport({
587
643
  setToasts(prev => prev.filter(t => t.id !== id));
588
644
  }, []);
589
645
 
646
+ // Moves focus to a control in the target toast, else in another toast that
647
+ // shows (newest first), else back to the element focused before focus
648
+ // entered the viewport, so a handoff never drops focus to <body>.
649
+ const handOffFocus = useCallback(
650
+ (note: FocusWithin | null, targetId: string | null) => {
651
+ const el = viewportRef.current;
652
+ const rowFor = (id: string) =>
653
+ el?.querySelector<HTMLElement>(`[data-toast-id="${id}"]`) ?? null;
654
+ const shownIds = toastsRef.current
655
+ .slice(maxVisibleRef.current > 0 ? -maxVisibleRef.current : 0)
656
+ .map(t => t.id)
657
+ .reverse();
658
+ const candidates =
659
+ targetId == null
660
+ ? []
661
+ : [targetId, ...shownIds.filter(id => id !== targetId)];
662
+ let target: HTMLElement | null = null;
663
+ for (const id of candidates) {
664
+ target = getFocusable(rowFor(id));
665
+ if (target != null) {
666
+ break;
667
+ }
668
+ }
669
+ const prev = prevFocusRef.current;
670
+ const isReturning = target == null && prev != null && prev.isConnected;
671
+ if (target == null) {
672
+ target = isReturning ? prev : el;
673
+ }
674
+ if (moveFocusLeftBehind(note, target) && isReturning) {
675
+ prevFocusRef.current = null;
676
+ }
677
+ },
678
+ [getFocusable],
679
+ );
680
+
590
681
  // After a dismissed toast unmounts, hand focus off so it never falls to
591
682
  // <body>. Runs in a layout effect once the toast list no longer contains
592
683
  // the dismissed toast.
@@ -602,27 +693,21 @@ export function ToastViewport({
602
693
  }
603
694
  focusHandoffIdRef.current = null;
604
695
  pendingFocusRef.current = null;
605
- const el = viewportRef.current;
606
- if (target !== 'restore' && el) {
607
- const nextNode = el.querySelector<HTMLElement>(
608
- `[data-toast-id="${target}"]`,
609
- );
610
- const focusable = getFocusable(nextNode) ?? nextNode;
611
- if (focusable) {
612
- focusable.focus();
613
- return;
614
- }
615
- }
616
- // No remaining toast to receive focus — restore the previously-focused
617
- // element if it's still connected, else fall back to the container.
618
- const prev = prevFocusRef.current;
619
- prevFocusRef.current = null;
620
- if (prev && prev.isConnected) {
621
- prev.focus();
622
- } else if (el) {
623
- el.focus();
696
+ const note = dismissFocusNoteRef.current;
697
+ dismissFocusNoteRef.current = null;
698
+ handOffFocus(note, target === 'restore' ? null : target);
699
+ }, [toasts, handOffFocus]);
700
+
701
+ // After an overwrite updated a row that held focus: focus stays on the same
702
+ // control, or, if the update removed it, moves as a dismissal hands it off.
703
+ useLayoutEffect(() => {
704
+ const overwrite = overwriteFocusRef.current;
705
+ if (overwrite == null) {
706
+ return;
624
707
  }
625
- }, [toasts, getFocusable]);
708
+ overwriteFocusRef.current = null;
709
+ handOffFocus(overwrite.note, overwrite.id);
710
+ }, [toasts, handOffFocus]);
626
711
 
627
712
  const findByUniqueID = useCallback((uid: string) => {
628
713
  return toastsRef.current.find(t => t.options.uniqueID === uid);
@@ -830,12 +915,13 @@ export function ToastViewport({
830
915
  <LayerContentBoundary>
831
916
  {visibleToasts.map(entry => (
832
917
  <ToastRow
833
- key={entry.id}
918
+ key={entry.rowKey ?? entry.id}
834
919
  entry={entry}
835
920
  isExiting={exitingIds.has(entry.id)}
836
921
  isReversed={isReversed}
837
922
  onExited={handleExited}
838
923
  onDismiss={removeToast}
924
+ isLiveRegion={isModalHosted}
839
925
  />
840
926
  ))}
841
927
  </LayerContentBoundary>
@@ -38,9 +38,10 @@ export interface ToastOptions {
38
38
  /**
39
39
  * Replaces the content of this toast's card with your own layout.
40
40
  *
41
- * Astryx keeps the card, live-region role and auto-hide behavior, then hands
42
- * the renderer this toast's content, resolved settings and `dismiss`
43
- * callback. The renderer owns every control inside its layout; call
41
+ * Astryx keeps the card and its auto-hide behavior (inside a
42
+ * `ToastViewport` the viewport announces the toast, and while a modal hosts
43
+ * the viewport the card announces itself), then hands the
44
+ * renderer this toast's content, resolved settings and `dismiss` callback. The renderer owns every control inside its layout; call
44
45
  * `dismiss` from the control that should close the toast. Astryx does not
45
46
  * inject a fallback control into custom content.
46
47
  *
@@ -100,4 +101,9 @@ export interface ToastEntry {
100
101
  id: string;
101
102
  options: ToastOptions;
102
103
  createdAt: number;
104
+ /**
105
+ * The row's identity in the stack. A uniqueID overwrite keeps the replaced
106
+ * toast's, so the card updates in place and focus inside it stays.
107
+ */
108
+ rowKey?: string;
103
109
  }