@astryxdesign/core 0.7.5 → 0.7.6-canary.5cd7a15

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 (463) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +5 -5
  4. package/dist/Avatar/Avatar.d.ts.map +1 -1
  5. package/dist/Avatar/Avatar.js +12 -3
  6. package/dist/Badge/Badge.d.ts.map +1 -1
  7. package/dist/Badge/Badge.js +7 -1
  8. package/dist/BottomSheet/BottomSheet.js +1 -1
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  10. package/dist/BottomSheet/BottomSheetPanel.js +2 -1
  11. package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
  12. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  13. package/dist/BottomSheet/useSheetGestures.js +30 -3
  14. package/dist/Button/Button.js +4 -4
  15. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  16. package/dist/CheckboxInput/CheckboxInput.js +6 -5
  17. package/dist/DateInput/DateInput.d.ts.map +1 -1
  18. package/dist/DateInput/DateInput.js +12 -4
  19. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  20. package/dist/DateInput/MonthScroller.js +101 -15
  21. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  22. package/dist/DateInput/TouchDateField.js +8 -3
  23. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  24. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  25. package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
  26. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  27. package/dist/DateTimeInput/DateTimeInput.js +23 -12
  28. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
  29. package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
  30. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  31. package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
  32. package/dist/Dialog/Dialog.d.ts.map +1 -1
  33. package/dist/Dialog/Dialog.js +34 -9
  34. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  35. package/dist/Dialog/DialogHeader.js +5 -4
  36. package/dist/Divider/Divider.d.ts.map +1 -1
  37. package/dist/Divider/Divider.js +2 -1
  38. package/dist/Drawer/Drawer.d.ts +1 -0
  39. package/dist/Drawer/Drawer.d.ts.map +1 -1
  40. package/dist/Drawer/Drawer.js +7 -2
  41. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  42. package/dist/DropdownMenu/DropdownMenu.js +4 -1
  43. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  44. package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
  45. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +10 -1
  46. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
  47. package/dist/DropdownMenu/lazyMenuBottomSheet.js +16 -2
  48. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  49. package/dist/DropdownMenu/renderDropdownItems.js +21 -16
  50. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  51. package/dist/EmptyState/EmptyState.js +2 -1
  52. package/dist/Field/FieldLabel.d.ts +1 -1
  53. package/dist/Field/FieldLabel.d.ts.map +1 -1
  54. package/dist/Field/FieldLabel.js +4 -4
  55. package/dist/Field/InputClearButton.js +3 -3
  56. package/dist/Field/PanelSearchInput.js +2 -2
  57. package/dist/FieldStatus/FieldStatus.js +2 -2
  58. package/dist/FileInput/FileInput.d.ts.map +1 -1
  59. package/dist/FileInput/FileInput.js +7 -1
  60. package/dist/Heading/Heading.js +1 -1
  61. package/dist/Icon/Icon.js +1 -1
  62. package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
  63. package/dist/Indicator/CheckIndicator.js +2 -1
  64. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  65. package/dist/Indicator/CheckboxIndicator.js +3 -1
  66. package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
  67. package/dist/Indicator/RadioIndicator.js +3 -1
  68. package/dist/Item/Item.d.ts.map +1 -1
  69. package/dist/Item/Item.js +2 -1
  70. package/dist/Item/ItemSwipeLayer.js +1 -1
  71. package/dist/Layout/Layout.js +1 -1
  72. package/dist/Layout/LayoutContent.js +1 -1
  73. package/dist/Layout/LayoutFooter.js +1 -1
  74. package/dist/Layout/LayoutHeader.js +1 -1
  75. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  76. package/dist/Lightbox/Lightbox.js +5 -0
  77. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
  78. package/dist/List/List.js +1 -1
  79. package/dist/List/ListItem.js +3 -3
  80. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  81. package/dist/MobileNav/MobileNav.js +7 -2
  82. package/dist/MultiSelector/MultiSelector.d.ts +1 -1
  83. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  84. package/dist/MultiSelector/MultiSelector.js +5 -2
  85. package/dist/NumberInput/NumberInput.d.ts +3 -1
  86. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  87. package/dist/NumberInput/NumberInput.js +3 -3
  88. package/dist/NumberInput/numberParser.d.ts +3 -0
  89. package/dist/NumberInput/numberParser.d.ts.map +1 -1
  90. package/dist/NumberInput/numberParser.js +7 -44
  91. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  92. package/dist/ProgressBar/ProgressBar.js +16 -12
  93. package/dist/Resizable/useResizable.d.ts.map +1 -1
  94. package/dist/Resizable/useResizable.js +28 -2
  95. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  96. package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
  97. package/dist/Selector/useSelectorPresentation.d.ts +8 -1
  98. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  99. package/dist/Selector/useSelectorPresentation.js +20 -4
  100. package/dist/Slider/Slider.d.ts.map +1 -1
  101. package/dist/Slider/Slider.js +7 -1
  102. package/dist/Spinner/Spinner.js +2 -2
  103. package/dist/Stack/Stack.js +1 -1
  104. package/dist/Stack/StackItem.js +1 -1
  105. package/dist/TabList/TabList.d.ts.map +1 -1
  106. package/dist/TabList/TabList.js +16 -1
  107. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  108. package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
  109. package/dist/Text/Text.js +1 -1
  110. package/dist/Text/useTruncation.d.ts.map +1 -1
  111. package/dist/Text/useTruncation.js +23 -5
  112. package/dist/TextArea/TextArea.d.ts +1 -1
  113. package/dist/TextArea/TextArea.d.ts.map +1 -1
  114. package/dist/TextArea/TextArea.js +13 -2
  115. package/dist/TextInput/TextInput.d.ts.map +1 -1
  116. package/dist/TextInput/TextInput.js +3 -3
  117. package/dist/TimeInput/TimeInput.d.ts +6 -2
  118. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  119. package/dist/TimeInput/TimeInput.js +21 -14
  120. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
  121. package/dist/TimeInput/TouchTimeField.js +5 -4
  122. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  123. package/dist/Timestamp/Timestamp.js +2 -2
  124. package/dist/Timestamp/TimestampHoverCard.js +4 -4
  125. package/dist/Toast/Toast.d.ts.map +1 -1
  126. package/dist/Toast/Toast.js +60 -20
  127. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  128. package/dist/Toast/ToastViewport.js +82 -51
  129. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  130. package/dist/Tokenizer/Tokenizer.js +13 -2
  131. package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
  132. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  133. package/dist/Toolbar/Toolbar.js +2 -1
  134. package/dist/Typeahead/Typeahead.d.ts +1 -1
  135. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  136. package/dist/Typeahead/Typeahead.js +9 -1
  137. package/dist/astryx.css +15 -1
  138. package/dist/gestures/GestureArena.d.ts +6 -0
  139. package/dist/gestures/GestureArena.d.ts.map +1 -1
  140. package/dist/gestures/GestureArena.js +39 -0
  141. package/dist/gestures/GestureHost.d.ts +352 -0
  142. package/dist/gestures/GestureHost.d.ts.map +1 -0
  143. package/dist/gestures/GestureHost.js +1773 -0
  144. package/dist/gestures/GestureRecognizer.d.ts +10 -0
  145. package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
  146. package/dist/gestures/GestureRecognizer.js +16 -0
  147. package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
  148. package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
  149. package/dist/gestures/LongPressGestureRecognizer.js +3 -0
  150. package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
  151. package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
  152. package/dist/gestures/PanGestureRecognizer.js +3 -0
  153. package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
  154. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
  155. package/dist/gestures/PinchGestureRecognizer.js +128 -0
  156. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
  157. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
  158. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
  159. package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
  160. package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
  161. package/dist/gestures/SwipeGestureRecognizer.js +148 -0
  162. package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
  163. package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
  164. package/dist/gestures/TapGestureRecognizer.js +3 -0
  165. package/dist/gestures/VelocityTracker.d.ts +6 -2
  166. package/dist/gestures/VelocityTracker.d.ts.map +1 -1
  167. package/dist/gestures/VelocityTracker.js +7 -3
  168. package/dist/gestures/haptics.d.ts +65 -0
  169. package/dist/gestures/haptics.d.ts.map +1 -0
  170. package/dist/gestures/haptics.js +182 -0
  171. package/dist/gestures/nativeScroll.d.ts +42 -0
  172. package/dist/gestures/nativeScroll.d.ts.map +1 -0
  173. package/dist/gestures/nativeScroll.js +228 -0
  174. package/dist/gestures/touchAction.d.ts +60 -0
  175. package/dist/gestures/touchAction.d.ts.map +1 -0
  176. package/dist/gestures/touchAction.js +157 -0
  177. package/dist/gestures/touchActionRule.d.ts +34 -0
  178. package/dist/gestures/touchActionRule.d.ts.map +1 -0
  179. package/dist/gestures/touchActionRule.js +140 -0
  180. package/dist/gestures/types.d.ts +18 -7
  181. package/dist/gestures/types.d.ts.map +1 -1
  182. package/dist/gestures/types.js +22 -2
  183. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
  184. package/dist/hooks/useAdaptivePresentation.js +4 -1
  185. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  186. package/dist/hooks/useMenuHover.js +12 -1
  187. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  188. package/dist/i18n/generated-locales/en.generated.js +3 -0
  189. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  190. package/dist/i18n/generated-locales/pseudo.generated.js +3 -0
  191. package/dist/motion/animator.d.ts +7 -1
  192. package/dist/motion/animator.d.ts.map +1 -1
  193. package/dist/motion/animator.js +119 -27
  194. package/dist/theme/defineTheme.js +1 -1
  195. package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
  196. package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
  197. package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
  198. package/dist/theme/domainTokens/index.d.ts +5 -4
  199. package/dist/theme/domainTokens/index.d.ts.map +1 -1
  200. package/dist/theme/domainTokens/index.js +5 -9
  201. package/dist/utils/focusReturn.d.ts +17 -1
  202. package/dist/utils/focusReturn.d.ts.map +1 -1
  203. package/dist/utils/focusReturn.js +11 -4
  204. package/dist/utils/getNodeText.d.ts +8 -0
  205. package/dist/utils/getNodeText.d.ts.map +1 -0
  206. package/dist/utils/getNodeText.js +37 -0
  207. package/dist/utils/interactionModality.d.ts +11 -2
  208. package/dist/utils/interactionModality.d.ts.map +1 -1
  209. package/dist/utils/interactionModality.js +58 -5
  210. package/dist/utils/plainDate.d.ts +2 -0
  211. package/dist/utils/plainDate.d.ts.map +1 -1
  212. package/dist/utils/plainDate.js +46 -3
  213. package/dist/utils/presentation.d.ts +50 -0
  214. package/dist/utils/presentation.d.ts.map +1 -0
  215. package/dist/utils/presentation.js +125 -0
  216. package/dist/utils/primaryClick.d.ts +11 -0
  217. package/dist/utils/primaryClick.d.ts.map +1 -0
  218. package/dist/utils/primaryClick.js +25 -0
  219. package/dist/utils/timeFormat.d.ts +47 -0
  220. package/dist/utils/timeFormat.d.ts.map +1 -0
  221. package/dist/utils/timeFormat.js +240 -0
  222. package/dist/utils/touchPresentation.d.ts.map +1 -1
  223. package/dist/utils/touchPresentation.js +5 -3
  224. package/dist/utils/touchPresentation.stylex.d.ts +15 -0
  225. package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
  226. package/dist/utils/touchPresentation.stylex.js +33 -0
  227. package/dist/utils/unicodeDigits.d.ts +20 -0
  228. package/dist/utils/unicodeDigits.d.ts.map +1 -0
  229. package/dist/utils/unicodeDigits.js +70 -0
  230. package/locales/en.json +15 -3
  231. package/locales/pseudo.json +9 -0
  232. package/package.json +5 -4
  233. package/src/AlertDialog/AlertDialog.tsx +6 -5
  234. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
  235. package/src/Avatar/Avatar.spec.md +22 -22
  236. package/src/Avatar/Avatar.tsx +18 -1
  237. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
  238. package/src/Badge/Badge.doc.mjs +1 -1
  239. package/src/Badge/Badge.tsx +19 -2
  240. package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
  241. package/src/BottomSheet/BottomSheet.test.tsx +46 -0
  242. package/src/BottomSheet/BottomSheet.tsx +1 -1
  243. package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
  244. package/src/BottomSheet/useSheetGestures.test.ts +52 -0
  245. package/src/BottomSheet/useSheetGestures.ts +37 -6
  246. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
  247. package/src/Button/Button.tsx +4 -4
  248. package/src/Calendar/Calendar.spec.md +87 -48
  249. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
  250. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
  251. package/src/CheckboxInput/CheckboxInput.tsx +6 -5
  252. package/src/Collapsible/Collapsible.spec.md +2 -0
  253. package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
  254. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
  255. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
  256. package/src/ComplexSelector/ComplexSelector.test.tsx +37 -0
  257. package/src/ContextMenu/ContextMenu.test.tsx +35 -0
  258. package/src/DateInput/DateInput.test.tsx +115 -3
  259. package/src/DateInput/DateInput.tsx +29 -14
  260. package/src/DateInput/DateInputTouch.test.tsx +76 -0
  261. package/src/DateInput/MonthScroller.tsx +106 -20
  262. package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
  263. package/src/DateInput/TouchDateField.tsx +14 -3
  264. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
  265. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
  266. package/src/DateRangeInput/DateRangeInput.tsx +16 -12
  267. package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
  268. package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
  269. package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
  270. package/src/DateTimeInput/DateTimeInput.tsx +46 -24
  271. package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
  272. package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
  273. package/src/Dialog/Dialog.test.tsx +216 -2
  274. package/src/Dialog/Dialog.tsx +42 -9
  275. package/src/Dialog/DialogHeader.tsx +5 -4
  276. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
  277. package/src/Divider/Divider.tsx +2 -1
  278. package/src/Drawer/Drawer.spec.md +111 -80
  279. package/src/Drawer/Drawer.test.tsx +35 -0
  280. package/src/Drawer/Drawer.tsx +13 -2
  281. package/src/Drawer/DrawerHeader.spec.md +181 -0
  282. package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
  283. package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
  284. package/src/DropdownMenu/DropdownMenu.tsx +7 -1
  285. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
  286. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
  287. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
  288. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
  289. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +53 -0
  290. package/src/DropdownMenu/lazyMenuBottomSheet.ts +23 -6
  291. package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
  292. package/src/EmptyState/EmptyState.tsx +2 -1
  293. package/src/Field/FieldLabel.tsx +5 -4
  294. package/src/Field/InputClearButton.tsx +3 -3
  295. package/src/Field/PanelSearchInput.tsx +2 -2
  296. package/src/FieldStatus/FieldStatus.tsx +2 -2
  297. package/src/FileInput/FileInput.test.tsx +21 -0
  298. package/src/FileInput/FileInput.tsx +4 -1
  299. package/src/FormLayout/FormLayout.spec.md +7 -8
  300. package/src/Heading/Heading.tsx +1 -1
  301. package/src/Icon/Icon.tsx +1 -1
  302. package/src/Indicator/CheckIndicator.spec.md +47 -42
  303. package/src/Indicator/CheckIndicator.tsx +2 -1
  304. package/src/Indicator/CheckboxIndicator.spec.md +40 -37
  305. package/src/Indicator/CheckboxIndicator.tsx +3 -1
  306. package/src/Indicator/Indicator.spec.md +188 -0
  307. package/src/Indicator/RadioIndicator.tsx +3 -1
  308. package/src/Item/Item.tsx +2 -1
  309. package/src/Item/ItemSwipeLayer.tsx +1 -1
  310. package/src/Layout/Layout.spec.md +127 -131
  311. package/src/Layout/Layout.tsx +1 -1
  312. package/src/Layout/LayoutContent.spec.md +205 -0
  313. package/src/Layout/LayoutContent.tsx +1 -1
  314. package/src/Layout/LayoutFooter.spec.md +190 -0
  315. package/src/Layout/LayoutFooter.tsx +1 -1
  316. package/src/Layout/LayoutHeader.spec.md +193 -0
  317. package/src/Layout/LayoutHeader.tsx +1 -1
  318. package/src/Layout/LayoutPanel.spec.md +194 -0
  319. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
  320. package/src/Lightbox/Lightbox.test.tsx +18 -0
  321. package/src/Lightbox/Lightbox.tsx +5 -0
  322. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
  323. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
  324. package/src/List/List.tsx +1 -1
  325. package/src/List/ListItem.spec.md +205 -0
  326. package/src/List/ListItem.tsx +3 -3
  327. package/src/MobileNav/MobileNav.test.tsx +15 -0
  328. package/src/MobileNav/MobileNav.tsx +9 -1
  329. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
  330. package/src/MoreMenu/MoreMenu.test.tsx +11 -0
  331. package/src/MultiSelector/MultiSelector.spec.md +7 -4
  332. package/src/MultiSelector/MultiSelector.test.tsx +118 -1
  333. package/src/MultiSelector/MultiSelector.tsx +6 -2
  334. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
  335. package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
  336. package/src/NumberInput/NumberInput.doc.mjs +13 -7
  337. package/src/NumberInput/NumberInput.spec.md +59 -37
  338. package/src/NumberInput/NumberInput.tsx +13 -3
  339. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
  340. package/src/NumberInput/numberParser.ts +7 -45
  341. package/src/ProgressBar/ProgressBar.test.tsx +108 -32
  342. package/src/ProgressBar/ProgressBar.tsx +58 -10
  343. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
  344. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
  345. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
  346. package/src/Resizable/useResizable.test.ts +43 -0
  347. package/src/Resizable/useResizable.ts +32 -9
  348. package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
  349. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
  350. package/src/Selector/Selector.test.tsx +90 -1
  351. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
  352. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
  353. package/src/Selector/useSelectorPresentation.ts +31 -3
  354. package/src/SideNav/SideNav.test.tsx +27 -0
  355. package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
  356. package/src/Slider/Slider.test.tsx +12 -0
  357. package/src/Slider/Slider.tsx +4 -1
  358. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
  359. package/src/Spinner/Spinner.tsx +2 -2
  360. package/src/Stack/Stack.tsx +1 -1
  361. package/src/Stack/StackItem.tsx +1 -1
  362. package/src/TabList/TabList.test.tsx +58 -4
  363. package/src/TabList/TabList.tsx +16 -1
  364. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
  365. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
  366. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
  367. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
  368. package/src/Text/Text.tsx +1 -1
  369. package/src/Text/useTruncation.test.tsx +128 -1
  370. package/src/Text/useTruncation.ts +23 -5
  371. package/src/TextArea/TextArea.spec.md +10 -13
  372. package/src/TextArea/TextArea.test.tsx +60 -0
  373. package/src/TextArea/TextArea.tsx +15 -1
  374. package/src/TextInput/TextInput.test.tsx +21 -0
  375. package/src/TextInput/TextInput.tsx +3 -1
  376. package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
  377. package/src/TimeInput/Presentation.test.tsx +21 -0
  378. package/src/TimeInput/TimeInput.doc.mjs +8 -8
  379. package/src/TimeInput/TimeInput.spec.md +45 -48
  380. package/src/TimeInput/TimeInput.test.tsx +76 -0
  381. package/src/TimeInput/TimeInput.tsx +36 -18
  382. package/src/TimeInput/TouchTimeField.tsx +4 -6
  383. package/src/Timestamp/Timestamp.spec.md +6 -12
  384. package/src/Timestamp/Timestamp.test.tsx +30 -5
  385. package/src/Timestamp/Timestamp.tsx +6 -2
  386. package/src/Timestamp/TimestampHoverCard.tsx +4 -4
  387. package/src/Toast/Toast.spec.md +14 -20
  388. package/src/Toast/Toast.tsx +104 -36
  389. package/src/Toast/ToastViewport.test.tsx +719 -1
  390. package/src/Toast/ToastViewport.tsx +100 -50
  391. package/src/Tokenizer/Tokenizer.spec.md +5 -3
  392. package/src/Tokenizer/Tokenizer.test.tsx +68 -0
  393. package/src/Tokenizer/Tokenizer.tsx +14 -2
  394. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
  395. package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
  396. package/src/Toolbar/Toolbar.spec.md +5 -6
  397. package/src/Toolbar/Toolbar.test.tsx +30 -0
  398. package/src/Toolbar/Toolbar.tsx +2 -1
  399. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
  400. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
  401. package/src/TopNav/TopNav.test.tsx +27 -0
  402. package/src/Typeahead/Typeahead.test.tsx +40 -0
  403. package/src/Typeahead/Typeahead.tsx +12 -1
  404. package/src/gestures/GestureArena.ts +35 -0
  405. package/src/gestures/GestureHost.ts +2065 -0
  406. package/src/gestures/GestureRecognizer.ts +18 -0
  407. package/src/gestures/LongPressGestureRecognizer.ts +4 -0
  408. package/src/gestures/PanGestureRecognizer.ts +4 -0
  409. package/src/gestures/PinchGestureRecognizer.ts +148 -0
  410. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
  411. package/src/gestures/SwipeGestureRecognizer.ts +197 -0
  412. package/src/gestures/TapGestureRecognizer.ts +4 -0
  413. package/src/gestures/VelocityTracker.ts +10 -3
  414. package/src/gestures/__tests__/GestureHost.test.ts +2087 -0
  415. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
  416. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
  417. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
  418. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
  419. package/src/gestures/__tests__/arena.test.ts +5 -0
  420. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
  421. package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
  422. package/src/gestures/__tests__/browser/fixture.ts +307 -0
  423. package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
  424. package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
  425. package/src/gestures/__tests__/haptics.test.ts +187 -0
  426. package/src/gestures/__tests__/harness.ts +21 -2
  427. package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
  428. package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
  429. package/src/gestures/haptics.ts +203 -0
  430. package/src/gestures/nativeScroll.ts +277 -0
  431. package/src/gestures/touchAction.ts +191 -0
  432. package/src/gestures/touchActionRule.ts +157 -0
  433. package/src/gestures/types.ts +30 -9
  434. package/src/hooks/useAdaptivePresentation.ts +4 -1
  435. package/src/hooks/useMenuHover.ts +15 -1
  436. package/src/i18n/generated-locales/en.generated.ts +4 -0
  437. package/src/i18n/generated-locales/pseudo.generated.ts +4 -0
  438. package/src/motion/README.md +11 -1
  439. package/src/motion/animator.test.ts +151 -3
  440. package/src/motion/animator.ts +149 -33
  441. package/src/theme/defineTheme.ts +1 -1
  442. package/src/theme/derivedVarRegistry.test.ts +4 -0
  443. package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
  444. package/src/theme/domainTokens/index.ts +6 -10
  445. package/src/utils/focusReturn.ts +17 -6
  446. package/src/utils/getNodeText.test.ts +24 -0
  447. package/src/utils/getNodeText.ts +37 -0
  448. package/src/utils/interactionModality.test.ts +45 -1
  449. package/src/utils/interactionModality.ts +64 -5
  450. package/src/utils/plainDate.test.ts +90 -0
  451. package/src/utils/plainDate.ts +58 -3
  452. package/src/utils/presentation.test.ts +380 -0
  453. package/src/utils/presentation.ts +180 -0
  454. package/src/utils/pressPaintPriority.test.ts +80 -0
  455. package/src/utils/primaryClick.test.ts +38 -0
  456. package/src/utils/primaryClick.ts +28 -0
  457. package/src/utils/timeFormat.test.ts +204 -0
  458. package/src/utils/timeFormat.ts +314 -0
  459. package/src/utils/touchPresentation.stylex.ts +43 -0
  460. package/src/utils/touchPresentation.test.ts +140 -1
  461. package/src/utils/touchPresentation.ts +5 -3
  462. package/src/utils/unicodeDigits.test.ts +44 -0
  463. package/src/utils/unicodeDigits.ts +71 -0
@@ -768,6 +768,20 @@ describe('Timestamp', () => {
768
768
  HTMLElement.prototype.hidePopover = originalHidePopover;
769
769
  });
770
770
 
771
+ it("keeps a caller's tabIndex while the hover card is attached (FR9)", async () => {
772
+ render(
773
+ <Timestamp
774
+ value={Date.now() / 1000 - 3600}
775
+ format="relative"
776
+ tabIndex={-1}
777
+ data-testid="ts"
778
+ />,
779
+ );
780
+ expect(screen.getByTestId('ts')).toHaveAttribute('tabindex', '-1');
781
+ await openTimestampHoverCard();
782
+ expect(screen.getByTestId('ts')).toHaveAttribute('tabindex', '-1');
783
+ });
784
+
771
785
  it('makes the <time> element focusable while the hover card is attached', () => {
772
786
  render(
773
787
  <Timestamp
@@ -1449,7 +1463,7 @@ describe('Timestamp pass-through props', () => {
1449
1463
  expect(time).toHaveAttribute('data-source', 'cms');
1450
1464
  });
1451
1465
 
1452
- it('keeps its own spelled-out label on a relative timestamp', () => {
1466
+ it("keeps a caller's aria-label on a relative timestamp (FR5)", () => {
1453
1467
  render(
1454
1468
  <Timestamp
1455
1469
  value={Date.now()}
@@ -1463,10 +1477,21 @@ describe('Timestamp pass-through props', () => {
1463
1477
  expect({
1464
1478
  ariaLabel: stamp.getAttribute('aria-label'),
1465
1479
  dataSource: stamp.getAttribute('data-source'),
1466
- }).toEqual({
1467
- ariaLabel: expect.not.stringMatching(/^Caller label$/),
1468
- dataSource: 'cms',
1469
- });
1480
+ }).toEqual({ariaLabel: 'Caller label', dataSource: 'cms'});
1481
+ });
1482
+
1483
+ it('names a relative timestamp by the full date when the caller names nothing (FR5)', () => {
1484
+ render(
1485
+ <Timestamp
1486
+ value={Date.now()}
1487
+ format="relative"
1488
+ aria-label={undefined}
1489
+ data-testid="stamp"
1490
+ />,
1491
+ );
1492
+ expect(screen.getByTestId('stamp').getAttribute('aria-label')).toMatch(
1493
+ /\d{4}/,
1494
+ );
1470
1495
  });
1471
1496
  });
1472
1497
 
@@ -443,7 +443,10 @@ export function Timestamp({
443
443
  // before rendering — but keep the guard local: an empty aria-label
444
444
  // must be omitted entirely (not rendered as aria-label="") so AT
445
445
  // falls back to reading the visible <time> content.
446
- {...(isRelativeFormat(effectiveFormat) && ariaLabelText !== ''
446
+ // A caller's own name wins (`component:Timestamp` FR5).
447
+ {...(isRelativeFormat(effectiveFormat) &&
448
+ ariaLabelText !== '' &&
449
+ rest['aria-label'] == null
447
450
  ? {'aria-label': ariaLabelText}
448
451
  : {})}
449
452
  // The hover card is anchored here with focusTrigger="always", which
@@ -454,7 +457,8 @@ export function Timestamp({
454
457
  // gratuitous tab stops otherwise. The card carries its own
455
458
  // dashed-underline hover indication as the affordance, so the anchor
456
459
  // needs no separate focus outline.
457
- {...(showTooltip ? {tabIndex: 0} : {})}>
460
+ // A caller's own tabIndex wins (`component:Timestamp` FR9).
461
+ {...(showTooltip && rest.tabIndex == null ? {tabIndex: 0} : {})}>
458
462
  {displayText}
459
463
  </time>
460
464
  </Text>
@@ -26,11 +26,11 @@
26
26
  import type {ReactNode} from 'react';
27
27
  import {useCallback} from 'react';
28
28
  import * as stylex from '@stylexjs/stylex';
29
- import {HoverCard} from '../HoverCard';
30
- import {IconButton} from '../IconButton';
31
- import {Icon} from '../Icon';
29
+ import {HoverCard} from '../HoverCard/HoverCard';
30
+ import {IconButton} from '../IconButton/IconButton';
31
+ import {Icon} from '../Icon/Icon';
32
32
  import {useClipboard} from '../hooks/useClipboard';
33
- import {useTranslator} from '../i18n';
33
+ import {useTranslator} from '../i18n/useTranslator';
34
34
  import {themeProps} from '../utils/themeProps';
35
35
  import {
36
36
  colorVars,
@@ -50,7 +50,7 @@ system_specs:
50
50
  | Behavior | A stack of non-modal notification cards at one viewport corner. Each toast is announced once, can auto-hide (pausing while hovered, focused, touched, or the window is blurred), and dismisses by its button, a vertical swipe toward its edge, or code. F6 moves focus into the newest toast. |
51
51
  | End-user impact | Users get brief notices without losing their place; screen-reader users hear each toast once; keyboard users reach toast controls with F6 and land somewhere sensible after dismissing; touch users swipe toasts away. |
52
52
  | Builder impact | None. |
53
- | Compatibility/readiness | Records shipped behavior; no public change. Twenty-one implementation gaps, listed under Current-state impact. |
53
+ | Compatibility/readiness | Records shipped behavior; no public change. Twenty implementation gaps, listed under Current-state impact. |
54
54
  | Review checks | Reject a toast announced more than once per show, a dismissal that drops focus to the body, an auto-hide that runs while the user is reading or interacting, or a swipe as the only way to dismiss. |
55
55
  | Governing rules | `spec:AST-086` FR1; `spec:AST-003` (modal rehosting); `spec:AST-094`; `spec:AST-100`; `spec:AST-059` FR6; `architecture:layer-runtime`; `design:bidirectionality`; `design:reduced-motion`. |
56
56
 
@@ -93,12 +93,9 @@ Consumer migration instructions belong in consumer docs and release notes.
93
93
  once at release on projected travel and cancel a release moving back, and
94
94
  `spec:AST-100` FR1, FR2, and FR4 take the release velocity from the shared
95
95
  tracker.
96
- - **The auto-hide pause is one flag.** Any pause condition ending, the pointer
97
- leaving, focus leaving, or the window regaining focus, resumes the timer
98
- while another still holds, so a toast can auto-hide with focus on its action
99
- or with the pointer over it. A toast raised while the window is already
100
- blurred starts its timer and can auto-hide unseen, since only a later blur
101
- pauses it.
96
+ - **A toast raised while the window is blurred runs its timer.** Only a later
97
+ blur pauses it, so a toast raised while the user is in another window can
98
+ auto-hide unseen.
102
99
  - **A control-free toast cannot take focus.** With no control in the newest
103
100
  toast, F6 leaves focus where it was; dismissing a focused toast beside a
104
101
  control-free one drops focus to the page body.
@@ -121,9 +118,9 @@ Consumer migration instructions belong in consumer docs and release notes.
121
118
  - **Some modals supply no outlet.** A modal Drawer, Lightbox, or MobileNav
122
119
  opens without supplying an outlet, so toasts stay behind it; `spec:AST-003`
123
120
  FR18 rehosts globally hosted layers above every native modal.
124
- - **A toast pushed out mid-collapse can linger.** A toast that a new toast
125
- pushes past the cap during its collapse, or that is still collapsing when
126
- its viewport moves into or out of any outlet FR12 names, comes back as an invisible zero-height row that never unmounts: it
121
+ - **A toast collapsing through an outlet move can linger.** A toast that is
122
+ still collapsing when its viewport moves into or out of any outlet FR12
123
+ names comes back as an invisible zero-height row that never unmounts: it
127
124
  keeps the notifications landmark exposed and F6 focuses its hidden dismiss
128
125
  button, and it holds one of the `maxVisible` places. With no provider
129
126
  above the caller a dismissal called before the fallback viewport
@@ -143,9 +140,6 @@ Consumer migration instructions belong in consumer docs and release notes.
143
140
  - **The action row does not track a held pointer.** A press that slides across
144
141
  the row's sibling actions does not act on the action it lifts on;
145
142
  `family:action-groups` has the row use the shared press tracking.
146
- - **Same-handler `ignore` toasts both announce.** Two `ignore` toasts with one
147
- `uniqueID` raised before the first renders are both announced, and the
148
- second replaces the first (FR8, ORD1).
149
143
  - **Nested viewports can show two landmarks.** An outer and an inner viewport
150
144
  that each hold a toast expose two notifications landmarks (FR11).
151
145
  - **Reduced motion is instant on every pointer.** Under reduced motion the
@@ -192,13 +186,13 @@ Consumer migration instructions belong in consumer docs and release notes.
192
186
  | ID | Invariant | Basis |
193
187
  | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
194
188
  | FR1 | A card carries `toast` with `type` and shows `body` and, on its first line, `endContent` then a dismiss Button named by `@astryx.toast.dismiss`; long body text wraps below. `renderContent` replaces that layout, receives `body`, `endContent`, `type`, timing, and a `dismiss` function, and gets no injected dismiss control. | Current source and tests |
195
- | FR2 | With auto-hide on (the default for `info`, off for `error`), the card dismisses with reason `auto` after `autoHideDuration` ms. The timer pauses while the pointer is over the card, focus is inside it, a touch or pen gesture is active, or the window is blurred, and resumes with the remaining time (at least 1 s). Implementation gaps: the pause is one flag, so any pause condition ending resumes the timer while another still holds, and a toast raised while the window is already blurred starts its timer (Current-state impact). | Current source and tests |
189
+ | FR2 | With auto-hide on (the default for `info`, off for `error`), the card dismisses with reason `auto` after `autoHideDuration` ms. The timer pauses while the pointer is over the card, focus is inside it, a touch or pen gesture is active, or the window is blurred, and resumes with the remaining time (at least 1 s). Each pause condition holds the timer on its own: one ending resumes it only when no other still holds, and focus moving between the card's own controls keeps it held. Implementation gap: a toast raised while the window is already blurred starts its timer (Current-state impact). | Current source and tests |
196
190
  | FR3 | The dismiss button, a swipe, a `dismiss` call, or the returned dismiss function dismiss with reason `manual`; `onHide` fires at most once, when the toast is dismissed, even when dismissed twice during its exit. Implementation gap: with no provider above the caller, a dismissal called before the fallback viewport mounts does nothing, so the toast shows and stays unless it auto-hides (Current-state impact). A toast replaced through `uniqueID` is not dismissed and reports no `onHide`. | Current source and tests |
197
191
  | FR4 | A drag toward the position's block edge, from any pointer (`spec:AST-094` FR43), decides once, at release, per `spec:AST-094` FR36–FR37: projected travel past the commit point dismisses, and a release moving back toward the start cancels. A short drag snaps back; horizontal intent, a second touch, or a gesture starting on an interactive descendant does not dismiss. Native page scrolling continues until touch intent matches the dismiss edge. In the default layout the visible dismiss button stays as a non-gesture alternative; custom content supplies its own. Implementation gaps: the shipped rule dismisses on net travel past a distance threshold or a fast flick timed by its own average speed, even after the finger moves back, and recognizes only touch and pen drags, not a mouse (Current-state impact). | Current source and tests |
198
192
  | FR5 | A new card slides in from its edge and fades in; a dismissed card collapses and fades before it unmounts; both slide only along the block axis. A dismissal without a swipe uses theme timing; a swipe's commit and snap-back start at the release velocity (`spec:AST-094` FR38). Under reduced motion the entrance and dismissal are an opacity-only fade of 200 ms or less on a coarse pointer or after a touch release (`design:reduced-motion` DR2) and immediate otherwise (DR3), and the card still unmounts. Implementation gaps: a swipe's settle is a CSS transition at theme timing, and under reduced motion every transition, including the row collapse and the swipe's throw, runs for 0.01 ms on every pointer (Current-state impact). | Current source and tests |
199
193
  | FR6 | The viewport stacks toasts at `position`, newest nearest the configured edge, with an 8 px gap and no extra space at the edge beyond the safe-area gutter it keeps; it spans the inline axis within its inset, honoring start and end insets in either direction. Each edge sits at the provider's `layerInset`, and a `toast.inset` edge, when set, replaces it (`spec:AST-059` FR6). Implementation gaps: a `toast.inset` edge set to 0 does not replace the provider's value, and an edge opposite the position, such as `top` for a bottom stack, moves the stack to that edge (Current-state impact). | Current source and tests |
200
194
  | FR7 | With `maxVisible` of 1 or more, at most that many toasts show; when more are raised, the oldest wait hidden and return as visible toasts leave. `maxVisible={0}` shows every toast. | Current source and tests |
201
- | FR8 | A toast whose `uniqueID` matches a toast the viewport holds, whether shown, hidden by the cap, or exiting, replaces it in place under `overwrite` (and is announced again; the card remounts, so focus inside it drops to the page body), and is dropped unshown and unannounced under `ignore` once the first has rendered. Implementation gaps: two `ignore` toasts with one `uniqueID` raised before the first renders are both announced and the second replaces the first; overwriting a toast hidden by the cap announces it while it is not shown, and nothing is announced when it surfaces (Current-state impact). | Current source and tests |
195
+ | FR8 | A toast whose `uniqueID` matches a toast the viewport holds, whether shown, hidden by the cap, or exiting, replaces it in place under `overwrite` (and is announced again; the card remounts, so focus inside it drops to the page body), and is dropped unshown and unannounced under `ignore`, even when both are raised before the first renders. Implementation gap: overwriting a toast hidden by the cap announces it while it is not shown, and nothing is announced when it surfaces (Current-state impact). | Current source and tests |
202
196
  | FR9 | Each shown toast has one announcement owner (`spec:AST-086` FR1): the viewport announces it once, when it is shown, `error` assertively and `info` politely, using the toast's text content, through its own persistent polite and assertive containers, which move with it into a modal (`spec:AST-086` FR10). Unrelated renders do not re-announce. Implementation gaps: the viewport announces through the shared body-level channel, which a modal hides, and announces at dispatch rather than when a toast appears (Current-state impact). | `spec:AST-086`; tests |
203
197
  | FR10 | F6 moves focus to the first control in the newest toast and remembers where focus was. Dismissing a toast that holds focus moves focus to a control in a remaining toast, or back to the remembered element when none remain. Implementation gap: with no control in the target toast, F6 leaves focus where it was and the handoff drops focus to the page, and dismissing the last toast after reaching it with Tab rather than F6 also drops focus to the page (Current-state impact). | Current source and tests |
204
198
  | FR11 | While a toast is visible the viewport is a `region` landmark named by `@astryx.toast.viewport`, without `aria-modal`; it exposes no landmark when empty, so an empty nested viewport adds none. Implementation gap: nested viewports that each hold a toast expose two landmarks (Current-state impact). | Current source and tests |
@@ -221,8 +215,8 @@ Consumer migration instructions belong in consumer docs and release notes.
221
215
 
222
216
  ### Transformation and precedence order
223
217
 
224
- - **ORD1 — Collision.** `ignore` drops a matching toast before announcement,
225
- apart from FR8's same-handler gap; `overwrite` replaces it, then announces.
218
+ - **ORD1 — Collision.** `ignore` drops a matching toast before announcement;
219
+ `overwrite` replaces it, then announces.
226
220
 
227
221
  ### Performance and resources
228
222
 
@@ -265,7 +259,7 @@ button), so no anatomy part can own the `toast` target.
265
259
  | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
266
260
  | -------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------- |
267
261
  | FR1, FR3 | `ToastViewport.test.tsx` layout, custom content, and onHide tests | Default, custom content, double dismiss | Dropped controls or a repeated `onHide` fails. | `audit:Toast/behavior` |
268
- | FR2 | `ToastViewport.test.tsx` blur-pause tests | Window blur, new toast during pause | A timer running while paused fails. | `audit:Toast/behavior` |
262
+ | FR2 | `ToastViewport.test.tsx` blur-pause and pause-source tests | Window blur, new toast during pause, overlapping pointer/focus/window holds, focus moving inside | A timer running while paused, or resuming while another hold remains, fails. | `audit:Toast/behavior` |
269
263
  | FR4, FR5 | `ToastViewport.test.tsx` gesture and motion tests | Pen and touch thresholds, short drag, horizontal intent, second touch, interactive descendant, RTL | A dismissal from a rejected gesture, a lost snap-back, or off-axis drift fails. | `audit:Toast/behavior` |
270
264
  | FR6–FR8 | `ToastViewport.test.tsx` placement, cap, and uniqueID tests; `Layer/LayerProvider.test.tsx` inset tests | Each position, cap 1 and an explicit cap, overwrite, ignore | Wrong order, cap, or collision fails. | `audit:Toast/layout` |
271
265
  | FR9–FR11 | `ToastViewport.test.tsx` announcement, F6, focus, landmark tests | Info, error, StrictMode, nested viewports | A repeated or wrong-urgency announcement, or focus to the body after F6 entry, fails. | `audit:Toast/accessibility` |
@@ -274,8 +268,8 @@ button), so no anatomy part can own the `toast` target.
274
268
  Read from source, with no focused test yet:
275
269
 
276
270
  - FR1: the `toast` target and its `type` state.
277
- - FR2: pausing while the pointer is over the card, focus is inside it, or a
278
- touch gesture is active, and the 1 s minimum on resume.
271
+ - FR2: pausing while a touch gesture is active, and the 1 s minimum on
272
+ resume.
279
273
  - FR4: a mouse drag dismissing, and a release moving back cancelling; the
280
274
  gesture tests assert today's touch-and-pen-only, net-travel rule.
281
275
  - FR5: a swipe's settle starting at the release velocity, and the
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import {useCallback, useEffect, useRef} from 'react';
21
- import type {ReactNode} from 'react';
21
+ import type {FocusEvent, ReactNode, RefObject} from 'react';
22
22
  import * as stylex from '@stylexjs/stylex';
23
23
  import {Button} from '../Button';
24
24
  import {Icon} from '../Icon';
@@ -172,6 +172,9 @@ export function Toast(props: ToastProps) {
172
172
  return <ToastSurface {...props} gestureDirection={1} />;
173
173
  }
174
174
 
175
+ /** A condition that holds a toast's auto-hide timer. */
176
+ type PauseSource = 'pointer' | 'focus' | 'gesture' | 'window';
177
+
175
178
  export function ToastSurface({
176
179
  type,
177
180
  body,
@@ -185,7 +188,13 @@ export function ToastSurface({
185
188
  }: ToastSurfaceProps) {
186
189
  const t = useTranslator();
187
190
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
188
- const isPausedRef = useRef(false);
191
+ // Every condition that holds the auto-hide timer (pointer over the card,
192
+ // focus inside it, a touch or pen gesture, the window blurred) is tracked on
193
+ // its own, so one ending resumes the timer only when no other still holds.
194
+ const pauseSourcesRef = useRef<Set<PauseSource>>(new Set());
195
+ // The card's root, read when a hold ends to drop a focus hold whose focused
196
+ // control was removed: an unmounting control fires no blur.
197
+ const cardRootRef = useRef<RefObject<HTMLDivElement | null> | null>(null);
189
198
  const remainingRef = useRef(autoHideDuration);
190
199
  // Will be initialized by startTimer when actually used
191
200
  const startTimeRef = useRef<number | null>(null);
@@ -197,7 +206,7 @@ export function ToastSurface({
197
206
  onDismissRef.current = onDismiss;
198
207
 
199
208
  const startTimer = useCallback(() => {
200
- if (!isAutoHide || isPausedRef.current) {
209
+ if (!isAutoHide || pauseSourcesRef.current.size > 0) {
201
210
  return;
202
211
  }
203
212
  if (timerRef.current) {
@@ -209,28 +218,53 @@ export function ToastSurface({
209
218
  }, remainingRef.current);
210
219
  }, [isAutoHide]);
211
220
 
212
- const pauseTimer = useCallback(() => {
213
- if (!isAutoHide || isPausedRef.current) {
214
- return;
215
- }
216
- isPausedRef.current = true;
217
- if (timerRef.current) {
218
- clearTimeout(timerRef.current);
219
- timerRef.current = null;
220
- }
221
- if (startTimeRef.current != null) {
222
- const elapsed = Date.now() - startTimeRef.current;
223
- remainingRef.current = Math.max(remainingRef.current - elapsed, 1000);
224
- }
225
- }, [isAutoHide]);
221
+ const pauseTimer = useCallback(
222
+ (source: PauseSource) => {
223
+ if (!isAutoHide) {
224
+ return;
225
+ }
226
+ const sources = pauseSourcesRef.current;
227
+ const wasRunning = sources.size === 0;
228
+ sources.add(source);
229
+ if (!wasRunning) {
230
+ return;
231
+ }
232
+ if (timerRef.current) {
233
+ clearTimeout(timerRef.current);
234
+ timerRef.current = null;
235
+ }
236
+ if (startTimeRef.current != null) {
237
+ const elapsed = Date.now() - startTimeRef.current;
238
+ remainingRef.current = Math.max(remainingRef.current - elapsed, 1000);
239
+ }
240
+ },
241
+ [isAutoHide],
242
+ );
226
243
 
227
- const resumeTimer = useCallback(() => {
228
- if (!isAutoHide || !isPausedRef.current) {
229
- return;
230
- }
231
- isPausedRef.current = false;
232
- startTimer();
233
- }, [isAutoHide, startTimer]);
244
+ const resumeTimer = useCallback(
245
+ (source: PauseSource) => {
246
+ if (!isAutoHide) {
247
+ return;
248
+ }
249
+ const sources = pauseSourcesRef.current;
250
+ if (!sources.delete(source)) {
251
+ return;
252
+ }
253
+ const root = cardRootRef.current?.current;
254
+ if (
255
+ sources.has('focus') &&
256
+ root != null &&
257
+ !root.contains(document.activeElement)
258
+ ) {
259
+ sources.delete('focus');
260
+ }
261
+ if (sources.size > 0) {
262
+ return;
263
+ }
264
+ startTimer();
265
+ },
266
+ [isAutoHide, startTimer],
267
+ );
234
268
 
235
269
  useEffect(() => {
236
270
  remainingRef.current = autoHideDuration;
@@ -250,15 +284,48 @@ export function ToastSurface({
250
284
  if (!isAutoHide) {
251
285
  return;
252
286
  }
253
- window.addEventListener('blur', pauseTimer);
254
- window.addEventListener('focus', resumeTimer);
287
+ const pauseForWindow = () => pauseTimer('window');
288
+ const resumeForWindow = () => resumeTimer('window');
289
+ window.addEventListener('blur', pauseForWindow);
290
+ window.addEventListener('focus', resumeForWindow);
255
291
  return () => {
256
- window.removeEventListener('blur', pauseTimer);
257
- window.removeEventListener('focus', resumeTimer);
292
+ window.removeEventListener('blur', pauseForWindow);
293
+ window.removeEventListener('focus', resumeForWindow);
258
294
  };
259
295
  }, [isAutoHide, pauseTimer, resumeTimer]);
260
296
 
261
- const isTimerPaused = useCallback(() => isPausedRef.current, []);
297
+ const isGesturePausing = useCallback(
298
+ () => pauseSourcesRef.current.has('gesture'),
299
+ [],
300
+ );
301
+ const pauseForGesture = useCallback(
302
+ () => pauseTimer('gesture'),
303
+ [pauseTimer],
304
+ );
305
+ const resumeForGesture = useCallback(
306
+ () => resumeTimer('gesture'),
307
+ [resumeTimer],
308
+ );
309
+ const pauseForPointer = useCallback(
310
+ () => pauseTimer('pointer'),
311
+ [pauseTimer],
312
+ );
313
+ const resumeForPointer = useCallback(
314
+ () => resumeTimer('pointer'),
315
+ [resumeTimer],
316
+ );
317
+ const pauseForFocus = useCallback(() => pauseTimer('focus'), [pauseTimer]);
318
+ const resumeForFocus = useCallback(
319
+ (event: FocusEvent<HTMLDivElement>) => {
320
+ // Focus moving between controls inside the card keeps it held.
321
+ const next = event.relatedTarget;
322
+ if (next instanceof Node && event.currentTarget.contains(next)) {
323
+ return;
324
+ }
325
+ resumeTimer('focus');
326
+ },
327
+ [resumeTimer],
328
+ );
262
329
  const dismissFromGesture = useCallback(() => {
263
330
  onDismissRef.current('manual');
264
331
  }, []);
@@ -266,12 +333,13 @@ export function ToastSurface({
266
333
  direction: gestureDirection,
267
334
  enabled: !isExiting,
268
335
  canPauseTimer: isAutoHide,
269
- isTimerPaused,
270
- pauseTimer,
271
- resumeTimer,
336
+ isTimerPaused: isGesturePausing,
337
+ pauseTimer: pauseForGesture,
338
+ resumeTimer: resumeForGesture,
272
339
  dismiss: dismissFromGesture,
273
340
  shouldIgnoreTarget: isInteractiveTarget,
274
341
  });
342
+ cardRootRef.current = rootRef;
275
343
 
276
344
  const handleDismiss = useCallback(() => {
277
345
  onDismiss('manual');
@@ -290,10 +358,10 @@ export function ToastSurface({
290
358
  role={isError ? 'alert' : 'status'}
291
359
  aria-live={isError ? 'assertive' : 'polite'}
292
360
  aria-atomic="true"
293
- onMouseEnter={pauseTimer}
294
- onMouseLeave={resumeTimer}
295
- onFocusCapture={pauseTimer}
296
- onBlurCapture={resumeTimer}
361
+ onMouseEnter={pauseForPointer}
362
+ onMouseLeave={resumeForPointer}
363
+ onFocusCapture={pauseForFocus}
364
+ onBlurCapture={resumeForFocus}
297
365
  {...gestureBindings}
298
366
  {...mergeProps(
299
367
  themeProps('toast', {type}),