@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
@@ -34,14 +34,21 @@ export function isFocusableTrigger(element) {
34
34
  * too, so a plain wrapper ignores its children's focus.
35
35
  */
36
36
  export function isFocusForTrigger(event, mode) {
37
+ return isFocusOnTrigger(event.target, event.currentTarget, mode);
38
+ }
39
+
40
+ /**
41
+ * `isFocusForTrigger` for focus already on `target`, inside `trigger`: the
42
+ * same rule, read when a trigger attaches with focus inside it.
43
+ */
44
+ export function isFocusOnTrigger(target, trigger, mode) {
37
45
  if (mode === 'always') {
38
46
  return true;
39
47
  }
40
48
  if (mode === 'never') {
41
49
  return false;
42
50
  }
43
- const trigger = event.currentTarget;
44
- return trigger != null && (event.target === trigger || isFocusableTrigger(trigger));
51
+ return trigger != null && (target === trigger || isFocusableTrigger(trigger));
45
52
  }
46
53
 
47
54
  /** Remembers whether a trigger's current focus counted when it arrived. */
package/locales/en.json CHANGED
@@ -1323,6 +1323,10 @@
1323
1323
  "defaultMessage": "Loading",
1324
1324
  "description": "Screen-reader-only default name for a standalone Spinner's role=\"status\" element, used when the consumer supplies neither `aria-label` nor a visible string label; also announced once when a busy input or selector starts loading, in place of its decorative Spinner. Present-progressive form. Kept separate from `button.loading` and `typeahead.loading` — translations may diverge."
1325
1325
  },
1326
+ "@astryx.scrollRegion.label": {
1327
+ "defaultMessage": "Content",
1328
+ "description": "Accessible name of a scrolling content region (LayoutContent, LayoutPanel or StackItem) when the caller gives none. A screen reader announces it with the region's group role while the region scrolls."
1329
+ },
1326
1330
  "@astryx.chatComposerDrawer.expand": {
1327
1331
  "defaultMessage": "Expand {label}",
1328
1332
  "description": "Screen-reader-only label on the ChatComposerDrawer toggle when the drawer is collapsed. `{label}` is the drawer's visible name — example: `Expand Attachments`. Pairs with `collapse`."
@@ -992,6 +992,9 @@
992
992
  "@astryx.spinner.loading": {
993
993
  "defaultMessage": "⟦Łóàðíñĝ⟧"
994
994
  },
995
+ "@astryx.scrollRegion.label": {
996
+ "defaultMessage": "⟦Çóñţéñţ⟧"
997
+ },
995
998
  "@astryx.chatComposerDrawer.expand": {
996
999
  "defaultMessage": "⟦Éẋþàñð {label}⟧"
997
1000
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.7.8",
3
+ "version": "0.7.9-canary.07992bb",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -838,6 +838,11 @@
838
838
  "source": "./src/internal/selectable-card.ts",
839
839
  "types": "./dist/internal/selectable-card.d.ts",
840
840
  "default": "./dist/internal/selectable-card.js"
841
+ },
842
+ "./internal/spinner-rotor": {
843
+ "source": "./src/internal/spinner-rotor.ts",
844
+ "types": "./dist/internal/spinner-rotor.d.ts",
845
+ "default": "./dist/internal/spinner-rotor.js"
841
846
  }
842
847
  },
843
848
  "files": [
@@ -870,6 +875,9 @@
870
875
  "react-dom": ">=19.0.0"
871
876
  },
872
877
  "devDependencies": {
878
+ "@astryxdesign/a11y-spec": "0.7.9-canary.07992bb",
879
+ "@astryxdesign/cli": "0.7.9-canary.07992bb",
880
+ "@astryxdesign/interaction-harness": "0.7.9-canary.07992bb",
873
881
  "@babel/cli": "^7.29.7",
874
882
  "@babel/core": "^7.29.7",
875
883
  "@babel/preset-react": "^7.29.7",
@@ -881,9 +889,7 @@
881
889
  "@testing-library/react": "^16.3.2",
882
890
  "@types/babel__core": "^7.20.5",
883
891
  "tailwindcss": "^4.3.2",
884
- "@astryxdesign/cli": "0.7.8",
885
- "@astryxdesign/interaction-harness": "0.0.0",
886
- "@astryxdesign/a11y-spec": "0.0.0"
892
+ "temporal-polyfill": "^1.0.6"
887
893
  },
888
894
  "dependencies": {
889
895
  "intl-messageformat": "^11.2.9"
@@ -4,20 +4,23 @@
4
4
 
5
5
  /**
6
6
  * @file observeMainOverflow.ts
7
- * @input AppShell's main region under `height="fill"`: its own scroll
8
- * container, holding its content as direct children with no wrapper
7
+ * @input A scroll container that holds its content as direct children with
8
+ * no wrapper: AppShell's main region under `height="fill"`, or a scrolling
9
+ * LayoutContent, LayoutPanel or StackItem
9
10
  * @output observeMainOverflow(main, onChange): reports whether main's content
10
11
  * overflows it on either axis, once at the start and on every change, and
11
12
  * returns the function that stops observing
12
- * @position Internal to AppShell and never exported. AppShell makes main a
13
+ * @position Internal and never exported. AppShell makes main a
13
14
  * keyboard stop only while it overflows (`spec:AST-025` FR12,
14
- * `component:AppShell` FR4 and PR1). Main has no inner content box, so its
15
- * content grows inside it without resizing it: its direct children's sizes
16
- * are watched instead.
15
+ * `component:AppShell` FR4 and PR1). useScrollRegionProps names a
16
+ * scrolling region only while it overflows. Main has no inner content
17
+ * box, so its content grows inside it without resizing it: its direct
18
+ * children's sizes are watched instead.
17
19
  *
18
20
  * SYNC: When modified, update:
19
21
  * - /packages/core/src/AppShell/AppShell.tsx
20
22
  * - /packages/core/src/AppShell/observeMainOverflow.test.ts
23
+ * - /packages/core/src/hooks/useScrollRegionProps.ts
21
24
  */
22
25
 
23
26
  import {
@@ -81,6 +81,7 @@ import {LayerDepthProvider} from '../Layer/LayerDepthContext';
81
81
  import {ModalAnnounceProvider} from '../hooks/ModalAnnounceProvider';
82
82
  import {useModalOutlet} from '../Layer/modalOutlet';
83
83
  import {useLayerDismissal} from '../Layer/useLayerDismissal';
84
+ import {useFocusRegionContainment} from '../hooks/useFocusTrap';
84
85
  import {focusForLastInput} from '../utils/focusReturn';
85
86
  import {useInteractionModalityTracking} from '../utils/interactionModality';
86
87
  import {
@@ -444,6 +445,10 @@ function StandaloneBottomSheet({
444
445
  false,
445
446
  );
446
447
  const isEntrySettled = settledOpen && isOpen;
448
+ // Only a modal sheet contains Tab; a non-modal one leaves the page's focus
449
+ // order alone. A closing sheet stops containing as soon as it starts to
450
+ // leave.
451
+ useFocusRegionContainment(dialogRef, isOpen && hasScrim);
447
452
  // Presentation outlives the controlled prop: a closing sheet stays mounted
448
453
  // and in its native host through its exit, and only its motion completion
449
454
  // (or a replace finishing) clears it. The open flip is derived during
@@ -26,6 +26,7 @@ import {
26
26
  useLayoutEffect,
27
27
  useMemo,
28
28
  useRef,
29
+ useSyncExternalStore,
29
30
  type ReactNode,
30
31
  } from 'react';
31
32
  import * as stylex from '@stylexjs/stylex';
@@ -60,6 +61,7 @@ import {
60
61
  paddingStyles,
61
62
  spacingStepToToken,
62
63
  } from '../Layout/padding.stylex';
64
+ import {subscribeInput} from '../utils/inputStream';
63
65
  import type {SpacingStep} from '../utils/types';
64
66
  import {
65
67
  isValidSnapPoint,
@@ -82,8 +84,20 @@ const HEIGHT_BUDGETS = {
82
84
  export type BottomSheetHeight = keyof typeof HEIGHT_BUDGETS;
83
85
  export type {BottomSheetSnapPoint};
84
86
 
85
- // SYNC: must match OVERSCROLL_MAX in useSheetGestures.ts.
86
- const OVERSCROLL_PADDING = 48;
87
+ // The reserve: how far the sheet's surface reaches below the screen, so a
88
+ // pull past the top never shows the page under it.
89
+ // SYNC: OVERSCROLL_MAX in useSheetGestures.ts must not exceed MIN_RESERVE.
90
+ const MIN_RESERVE = 48;
91
+ // A finger can pull past the top by at most the viewport's height. The rubber
92
+ // band over a sheet as tall as the viewport then shows 35.5% of it.
93
+ const RESERVE_VIEWPORT_SHARE = 0.4;
94
+
95
+ function sheetReserve(viewportHeight: number): number {
96
+ return Math.max(
97
+ MIN_RESERVE,
98
+ Math.ceil(viewportHeight * RESERVE_VIEWPORT_SHARE),
99
+ );
100
+ }
87
101
  const MOBILE_KEYBOARD_BOTTOM_CLEARANCE = 48;
88
102
  const TRANSITION_BACKSTOP_BUFFER_MS = 50;
89
103
  // The floating handle bar's height. The bar is out of flow, so this is not
@@ -92,6 +106,22 @@ const TRANSITION_BACKSTOP_BUFFER_MS = 50;
92
106
  // padding already provides.
93
107
  const HANDLE_BAR_HEIGHT = spacingVars['--spacing-6'];
94
108
 
109
+ const BLOCK_SIZE_PROPERTIES = [
110
+ 'height',
111
+ 'minHeight',
112
+ 'maxHeight',
113
+ 'blockSize',
114
+ 'minBlockSize',
115
+ 'maxBlockSize',
116
+ ] as const;
117
+
118
+ /** Whether StyleX styles hold any style: they can nest in arrays. */
119
+ function hasStyles(xstyle: unknown): boolean {
120
+ return Array.isArray(xstyle)
121
+ ? xstyle.some(hasStyles)
122
+ : xstyle != null && typeof xstyle === 'object';
123
+ }
124
+
95
125
  // Measure the viewport that the height budgets use. They are `dvh`, which
96
126
  // the virtual keyboard does not shrink, so `visualViewport` would differ by
97
127
  // the keyboard's height. `useMobileKeyboard` handles the keyboard and does
@@ -100,6 +130,14 @@ function layoutViewportHeight(): number {
100
130
  return typeof window === 'undefined' ? 0 : window.innerHeight;
101
131
  }
102
132
 
133
+ function subscribeToResize(onChange: () => void): () => void {
134
+ return subscribeInput(window, 'resize', onChange);
135
+ }
136
+
137
+ function subscribeToNothing(): () => void {
138
+ return () => {};
139
+ }
140
+
103
141
  /**
104
142
  * A stable identity for a set of snap points. Type-tagged, so the fraction
105
143
  * `0.5` and the (invalid) string `'0.5'` cannot collide on one key.
@@ -159,8 +197,8 @@ const styles = stylex.create({
159
197
  boxShadow: shadowVars['--shadow-high'],
160
198
  outline: 'none',
161
199
  overflow: 'hidden',
162
- paddingBlockEnd: `calc(env(safe-area-inset-bottom, 0px) + ${OVERSCROLL_PADDING}px)`,
163
- marginBlockEnd: `${-OVERSCROLL_PADDING}px`,
200
+ paddingBlockEnd: `calc(env(safe-area-inset-bottom, 0px) + var(--_sheet-reserve, ${MIN_RESERVE}px))`,
201
+ marginBlockEnd: `calc(-1 * var(--_sheet-reserve, ${MIN_RESERVE}px))`,
164
202
  // Under reduced motion no transform runs. On a coarse primary pointer the
165
203
  // entrance is an opacity cross-fade of at most 200 ms. A fine pointer gets
166
204
  // the change at once (the 0.01s collapse).
@@ -340,11 +378,11 @@ const styles = stylex.create({
340
378
  },
341
379
  }),
342
380
  budget: {
343
- height: `calc(var(--_sheet-budget) + ${OVERSCROLL_PADDING}px)`,
381
+ height: `calc(var(--_sheet-budget) + var(--_sheet-reserve, ${MIN_RESERVE}px))`,
344
382
  },
345
383
  hugHeight: {
346
384
  height: 'fit-content',
347
- maxHeight: `calc(${HEIGHT_BUDGETS.hug} + ${OVERSCROLL_PADDING}px)`,
385
+ maxHeight: `calc(${HEIGHT_BUDGETS.hug} + var(--_sheet-reserve, ${MIN_RESERVE}px))`,
348
386
  },
349
387
  });
350
388
 
@@ -649,6 +687,24 @@ export function BottomSheetPanel({
649
687
 
650
688
  // A caller's height owns the element's height: the drag never writes it.
651
689
  const consumerHeight = style?.height;
690
+ // StyleX styles and a class name can size the sheet too. Their rules are
691
+ // only class names here, so any of them counts as a size.
692
+ const isSizedByCaller =
693
+ BLOCK_SIZE_PROPERTIES.some(property => style?.[property] != null) ||
694
+ hasStyles(xstyle) ||
695
+ (className != null && className !== '');
696
+ // Only a finger stretches the sheet past MIN_RESERVE, so a device with no
697
+ // touch pointer keeps the smallest reserve. So does a sheet the caller
698
+ // sizes, which nothing extends by the reserve; the stretch stops at it.
699
+ const hasTouchPointer = useMediaQuery('(any-pointer: coarse)');
700
+ const tracksViewport = isPresented && hasTouchPointer && !isSizedByCaller;
701
+ const viewportHeight = useSyncExternalStore(
702
+ tracksViewport ? subscribeToResize : subscribeToNothing,
703
+ () => (tracksViewport ? layoutViewportHeight() : 0),
704
+ () => 0,
705
+ );
706
+ const reserve =
707
+ viewportHeight > 0 ? sheetReserve(viewportHeight) : MIN_RESERVE;
652
708
  const {
653
709
  contentProps,
654
710
  handleProps,
@@ -668,7 +724,7 @@ export function BottomSheetPanel({
668
724
  isOpen: isInteractive,
669
725
  isOpenRequested: isOpenRequested ?? isInteractive,
670
726
  canDismiss: isSwipeDismissAllowed,
671
- offscreenBlockEndInset: OVERSCROLL_PADDING,
727
+ offscreenBlockEndInset: reserve,
672
728
  onDismiss,
673
729
  snapHeights,
674
730
  onTravelStart: () => {
@@ -931,6 +987,7 @@ export function BottomSheetPanel({
931
987
  className,
932
988
  {
933
989
  ['--_sheet-budget' as string]: budget,
990
+ ['--_sheet-reserve' as string]: `${reserve}px`,
934
991
  ...(isInteractive
935
992
  ? gestureStyle
936
993
  : isRetained || isClosing
@@ -64,6 +64,8 @@ import {
64
64
  type UIEvent as ReactUIEvent,
65
65
  } from 'react';
66
66
 
67
+ import {rubberBand} from '../motion/rubberBand';
68
+ import {scroll} from '../motion/uikit/scroll';
67
69
  import {
68
70
  computeDetentOffsets,
69
71
  peekOffsetFor,
@@ -112,12 +114,19 @@ function isSyntheticTouch(event: ReactPointerEvent): boolean {
112
114
  return (event as SyntheticTouchPointer).syntheticTouch === true;
113
115
  }
114
116
 
115
- // Rubber-band factor for dragging up past fully-open, capped at OVERSCROLL_MAX
116
- // (the sheet reserves that much bottom padding for the lift to reveal).
117
+ type StylusTouch = Touch & {touchType?: string};
118
+
119
+ // An Apple Pencil raises touch events too, marked `stylus`. Its drag is a
120
+ // pen's, as on the grabber, where WebKit reports it as a pen.
121
+ function touchPointerType(touch: Touch): string {
122
+ return (touch as StylusTouch).touchType === 'stylus' ? 'pen' : 'touch';
123
+ }
124
+
125
+ // A mouse's stretch past fully open: a resisted fraction of the drag, capped
126
+ // at OVERSCROLL_MAX.
117
127
  const OVERSCROLL_RESISTANCE = 0.35;
118
- // SYNC: must match OVERSCROLL_PADDING in BottomSheetPanel.tsx (the reserved
119
- // bottom padding the lift reveals). Kept as a local const rather than a shared
120
- // import so it can be used inside stylex.create there.
128
+ // SYNC: must not exceed MIN_RESERVE in BottomSheetPanel.tsx, the smallest
129
+ // reserve the sheet keeps below the screen.
121
130
  const OVERSCROLL_MAX = 48;
122
131
  // Travel past the point where a scrolling gesture ran out of content before it
123
132
  // hands the sheet the rest of the pull. Small enough to feel continuous with
@@ -308,6 +317,36 @@ function chainAtBottom(chain: ReadonlyArray<HTMLElement>): boolean {
308
317
  return chain.every(elementAtBottom);
309
318
  }
310
319
 
320
+ function chainScrolls(chain: ReadonlyArray<HTMLElement>): boolean {
321
+ return chain.some(element => element.scrollHeight - element.clientHeight > 1);
322
+ }
323
+
324
+ /**
325
+ * iOS rubber-bands a scroller past its end even under `overscroll-behavior:
326
+ * contain`. While the content sits at its end mid-pull, `none` holds it still,
327
+ * so only the sheet moves. Returns the undo.
328
+ */
329
+ function holdAtEnd(chain: ReadonlyArray<HTMLElement>): () => void {
330
+ const previous = chain.map(element => element.style.overscrollBehaviorY);
331
+ for (const element of chain) {
332
+ element.style.overscrollBehaviorY = 'none';
333
+ }
334
+ return () => {
335
+ chain.forEach((element, index) => {
336
+ element.style.overscrollBehaviorY = previous[index];
337
+ });
338
+ };
339
+ }
340
+
341
+ function releaseContentHold(
342
+ armed: {releaseContentHold: (() => void) | null} | null,
343
+ ): void {
344
+ if (armed?.releaseContentHold != null) {
345
+ armed.releaseContentHold();
346
+ armed.releaseContentHold = null;
347
+ }
348
+ }
349
+
311
350
  function visibleHeightForOffset(
312
351
  sheetHeight: number,
313
352
  offset: number,
@@ -690,6 +729,8 @@ export function useSheetGestures({
690
729
  samples: {t: number; y: number}[];
691
730
  velocity: number;
692
731
  height: number;
732
+ /** The surface kept below the screen when the drag began. */
733
+ reserve: number;
693
734
  baseOffset: number;
694
735
  baseLayoutOffset: number;
695
736
  renderedOffset: number;
@@ -1171,7 +1212,7 @@ export function useSheetGestures({
1171
1212
  const baseLayoutOffset = settledLayoutOffsetRef.current;
1172
1213
  dragStateRef.current = {
1173
1214
  syntheticTouch,
1174
- pointerType: syntheticTouch ? 'touch' : event.pointerType,
1215
+ pointerType: event.pointerType,
1175
1216
  pointerId: event.pointerId,
1176
1217
  startCoord: start,
1177
1218
  lastCoord: event.clientY,
@@ -1179,6 +1220,7 @@ export function useSheetGestures({
1179
1220
  samples: [{t: event.timeStamp, y: event.clientY}],
1180
1221
  velocity: 0,
1181
1222
  height: sheetHeight,
1223
+ reserve: offscreenBlockEndInsetRef.current,
1182
1224
  baseOffset: settledOffset,
1183
1225
  baseLayoutOffset,
1184
1226
  renderedOffset: settledOffset,
@@ -1279,8 +1321,19 @@ export function useSheetGestures({
1279
1321
  const maxDetentOffset = offsets[offsets.length - 1];
1280
1322
  let next: number;
1281
1323
  if (raw < 0) {
1282
- // Up past fully-open: damped + capped rubber-band; springs back on release.
1283
- next = Math.max(-OVERSCROLL_MAX, raw * OVERSCROLL_RESISTANCE);
1324
+ // A touch device's reserve covers the whole stretch. The clamp binds
1325
+ // only where the reserve is short.
1326
+ next =
1327
+ state.pointerType === 'touch'
1328
+ ? Math.max(
1329
+ -state.reserve,
1330
+ rubberBand(
1331
+ raw,
1332
+ state.height - state.reserve,
1333
+ scroll.rubberBand.coefficient,
1334
+ ),
1335
+ )
1336
+ : Math.max(-OVERSCROLL_MAX, raw * OVERSCROLL_RESISTANCE);
1284
1337
  } else if (raw > maxDetentOffset) {
1285
1338
  // In the dismiss zone: no magnet, so it doesn't fight a drag-to-close.
1286
1339
  next = raw;
@@ -1482,6 +1535,8 @@ export function useSheetGestures({
1482
1535
  // armed at `touchstart`. That drag never cancelled the native scroll, so
1483
1536
  // it has to yield again if the finger comes back down.
1484
1537
  promotedAtContentEnd: boolean;
1538
+ // Null while no hold is set.
1539
+ releaseContentHold: (() => void) | null;
1485
1540
  } | null>(null);
1486
1541
  const previousTouchHandlersRef = useRef<{
1487
1542
  start: (e: TouchEvent) => void;
@@ -1495,6 +1550,7 @@ export function useSheetGestures({
1495
1550
  // the gesture; the sheet returns to its detent.
1496
1551
  const yieldToPinch = useCallback(
1497
1552
  (target?: HTMLElement) => {
1553
+ releaseContentHold(touchDragRef.current);
1498
1554
  touchDragRef.current = null;
1499
1555
  armedBodyRef.current = null;
1500
1556
  cancelDrag(target);
@@ -1522,6 +1578,7 @@ export function useSheetGestures({
1522
1578
  ({
1523
1579
  syntheticTouch: true,
1524
1580
  pointerId: touch.identifier,
1581
+ pointerType: touchPointerType(touch),
1525
1582
  clientY: touch.clientY,
1526
1583
  timeStamp: Date.now(),
1527
1584
  currentTarget: target,
@@ -1542,21 +1599,21 @@ export function useSheetGestures({
1542
1599
  // scroll, but it is tracked all the same: the scroller can run out of
1543
1600
  // content while the finger is still down (see onTouchMove). A touch
1544
1601
  // inside a layer this sheet hosts is that layer's, never tracked here.
1602
+ releaseContentHold(touchDragRef.current);
1545
1603
  if (!touch || isInsideNestedLayer(event.target, body)) {
1546
1604
  touchDragRef.current = null;
1547
1605
  return;
1548
1606
  }
1549
1607
  const scrollers = scrollChainUnder(event.target, body);
1550
1608
  const top = chainAtTop(scrollers);
1551
- // The bottom edge hands off so the sheet can EXPAND, so it is only a
1552
- // handoff when a taller detent exists. Already at the tallest, an
1553
- // upward pull has nowhere to travel: promoting it would trade the
1554
- // user's scroll for a rubber-band the release throws straight back, and
1555
- // — because promotion preventDefault()s the rest of the gesture — would
1556
- // strand the scroller for as long as the finger stays down, so reversing
1557
- // downward to scroll back would collapse the sheet instead. Leave the
1558
- // gesture with the content.
1559
- const bottom = chainAtBottom(scrollers) && activeOffsetRef.current > 0;
1609
+ // Claiming a pull up from its first move blocks the content's scroll for
1610
+ // the whole touch. So it is claimed here only below the tallest detent,
1611
+ // or over content that cannot scroll. At the tallest detent, a list at
1612
+ // its end keeps its touch. Its pull reaches the sheet through the
1613
+ // content-end hand-off below, and a reversal hands it back.
1614
+ const bottom =
1615
+ chainAtBottom(scrollers) &&
1616
+ (activeOffsetRef.current > 0 || !chainScrolls(scrollers));
1560
1617
  touchDragRef.current = {
1561
1618
  id: touch.identifier,
1562
1619
  startY: touch.clientY,
@@ -1565,6 +1622,7 @@ export function useSheetGestures({
1565
1622
  bottom,
1566
1623
  contentEndY: null,
1567
1624
  promotedAtContentEnd: false,
1625
+ releaseContentHold: null,
1568
1626
  };
1569
1627
  };
1570
1628
 
@@ -1594,6 +1652,7 @@ export function useSheetGestures({
1594
1652
  // at once; a later pull past the end promotes again.
1595
1653
  armed.contentEndY = null;
1596
1654
  armed.promotedAtContentEnd = false;
1655
+ releaseContentHold(armed);
1597
1656
  cancelDragRef.current(scroller);
1598
1657
  return;
1599
1658
  }
@@ -1627,6 +1686,7 @@ export function useSheetGestures({
1627
1686
  chainAtBottom(armed.scrollers);
1628
1687
  if (pullDownAtTop || pullUpAtBottom) {
1629
1688
  event.preventDefault();
1689
+ releaseContentHold(armed);
1630
1690
  touchDragRef.current = null;
1631
1691
  beginDragRef.current(
1632
1692
  asPointer(t, scroller),
@@ -1646,13 +1706,14 @@ export function useSheetGestures({
1646
1706
  // cannot see this, and re-arming for a preventDefault() promotion would
1647
1707
  // be useless anyway: once the browser has committed the gesture to
1648
1708
  // scrolling, every remaining touchmove is non-cancelable. Nothing needs
1649
- // cancelling either — the scroller is clamped at its maximum, so further
1650
- // upward travel scrolls nothing. Anchor at the point where the content
1709
+ // cancelling either: the hold clamps the scroller at its maximum, so
1710
+ // further upward travel scrolls nothing. Anchor at the point where the content
1651
1711
  // ran out and give the sheet everything past it, so the pull continues
1652
1712
  // into the sheet with no jump and no lost scrolling.
1653
- if (activeOffsetRef.current > 0 && chainAtBottom(armed.scrollers)) {
1713
+ if (chainAtBottom(armed.scrollers)) {
1654
1714
  if (armed.contentEndY == null) {
1655
1715
  armed.contentEndY = t.clientY;
1716
+ armed.releaseContentHold ??= holdAtEnd(armed.scrollers);
1656
1717
  } else if (armed.contentEndY - t.clientY >= CONTENT_END_HANDOFF_SLOP) {
1657
1718
  armed.promotedAtContentEnd = true;
1658
1719
  beginDragRef.current(
@@ -1664,10 +1725,12 @@ export function useSheetGestures({
1664
1725
  }
1665
1726
  } else {
1666
1727
  armed.contentEndY = null;
1728
+ releaseContentHold(armed);
1667
1729
  }
1668
1730
  };
1669
1731
 
1670
1732
  const onTouchEnd = (event: TouchEvent) => {
1733
+ releaseContentHold(touchDragRef.current);
1671
1734
  touchDragRef.current = null;
1672
1735
  const pointerId = dragStateRef.current?.pointerId;
1673
1736
  if (pointerId == null) {
@@ -39,6 +39,11 @@ export const docs = {
39
39
  description:
40
40
  'Show two months side by side when the user frequently selects dates that span a month boundary.',
41
41
  },
42
+ {
43
+ guidance: true,
44
+ description:
45
+ 'For a custom month view, use useCalendarNavigation and useCalendarDays from @astryxdesign/core/Calendar. To show another calendar system, pass the same calendar to both and feed visibleCalendarMonths into useCalendarDays. The hooks default to gregory and never read the InternationalizationProvider calendar.',
46
+ },
42
47
  {
43
48
  guidance: false,
44
49
  description:
@@ -169,13 +174,15 @@ export const docs = {
169
174
  {
170
175
  name: 'hasWeekNumbers',
171
176
  type: 'boolean',
172
- description: 'Show ISO week numbers.',
177
+ description:
178
+ 'Show ISO 8601 week numbers. They are ISO 8601 week numbers in every calendar system.',
173
179
  default: 'false',
174
180
  },
175
181
  {
176
182
  name: 'hasVariableRowCount',
177
183
  type: 'boolean',
178
- description: 'Variable vs fixed 6-row grid.',
184
+ description:
185
+ 'Give each month only the rows it needs. The default fixed 6-row grid keeps the same height from month to month; a variable row count trades that fixed height for a more compact grid.',
179
186
  default: 'false',
180
187
  },
181
188
  {
@@ -185,6 +192,48 @@ export const docs = {
185
192
  'First day of week. Accepts a number (0=Sunday) or a three-letter day name (e.g. "mon").',
186
193
  default: '0',
187
194
  },
195
+ {
196
+ name: 'calendar',
197
+ type: "'gregory' | 'iso8601' | 'buddhist' | 'roc' | 'japanese' | 'persian' | 'indian' | 'hebrew' | 'chinese' | 'dangi' | 'coptic' | 'ethiopic' | 'ethioaa' | 'islamic-civil' | 'islamic-tbla' | 'islamic-umalqura'",
198
+ description:
199
+ "Calendar system the grid shows, such as 'persian', 'hebrew', or 'islamic-umalqura'. It sets the months, their names, and month and year navigation. value, min, max, focusDate, and every callback stay Gregorian ISO dates. Calendars other than gregory, iso8601, buddhist, roc, and japanese need Temporal; where the runtime lacks it, load a polyfill such as temporal-polyfill/full/global on the server and in the browser, or the grid shows Gregorian months. Month starts in the Chinese and Dangi calendars come from the runtime's Temporal, and some polyfills start a few months one day apart from native Temporal; when server and browser markup must match, use one implementation on both sides, such as installImplementation() from temporal-polyfill/full/shim, which replaces a native Temporal.",
200
+ default: 'the InternationalizationProvider calendar, else gregory',
201
+ },
202
+ ],
203
+ examples: [
204
+ {
205
+ label: 'A custom month view in the Persian calendar',
206
+ code: `import {Button} from '@astryxdesign/core/Button';
207
+ import {
208
+ useCalendarDays,
209
+ useCalendarNavigation,
210
+ } from '@astryxdesign/core/Calendar';
211
+
212
+ function PersianMonth() {
213
+ const {monthYearLabel, visibleCalendarMonths, navigateMonth} =
214
+ useCalendarNavigation({initialValue: '2026-10-30', calendar: 'persian'});
215
+ // visibleCalendarMonths[0] is {year: 1405, month: 8}: Aban 1405.
216
+ const {weeks} = useCalendarDays({
217
+ ...visibleCalendarMonths[0],
218
+ calendar: 'persian',
219
+ });
220
+ return (
221
+ <section aria-label={monthYearLabel}>
222
+ <Button label="Previous month" onClick={() => navigateMonth(-1)} />
223
+ <Button label="Next month" onClick={() => navigateMonth(1)} />
224
+ {weeks.map(week => (
225
+ <div key={week[0].iso}>
226
+ {week.map(day => (
227
+ <span key={day.iso} data-date={day.iso}>
228
+ {day.isOutside ? '' : day.dayNumber}
229
+ </span>
230
+ ))}
231
+ </div>
232
+ ))}
233
+ </section>
234
+ );
235
+ }`,
236
+ },
188
237
  ],
189
238
  theming: {
190
239
  targets: [
@@ -230,6 +279,11 @@ export const docsZh = {
230
279
  description:
231
280
  'Show two months side by side when the user frequently selects dates that span a month boundary.',
232
281
  },
282
+ {
283
+ guidance: true,
284
+ description:
285
+ 'For a custom month view, use useCalendarNavigation and useCalendarDays from @astryxdesign/core/Calendar. To show another calendar system, pass the same calendar to both and feed visibleCalendarMonths into useCalendarDays. The hooks default to gregory and never read the InternationalizationProvider calendar.',
286
+ },
233
287
  {
234
288
  guidance: false,
235
289
  description:
@@ -311,13 +365,14 @@ export const docsZh = {
311
365
  {
312
366
  name: 'hasWeekNumbers',
313
367
  type: 'boolean',
314
- description: '显示 ISO 周数。',
368
+ description: '显示 ISO 8601 周数。在任何历法中都是 ISO 8601 周数。',
315
369
  default: 'false',
316
370
  },
317
371
  {
318
372
  name: 'hasVariableRowCount',
319
373
  type: 'boolean',
320
- description: '可变行数与固定 6 行网格。',
374
+ description:
375
+ '每个月只显示所需的行数。默认的固定 6 行网格使每个月高度相同;可变行数以固定高度换取更紧凑的网格。',
321
376
  default: 'false',
322
377
  },
323
378
  {
@@ -327,6 +382,13 @@ export const docsZh = {
327
382
  '每周起始日。可为数字(0=周日)或三字母星期缩写(如 "mon")。',
328
383
  default: '0',
329
384
  },
385
+ {
386
+ name: 'calendar',
387
+ type: "'gregory' | 'iso8601' | 'buddhist' | 'roc' | 'japanese' | 'persian' | 'indian' | 'hebrew' | 'chinese' | 'dangi' | 'coptic' | 'ethiopic' | 'ethioaa' | 'islamic-civil' | 'islamic-tbla' | 'islamic-umalqura'",
388
+ description:
389
+ "网格显示的历法,如 'persian'、'hebrew' 或 'islamic-umalqura'。它决定月份、月份名称以及按月和按年的导航。value、min、max、focusDate 和所有回调仍为公历 ISO 日期。gregory、iso8601、buddhist、roc 和 japanese 以外的历法需要 Temporal;运行环境没有 Temporal 时,请在服务端和浏览器中加载 polyfill(如 temporal-polyfill/full/global),否则网格显示公历月份。农历(chinese)和檀纪历(dangi)的月初取自运行环境的 Temporal,部分 polyfill 与原生 Temporal 有少数月份的月初相差一天;服务端渲染且需要两端标记一致时,请在两端使用同一种实现,例如 temporal-polyfill/full/shim 的 installImplementation(),它会替换原生 Temporal。",
390
+ default: 'InternationalizationProvider 的历法,否则为 gregory',
391
+ },
330
392
  ],
331
393
  theming: {
332
394
  targets: [
@@ -374,6 +436,11 @@ export const docsDense = {
374
436
  description:
375
437
  'Show two months side by side when user frequently selects dates spanning a month boundary.',
376
438
  },
439
+ {
440
+ guidance: true,
441
+ description:
442
+ 'Custom month view: useCalendarNavigation + useCalendarDays; pass the same calendar to both, feed visibleCalendarMonths into useCalendarDays. Default gregory; never read the provider calendar.',
443
+ },
377
444
  {
378
445
  guidance: false,
379
446
  description:
@@ -400,8 +467,11 @@ export const docsDense = {
400
467
  onFocusDateChange: 'navigation callback',
401
468
  handleRef: 'imperative navigation handle',
402
469
  hasOutsideDays: 'show days from adjacent months',
403
- hasWeekNumbers: 'show ISO week numbers',
404
- hasVariableRowCount: 'variable vs fixed 6-row grid',
470
+ hasWeekNumbers: 'show ISO 8601 week numbers, in every calendar system',
471
+ hasVariableRowCount:
472
+ 'only the rows each month needs; trades the fixed 6-row height for a compact grid',
405
473
  weekStartsOn: 'first day of week (0=Sunday, or name e.g. "mon")',
474
+ calendar:
475
+ 'calendar system shown (default: provider calendar, else gregory); values stay Gregorian ISO; non-Gregorian-aligned calendars need Temporal',
406
476
  },
407
477
  };