@astryxdesign/core 0.7.8 → 0.7.9-canary.07992bb

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 (403) hide show
  1. package/dist/AppShell/observeMainOverflow.d.ts.map +1 -1
  2. package/dist/AppShell/observeMainOverflow.js +9 -6
  3. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  4. package/dist/BottomSheet/BottomSheet.js +5 -0
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheetPanel.js +40 -8
  7. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  8. package/dist/BottomSheet/useSheetGestures.js +61 -21
  9. package/dist/Calendar/Calendar.d.ts +14 -2
  10. package/dist/Calendar/Calendar.d.ts.map +1 -1
  11. package/dist/Calendar/Calendar.js +63 -74
  12. package/dist/Calendar/getInitialFocusDate.d.ts +4 -1
  13. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -1
  14. package/dist/Calendar/getInitialFocusDate.js +7 -5
  15. package/dist/Calendar/hooks/useCalendarDays.d.ts +19 -2
  16. package/dist/Calendar/hooks/useCalendarDays.d.ts.map +1 -1
  17. package/dist/Calendar/hooks/useCalendarDays.js +57 -64
  18. package/dist/Calendar/hooks/useCalendarNavigation.d.ts +28 -3
  19. package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
  20. package/dist/Calendar/hooks/useCalendarNavigation.js +33 -22
  21. package/dist/Calendar/styles.d.ts +1 -1
  22. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  23. package/dist/CheckboxInput/CheckboxInput.js +54 -11
  24. package/dist/ClickableCard/ClickableCard.d.ts +1 -1
  25. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  26. package/dist/ClickableCard/ClickableCard.js +5 -1
  27. package/dist/DateInput/DateInput.d.ts.map +1 -1
  28. package/dist/DateInput/DateInput.js +4 -1
  29. package/dist/DateInput/PickerWheel.d.ts.map +1 -1
  30. package/dist/DateInput/PickerWheel.js +7 -1
  31. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  32. package/dist/DateRangeInput/DateRangeInput.js +5 -2
  33. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  34. package/dist/DateTimeInput/DateTimeInput.js +4 -1
  35. package/dist/Dialog/Dialog.d.ts +2 -1
  36. package/dist/Dialog/Dialog.d.ts.map +1 -1
  37. package/dist/Dialog/Dialog.js +12 -1
  38. package/dist/Drawer/useDrawerDialogPresence.d.ts +2 -1
  39. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  40. package/dist/Drawer/useDrawerDialogPresence.js +11 -2
  41. package/dist/DropdownMenu/DropdownMenu.d.ts +9 -1
  42. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  43. package/dist/DropdownMenu/DropdownMenu.js +9 -4
  44. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  45. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +3 -2
  46. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  47. package/dist/DropdownMenu/DropdownMenuItem.js +3 -2
  48. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  49. package/dist/DropdownMenu/DropdownMenuRadioItem.js +3 -2
  50. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  51. package/dist/DropdownMenu/DropdownMenuSubMenu.js +3 -2
  52. package/dist/Field/inputDisabledFocus.d.ts +4 -4
  53. package/dist/Field/inputStyles.stylex.d.ts +1 -1
  54. package/dist/FileInput/FileInput.d.ts.map +1 -1
  55. package/dist/FileInput/FileInput.js +16 -2
  56. package/dist/HoverCard/useHoverCard.d.ts +3 -1
  57. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  58. package/dist/Icon/Icon.d.ts +1 -1
  59. package/dist/InfoTip/InfoTip.d.ts +1 -0
  60. package/dist/InfoTip/InfoTip.d.ts.map +1 -1
  61. package/dist/InfoTip/InfoTip.js +1 -0
  62. package/dist/InputGroup/InputGroup.d.ts +4 -3
  63. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  64. package/dist/InputGroup/InputGroup.js +2 -2
  65. package/dist/InputGroup/groupStyles.d.ts +1 -1
  66. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  67. package/dist/InputGroup/groupStyles.js +1 -1
  68. package/dist/Item/Item.d.ts +2 -1
  69. package/dist/Item/Item.d.ts.map +1 -1
  70. package/dist/Item/Item.js +29 -6
  71. package/dist/Layer/holdEntranceInput.d.ts +24 -0
  72. package/dist/Layer/holdEntranceInput.d.ts.map +1 -0
  73. package/dist/Layer/holdEntranceInput.js +65 -0
  74. package/dist/Layer/inputHold.d.ts +49 -0
  75. package/dist/Layer/inputHold.d.ts.map +1 -0
  76. package/dist/Layer/inputHold.js +636 -0
  77. package/dist/Layer/preserveAnimations.d.ts +1 -5
  78. package/dist/Layer/preserveAnimations.d.ts.map +1 -1
  79. package/dist/Layer/preserveAnimations.js +12 -214
  80. package/dist/Layer/useLayer.d.ts +3 -1
  81. package/dist/Layer/useLayer.d.ts.map +1 -1
  82. package/dist/Layer/useLayer.js +64 -4
  83. package/dist/Layout/LayoutContent.d.ts +3 -1
  84. package/dist/Layout/LayoutContent.d.ts.map +1 -1
  85. package/dist/Layout/LayoutContent.js +12 -4
  86. package/dist/Layout/LayoutPanel.d.ts +4 -1
  87. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  88. package/dist/Layout/LayoutPanel.js +13 -4
  89. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  90. package/dist/Lightbox/Lightbox.js +4 -0
  91. package/dist/MetadataList/MetadataList.js +1 -1
  92. package/dist/MobileNav/MobileNav.d.ts +11 -1
  93. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  94. package/dist/MobileNav/MobileNav.js +39 -1
  95. package/dist/MoreMenu/MoreMenu.d.ts +5 -3
  96. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  97. package/dist/NumberInput/NumberInput.d.ts +4 -2
  98. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  99. package/dist/NumberInput/NumberInput.js +244 -11
  100. package/dist/Popover/usePopover.d.ts +1 -0
  101. package/dist/Popover/usePopover.d.ts.map +1 -1
  102. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  103. package/dist/PowerSearch/PowerSearch.js +12 -5
  104. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -1
  105. package/dist/PowerSearch/PowerSearchBottomSheet.js +4 -2
  106. package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
  107. package/dist/PowerSearch/PowerSearchToken.js +3 -1
  108. package/dist/PowerSearch/formatFilterValue.d.ts +9 -3
  109. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  110. package/dist/PowerSearch/formatFilterValue.js +14 -11
  111. package/dist/PowerSearch/formatTokenValue.d.ts +6 -2
  112. package/dist/PowerSearch/formatTokenValue.d.ts.map +1 -1
  113. package/dist/PowerSearch/formatTokenValue.js +6 -3
  114. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  115. package/dist/RadioList/RadioListItem.js +22 -4
  116. package/dist/SelectableCard/SelectableCard.d.ts +1 -1
  117. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  118. package/dist/SelectableCard/SelectableCard.js +5 -1
  119. package/dist/Spinner/Spinner.d.ts.map +1 -1
  120. package/dist/Spinner/Spinner.js +14 -71
  121. package/dist/Spinner/index.d.ts +1 -1
  122. package/dist/Spinner/index.d.ts.map +1 -1
  123. package/dist/Spinner/spinnerRotor.d.ts +28 -0
  124. package/dist/Spinner/spinnerRotor.d.ts.map +1 -0
  125. package/dist/Spinner/spinnerRotor.js +98 -0
  126. package/dist/Stack/StackItem.d.ts +8 -4
  127. package/dist/Stack/StackItem.d.ts.map +1 -1
  128. package/dist/Stack/StackItem.js +17 -4
  129. package/dist/Switch/Switch.d.ts.map +1 -1
  130. package/dist/Switch/Switch.js +13 -10
  131. package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
  132. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  133. package/dist/Timestamp/Timestamp.js +14 -8
  134. package/dist/Timestamp/formatInstant.d.ts +10 -1
  135. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  136. package/dist/Timestamp/formatInstant.js +13 -27
  137. package/dist/Timestamp/tooltipEntries.d.ts +5 -3
  138. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
  139. package/dist/Timestamp/tooltipEntries.js +6 -4
  140. package/dist/Token/Token.d.ts.map +1 -1
  141. package/dist/Token/Token.js +9 -0
  142. package/dist/Token/TokenLink.d.ts +1 -1
  143. package/dist/Token/TokenLink.d.ts.map +1 -1
  144. package/dist/Token/TokenLink.js +4 -1
  145. package/dist/Tokenizer/Tokenizer.d.ts +14 -10
  146. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  147. package/dist/Tokenizer/Tokenizer.js +189 -89
  148. package/dist/Tooltip/useTooltip.d.ts +3 -1
  149. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  150. package/dist/Tooltip/useTooltip.js +162 -59
  151. package/dist/TreeList/TreeListItem.d.ts +2 -1
  152. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  153. package/dist/TreeList/TreeListItem.js +27 -3
  154. package/dist/astryx.css +45 -18
  155. package/dist/gestures/GestureHost.d.ts +2 -2
  156. package/dist/gestures/GestureHost.d.ts.map +1 -1
  157. package/dist/gestures/GestureHost.js +21 -6
  158. package/dist/gestures/PanGestureRecognizer.d.ts +11 -1
  159. package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
  160. package/dist/gestures/PanGestureRecognizer.js +15 -1
  161. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +6 -1
  162. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -1
  163. package/dist/gestures/nativeScroll.d.ts +3 -6
  164. package/dist/gestures/nativeScroll.d.ts.map +1 -1
  165. package/dist/gestures/nativeScroll.js +7 -52
  166. package/dist/gestures/wheelSegmenter.d.ts +68 -1
  167. package/dist/gestures/wheelSegmenter.d.ts.map +1 -1
  168. package/dist/gestures/wheelSegmenter.js +199 -10
  169. package/dist/hooks/StackItemScrollRegion.d.ts +31 -0
  170. package/dist/hooks/StackItemScrollRegion.d.ts.map +1 -0
  171. package/dist/hooks/StackItemScrollRegion.js +40 -0
  172. package/dist/hooks/menuPressGesture.d.ts +133 -21
  173. package/dist/hooks/menuPressGesture.d.ts.map +1 -1
  174. package/dist/hooks/menuPressGesture.js +155 -30
  175. package/dist/hooks/menuPressValues.d.ts +16 -0
  176. package/dist/hooks/menuPressValues.d.ts.map +1 -0
  177. package/dist/hooks/menuPressValues.js +18 -0
  178. package/dist/hooks/useClickableContainer.d.ts +27 -5
  179. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  180. package/dist/hooks/useClickableContainer.js +41 -5
  181. package/dist/hooks/useFocusTrap.d.ts +10 -0
  182. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  183. package/dist/hooks/useFocusTrap.js +225 -132
  184. package/dist/hooks/useMenuPress.d.ts +34 -1
  185. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  186. package/dist/hooks/useMenuPress.js +93 -5
  187. package/dist/hooks/usePressFeedback.d.ts +6 -6
  188. package/dist/hooks/usePressFeedback.js +6 -6
  189. package/dist/hooks/useScrollRegionProps.d.ts +35 -0
  190. package/dist/hooks/useScrollRegionProps.d.ts.map +1 -0
  191. package/dist/hooks/useScrollRegionProps.js +51 -0
  192. package/dist/i18n/InternationalizationContext.d.ts +6 -0
  193. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  194. package/dist/i18n/InternationalizationProvider.d.ts +21 -3
  195. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  196. package/dist/i18n/InternationalizationProvider.js +8 -3
  197. package/dist/i18n/generated-english/hooks/useScrollRegionProps.generated.d.ts +8 -0
  198. package/dist/i18n/generated-english/hooks/useScrollRegionProps.generated.d.ts.map +1 -0
  199. package/dist/i18n/generated-english/hooks/useScrollRegionProps.generated.js +12 -0
  200. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  201. package/dist/i18n/generated-locales/en.generated.js +1 -0
  202. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  203. package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
  204. package/dist/i18n/index.d.ts +2 -0
  205. package/dist/i18n/index.d.ts.map +1 -1
  206. package/dist/i18n/index.js +1 -0
  207. package/dist/i18n/useCalendarSystem.d.ts +19 -0
  208. package/dist/i18n/useCalendarSystem.d.ts.map +1 -0
  209. package/dist/i18n/useCalendarSystem.js +61 -0
  210. package/dist/internal/spinner-rotor.d.ts +16 -0
  211. package/dist/internal/spinner-rotor.d.ts.map +1 -0
  212. package/dist/internal/spinner-rotor.js +16 -0
  213. package/dist/internal/touch.d.ts +14 -1
  214. package/dist/internal/touch.d.ts.map +1 -1
  215. package/dist/internal/touch.js +20 -2
  216. package/dist/motion/carryAnimations.d.ts +58 -0
  217. package/dist/motion/carryAnimations.d.ts.map +1 -0
  218. package/dist/motion/carryAnimations.js +305 -0
  219. package/dist/motion/transientCopy.d.ts +4 -2
  220. package/dist/motion/transientCopy.d.ts.map +1 -1
  221. package/dist/motion/transientCopy.js +34 -5
  222. package/dist/motion/uikit/gestures.d.ts +41 -0
  223. package/dist/motion/uikit/gestures.d.ts.map +1 -1
  224. package/dist/motion/uikit/gestures.js +25 -0
  225. package/dist/motion/uikit/index.d.ts +1 -1
  226. package/dist/motion/uikit/index.d.ts.map +1 -1
  227. package/dist/motion/uikit/index.js +1 -1
  228. package/dist/motion/uikit/timing.d.ts +14 -10
  229. package/dist/motion/uikit/timing.d.ts.map +1 -1
  230. package/dist/motion/uikit/timing.js +7 -5
  231. package/dist/privateAttributes/menuPressLit.stylex.d.ts +52 -0
  232. package/dist/privateAttributes/menuPressLit.stylex.d.ts.map +1 -0
  233. package/dist/privateAttributes/menuPressLit.stylex.js +99 -0
  234. package/dist/privateAttributes/press.stylex.d.ts +139 -0
  235. package/dist/privateAttributes/press.stylex.d.ts.map +1 -0
  236. package/dist/privateAttributes/press.stylex.js +97 -0
  237. package/dist/theme/themeMotion.d.ts +3 -2
  238. package/dist/theme/themeMotion.d.ts.map +1 -1
  239. package/dist/theme/themeMotion.js +3 -2
  240. package/dist/theme/tokens.stylex.d.ts +2 -2
  241. package/dist/theme/tokens.stylex.js +1 -1
  242. package/dist/utils/calendarSystem.d.ts +114 -0
  243. package/dist/utils/calendarSystem.d.ts.map +1 -0
  244. package/dist/utils/calendarSystem.js +386 -0
  245. package/dist/utils/flatTree.d.ts +6 -3
  246. package/dist/utils/flatTree.d.ts.map +1 -1
  247. package/dist/utils/flatTree.js +13 -3
  248. package/dist/utils/hitArea.d.ts.map +1 -1
  249. package/dist/utils/hitArea.js +82 -5
  250. package/dist/utils/inputStream.d.ts +14 -1
  251. package/dist/utils/inputStream.d.ts.map +1 -1
  252. package/dist/utils/inputStream.js +40 -9
  253. package/dist/utils/interactionOverlay.stylex.d.ts +5 -9
  254. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  255. package/dist/utils/interactionOverlay.stylex.js +29 -31
  256. package/dist/utils/plainDate.d.ts +23 -7
  257. package/dist/utils/plainDate.d.ts.map +1 -1
  258. package/dist/utils/plainDate.js +57 -18
  259. package/dist/utils/pressFeedback.d.ts +10 -2
  260. package/dist/utils/pressFeedback.d.ts.map +1 -1
  261. package/dist/utils/pressFeedback.js +40 -15
  262. package/dist/utils/pressGesture.d.ts +12 -6
  263. package/dist/utils/pressGesture.d.ts.map +1 -1
  264. package/dist/utils/pressGesture.js +26 -8
  265. package/dist/utils/scrollView.d.ts +25 -0
  266. package/dist/utils/scrollView.d.ts.map +1 -0
  267. package/dist/utils/scrollView.js +108 -0
  268. package/dist/utils/selectionLock.d.ts.map +1 -1
  269. package/dist/utils/selectionLock.js +1 -10
  270. package/dist/utils/touchHitArea.stylex.d.ts +35 -0
  271. package/dist/utils/touchHitArea.stylex.d.ts.map +1 -0
  272. package/dist/utils/touchHitArea.stylex.js +32 -0
  273. package/dist/utils/triggerFocus.d.ts +5 -0
  274. package/dist/utils/triggerFocus.d.ts.map +1 -1
  275. package/dist/utils/triggerFocus.js +9 -2
  276. package/locales/en.json +4 -0
  277. package/locales/pseudo.json +3 -0
  278. package/package.json +10 -4
  279. package/src/AppShell/observeMainOverflow.ts +9 -6
  280. package/src/BottomSheet/BottomSheet.tsx +5 -0
  281. package/src/BottomSheet/BottomSheetPanel.tsx +64 -7
  282. package/src/BottomSheet/useSheetGestures.ts +83 -20
  283. package/src/Calendar/Calendar.doc.mjs +76 -6
  284. package/src/Calendar/Calendar.tsx +123 -104
  285. package/src/Calendar/getInitialFocusDate.ts +22 -8
  286. package/src/Calendar/hooks/useCalendarDays.ts +89 -63
  287. package/src/Calendar/hooks/useCalendarNavigation.ts +68 -26
  288. package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -2
  289. package/src/CheckboxInput/CheckboxInput.tsx +71 -9
  290. package/src/ClickableCard/ClickableCard.tsx +8 -1
  291. package/src/DateInput/DateInput.tsx +2 -0
  292. package/src/DateInput/PickerWheel.tsx +7 -2
  293. package/src/DateRangeInput/DateRangeInput.tsx +3 -1
  294. package/src/DateTimeInput/DateTimeInput.tsx +2 -0
  295. package/src/Dialog/Dialog.doc.mjs +1 -1
  296. package/src/Dialog/Dialog.tsx +12 -1
  297. package/src/Drawer/Drawer.doc.mjs +1 -1
  298. package/src/Drawer/useDrawerDialogPresence.ts +9 -2
  299. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -2
  300. package/src/DropdownMenu/DropdownMenu.tsx +29 -5
  301. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -1
  302. package/src/DropdownMenu/DropdownMenuItem.tsx +8 -4
  303. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -1
  304. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -1
  305. package/src/FileInput/FileInput.tsx +16 -2
  306. package/src/HoverCard/useHoverCard.tsx +3 -1
  307. package/src/InfoTip/InfoTip.doc.mjs +26 -2
  308. package/src/InfoTip/InfoTip.tsx +1 -0
  309. package/src/InputGroup/InputGroup.doc.mjs +4 -3
  310. package/src/InputGroup/InputGroup.tsx +4 -3
  311. package/src/InputGroup/groupStyles.ts +3 -1
  312. package/src/Item/Item.tsx +51 -9
  313. package/src/Layer/holdEntranceInput.ts +84 -0
  314. package/src/Layer/inputHold.ts +734 -0
  315. package/src/Layer/preserveAnimations.ts +12 -275
  316. package/src/Layer/useLayer.tsx +77 -4
  317. package/src/Layout/LayoutContent.doc.mjs +6 -3
  318. package/src/Layout/LayoutContent.tsx +10 -4
  319. package/src/Layout/LayoutPanel.doc.mjs +6 -3
  320. package/src/Layout/LayoutPanel.tsx +11 -4
  321. package/src/Lightbox/Lightbox.tsx +4 -0
  322. package/src/MetadataList/MetadataList.tsx +1 -1
  323. package/src/MobileNav/MobileNav.doc.mjs +3 -3
  324. package/src/MobileNav/MobileNav.tsx +39 -1
  325. package/src/MoreMenu/MoreMenu.doc.mjs +2 -4
  326. package/src/MoreMenu/MoreMenu.tsx +5 -3
  327. package/src/NumberInput/NumberInput.doc.mjs +1 -1
  328. package/src/NumberInput/NumberInput.tsx +259 -11
  329. package/src/Popover/usePopover.tsx +1 -0
  330. package/src/PowerSearch/PowerSearch.doc.mjs +3 -3
  331. package/src/PowerSearch/PowerSearch.tsx +20 -3
  332. package/src/PowerSearch/PowerSearchBottomSheet.tsx +4 -1
  333. package/src/PowerSearch/PowerSearchToken.tsx +3 -0
  334. package/src/PowerSearch/formatFilterValue.ts +21 -11
  335. package/src/PowerSearch/formatTokenValue.ts +7 -1
  336. package/src/RadioList/RadioListItem.doc.mjs +3 -2
  337. package/src/RadioList/RadioListItem.tsx +25 -7
  338. package/src/SelectableCard/SelectableCard.tsx +8 -1
  339. package/src/Spinner/Spinner.tsx +7 -89
  340. package/src/Spinner/index.ts +1 -5
  341. package/src/Spinner/spinnerRotor.ts +113 -0
  342. package/src/Stack/Stack.doc.mjs +1 -1
  343. package/src/Stack/StackItem.tsx +27 -13
  344. package/src/Switch/Switch.tsx +7 -6
  345. package/src/Table/plugins/sortable/useTableSortable.tsx +1 -1
  346. package/src/Timestamp/Timestamp.doc.mjs +3 -3
  347. package/src/Timestamp/Timestamp.tsx +13 -8
  348. package/src/Timestamp/formatInstant.ts +49 -28
  349. package/src/Timestamp/tooltipEntries.ts +7 -3
  350. package/src/Token/Token.tsx +18 -1
  351. package/src/Token/TokenLink.tsx +7 -1
  352. package/src/Tokenizer/Tokenizer.doc.mjs +3 -3
  353. package/src/Tokenizer/Tokenizer.tsx +252 -115
  354. package/src/Tooltip/useTooltip.tsx +178 -80
  355. package/src/TreeList/TreeListItem.tsx +36 -2
  356. package/src/gestures/GestureHost.ts +21 -5
  357. package/src/gestures/PanGestureRecognizer.ts +19 -2
  358. package/src/gestures/README.md +14 -5
  359. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +6 -1
  360. package/src/gestures/nativeScroll.ts +12 -68
  361. package/src/gestures/wheelSegmenter.ts +243 -10
  362. package/src/hooks/StackItemScrollRegion.tsx +57 -0
  363. package/src/hooks/menuPressGesture.ts +244 -38
  364. package/src/hooks/menuPressValues.ts +18 -0
  365. package/src/hooks/useClickableContainer.doc.mjs +8 -2
  366. package/src/hooks/useClickableContainer.ts +72 -6
  367. package/src/hooks/useFocusTrap.ts +268 -151
  368. package/src/hooks/useMenuPress.ts +118 -11
  369. package/src/hooks/usePressFeedback.doc.mjs +3 -3
  370. package/src/hooks/usePressFeedback.ts +6 -6
  371. package/src/hooks/useScrollRegionProps.ts +76 -0
  372. package/src/i18n/InternationalizationContext.ts +6 -0
  373. package/src/i18n/InternationalizationProvider.doc.mjs +29 -1
  374. package/src/i18n/InternationalizationProvider.tsx +33 -3
  375. package/src/i18n/generated-english/hooks/useScrollRegionProps.generated.ts +13 -0
  376. package/src/i18n/generated-locales/en.generated.ts +1 -0
  377. package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
  378. package/src/i18n/index.ts +2 -0
  379. package/src/i18n/useCalendarSystem.ts +95 -0
  380. package/src/internal/spinner-rotor.ts +20 -0
  381. package/src/internal/touch.ts +34 -1
  382. package/src/motion/MEASUREMENTS.md +11 -0
  383. package/src/motion/carryAnimations.ts +389 -0
  384. package/src/motion/transientCopy.ts +45 -5
  385. package/src/motion/uikit/gestures.ts +22 -0
  386. package/src/motion/uikit/index.ts +1 -0
  387. package/src/motion/uikit/timing.ts +7 -5
  388. package/src/privateAttributes/menuPressLit.stylex.ts +104 -0
  389. package/src/privateAttributes/press.stylex.ts +243 -0
  390. package/src/theme/themeMotion.ts +3 -2
  391. package/src/theme/tokens.stylex.ts +1 -1
  392. package/src/utils/calendarSystem.ts +548 -0
  393. package/src/utils/flatTree.ts +15 -3
  394. package/src/utils/hitArea.ts +107 -5
  395. package/src/utils/inputStream.ts +47 -10
  396. package/src/utils/interactionOverlay.stylex.ts +19 -22
  397. package/src/utils/plainDate.ts +84 -19
  398. package/src/utils/pressFeedback.ts +46 -14
  399. package/src/utils/pressGesture.ts +35 -9
  400. package/src/utils/scrollView.ts +135 -0
  401. package/src/utils/selectionLock.ts +5 -10
  402. package/src/utils/touchHitArea.stylex.ts +41 -0
  403. package/src/utils/triggerFocus.ts +17 -4
@@ -13,6 +13,7 @@ import React from 'react';
13
13
  import * as stylex from '@stylexjs/stylex';
14
14
  import {Token} from '../Token';
15
15
  import {useLocale} from '../i18n/useLocale';
16
+ import {useCalendarSystem} from '../i18n/useCalendarSystem';
16
17
  import {fontWeightVars} from '../theme/tokens.stylex';
17
18
  import {formatFilterValue} from './formatFilterValue';
18
19
  import {resolveOperatorLabel} from './resolveOperatorLabel';
@@ -47,6 +48,7 @@ export function PowerSearchToken({
47
48
  const config = useInternalConfig(configProp);
48
49
  const t = useComponentTranslator();
49
50
  const locale = useLocale();
51
+ const calendar = useCalendarSystem(undefined, 'PowerSearchToken');
50
52
 
51
53
  const fieldLabel = field.label;
52
54
  const resolvedOperatorLabel = resolveOperatorLabel(operator, t);
@@ -68,6 +70,7 @@ export function PowerSearchToken({
68
70
  t,
69
71
  locale,
70
72
  timezoneID,
73
+ calendar,
71
74
  );
72
75
 
73
76
  const valueContent = valueStr ? (
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * @file formatFilterValue.ts
5
- * @input OperatorValue, FilterValue, config
5
+ * @input OperatorValue, FilterValue, config, locale, time zone, calendar
6
6
  * @output Formatted display string for a filter value
7
7
  * @position Pure utility; consumed by PowerSearch token rendering
8
8
  *
@@ -13,6 +13,10 @@
13
13
  import type {OperatorValue, FilterValue, EnumItem} from './types';
14
14
  import type {InternalConfig} from './useInternalConfig';
15
15
  import type {Locale, TranslatorFn} from '../i18n';
16
+ import {
17
+ formatCalendarInstant,
18
+ type CalendarSystem,
19
+ } from '../utils/calendarSystem';
16
20
  import {truncateCharacters} from '../utils/characters';
17
21
  import {registerEnglishFallback} from '../i18n/englishFallback';
18
22
  import english from '../i18n/generated-english/PowerSearch/formatFilterValue.generated';
@@ -67,25 +71,24 @@ export function formatDateAbsolute(
67
71
  locale: Locale,
68
72
  timezoneID: string | undefined,
69
73
  isDateOnly = true,
74
+ calendar: CalendarSystem = 'gregory',
70
75
  ): string {
71
76
  const options: Intl.DateTimeFormatOptions = isDateOnly
72
- ? {
73
- year: 'numeric',
74
- month: 'short',
75
- day: 'numeric',
76
- timeZone: resolveDateZone(timezoneID, true),
77
- calendar: 'gregory',
78
- }
77
+ ? {year: 'numeric', month: 'short', day: 'numeric'}
79
78
  : {
80
79
  year: 'numeric',
81
80
  month: 'short',
82
81
  day: 'numeric',
83
82
  hour: 'numeric',
84
83
  minute: '2-digit',
85
- timeZone: resolveDateZone(timezoneID, false),
86
- calendar: 'gregory',
87
84
  };
88
- return new Intl.DateTimeFormat(locale, options).format(unixSeconds * 1000);
85
+ return formatCalendarInstant(
86
+ calendar,
87
+ new Date(unixSeconds * 1000),
88
+ options,
89
+ locale,
90
+ resolveDateZone(timezoneID, isDateOnly),
91
+ );
89
92
  }
90
93
 
91
94
  function formatRelativeDate(value: string): string {
@@ -100,6 +103,11 @@ function formatDateRange(
100
103
  return t('@astryx.powersearch.valueEditor.dateRange');
101
104
  }
102
105
 
106
+ /**
107
+ * The display text of a filter value. A `date_absolute` value is written in
108
+ * `calendar`, which defaults to Gregorian; PowerSearch passes the calendar that
109
+ * its provider resolves.
110
+ */
103
111
  export function formatFilterValue(
104
112
  _config: InternalConfig,
105
113
  operatorValue: OperatorValue,
@@ -108,6 +116,7 @@ export function formatFilterValue(
108
116
  t: TranslatorFn,
109
117
  locale: Locale,
110
118
  timezoneID?: string,
119
+ calendar: CalendarSystem = 'gregory',
111
120
  ): string {
112
121
  switch (filterValue.type) {
113
122
  case 'empty':
@@ -210,6 +219,7 @@ export function formatFilterValue(
210
219
  timezoneID,
211
220
  operatorValue.type !== 'date_absolute' ||
212
221
  operatorValue.isDateOnly !== false,
222
+ calendar,
213
223
  ),
214
224
  maxLength,
215
225
  );
@@ -3,7 +3,8 @@
3
3
  /**
4
4
  * @fileoverview The text a PowerSearch filter token shows for a value.
5
5
  *
6
- * @input `OperatorValue`, `FilterValue`, a width budget, and the locale.
6
+ * @input `OperatorValue`, `FilterValue`, a width budget, the locale, and the
7
+ * calendar.
7
8
  * @output `formatTokenValueText` — one string, or null for an empty value.
8
9
  * @position Shared by the token itself (`PowerSearch.tsx`) and the sheet
9
10
  * presentation's Filters rows and removal announcement
@@ -13,6 +14,7 @@
13
14
 
14
15
  import type {OperatorValue, FilterValue, EnumItem} from './types';
15
16
  import type {Locale, TranslatorFn} from '../i18n';
17
+ import type {CalendarSystem} from '../utils/calendarSystem';
16
18
  import {truncateCharacters} from '../utils/characters';
17
19
  import {formatDateAbsolute, formatSharedNumber} from './formatFilterValue';
18
20
  import {registerEnglishFallback} from '../i18n/englishFallback';
@@ -103,6 +105,8 @@ function formatListText(
103
105
  * the sheet presentation's Filters rows and its removal announcement — can
104
106
  * use the same words and the same zone handling. `maxLength` is the token's
105
107
  * width budget; pass `Number.MAX_SAFE_INTEGER` for the untruncated text.
108
+ * A `date_absolute` value is written in `calendar`, the calendar that the
109
+ * provider resolves.
106
110
  * With `t`, the list, date-range and nested summaries resolve through the
107
111
  * catalog (`@astryx.powersearch.valueEditor.*`); without it they are the
108
112
  * token's own literals. With `formatList` (the provider's list formatter),
@@ -114,6 +118,7 @@ export function formatTokenValueText(
114
118
  filterValue: FilterValue,
115
119
  maxLength: number,
116
120
  locale: Locale,
121
+ calendar: CalendarSystem,
117
122
  t?: TranslatorFn,
118
123
  formatList?: ListFormatFn,
119
124
  timezoneID?: string,
@@ -182,6 +187,7 @@ export function formatTokenValueText(
182
187
  timezoneID,
183
188
  operatorValue.type !== 'date_absolute' ||
184
189
  operatorValue.isDateOnly !== false,
190
+ calendar,
185
191
  ),
186
192
  maxLength,
187
193
  );
@@ -8,7 +8,7 @@ export const docs = {
8
8
  displayName: 'Radio List Item',
9
9
  isHiddenFromOverview: true,
10
10
  description:
11
- 'Individual radio item with label, description, and content slots.',
11
+ 'Individual radio item with label, description, and content slots. Under a coarse pointer an enabled option’s target grows toward 44 px through an invisible hit area. It never shrinks below the row and stops at half the gap to a closer neighbour; the row keeps its size. That hit area is the row’s `::after`, so decorate a row with a child.',
12
12
  // RadioListItem requires RadioList context; wrap it so the preview doesn't throw.
13
13
  playground: {
14
14
  defaults: {value: 'option-1', label: 'Option'},
@@ -97,7 +97,8 @@ export const docsZh = {
97
97
  name: 'RadioListItem',
98
98
  isHiddenFromOverview: true,
99
99
  displayName: 'Radio List Item',
100
- description: '单个单选选项,包含标签、描述和内容插槽。',
100
+ description:
101
+ '单个单选选项,包含标签、描述和内容插槽。在粗指针(触摸)下,已启用选项的目标通过不可见的点击区域向 44 px 扩展。它不小于所在行,与相邻控件较近时止于间距的一半;行尺寸不变。该点击区域是行的 `::after`,因此请用子元素装饰行。',
101
102
  props: [
102
103
  {
103
104
  name: 'label',
@@ -18,7 +18,7 @@
18
18
  * - /packages/cli/assets/templates/blocks/components/RadioList/ (showcase blocks)
19
19
  */
20
20
 
21
- import React, {use, useId, useRef, type ReactNode} from 'react';
21
+ import React, {use, useCallback, useId, useRef, type ReactNode} from 'react';
22
22
  import * as stylex from '@stylexjs/stylex';
23
23
  import type {StyleXStyles} from '@stylexjs/stylex';
24
24
  import type {BaseProps} from '../BaseProps';
@@ -32,9 +32,13 @@ import {rtlStyles} from '../utils/rtlStyles';
32
32
  import {indicatorScope} from '../Indicator/indicator.markers.stylex';
33
33
  import {useIndicatorFocusRing} from '../hooks/useIndicatorFocusRing';
34
34
  import {usePressFeedback} from '../hooks/usePressFeedback';
35
+ import {useMergedRefs} from '../hooks/useMergedRefs';
36
+ import {watchHitArea, writeHitInsets} from '../utils/hitArea';
37
+ import {touchHitAreaStyles} from '../utils/touchHitArea.stylex';
35
38
  import {useIndicator} from '../Indicator/useIndicator';
36
39
  import {Item, type ItemAlign} from '../Item/Item';
37
40
  import {themeProps} from '../utils/themeProps';
41
+ import {rowPressLayerStyles} from '../privateAttributes/press.stylex';
38
42
 
39
43
  // The touch press's paint, declared by the shared overlay styles on the
40
44
  // element the controller writes to (`pressedAlpha`) at the press's strength,
@@ -147,11 +151,6 @@ const rowStyles = stylex.create({
147
151
  // Suppress Item's interactive hover/press background so the resting and
148
152
  // hovered row look identical by default (a theme can restyle either).
149
153
  backgroundColor: 'transparent',
150
- // ...and the touch press's overlay image, for the same reason: under a
151
- // finger only the radio circle paints, as under a mouse. The press's
152
- // strength still lands on the row, which is where the layer over the
153
- // circle reads it.
154
- backgroundImage: 'none',
155
154
  },
156
155
  // The mouse press (a finger's lives on `data-astryx-press`): the row is
157
156
  // pressed, but not through a nested link or button, which keep their own
@@ -309,6 +308,21 @@ export function RadioListItem({
309
308
  // `<label htmlFor>` would double-fire under delegation.
310
309
  const radioRef = useRef<HTMLInputElement>(null);
311
310
 
311
+ // Under a coarse pointer an option's row grows toward 44 px through an
312
+ // invisible band, and the row keeps its size. A tap on the band lands on
313
+ // the row, which hands it to the radio. The watch lives as long as the row
314
+ // is attached: the ref's cleanup stops it.
315
+ const watchRowHitArea = useCallback(
316
+ (row: HTMLDivElement | null) => {
317
+ if (row == null || isDisabled) {
318
+ return;
319
+ }
320
+ return watchHitArea(row, insets => writeHitInsets(row, insets));
321
+ },
322
+ [isDisabled],
323
+ );
324
+ const mergedRowRef = useMergedRefs(ref, watchRowHitArea);
325
+
312
326
  const radioCircle = (
313
327
  <div
314
328
  {...stylex.props(
@@ -391,7 +405,7 @@ export function RadioListItem({
391
405
 
392
406
  return (
393
407
  <Item
394
- ref={ref}
408
+ ref={mergedRowRef}
395
409
  {...(isDisabled ? undefined : pressable)}
396
410
  startContent={mediaContent}
397
411
  // Delegate row-surface clicks (label text, description, and the empty
@@ -414,12 +428,16 @@ export function RadioListItem({
414
428
  // the painting row element (Item), the same element that carries the
415
429
  // theme target, so a theme's hover styling stays in step with the tint.
416
430
  !isDisabled && indicatorScope,
431
+ // The row keeps no press paint of its own: the circle paints, and
432
+ // reads the strength whose release the row keeps.
433
+ rowPressLayerStyles.cellWithoutLayer,
417
434
  // Restore the bare default look: zero Item's padding/radius and its
418
435
  // interactive hover/press background. Applied after Item's own base
419
436
  // styles so it wins within the base layer; a `radio-list-item` theme
420
437
  // still overrides it from the higher `astryx-theme` layer.
421
438
  rowStyles.root,
422
439
  !isDisabled && rowStyles.press,
440
+ !isDisabled && touchHitAreaStyles.layer,
423
441
  xstyle,
424
442
  ] as StyleXStyles
425
443
  }
@@ -44,6 +44,7 @@ import type {StyleXStyles} from '@stylexjs/stylex';
44
44
  import {colorVars, durationVars, easeVars} from '../theme/tokens.stylex';
45
45
  import type {Elevation, SizeValue, SpacingStep} from '../utils/types';
46
46
  import {isImeKeyEvent, mergeProps} from '../utils';
47
+ import {chainEventHandlers} from '../utils/chainEventHandlers';
47
48
  import {Card} from '../Card/Card';
48
49
  import type {CardVariant} from '../Card/Card';
49
50
  import {useClickableContainer} from '../hooks/useClickableContainer';
@@ -398,6 +399,7 @@ export function SelectableCard({
398
399
  onChange,
399
400
  onClick: onClickProp,
400
401
  onMouseUp: onMouseUpProp,
402
+ onPointerDown: onPointerDownProp,
401
403
  isDisabled = false,
402
404
  children,
403
405
  padding,
@@ -458,7 +460,7 @@ export function SelectableCard({
458
460
  [isDisabled, requestChange],
459
461
  );
460
462
 
461
- const {onClick, onMouseUp} = useClickableContainer({
463
+ const {onClick, onMouseUp, onPointerDown} = useClickableContainer({
462
464
  containerRef,
463
465
  interactiveRef,
464
466
  onClick: handleClick,
@@ -530,6 +532,11 @@ export function SelectableCard({
530
532
  }
531
533
  onClick={!isDisabled ? composedOnClick : undefined}
532
534
  onMouseUp={!isDisabled ? composedOnMouseUp : undefined}
535
+ onPointerDown={
536
+ !isDisabled
537
+ ? chainEventHandlers(onPointerDown, onPointerDownProp)
538
+ : onPointerDownProp
539
+ }
533
540
  {...props}
534
541
  onClickCapture={
535
542
  !isDisabled && handleLayerClickCapture
@@ -18,7 +18,7 @@
18
18
 
19
19
  import {useId, type ReactNode} from 'react';
20
20
  import * as stylex from '@stylexjs/stylex';
21
- import {colorVars, durationVars, spacingVars} from '../theme/tokens.stylex';
21
+ import {colorVars, spacingVars} from '../theme/tokens.stylex';
22
22
  import type {BaseProps} from '../BaseProps';
23
23
  import {Text} from '../Text/Text';
24
24
  import {mergeProps} from '../utils/mergeProps';
@@ -28,6 +28,7 @@ import {
28
28
  useComponentTranslator,
29
29
  } from '../i18n/useComponentTranslator';
30
30
  import english from '../i18n/generated-english/Spinner/Spinner.generated';
31
+ import {spinnerRotorStyles, syncRotationPhase} from './spinnerRotor';
31
32
 
32
33
  registerEnglishFallback(english);
33
34
 
@@ -36,13 +37,13 @@ registerEnglishFallback(english);
36
37
  // =============================================================================
37
38
 
38
39
  /**
39
- * Default fraction of the ring the moving arc covers. The canvas ring this
40
- * replaces swept 135deg, not the 270deg its constant's comment claimed.
40
+ * The fraction of the ring the arc covers: SPINNER_ARC_FRACTION. StyleX reads
41
+ * only a literal in this file, and a test pins the two together.
41
42
  *
42
43
  * Themeable via `--spinner-arc-fraction`, declared alongside the other public
43
44
  * vars in `sizeStyles` below. Only the inline `strokeDasharray` attribute
44
- * (the pre-stylesheet render — see its own comment) still reads this
45
- * constant directly; the CSS side composes the dash from the live var.
45
+ * (the pre-stylesheet render) still reads the constant directly; the CSS side
46
+ * composes the dash from the live var.
46
47
  */
47
48
  const ARC_FRACTION = 0.375;
48
49
 
@@ -152,75 +153,6 @@ function registerSpinnerVars(): void {
152
153
  // elide.
153
154
  registerSpinnerVars();
154
155
 
155
- /**
156
- * Pin every ring's rotation to the document timeline's origin instead of its
157
- * own start time, so spinners mounted seconds apart stay in phase.
158
- *
159
- * Setting `startTime` is exact where arithmetic on a clock read is not. A
160
- * negative `animation-delay` computed at mount is only as good as the gap
161
- * between the clock read and the frame the animation starts in. At 10x CPU
162
- * throttling that gap measured 116 degrees of drift.
163
- *
164
- * Rotors are collected and pinned in one frame. `getAnimations()` resolves
165
- * style, and `startTime` dirties it again. So pinning them one at a time makes
166
- * each mount re-force what the previous one invalidated: 53 style recalcs for
167
- * 38 spinners, against 19 batched.
168
- */
169
- const pendingRotors = new Set<HTMLSpanElement>();
170
- let flushScheduled = false;
171
-
172
- function pinRotorsToTimelineOrigin(): void {
173
- flushScheduled = false;
174
- const animations: Animation[] = [];
175
- for (const rotor of pendingRotors) {
176
- animations.push(...rotor.getAnimations());
177
- }
178
- pendingRotors.clear();
179
- for (const animation of animations) {
180
- animation.startTime = 0;
181
- }
182
- }
183
-
184
- /**
185
- * Ref callback for the rotor: the one place a mounted ring touches the DOM.
186
- *
187
- * It reads nothing back — the geometry is resolved by the cascade, not in JS.
188
- */
189
- function syncRotationPhase(
190
- rotor: HTMLSpanElement | null,
191
- ): (() => void) | undefined {
192
- if (rotor == null) {
193
- return undefined;
194
- }
195
- // jsdom implements no Web Animations, and this runs in every consumer's
196
- // component tests.
197
- if (typeof rotor.getAnimations !== 'function') {
198
- return undefined;
199
- }
200
- pendingRotors.add(rotor);
201
- if (!flushScheduled) {
202
- flushScheduled = true;
203
- requestAnimationFrame(pinRotorsToTimelineOrigin);
204
- }
205
- return () => {
206
- pendingRotors.delete(rotor);
207
- };
208
- }
209
-
210
- // =============================================================================
211
- // Animation
212
- // =============================================================================
213
-
214
- /**
215
- * A `transform` animation runs on the compositor, so the ring keeps turning
216
- * while a script runs. A `stroke-dashoffset` animation would repaint the arc
217
- * on the main thread every frame, so it would stop while a script runs.
218
- */
219
- const rotation = stylex.keyframes({
220
- '0%': {transform: 'rotate(0deg)'},
221
- '100%': {transform: 'rotate(360deg)'},
222
- });
223
-
224
156
  // =============================================================================
225
157
  // Styles
226
158
  // =============================================================================
@@ -299,20 +231,6 @@ const styles = stylex.create({
299
231
  gridArea: '1 / 1',
300
232
  width: `var(${BOX_SIZE})`,
301
233
  height: `var(${BOX_SIZE})`,
302
- // Mobile Safari keeps a re-timed animation off the main thread only on an
303
- // element with its own layer. So the rotor declares one before the start
304
- // time is set (see `pinRotorsToTimelineOrigin`).
305
- willChange: 'transform',
306
- // Reduced motion slows the turn but never stops it: a still ring reads as
307
- // broken. The status role and its name still say that work is busy.
308
- animationDuration: {
309
- // The loop reads the duration before a theme's tempo scales it.
310
- default: `var(--_astryx-pre-tempo-duration-slow-min, ${durationVars['--duration-slow-min']})`,
311
- '@media (prefers-reduced-motion: reduce)': '3s',
312
- },
313
- animationIterationCount: 'infinite',
314
- animationName: rotation,
315
- animationTimingFunction: 'linear',
316
234
  },
317
235
  circle: {
318
236
  fill: 'none',
@@ -594,7 +512,7 @@ export function Spinner({
594
512
  <span
595
513
  ref={syncRotationPhase}
596
514
  aria-hidden="true"
597
- {...stylex.props(styles.rotor)}>
515
+ {...stylex.props(styles.rotor, spinnerRotorStyles.turn)}>
598
516
  <svg
599
517
  width={frameSize}
600
518
  height={frameSize}
@@ -12,8 +12,4 @@
12
12
  */
13
13
 
14
14
  export {Spinner} from './Spinner';
15
- export type {
16
- SpinnerProps,
17
- SpinnerSize,
18
- SpinnerShade,
19
- } from './Spinner';
15
+ export type {SpinnerProps, SpinnerSize, SpinnerShade} from './Spinner';
@@ -0,0 +1,113 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file spinnerRotor.ts
7
+ * @input StyleX keyframes and the slow-min duration token
8
+ * @output The one turning arc: SPINNER_ARC_FRACTION, spinnerRotorStyles.turn
9
+ * and syncRotationPhase
10
+ * @position Spinner's rotor, shared by Spinner and Lab's indeterminate
11
+ * CircularProgress (through `@astryxdesign/core/internal/spinner-rotor`), so
12
+ * both rings turn the same way on the compositor.
13
+ */
14
+
15
+ import * as stylex from '@stylexjs/stylex';
16
+ import {durationVars} from '../theme/tokens.stylex';
17
+
18
+ /** The fraction of the ring the arc covers: 135°. */
19
+ export const SPINNER_ARC_FRACTION = 0.375;
20
+
21
+ /**
22
+ * Full turns in one iteration of the rotation.
23
+ *
24
+ * Chromium wakes the main thread at each iteration boundary while the
25
+ * document listens for `animationiteration`. React listens for it on every
26
+ * root. One iteration holds 100 turns, so the wake comes once in 100 turns.
27
+ * Each turn is 360 degrees, so the ring looks the same.
28
+ */
29
+ const TURNS_PER_ITERATION = 100;
30
+
31
+ /**
32
+ * A `transform` animation runs on the compositor, so the ring keeps turning
33
+ * while a script runs. A `stroke-dashoffset` animation would repaint the arc
34
+ * on the main thread every frame, so it would stop while a script runs.
35
+ */
36
+ const rotation = stylex.keyframes({
37
+ '0%': {transform: 'rotate(0deg)'},
38
+ '100%': {transform: `rotate(${360 * TURNS_PER_ITERATION}deg)`},
39
+ });
40
+
41
+ export const spinnerRotorStyles = stylex.create({
42
+ turn: {
43
+ // Mobile Safari keeps a re-timed animation off the main thread only on an
44
+ // element with its own layer. So the rotor declares one before the start
45
+ // time is set (see `pinRotorsToTimelineOrigin`).
46
+ willChange: 'transform',
47
+ // Reduced motion slows the turn but never stops it: a still ring reads as
48
+ // broken. The ring's role and name still say that work is busy.
49
+ animationDuration: {
50
+ // The loop reads the duration before a theme's tempo scales it.
51
+ default: `calc(var(--_astryx-pre-tempo-duration-slow-min, ${durationVars['--duration-slow-min']}) * ${TURNS_PER_ITERATION})`,
52
+ '@media (prefers-reduced-motion: reduce)': `calc(3s * ${TURNS_PER_ITERATION})`,
53
+ },
54
+ animationIterationCount: 'infinite',
55
+ animationName: rotation,
56
+ animationTimingFunction: 'linear',
57
+ },
58
+ });
59
+
60
+ /**
61
+ * Pin every ring's rotation to the document timeline's origin instead of its
62
+ * own start time, so spinners mounted seconds apart stay in phase.
63
+ *
64
+ * Setting `startTime` is exact where arithmetic on a clock read is not. A
65
+ * negative `animation-delay` computed at mount is only as good as the gap
66
+ * between the clock read and the frame the animation starts in. At 10x CPU
67
+ * throttling that gap measured 116 degrees of drift.
68
+ *
69
+ * Rotors are collected and pinned in one frame. `getAnimations()` resolves
70
+ * style, and `startTime` dirties it again. So pinning them one at a time makes
71
+ * each mount re-force what the previous one invalidated: 53 style recalcs for
72
+ * 38 spinners, against 19 batched.
73
+ */
74
+ const pendingRotors = new Set<Element>();
75
+ let flushScheduled = false;
76
+
77
+ function pinRotorsToTimelineOrigin(): void {
78
+ flushScheduled = false;
79
+ const animations: Animation[] = [];
80
+ for (const rotor of pendingRotors) {
81
+ animations.push(...rotor.getAnimations());
82
+ }
83
+ pendingRotors.clear();
84
+ for (const animation of animations) {
85
+ animation.startTime = 0;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Ref callback for a rotor: the one place a mounted ring touches the DOM.
91
+ *
92
+ * It reads nothing back — the geometry is resolved by the cascade, not in JS.
93
+ */
94
+ export function syncRotationPhase(
95
+ rotor: Element | null,
96
+ ): (() => void) | undefined {
97
+ if (rotor == null) {
98
+ return undefined;
99
+ }
100
+ // jsdom implements no Web Animations, and this runs in every consumer's
101
+ // component tests.
102
+ if (typeof rotor.getAnimations !== 'function') {
103
+ return undefined;
104
+ }
105
+ pendingRotors.add(rotor);
106
+ if (!flushScheduled) {
107
+ flushScheduled = true;
108
+ requestAnimationFrame(pinRotorsToTimelineOrigin);
109
+ }
110
+ return () => {
111
+ pendingRotors.delete(rotor);
112
+ };
113
+ }
@@ -524,7 +524,7 @@ export const docs = {
524
524
  name: 'isScrollable',
525
525
  type: 'boolean',
526
526
  description:
527
- 'Enables scrollable overflow (overflow: auto). StackItem already applies the flex min-height/min-width reset, so <StackItem size="fill" isScrollable> is a complete scroll region. Matches isScrollable on LayoutContent and LayoutPanel.',
527
+ 'Enables scrollable overflow (overflow: auto). StackItem already applies the flex min-height/min-width reset, so <StackItem size="fill" isScrollable> is a complete scroll region. Matches isScrollable on LayoutContent and LayoutPanel. While its content overflows, a div or span item is a group named by aria-label, or by a translated "Content"; another element keeps its own role.',
528
528
  default: 'false',
529
529
  },
530
530
  {
@@ -2,9 +2,11 @@
2
2
 
3
3
  /**
4
4
  * @file StackItem.tsx
5
- * @input Uses React, ElementType, stackItem utility
5
+ * @input Uses React, ElementType, stackItem utility, StackItemScrollRegion
6
6
  * @output Exports StackItem polymorphic component and StackItemProps
7
- * @position Layout/Stack component; uses stackItem.stylex.ts
7
+ * @position Layout/Stack component; uses stackItem.stylex.ts. A scrollable
8
+ * `div` or `span` item renders through StackItemScrollRegion, which names
9
+ * it while it scrolls.
8
10
  *
9
11
  * SYNC: When modified, update these files to stay in sync:
10
12
  * - /packages/core/src/Stack/Stack.doc.mjs
@@ -21,6 +23,7 @@ import {
21
23
  } from './stackItem.stylex';
22
24
  import {mergeProps} from '../utils/mergeProps';
23
25
  import {themeProps} from '../utils/themeProps';
26
+ import {StackItemScrollRegion} from '../hooks/StackItemScrollRegion';
24
27
 
25
28
  const overflowStyles = stylex.create({
26
29
  scrollable: {
@@ -53,7 +56,9 @@ export interface StackItemProps extends BaseProps<HTMLElement> {
53
56
  * reset, so `<StackItem size="fill" isScrollable>` is a complete scroll
54
57
  * region — it grows to fill the stack and scrolls its own overflow with
55
58
  * no extra style plumbing. Matches `isScrollable` on `LayoutContent`
56
- * and `LayoutPanel`.
59
+ * and `LayoutPanel`. While its content overflows, a `div` or `span` item
60
+ * is a group named by the caller's `aria-label`, or by a translated
61
+ * "Content". Another element keeps its own role.
57
62
  * @default false
58
63
  */
59
64
  isScrollable?: boolean;
@@ -102,18 +107,27 @@ export function StackItem({
102
107
  xstyle,
103
108
  );
104
109
 
110
+ const elementProps = {
111
+ ...mergeProps(
112
+ themeProps('stack-item', {size}),
113
+ stylexProps,
114
+ className,
115
+ style,
116
+ ),
117
+ ...props,
118
+ };
119
+
120
+ if (isScrollable && (element === 'div' || element === 'span')) {
121
+ return createElement(
122
+ StackItemScrollRegion,
123
+ {as: element, ref, elementProps},
124
+ children,
125
+ );
126
+ }
127
+
105
128
  return createElement(
106
129
  element,
107
- {
108
- ref: ref as Ref<Element>,
109
- ...mergeProps(
110
- themeProps('stack-item', {size}),
111
- stylexProps,
112
- className,
113
- style,
114
- ),
115
- ...props,
116
- },
130
+ {ref: ref as Ref<Element>, ...elementProps},
117
131
  children,
118
132
  );
119
133
  }
@@ -62,9 +62,9 @@ import {
62
62
  import english from '../i18n/generated-english/Switch/Switch.generated';
63
63
 
64
64
  registerEnglishFallback(english);
65
+ import {reducedMotionFadeConsts} from '../motion/reducedMotion.stylex';
65
66
  import {
66
67
  interactionOverlayStyles,
67
- pressConsts,
68
68
  pressVars,
69
69
  } from '../utils/interactionOverlay.stylex';
70
70
  import {pressHoldsDragProps} from '../privateAttributes/pressHoldsDrag.stylex';
@@ -93,14 +93,15 @@ const PRESS_FADING = '[data-astryx-press="fading"]';
93
93
 
94
94
  /**
95
95
  * The shared press release, for the press layers the switch fades on the
96
- * compositor. The clock scales by the theme's tempo, and the curve is the
97
- * controller's own. Switch.test.tsx compares the two.
96
+ * compositor: the theme's exit motion tokens, no longer than the Reduce
97
+ * Motion fade under Reduce Motion. Switch.test.tsx compares them with
98
+ * the overlay's.
98
99
  * SYNC: utils/interactionOverlay.stylex.ts PRESS_RELEASE_DURATION,
99
100
  * PRESS_RELEASE_DURATION_REDUCED and RELEASE_EASE.
100
101
  */
101
- const PRESS_RELEASE_DURATION = `calc(${pressConsts.releaseMs}ms * min(var(--_astryx-motion-tempo, 1), 1.5))`;
102
- const PRESS_RELEASE_DURATION_REDUCED = `min(${pressConsts.releaseMs}ms, calc(${pressConsts.releaseMs}ms * var(--_astryx-motion-tempo, 1)))`;
103
- const PRESS_RELEASE_EASE = 'cubic-bezier(0, 0, 0.2, 1)';
102
+ const PRESS_RELEASE_DURATION = durationVars['--duration-exit'];
103
+ const PRESS_RELEASE_DURATION_REDUCED = `min(${durationVars['--duration-exit']}, ${reducedMotionFadeConsts.duration})`;
104
+ const PRESS_RELEASE_EASE = easeVars['--ease-exit'];
104
105
 
105
106
  // The measured hit insets (utils/hitArea), 0 until measured.
106
107
  const HIT_TOP = 'var(--astryx-hit-inset-top, 0px)';
@@ -168,7 +168,7 @@ const sortStyles = stylex.create({
168
168
  fontSize: 10,
169
169
  // eslint-disable-next-line @astryx/no-hardcoded-styles -- no token for lineHeight:1 (tight badge)
170
170
  lineHeight: '1',
171
- color: colorVars['--color-accent'],
171
+ color: colorVars['--color-text-accent'],
172
172
  },
173
173
  });
174
174