@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
@@ -4,7 +4,7 @@
4
4
  * @file menuPressGesture.ts
5
5
  * @input A menu press gesture and one pointer event, both as plain data
6
6
  * @output The pure `menuPressStep` state machine, its state, event and effect
7
- * types, and the settle constant
7
+ * types, the settle constant, and the finger's handoff distance
8
8
  * @position Pure module under `useMenuPress`. It has no DOM and no React, so
9
9
  * a unit test can check each transition without a browser.
10
10
  *
@@ -13,12 +13,24 @@
13
13
  * - /packages/core/src/hooks/useMenuPress.ts
14
14
  */
15
15
 
16
- /** How long the opening press must last before its release may act. */
16
+ import {MENU_PRESS_HANDOFF_PX as UIKIT_MENU_PRESS_HANDOFF_PX} from './menuPressValues';
17
+
18
+ /**
19
+ * How long the opening press must last before its release may act, unless a
20
+ * finger's handoff applies.
21
+ */
17
22
  export const MENU_PRESS_SETTLE_MS = 300;
18
23
 
24
+ /**
25
+ * How far a finger that opened the menu travels, from the first sample the
26
+ * menu sees, before it starts picking (UIMenu's scrub handoff).
27
+ */
28
+ export const MENU_PRESS_HANDOFF_PX: number = UIKIT_MENU_PRESS_HANDOFF_PX;
29
+
19
30
  export type MenuPressPointerType = 'mouse' | 'touch' | 'pen';
20
31
 
21
- export type MenuPressGesture<T> =
32
+ /** @internal The machine's own shape, with the finger's handoff; not public contract. */
33
+ export type MenuPressState<T> =
22
34
  | {phase: 'idle'}
23
35
  | {
24
36
  phase: 'triggerPress';
@@ -29,6 +41,12 @@ export type MenuPressGesture<T> =
29
41
  phase: 'open';
30
42
  pointerType: MenuPressPointerType;
31
43
  openedAt: number;
44
+ /**
45
+ * A finger's handoff: the first position the menu saw after it opened,
46
+ * which the scrub distance is counted from. Unset until that sample.
47
+ * @internal Not public contract.
48
+ */
49
+ handoffFrom?: {x: number; y: number};
32
50
  }
33
51
  | {
34
52
  phase: 'tracking';
@@ -39,7 +57,8 @@ export type MenuPressGesture<T> =
39
57
  row: T | null;
40
58
  };
41
59
 
42
- export type MenuPressEvent<T> =
60
+ /** @internal The machine's own shape, with the finger's handoff; not public contract. */
61
+ export type MenuPressInput<T> =
43
62
  | {
44
63
  type: 'down';
45
64
  target: 'trigger' | 'menu';
@@ -50,18 +69,38 @@ export type MenuPressEvent<T> =
50
69
  }
51
70
  /** The menu opened while the trigger press is still held. */
52
71
  | {type: 'opened'; time: number}
53
- | {type: 'move'; row: T | null; isInMenu: boolean; time: number}
72
+ | {
73
+ type: 'move';
74
+ row: T | null;
75
+ isInMenu: boolean;
76
+ time: number;
77
+ /**
78
+ * Viewport position, for a finger's handoff distance.
79
+ * @internal Not public contract.
80
+ */
81
+ x?: number;
82
+ /** @internal See `x`. */
83
+ y?: number;
84
+ }
54
85
  | {
55
86
  type: 'up';
56
87
  row: T | null;
57
88
  isInMenu: boolean;
58
89
  isOnTrigger: boolean;
59
90
  time: number;
91
+ /**
92
+ * Viewport position, for a finger's handoff distance.
93
+ * @internal Not public contract.
94
+ */
95
+ x?: number;
96
+ /** @internal See `x`. */
97
+ y?: number;
60
98
  }
61
99
  /** The browser took the gesture: pointercancel, a second pointer, a scroll. */
62
100
  | {type: 'cancel'};
63
101
 
64
- export type MenuPressEffect<T> =
102
+ /** @internal The machine's own shape, with the finger's handoff; not public contract. */
103
+ export type MenuPressOutcome<T> =
65
104
  | {type: 'none'}
66
105
  /** A mouse pressed the trigger: open the menu now, under the held pointer. */
67
106
  | {type: 'open'}
@@ -71,27 +110,52 @@ export type MenuPressEffect<T> =
71
110
  /**
72
111
  * The gesture ended without a row acting. `stray` says whether the browser
73
112
  * will still report a `click` for this gesture that must not act; `dismiss`
74
- * says the menu should close (a mouse released outside it).
113
+ * says the menu should close: a mouse released outside it.
75
114
  */
76
115
  | {type: 'settle'; stray: boolean; dismiss: boolean};
77
116
 
78
- export interface MenuPressStep<T> {
79
- gesture: MenuPressGesture<T>;
80
- effect: MenuPressEffect<T>;
117
+ /** @internal The machine's own shape, with the finger's handoff; not public contract. */
118
+ export interface MenuPressTransition<T> {
119
+ gesture: MenuPressState<T>;
120
+ effect: MenuPressOutcome<T>;
81
121
  }
82
122
 
83
- export const IDLE_MENU_PRESS: MenuPressGesture<never> = {phase: 'idle'};
123
+ export const IDLE_MENU_PRESS: MenuPressState<never> = {phase: 'idle'};
84
124
 
85
- const NONE: MenuPressEffect<never> = {type: 'none'};
125
+ const NONE: MenuPressOutcome<never> = {type: 'none'};
86
126
 
87
- function idle<T>(effect: MenuPressEffect<T>): MenuPressStep<T> {
127
+ function idle<T>(effect: MenuPressOutcome<T>): MenuPressTransition<T> {
88
128
  return {gesture: {phase: 'idle'}, effect};
89
129
  }
90
130
 
91
- function highlightEffect<T>(row: T | null): MenuPressEffect<T> {
131
+ function highlightEffect<T>(row: T | null): MenuPressOutcome<T> {
92
132
  return row == null ? {type: 'clear'} : {type: 'highlight', row};
93
133
  }
94
134
 
135
+ /** A finger or a pen: UIKit's touch rules apply. */
136
+ function isFinger(pointerType: MenuPressPointerType): boolean {
137
+ return pointerType !== 'mouse';
138
+ }
139
+
140
+ function hasPosition(event: {
141
+ x?: number;
142
+ y?: number;
143
+ }): event is {x: number; y: number} {
144
+ return event.x != null && event.y != null;
145
+ }
146
+
147
+ /**
148
+ * Whether a finger has travelled the handoff distance from `from`. The
149
+ * distance is a straight line, as UIMenu measures it: a diagonal 7 pt each
150
+ * way does not hand off, 9 pt each way does.
151
+ */
152
+ function hasPassedHandoff(
153
+ from: {x: number; y: number},
154
+ to: {x: number; y: number},
155
+ ): boolean {
156
+ return Math.hypot(to.x - from.x, to.y - from.y) >= MENU_PRESS_HANDOFF_PX;
157
+ }
158
+
95
159
  /**
96
160
  * Resolve a release while the highlight is tracking the pointer: over an
97
161
  * enabled row it acts, over the menu's chrome it acts on nothing, outside the
@@ -100,8 +164,8 @@ function highlightEffect<T>(row: T | null): MenuPressEffect<T> {
100
164
  */
101
165
  function release<T>(
102
166
  pointerType: MenuPressPointerType,
103
- event: Extract<MenuPressEvent<T>, {type: 'up'}>,
104
- ): MenuPressStep<T> {
167
+ event: Extract<MenuPressInput<T>, {type: 'up'}>,
168
+ ): MenuPressTransition<T> {
105
169
  if (event.row != null) {
106
170
  return idle({type: 'act', row: event.row});
107
171
  }
@@ -119,10 +183,15 @@ function release<T>(
119
183
  * - `idle`: no gesture is live.
120
184
  * - `triggerPress`: a finger rests on the trigger, and the menu is not open
121
185
  * yet. A mouse press on the trigger skips this state and opens at once.
122
- * - `open`: the menu opened under a held pointer, and the pointer has not
123
- * entered it yet.
186
+ * - `open`: the menu opened under a held pointer, and the pointer does not
187
+ * pick yet.
124
188
  * - `tracking`: the pointer is held, and the highlight follows it.
125
189
  *
190
+ * A finger or a pen that opened the menu picks only after it travels
191
+ * `MENU_PRESS_HANDOFF_PX` from the first position the menu sees. UIMenu
192
+ * hands off the same way. Until then nothing lights, and its lift acts on
193
+ * nothing. A host that reports no positions keeps the settle rule.
194
+ *
126
195
  * The settle rule: the release of the opening gesture acts only after the
127
196
  * pointer entered the menu or the press lasted `MENU_PRESS_SETTLE_MS`. Else a
128
197
  * menu that opens under the pointer would pick a row that nobody chose.
@@ -130,11 +199,14 @@ function release<T>(
130
199
  * Rows are opaque (`T`). The hook feeds in the enabled row under the
131
200
  * pointer, or `null` over a divider, a heading, a disabled row, or outside
132
201
  * the menu.
202
+ *
203
+ * @internal The machine's own shape, with the finger's handoff; not public
204
+ * contract.
133
205
  */
134
- export function menuPressStep<T>(
135
- gesture: MenuPressGesture<T>,
136
- event: MenuPressEvent<T>,
137
- ): MenuPressStep<T> {
206
+ export function menuPressTransition<T>(
207
+ gesture: MenuPressState<T>,
208
+ event: MenuPressInput<T>,
209
+ ): MenuPressTransition<T> {
138
210
  switch (gesture.phase) {
139
211
  case 'idle':
140
212
  return stepIdle(gesture, event);
@@ -148,9 +220,9 @@ export function menuPressStep<T>(
148
220
  }
149
221
 
150
222
  function stepIdle<T>(
151
- gesture: Extract<MenuPressGesture<T>, {phase: 'idle'}>,
152
- event: MenuPressEvent<T>,
153
- ): MenuPressStep<T> {
223
+ gesture: Extract<MenuPressState<T>, {phase: 'idle'}>,
224
+ event: MenuPressInput<T>,
225
+ ): MenuPressTransition<T> {
154
226
  if (event.type !== 'down') {
155
227
  return {gesture, effect: NONE};
156
228
  }
@@ -186,9 +258,9 @@ function stepIdle<T>(
186
258
  }
187
259
 
188
260
  function stepTriggerPress<T>(
189
- gesture: Extract<MenuPressGesture<T>, {phase: 'triggerPress'}>,
190
- event: MenuPressEvent<T>,
191
- ): MenuPressStep<T> {
261
+ gesture: Extract<MenuPressState<T>, {phase: 'triggerPress'}>,
262
+ event: MenuPressInput<T>,
263
+ ): MenuPressTransition<T> {
192
264
  switch (event.type) {
193
265
  case 'opened':
194
266
  return {
@@ -216,11 +288,34 @@ function stepTriggerPress<T>(
216
288
  }
217
289
 
218
290
  function stepOpen<T>(
219
- gesture: Extract<MenuPressGesture<T>, {phase: 'open'}>,
220
- event: MenuPressEvent<T>,
221
- ): MenuPressStep<T> {
291
+ gesture: Extract<MenuPressState<T>, {phase: 'open'}>,
292
+ event: MenuPressInput<T>,
293
+ ): MenuPressTransition<T> {
222
294
  switch (event.type) {
223
- case 'move':
295
+ case 'move': {
296
+ if (isFinger(gesture.pointerType) && hasPosition(event)) {
297
+ // UIMenu counts from the first sample after the open, not from the
298
+ // open point.
299
+ const from = gesture.handoffFrom;
300
+ if (from == null) {
301
+ return {
302
+ gesture: {...gesture, handoffFrom: {x: event.x, y: event.y}},
303
+ effect: NONE,
304
+ };
305
+ }
306
+ if (!hasPassedHandoff(from, event)) {
307
+ return {gesture, effect: NONE};
308
+ }
309
+ return {
310
+ gesture: {
311
+ phase: 'tracking',
312
+ pointerType: gesture.pointerType,
313
+ origin: 'trigger',
314
+ row: event.row,
315
+ },
316
+ effect: highlightEffect(event.row),
317
+ };
318
+ }
224
319
  if (!event.isInMenu) {
225
320
  return {gesture, effect: NONE};
226
321
  }
@@ -233,7 +328,14 @@ function stepOpen<T>(
233
328
  },
234
329
  effect: highlightEffect(event.row),
235
330
  };
331
+ }
236
332
  case 'up': {
333
+ if (isFinger(gesture.pointerType) && hasPosition(event)) {
334
+ // The finger never travelled the handoff distance, so the row under it
335
+ // appeared under a press nobody moved. Nothing acts, the menu stays,
336
+ // and the trigger's click for this press is spent.
337
+ return idle({type: 'settle', stray: true, dismiss: false});
338
+ }
237
339
  const hasSettled = event.time - gesture.openedAt >= MENU_PRESS_SETTLE_MS;
238
340
  if (!hasSettled) {
239
341
  // The pointer never entered the menu and the press was short: the
@@ -245,7 +347,7 @@ function stepOpen<T>(
245
347
  }
246
348
  case 'cancel':
247
349
  // As above: the gesture ends, so its document listeners come off. The
248
- // menu stays open — a cancelled pointer did not ask to close it.
350
+ // menu stays open: a cancelled pointer did not ask to close it.
249
351
  return idle({type: 'settle', stray: false, dismiss: false});
250
352
  case 'down':
251
353
  case 'opened':
@@ -254,11 +356,11 @@ function stepOpen<T>(
254
356
  }
255
357
 
256
358
  function stepTracking<T>(
257
- gesture: Extract<MenuPressGesture<T>, {phase: 'tracking'}>,
258
- event: MenuPressEvent<T>,
259
- ): MenuPressStep<T> {
359
+ gesture: Extract<MenuPressState<T>, {phase: 'tracking'}>,
360
+ event: MenuPressInput<T>,
361
+ ): MenuPressTransition<T> {
260
362
  switch (event.type) {
261
- case 'move': {
363
+ case 'move':
262
364
  if (event.row === gesture.row) {
263
365
  return {gesture, effect: NONE};
264
366
  }
@@ -266,7 +368,6 @@ function stepTracking<T>(
266
368
  gesture: {...gesture, row: event.row},
267
369
  effect: highlightEffect(event.row),
268
370
  };
269
- }
270
371
  case 'up':
271
372
  return release(gesture.pointerType, event);
272
373
  case 'cancel':
@@ -278,3 +379,108 @@ function stepTracking<T>(
278
379
  return {gesture, effect: NONE};
279
380
  }
280
381
  }
382
+
383
+ // ---------------------------------------------------------------------------
384
+ // The released public shape (`@astryxdesign/core/hooks`). The handoff
385
+ // positions and `handoffFrom` stay internal: a caller of
386
+ // `menuPressStep` keeps the settle rule, as released.
387
+ // ---------------------------------------------------------------------------
388
+
389
+ export type MenuPressGesture<T> =
390
+ | {phase: 'idle'}
391
+ | {
392
+ phase: 'triggerPress';
393
+ pointerType: MenuPressPointerType;
394
+ startedAt: number;
395
+ }
396
+ | {
397
+ phase: 'open';
398
+ pointerType: MenuPressPointerType;
399
+ openedAt: number;
400
+ }
401
+ | {
402
+ phase: 'tracking';
403
+ pointerType: MenuPressPointerType;
404
+ /** Where the press began. */
405
+ origin: 'trigger' | 'menu';
406
+ /** The enabled row currently under the pointer, if any. */
407
+ row: T | null;
408
+ };
409
+
410
+ export type MenuPressEvent<T> =
411
+ | {
412
+ type: 'down';
413
+ target: 'trigger' | 'menu';
414
+ pointerType: MenuPressPointerType;
415
+ /** The enabled row under the press when it lands inside the menu. */
416
+ row: T | null;
417
+ time: number;
418
+ }
419
+ /** The menu opened while the trigger press is still held. */
420
+ | {type: 'opened'; time: number}
421
+ | {type: 'move'; row: T | null; isInMenu: boolean; time: number}
422
+ | {
423
+ type: 'up';
424
+ row: T | null;
425
+ isInMenu: boolean;
426
+ isOnTrigger: boolean;
427
+ time: number;
428
+ }
429
+ /** The browser took the gesture: pointercancel, a second pointer, a scroll. */
430
+ | {type: 'cancel'};
431
+
432
+ export type MenuPressEffect<T> =
433
+ | {type: 'none'}
434
+ /** A mouse pressed the trigger: open the menu now, under the held pointer. */
435
+ | {type: 'open'}
436
+ | {type: 'highlight'; row: T}
437
+ | {type: 'clear'}
438
+ | {type: 'act'; row: T}
439
+ /**
440
+ * The gesture ends without a row acting. Its `stray` flag is true when the
441
+ * browser still sends a `click` for this gesture. That click must not act.
442
+ * Its `dismiss` flag is true when the menu must close (a mouse released
443
+ * outside it).
444
+ */
445
+ | {type: 'settle'; stray: boolean; dismiss: boolean};
446
+
447
+ export interface MenuPressStep<T> {
448
+ gesture: MenuPressGesture<T>;
449
+ effect: MenuPressEffect<T>;
450
+ }
451
+
452
+ /**
453
+ * The menu press machine, as released: one step from a gesture and an event
454
+ * to the next gesture and the effect to apply. Pure.
455
+ */
456
+ export function menuPressStep<T>(
457
+ gesture: MenuPressGesture<T>,
458
+ event: MenuPressEvent<T>,
459
+ ): MenuPressStep<T> {
460
+ // The released machine keeps the settle rule. Positions start the handoff,
461
+ // so a caller's event reaches the transition without them.
462
+ const {
463
+ x: _x,
464
+ y: _y,
465
+ ...released
466
+ } = event as MenuPressEvent<T> & {x?: number; y?: number};
467
+ const {gesture: next, effect} = menuPressTransition(
468
+ gesture,
469
+ released as MenuPressEvent<T>,
470
+ );
471
+ return {
472
+ gesture: publicGesture(next),
473
+ effect,
474
+ };
475
+ }
476
+
477
+ function publicGesture<T>(gesture: MenuPressState<T>): MenuPressGesture<T> {
478
+ if (gesture.phase !== 'open') {
479
+ return gesture;
480
+ }
481
+ return {
482
+ phase: 'open',
483
+ pointerType: gesture.pointerType,
484
+ openedAt: gesture.openedAt,
485
+ };
486
+ }
@@ -0,0 +1,18 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file menuPressValues.ts
5
+ * @input None
6
+ * @output The UIKit value the menu press model reads
7
+ * @position A copy of one UIKit table entry, motion/uikit gestures.ts
8
+ * `UIKIT_MENU_PRESS.handoff`. A menu then bundles this number, not the
9
+ * table with its measurement records. The table stays the source and
10
+ * documents the measurement. menuPressValues.test.ts fails when the copy
11
+ * drifts.
12
+ *
13
+ * SYNC: When modified, update:
14
+ * - /packages/core/src/motion/uikit/gestures.ts
15
+ */
16
+
17
+ /** How far the opening finger travels after the menu opens before it picks. */
18
+ export const MENU_PRESS_HANDOFF_PX = 10;
@@ -61,12 +61,17 @@ export const docs = {
61
61
  type: '(event: MouseEvent<HTMLElement>) => void',
62
62
  description: 'Mouse up handler to attach to the container (handles middle-click navigation for href).',
63
63
  },
64
+ {
65
+ name: 'onPointerDown',
66
+ type: '(event: PointerEvent<HTMLElement>) => void',
67
+ description: 'Pointer down handler to attach to the container. On a container that paints its press with usePressFeedback, it drops a touch press that starts on a nested interactive element, which does not activate the container. A mouse :active press paint is unchanged. Without it, the container keeps its click rules and its pressed state for every press.',
68
+ },
64
69
  ],
65
70
  usage: {
66
71
  description:
67
72
  'Makes a container element clickable while preserving nested interactive element behavior. Solves the "nested interactive elements" problem: when a card is clickable but contains buttons/links, clicking those should NOT trigger the card\'s action. Detects interactive ancestors between the click target and the container, and ignores text selections. Supports href navigation (including middle-click and Ctrl/Cmd+click for new tabs).',
68
73
  bestPractices: [
69
- { guidance: true, description: 'Attach both onClick and onMouseUp to the container element for full click handling including middle-click.' },
74
+ { guidance: true, description: 'Attach onClick, onMouseUp and onPointerDown to the container element: full click handling including middle-click, and no touch pressed state for a press on a nested control.' },
70
75
  { guidance: true, description: 'Use inside ClickableCard or SelectableCard for the standard card interaction pattern.' },
71
76
  { guidance: false, description: 'Use when the entire container is a single interactive element; just use a <button> or <a> directly.' },
72
77
  ],
@@ -93,12 +98,13 @@ export const docsDense = {
93
98
  returnDescriptions: {
94
99
  onClick: 'click handler for container element.',
95
100
  onMouseUp: 'mouse up handler for container (handles middle-click navigation for href).',
101
+ onPointerDown: 'pointer down handler for container: drops usePressFeedback touch press starting on nested interactive element; mouse :active paint unchanged.',
96
102
  },
97
103
  usage: {
98
104
  description:
99
105
  'Makes container element clickable while preserving nested interactive element behavior. Solves "nested interactive elements" problem: when card is clickable but contains buttons/links, clicking those must NOT trigger card\'s action. Detects interactive ancestors between click target + container, ignores text selections. Supports href navigation (incl. middle-click + Ctrl/Cmd+click for new tabs).',
100
106
  bestPractices: [
101
- { guidance: true, description: 'Attach both onClick + onMouseUp to container element for full click handling incl. middle-click.' },
107
+ { guidance: true, description: 'Attach onClick + onMouseUp + onPointerDown to container element: full click handling incl. middle-click, no touch pressed state for press on nested control.' },
102
108
  { guidance: true, description: 'Use inside ClickableCard / SelectableCard for standard card interaction pattern.' },
103
109
  { guidance: false, description: 'Use when entire container is single interactive element; just use <button> / <a> directly.' },
104
110
  ],
@@ -5,19 +5,28 @@
5
5
  /**
6
6
  * @file useClickableContainer.ts
7
7
  * @input Container ref, interactive element ref, click/href handlers
8
- * @output onClick and onMouseUp handlers for the container; INTERACTIVE_SELECTORS list;
8
+ * @output onClick, onMouseUp and onPointerDown handlers for the container;
9
+ * INTERACTIVE_SELECTORS list;
9
10
  * hasInteractiveAncestor and the shared innermostInteractive walk
10
11
  * @position Core hook for clickable containers with nested interactive
11
12
  * elements. A click on a nested button or link does not run the card's
12
- * action. Each navigation passes the rule in utils/safeUrl.ts first.
13
+ * action, and a finger press there shows no press on the card. Each navigation
14
+ * passes the rule in utils/safeUrl.ts first.
13
15
  *
14
16
  * SYNC: When modified, update:
15
17
  * - /packages/core/src/hooks/index.ts (export)
16
18
  */
17
19
 
18
- import {useCallback, useEffect, type RefObject, type MouseEvent} from 'react';
20
+ import {
21
+ useCallback,
22
+ useEffect,
23
+ type RefObject,
24
+ type MouseEvent,
25
+ type PointerEvent,
26
+ } from 'react';
19
27
  import {isLayerSurface} from '../Layer/layerSurfacePress';
20
28
  import {isSafeUrl} from '../utils/safeUrl';
29
+ import {cancelPress} from '../utils/pressFeedback';
21
30
 
22
31
  /**
23
32
  * The attribute this hook sets on the container it makes clickable. A click
@@ -118,6 +127,34 @@ function belongsToSomethingElse(element: Element): boolean {
118
127
  return matchesInteractiveSelectors(element) || isLayerSurface(element);
119
128
  }
120
129
 
130
+ /**
131
+ * The touch half of a container's press rule: a finger press that starts on a nested interactive element
132
+ * other than the container's own action does not activate the container, so
133
+ * it ends the press controller's paint on `container`. A mouse or pen press,
134
+ * and a press on the container's surface or on `ownAction`, are left alone.
135
+ * Not part of the package's public API.
136
+ */
137
+ export function cancelNestedTouchPress(
138
+ event: PointerEvent<HTMLElement>,
139
+ container: HTMLElement | null,
140
+ ownAction: Element | null,
141
+ ): void {
142
+ const target = event.target;
143
+ if (
144
+ event.pointerType !== 'touch' ||
145
+ container == null ||
146
+ !(target instanceof Element) ||
147
+ target === event.currentTarget ||
148
+ !hasInteractiveAncestor(target, container) ||
149
+ // The container's own action, such as the control it delegates to: a
150
+ // press on it activates the container.
151
+ ownAction?.contains(target) === true
152
+ ) {
153
+ return;
154
+ }
155
+ cancelPress(container);
156
+ }
157
+
121
158
  /**
122
159
  * Check if there's a text selection inside the node (don't navigate on text select)
123
160
  *
@@ -159,6 +196,14 @@ export interface UseClickableContainerOptions {
159
196
  export interface ClickableContainerResult {
160
197
  onClick: (event: MouseEvent<HTMLElement>) => void;
161
198
  onMouseUp: (event: MouseEvent<HTMLElement>) => void;
199
+ /**
200
+ * Spread on the container with `onClick`. A press that starts on a nested
201
+ * interactive element does not activate the container. On a container that
202
+ * paints its press with `usePressFeedback`, this drops that touch press.
203
+ * A mouse `:active` press paint is unchanged. A container without it keeps
204
+ * its activation rule and shows its pressed state for every press.
205
+ */
206
+ onPointerDown: (event: PointerEvent<HTMLElement>) => void;
162
207
  }
163
208
 
164
209
  /**
@@ -176,12 +221,16 @@ export interface ClickableContainerResult {
176
221
  * @example
177
222
  * ```
178
223
  * const containerRef = useRef<HTMLDivElement>(null);
179
- * const { onClick, onMouseUp } = useClickableContainer({
224
+ * const { onClick, onMouseUp, onPointerDown } = useClickableContainer({
180
225
  * containerRef,
181
226
  * onClick: () => console.log('card clicked'),
182
227
  * });
183
228
  * return (
184
- * <div ref={containerRef} onClick={onClick} onMouseUp={onMouseUp}>
229
+ * <div
230
+ * ref={containerRef}
231
+ * onClick={onClick}
232
+ * onMouseUp={onMouseUp}
233
+ * onPointerDown={onPointerDown}>
185
234
  * <p>Click anywhere on this card</p>
186
235
  * <button onClick={() => alert('button')}>Nested button</button>
187
236
  * </div>
@@ -305,5 +354,22 @@ export function useClickableContainer({
305
354
  [containerRef, href, disabled],
306
355
  );
307
356
 
308
- return {onClick, onMouseUp};
357
+ // The press-state counterpart of onClick's rule, for touch: a finger press
358
+ // that would not activate the container shows no press on it. A mouse or
359
+ // pen press keeps its `:active` paint.
360
+ const onPointerDown = useCallback(
361
+ (event: PointerEvent<HTMLElement>) => {
362
+ if (disabled) {
363
+ return;
364
+ }
365
+ cancelNestedTouchPress(
366
+ event,
367
+ containerRef.current,
368
+ interactiveRef?.current ?? null,
369
+ );
370
+ },
371
+ [containerRef, interactiveRef, disabled],
372
+ );
373
+
374
+ return {onClick, onMouseUp, onPointerDown};
309
375
  }