@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
@@ -0,0 +1,157 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file touchActionRule.ts
5
+ * @input An element and the touch-action its recognizers derive
6
+ * @output Exports markTouchAction and unmarkTouchAction: the element carries
7
+ * the derived value in TOUCH_ACTION_ATTRIBUTE, and its document or shadow
8
+ * root holds one zero-specificity rule per value in the first cascade
9
+ * layer (`reset`), so any author rule, in any layer or none, or inline
10
+ * value outranks the host's (`spec:AST-094` FR18, DEC-8); a root's rules
11
+ * go with its last marked element (FR24)
12
+ * @position The gesture host's touch-action writer (GestureHost.ts)
13
+ *
14
+ * The mark is set as a recognizer attaches, so a subtree that mounts with
15
+ * it is styled once with its touch-action: a value written after that first
16
+ * styling restyles every descendant (each one's effective touch-action
17
+ * changes). Until the host reads the element's own value, the value in
18
+ * force is the author's where the author sets one, and the derived value
19
+ * elsewhere: never wider than the author's.
20
+ *
21
+ * SYNC: When modified, update GestureHost.ts.
22
+ */
23
+
24
+ /** The attribute that carries the host's derived touch-action. */
25
+ export const TOUCH_ACTION_ATTRIBUTE = 'data-astryx-touch-action';
26
+
27
+ /**
28
+ * The cascade layer the rules sit in: the first in the order every package
29
+ * stylesheet declares (`spec:AST-080` FR3), so an author rule in any later
30
+ * layer, or in none, outranks them. Unlayered, they would outrank every
31
+ * layered author rule.
32
+ */
33
+ const RULE_LAYER = 'reset';
34
+
35
+ type RuleRoot = Document | ShadowRoot;
36
+
37
+ /**
38
+ * Per document or shadow root: the values it has a rule for, their rules'
39
+ * text, where that text goes (an adopted sheet, or a style element where
40
+ * constructable sheets are missing), and the elements marked there. The
41
+ * rules go with the root's last marked element (`spec:AST-094` FR24).
42
+ */
43
+ const sheets = new WeakMap<
44
+ RuleRoot,
45
+ {
46
+ readonly values: Set<string>;
47
+ readonly marked: Set<Element>;
48
+ css: string;
49
+ write(css: string): void;
50
+ remove(): void;
51
+ }
52
+ >();
53
+
54
+ /** Where each marked element's rule lives, for its unmark after it leaves. */
55
+ const markedRoots = new WeakMap<Element, RuleRoot>();
56
+
57
+ function sheetOf(root: RuleRoot) {
58
+ let entry = sheets.get(root);
59
+ if (entry == null) {
60
+ const doc = root.nodeType === 9 ? (root as Document) : root.ownerDocument;
61
+ let write: ((css: string) => void) | undefined;
62
+ let remove: (() => void) | undefined;
63
+ // A constructable sheet where the engine has one; a style element only
64
+ // where it has none, so any other error from a sheet is not hidden.
65
+ const Sheet = doc?.defaultView?.CSSStyleSheet;
66
+ if (
67
+ typeof Sheet === 'function' &&
68
+ 'replaceSync' in Sheet.prototype &&
69
+ 'adoptedStyleSheets' in root
70
+ ) {
71
+ const sheet = new Sheet();
72
+ root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
73
+ write = css => sheet.replaceSync(css);
74
+ remove = () => {
75
+ root.adoptedStyleSheets = root.adoptedStyleSheets.filter(
76
+ adopted => adopted !== sheet,
77
+ );
78
+ };
79
+ } else {
80
+ const style = doc?.createElement('style');
81
+ const parent =
82
+ root.nodeType === 9 ? (doc?.head ?? doc?.documentElement) : root;
83
+ if (style != null && parent != null) {
84
+ parent.append(style);
85
+ write = css => {
86
+ style.textContent = css;
87
+ };
88
+ remove = () => style.remove();
89
+ }
90
+ }
91
+ if (write == null || remove == null) {
92
+ return null;
93
+ }
94
+ entry = {values: new Set(), marked: new Set(), css: '', write, remove};
95
+ sheets.set(root, entry);
96
+ }
97
+ return entry;
98
+ }
99
+
100
+ /**
101
+ * Give `element` the derived touch-action under a rule any author rule
102
+ * outranks. Returns false where no rule can hold it (a detached element):
103
+ * the caller writes nothing until the element is in a document.
104
+ */
105
+ export function markTouchAction(element: Element, value: string): boolean {
106
+ const root = element.getRootNode() as RuleRoot;
107
+ const entry =
108
+ element.isConnected && (root.nodeType === 9 || 'host' in root)
109
+ ? sheetOf(root)
110
+ : null;
111
+ if (entry == null) {
112
+ return false;
113
+ }
114
+ if (!entry.values.has(value)) {
115
+ entry.values.add(value);
116
+ entry.css += `:where([${TOUCH_ACTION_ATTRIBUTE}="${value}"]){touch-action:${value}}`;
117
+ entry.write(`@layer ${RULE_LAYER}{${entry.css}}`);
118
+ }
119
+ if (element.getAttribute(TOUCH_ACTION_ATTRIBUTE) !== value) {
120
+ element.setAttribute(TOUCH_ACTION_ATTRIBUTE, value);
121
+ }
122
+ const previous = markedRoots.get(element);
123
+ if (previous != null && previous !== root) {
124
+ // Moved to another root: the old one lets it go.
125
+ releaseMark(element, previous);
126
+ }
127
+ entry.marked.add(element);
128
+ markedRoots.set(element, root);
129
+ return true;
130
+ }
131
+
132
+ /** `root` no longer holds `element`'s mark; its last mark takes the rules. */
133
+ function releaseMark(element: Element, root: RuleRoot): void {
134
+ const entry = sheets.get(root);
135
+ if (entry == null) {
136
+ return;
137
+ }
138
+ entry.marked.delete(element);
139
+ if (entry.marked.size === 0) {
140
+ entry.remove();
141
+ sheets.delete(root);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Remove the host's mark; the element's own touch-action stands. The last
147
+ * mark in a document or shadow root takes its rules with it.
148
+ */
149
+ export function unmarkTouchAction(element: Element): void {
150
+ element.removeAttribute(TOUCH_ACTION_ATTRIBUTE);
151
+ const root = markedRoots.get(element);
152
+ if (root == null) {
153
+ return;
154
+ }
155
+ markedRoots.delete(element);
156
+ releaseMark(element, root);
157
+ }
@@ -46,13 +46,6 @@ export interface NativeScrollProbe {
46
46
  * lets the browser pan that axis).
47
47
  */
48
48
  canScroll(dx: number, dy: number): boolean;
49
- /**
50
- * How much further (px) the scroller that would move for a finger moving
51
- * by (dx, dy) can scroll before it reaches its edge; 0 when none would.
52
- * Lets a reader place the point where the content runs out between two
53
- * samples instead of at the next one.
54
- */
55
- distanceToEdge?(dx: number, dy: number): number;
56
49
  /** Whether any element under the touch can scroll natively at all. */
57
50
  readonly isInsideScroller: boolean;
58
51
  /**
@@ -127,6 +120,17 @@ export interface GestureEventContext {
127
120
  export interface GestureEnvironment {
128
121
  viewportSize(): {readonly width: number; readonly height: number};
129
122
  isRtl(view: unknown): boolean;
123
+ /**
124
+ * The box of a recognizer's view in viewport coordinates, or null when it
125
+ * has none to measure. A screen-edge pan reads it to tell a view within
126
+ * its edge band from any other.
127
+ */
128
+ viewRect?(view: unknown): {
129
+ readonly left: number;
130
+ readonly top: number;
131
+ readonly width: number;
132
+ readonly height: number;
133
+ } | null;
130
134
  }
131
135
 
132
136
  export const DEFAULT_ENVIRONMENT: GestureEnvironment = {
@@ -136,8 +140,18 @@ export const DEFAULT_ENVIRONMENT: GestureEnvironment = {
136
140
  }
137
141
  return {width: window.innerWidth, height: window.innerHeight};
138
142
  },
139
- isRtl() {
140
- return false;
143
+ isRtl(view) {
144
+ if (typeof window === 'undefined' || !(view instanceof Element)) {
145
+ return false;
146
+ }
147
+ return window.getComputedStyle(view).direction === 'rtl';
148
+ },
149
+ viewRect(view) {
150
+ if (typeof Element === 'undefined' || !(view instanceof Element)) {
151
+ return null;
152
+ }
153
+ const {left, top, width, height} = view.getBoundingClientRect();
154
+ return {left, top, width, height};
141
155
  },
142
156
  };
143
157
 
@@ -158,4 +172,11 @@ export interface TouchConsumer {
158
172
  * (attached above this consumer) from beginning for a touch on it.
159
173
  */
160
174
  allowsAncestorRecognizer?(recognizer: unknown): boolean;
175
+ /**
176
+ * Whether the consumer takes touches now (a disabled control does not).
177
+ * The host listens only while something enabled is attached
178
+ * (`spec:AST-094` FR24); after this changes, call `host.update(element)`.
179
+ * @default true
180
+ */
181
+ isEnabled?(): boolean;
161
182
  }
@@ -7,7 +7,10 @@
7
7
  * @input Uses React's external-store subscription over window.matchMedia
8
8
  * and the touch presentation's compact-touch query
9
9
  * @output Resolves popover/bottom-sheet presentation policy
10
- * @position Internal overlay policy shared across component families
10
+ * @position Internal overlay policy shared across component families. A
11
+ * component that also switches input modes under a finger resolves its
12
+ * `presentation` with usePresentation (utils/presentation.ts), whose
13
+ * surface is this one's
11
14
  */
12
15
 
13
16
  import {useCallback, useSyncExternalStore} from 'react';
@@ -288,6 +288,20 @@ export function useMenuHover<T extends HTMLElement = HTMLElement>(
288
288
  }
289
289
  }, [hide, menuRef]);
290
290
 
291
+ // A menu placed in the content can own its own list keys (NavHeadingMenu
292
+ // runs useListFocus over the same `menuitem`s); a key it consumed must not
293
+ // move focus a second time here as it bubbles out (APG menu: one item per
294
+ // arrow press).
295
+ const handleContentKeyDown = useCallback(
296
+ (event: React.KeyboardEvent) => {
297
+ if (event.defaultPrevented) {
298
+ return;
299
+ }
300
+ handleListKeyDown(event);
301
+ },
302
+ [handleListKeyDown],
303
+ );
304
+
291
305
  useEffect(() => {
292
306
  escapeHandlerRef.current = () => {
293
307
  clearTimeouts();
@@ -535,7 +549,7 @@ export function useMenuHover<T extends HTMLElement = HTMLElement>(
535
549
  contentProps: {
536
550
  onMouseEnter: handleContentMouseEnter,
537
551
  onMouseLeave: handleMouseLeave,
538
- onKeyDown: handleListKeyDown,
552
+ onKeyDown: handleContentKeyDown,
539
553
  },
540
554
  menuRef,
541
555
  focusFirst,
@@ -233,6 +233,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
233
233
  '@astryx.tabList.label': 'Tabs',
234
234
  '@astryx.table.label': 'Table',
235
235
  '@astryx.table.noData': 'No data',
236
+ '@astryx.table.columnResize.resizeColumn': 'Resize column {label}',
236
237
  '@astryx.table.filter.allPlaceholder': 'All',
237
238
  '@astryx.table.filter.reset': 'Reset',
238
239
  '@astryx.table.filter.apply': 'Apply',
@@ -427,6 +428,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
427
428
  '@astryx.tableTree.expandRow': 'Expand row',
428
429
  '@astryx.tableTree.collapseAllRows': 'Collapse all rows',
429
430
  '@astryx.tableTree.expandAllRows': 'Expand all rows',
431
+ '@astryx.textArea.counter': '{count, number}/{limit, number}',
430
432
  '@astryx.textArea.charactersRemaining':
431
433
  '{count, number} {count, plural, one {character} other {characters}} remaining',
432
434
  '@astryx.textArea.charactersOverLimit':
@@ -449,6 +451,8 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
449
451
  '@astryx.multiSelector.allSelected': 'All selected',
450
452
  '@astryx.multiSelector.selectionCount':
451
453
  '{count, number} of {total, number} selected',
454
+ '@astryx.multiSelector.selectedCount':
455
+ '{count, plural, one {# selected} other {# selected}}',
452
456
  '@astryx.multiSelector.createOption': 'Create "{query}"',
453
457
  '@astryx.multiSelector.optionCreated': '{label} created',
454
458
  '@astryx.multiSelector.emptySearchResults': 'No results found',
@@ -239,6 +239,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
239
239
  '@astryx.tabList.label': '⟦Ţàƀš⟧',
240
240
  '@astryx.table.label': '⟦Ţàƀłé⟧',
241
241
  '@astryx.table.noData': '⟦Ñó ðàţà⟧',
242
+ '@astryx.table.columnResize.resizeColumn': '⟦Řéšížé çółúɱñ {label}⟧',
242
243
  '@astryx.table.filter.allPlaceholder': '⟦Àłł⟧',
243
244
  '@astryx.table.filter.reset': '⟦Řéšéţ⟧',
244
245
  '@astryx.table.filter.apply': '⟦Àþþłý⟧',
@@ -436,6 +437,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
436
437
  '@astryx.tableTree.expandRow': '⟦Éẋþàñð řóŵ⟧',
437
438
  '@astryx.tableTree.collapseAllRows': '⟦Çółłàþšé àłł řóŵš⟧',
438
439
  '@astryx.tableTree.expandAllRows': '⟦Éẋþàñð àłł řóŵš⟧',
440
+ '@astryx.textArea.counter': '⟦{count, number}/{limit, number}⟧',
439
441
  '@astryx.textArea.charactersRemaining':
440
442
  '⟦{count, number} {count, plural, one {character} other {characters}} řéɱàíñíñĝ⟧',
441
443
  '@astryx.textArea.charactersOverLimit':
@@ -458,6 +460,8 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
458
460
  '@astryx.multiSelector.allSelected': '⟦Àłł šéłéçţéð⟧',
459
461
  '@astryx.multiSelector.selectionCount':
460
462
  '⟦{count, number} óƒ {total, number} šéłéçţéð⟧',
463
+ '@astryx.multiSelector.selectedCount':
464
+ '⟦{count, plural, one {# selected} other {# selected}}⟧',
461
465
  '@astryx.multiSelector.createOption': '⟦Çřéàţé "{query}"⟧',
462
466
  '@astryx.multiSelector.optionCreated': '⟦{label} çřéàţéð⟧',
463
467
  '@astryx.multiSelector.emptySearchResults': '⟦Ñó řéšúłţš ƒóúñð⟧',
@@ -81,7 +81,10 @@ await handle.finished; // true if it ran to the end, false if interrupted
81
81
  - Promotion: every animation the animator creates, and every `set`, gives its
82
82
  channels a compositing layer in the same task (inline `will-change`, merged
83
83
  with the element's own), cleared 500 ms after the last motion. Mobile Safari
84
- needs it to run a curve through a blocked main thread.
84
+ needs it to run a curve through a blocked main thread. A pseudo-element is
85
+ promoted through `--astryx-motion-will-change-<pseudo>` on its host; its
86
+ style opts in with
87
+ `willChange: 'var(--astryx-motion-will-change-before, auto)'`.
85
88
  - `atRest(properties)` resolves once none of them is moving, after the last
86
89
  curve ends, whoever started it.
87
90
  - `rebase({y: -shift})` moves properties at once, as a layout shift does; a
@@ -121,6 +124,13 @@ await handle.finished; // true if it ran to the end, false if interrupted
121
124
  - `animatorFor(element)` returns the one animator for an element, so code
122
125
  that animates it from several places (a swipe on x, a removal on y)
123
126
  composes in one `translate` track.
127
+ - `createAnimator(dialog, {pseudoElement: '::backdrop'})` drives a
128
+ pseudo-element; with no inline style to hold it, a static filling animation
129
+ holds its end value until the next animation replaces it, while the
130
+ pseudo-element exists. A dialog's `::backdrop` is made again each time the
131
+ dialog opens, so a presentation `set`s its starting value right after
132
+ `showModal()`, in the same task (a `set` before it lands on the backdrop
133
+ the open replaces).
124
134
 
125
135
  ### How it stays on the compositor
126
136
 
@@ -67,18 +67,23 @@ const requestFrame = (callback: () => void) => {
67
67
  * properties, so the page is modeled here).
68
68
  */
69
69
  let stylesheet: Record<string, string> = {};
70
+ /** The stylesheet's values for a pseudo-element of the element. */
71
+ let pseudoStylesheet: Record<string, string> = {};
70
72
 
71
73
  beforeEach(() => {
72
74
  clock = 1000;
73
75
  recorded = [];
74
76
  frameQueue = [];
75
77
  stylesheet = {};
78
+ pseudoStylesheet = {};
76
79
  vi.spyOn(window, 'getComputedStyle').mockImplementation(
77
- element =>
80
+ (element, pseudo) =>
78
81
  ({
79
82
  getPropertyValue: (property: string) =>
80
- (element as HTMLElement).style.getPropertyValue(property) ||
81
- (stylesheet[property] ?? ''),
83
+ pseudo != null
84
+ ? (pseudoStylesheet[property] ?? '')
85
+ : (element as HTMLElement).style.getPropertyValue(property) ||
86
+ (stylesheet[property] ?? ''),
82
87
  }) as unknown as CSSStyleDeclaration,
83
88
  );
84
89
  vi.useFakeTimers();
@@ -799,6 +804,12 @@ describe('animatorFor: one animator per element', () => {
799
804
  const b = document.createElement('div');
800
805
  expect(animatorFor(a)).toBe(animatorFor(a));
801
806
  expect(animatorFor(a)).not.toBe(animatorFor(b));
807
+ expect(animatorFor(a, {pseudoElement: '::backdrop'})).toBe(
808
+ animatorFor(a, {pseudoElement: '::backdrop'}),
809
+ );
810
+ expect(animatorFor(a, {pseudoElement: '::backdrop'})).not.toBe(
811
+ animatorFor(a),
812
+ );
802
813
  });
803
814
 
804
815
  it('composes two callers’ axes in one translate track', async () => {
@@ -902,6 +913,24 @@ describe('promotion: a compositing layer while it moves', () => {
902
913
  expect(element.style.willChange).toBe('');
903
914
  });
904
915
 
916
+ it('promotes a pseudo-element through a custom property on its host', async () => {
917
+ const {animator, element} = setup({pseudoElement: '::backdrop'});
918
+ animator.set({opacity: 0});
919
+ // set() promotes a pseudo-element too.
920
+ expect(
921
+ element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
922
+ ).toBe('opacity');
923
+ animator.animate({opacity: 1}, {spring: springs.alertPresent});
924
+ expect(
925
+ element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
926
+ ).toBe('opacity');
927
+ expect(element.style.willChange).toBe('');
928
+ await advance(5000 + PROMOTION_LINGER_MS);
929
+ expect(
930
+ element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
931
+ ).toBe('');
932
+ });
933
+
905
934
  it('ends promotion on the element it leaves', () => {
906
935
  const {animator, element} = setup();
907
936
  animator.animate({x: 50}, {spring: springs.settle});
@@ -1937,6 +1966,75 @@ describe('Reduced Motion (design:reduced-motion)', () => {
1937
1966
  expect(element.style.translate).toBe('100px 0px');
1938
1967
  });
1939
1968
 
1969
+ it("a pseudo-element's dip borrows its own opacity and hands it back without touching the host", async () => {
1970
+ for (const role of [undefined, 'exit'] as const) {
1971
+ const {animator, element} = setup({
1972
+ reduced: true,
1973
+ coarse: true,
1974
+ pseudoElement: '::before',
1975
+ });
1976
+ element.style.opacity = '0.5'; // the host's own, untouched
1977
+ pseudoStylesheet = {opacity: '0.3'};
1978
+ animator.animate(
1979
+ {x: 100},
1980
+ {spring: springs.navigationPush},
1981
+ role == null ? {} : {crossfadeAs: role},
1982
+ );
1983
+ await commitFrame(clock);
1984
+ const [fadeLayer] = live('opacity');
1985
+ const values = fadeLayer.keyframes.map(k => Number(k.opacity));
1986
+ expect(Math.max(...values)).toBeCloseTo(0.3, 9);
1987
+ await advance(fade * 2 + END_HOLD_MS + 1);
1988
+ expect(animator.get('x')).toBe(100);
1989
+ // The pseudo-element's stylesheet owns its opacity again; the host
1990
+ // keeps its own.
1991
+ expect(live('opacity')).toEqual([]);
1992
+ expect(element.style.opacity).toBe('0.5');
1993
+ }
1994
+ });
1995
+
1996
+ it("a pseudo-element's identity hold follows a node replacement", () => {
1997
+ const {animator, element} = setup({pseudoElement: '::backdrop'});
1998
+ pseudoStylesheet = {opacity: '0'};
1999
+ animator.set({opacity: 1});
2000
+ animator.attach(null);
2001
+ const next = document.createElement('div');
2002
+ document.body.append(next);
2003
+ animator.attach(next);
2004
+ const held = live('opacity').filter(
2005
+ animation => animation.element === next,
2006
+ );
2007
+ expect(held).toHaveLength(1);
2008
+ expect(Number(held[0].keyframes.at(-1)!.opacity)).toBe(1);
2009
+ expect(live('opacity').some(a => a.element === element)).toBe(false);
2010
+ next.remove();
2011
+ });
2012
+
2013
+ it('a pseudo-element shown over a hiding stylesheet keeps its own opacity through a move', async () => {
2014
+ for (const role of [undefined, 'entrance'] as const) {
2015
+ const {animator} = setup({
2016
+ reduced: true,
2017
+ coarse: true,
2018
+ pseudoElement: '::backdrop',
2019
+ });
2020
+ pseudoStylesheet = {opacity: '0'};
2021
+ animator.set({opacity: 1});
2022
+ animator.animate(
2023
+ {x: 100},
2024
+ {spring: springs.navigationPush},
2025
+ role == null ? {} : {crossfadeAs: role},
2026
+ );
2027
+ await commitFrame(clock);
2028
+ for (let step = 0; step < 8; step++) {
2029
+ await advance(fade / 4);
2030
+ }
2031
+ expect(animator.get('x')).toBe(100);
2032
+ expect(animator.get('opacity')).toBe(1);
2033
+ const [held] = live('opacity');
2034
+ expect(Number(held.keyframes.at(-1)?.opacity)).toBe(1);
2035
+ }
2036
+ });
2037
+
1940
2038
  it('a move dips: fades out, lands while transparent, fades back in', async () => {
1941
2039
  const {animator, element} = reduced();
1942
2040
  animator.animate({x: 100}, {spring: springs.navigationPush});
@@ -2555,6 +2653,56 @@ describe('decay: momentum curves (a coast)', () => {
2555
2653
  });
2556
2654
  });
2557
2655
 
2656
+ describe('pseudoElement: a dialog\u2019s ::backdrop', () => {
2657
+ it('targets the pseudo-element, writes no inline style and holds the end state', async () => {
2658
+ const {animator, element} = setup({pseudoElement: '::backdrop'});
2659
+ animator.set({opacity: 0});
2660
+ const [held] = live('opacity');
2661
+ expect(held.options).toMatchObject({
2662
+ fill: 'both',
2663
+ pseudoElement: '::backdrop',
2664
+ });
2665
+ expect(held.keyframes.map(k => k.opacity)).toEqual(['0', '0']);
2666
+ expect(element.style.opacity).toBe('');
2667
+
2668
+ animator.animate({opacity: 1}, {spring: springs.alertPresent});
2669
+ // The new animation exists before the hold goes: no frame in between.
2670
+ expect(held.cancelled).toBe(true);
2671
+ const [fade] = live('opacity');
2672
+ expect(fade.options).toMatchObject({
2673
+ fill: 'both',
2674
+ pseudoElement: '::backdrop',
2675
+ });
2676
+ expect(fade.keyframes[fade.keyframes.length - 1].opacity).toBe('1');
2677
+ expect(element.style.opacity).toBe('');
2678
+ await advance(Number(fade.options.duration) + 1000);
2679
+ // Past the curve's end, a static animation holds the end value.
2680
+ expect(fade.cancelled).toBe(true);
2681
+ const [rest] = live('opacity');
2682
+ expect(rest.options).toMatchObject({
2683
+ fill: 'both',
2684
+ pseudoElement: '::backdrop',
2685
+ });
2686
+ expect(rest.keyframes.map(k => k.opacity)).toEqual(['1', '1']);
2687
+ expect(animator.get('opacity')).toBe(1);
2688
+ });
2689
+
2690
+ it('an exit holds 0 until the next animation replaces it', async () => {
2691
+ const {animator} = setup({
2692
+ pseudoElement: '::backdrop',
2693
+ initial: {opacity: 1},
2694
+ });
2695
+ animator.animate({opacity: 0}, {spring: springs.alertDismiss});
2696
+ const [fade] = live('opacity');
2697
+ await advance(Number(fade.options.duration) + 1000);
2698
+ const [rest] = live('opacity');
2699
+ expect(rest.keyframes.map(k => k.opacity)).toEqual(['0', '0']);
2700
+ animator.animate({opacity: 1}, {spring: springs.alertPresent});
2701
+ expect(rest.cancelled).toBe(true);
2702
+ expect(live('opacity')).toHaveLength(1);
2703
+ });
2704
+ });
2705
+
2558
2706
  describe('attach', () => {
2559
2707
  it('keeps state without an element and resumes the curve on attach', () => {
2560
2708
  const animator = createAnimator(null, {