@astryxdesign/core 0.4.5 → 0.4.6-canary.cea9ecd

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 (538) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/dist/Avatar/Avatar.d.ts +10 -5
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +139 -30
  5. package/dist/Avatar/AvatarStatusDot.d.ts +2 -1
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/Avatar/AvatarStatusDot.js +30 -4
  8. package/dist/Avatar/AvatarStatusLabelContext.d.ts +39 -0
  9. package/dist/Avatar/AvatarStatusLabelContext.d.ts.map +1 -0
  10. package/dist/Avatar/AvatarStatusLabelContext.js +41 -0
  11. package/dist/AvatarGroup/AvatarGroup.d.ts +3 -1
  12. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  13. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroupOverflow.js +15 -9
  15. package/dist/Blockquote/Blockquote.d.ts +4 -3
  16. package/dist/Blockquote/Blockquote.d.ts.map +1 -1
  17. package/dist/Blockquote/Blockquote.js +9 -10
  18. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  19. package/dist/BottomSheet/BottomSheetPanel.js +21 -5
  20. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  21. package/dist/Breadcrumbs/BreadcrumbItem.js +47 -34
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +7 -7
  24. package/dist/Calendar/Calendar.d.ts.map +1 -1
  25. package/dist/Calendar/Calendar.js +21 -12
  26. package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
  27. package/dist/Calendar/hooks/useCalendarNavigation.js +8 -4
  28. package/dist/Calendar/styles.d.ts +2 -2
  29. package/dist/Calendar/styles.d.ts.map +1 -1
  30. package/dist/Calendar/styles.js +5 -5
  31. package/dist/Center/Center.d.ts +23 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +12 -6
  34. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  35. package/dist/Chat/ChatComposer.js +3 -3
  36. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  37. package/dist/Chat/ChatComposerDrawer.js +2 -2
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +2 -2
  40. package/dist/Chat/useChatDictation.d.ts +1 -1
  41. package/dist/Chat/useChatDictation.d.ts.map +1 -1
  42. package/dist/Chat/useSpeechRecognition.d.ts +1 -1
  43. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  44. package/dist/Chat/useSpeechRecognition.js +4 -3
  45. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  46. package/dist/Chat/useTriggerMenu.js +2 -2
  47. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  48. package/dist/CheckboxInput/CheckboxInput.js +2 -2
  49. package/dist/Citation/Citation.d.ts.map +1 -1
  50. package/dist/Citation/Citation.js +5 -5
  51. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  52. package/dist/ClickableCard/ClickableCard.js +4 -4
  53. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  54. package/dist/CodeBlock/CodeBlock.js +1 -1
  55. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  56. package/dist/Collapsible/Collapsible.js +2 -2
  57. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  58. package/dist/CommandPalette/CommandPaletteItem.js +3 -3
  59. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  60. package/dist/ComplexSelector/ComplexSelector.js +6 -6
  61. package/dist/DateInput/DateInput.d.ts +31 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +90 -22
  64. package/dist/DateInput/MonthScroller.d.ts +35 -0
  65. package/dist/DateInput/MonthScroller.d.ts.map +1 -0
  66. package/dist/DateInput/MonthScroller.js +658 -0
  67. package/dist/DateInput/MonthYearWheels.d.ts +24 -0
  68. package/dist/DateInput/MonthYearWheels.d.ts.map +1 -0
  69. package/dist/DateInput/MonthYearWheels.js +114 -0
  70. package/dist/DateInput/TouchDateField.d.ts +10 -0
  71. package/dist/DateInput/TouchDateField.d.ts.map +1 -0
  72. package/dist/DateInput/TouchDateField.js +810 -0
  73. package/dist/DateInput/Wheel.d.ts +41 -0
  74. package/dist/DateInput/Wheel.d.ts.map +1 -0
  75. package/dist/DateInput/Wheel.js +315 -0
  76. package/dist/DateInput/monthGeometry.d.ts +65 -0
  77. package/dist/DateInput/monthGeometry.d.ts.map +1 -0
  78. package/dist/DateInput/monthGeometry.js +98 -0
  79. package/dist/DateInput/tokens.stylex.d.ts +63 -0
  80. package/dist/DateInput/tokens.stylex.d.ts.map +1 -0
  81. package/dist/DateInput/tokens.stylex.js +74 -0
  82. package/dist/DateInput/useOwnScrollGesture.d.ts +41 -0
  83. package/dist/DateInput/useOwnScrollGesture.d.ts.map +1 -0
  84. package/dist/DateInput/useOwnScrollGesture.js +202 -0
  85. package/dist/DateInput/usePointerDragScroll.d.ts +15 -0
  86. package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -0
  87. package/dist/DateInput/usePointerDragScroll.js +216 -0
  88. package/dist/DateInput/useScrollSettle.d.ts +26 -0
  89. package/dist/DateInput/useScrollSettle.d.ts.map +1 -0
  90. package/dist/DateInput/useScrollSettle.js +149 -0
  91. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  92. package/dist/DateRangeInput/DateRangeInput.js +17 -14
  93. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  94. package/dist/DateTimeInput/DateTimeInput.js +17 -14
  95. package/dist/Dialog/Dialog.d.ts.map +1 -1
  96. package/dist/Dialog/Dialog.js +3 -3
  97. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  98. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +2 -2
  99. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  100. package/dist/DropdownMenu/DropdownMenuItem.js +2 -2
  101. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  102. package/dist/DropdownMenu/DropdownMenuRadioItem.js +2 -2
  103. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  104. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -2
  105. package/dist/Field/FieldLabel.d.ts.map +1 -1
  106. package/dist/Field/FieldLabel.js +4 -4
  107. package/dist/Field/InputClearButton.d.ts.map +1 -1
  108. package/dist/Field/InputClearButton.js +6 -0
  109. package/dist/Field/inputStyles.stylex.d.ts +1 -1
  110. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  111. package/dist/Field/inputStyles.stylex.js +5 -5
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +6 -6
  114. package/dist/HoverCard/HoverCard.d.ts +13 -3
  115. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  116. package/dist/HoverCard/HoverCard.js +2 -0
  117. package/dist/HoverCard/index.d.ts +1 -1
  118. package/dist/HoverCard/index.d.ts.map +1 -1
  119. package/dist/HoverCard/useHoverCard.d.ts +17 -2
  120. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  121. package/dist/HoverCard/useHoverCard.js +78 -9
  122. package/dist/InputGroup/InputGroup.js +1 -1
  123. package/dist/Item/Item.d.ts +9 -1
  124. package/dist/Item/Item.d.ts.map +1 -1
  125. package/dist/Item/Item.js +52 -14
  126. package/dist/Layer/index.d.ts +4 -2
  127. package/dist/Layer/index.d.ts.map +1 -1
  128. package/dist/Layer/index.js +2 -0
  129. package/dist/Layer/useLayer.d.ts.map +1 -1
  130. package/dist/Layer/useLayer.js +3 -2
  131. package/dist/Layer/useTouchTrigger.d.ts +113 -0
  132. package/dist/Layer/useTouchTrigger.d.ts.map +1 -0
  133. package/dist/Layer/useTouchTrigger.js +241 -0
  134. package/dist/Layout/container.stylex.d.ts +4 -2
  135. package/dist/Layout/container.stylex.d.ts.map +1 -1
  136. package/dist/Layout/container.stylex.js +19 -11
  137. package/dist/Layout/index.d.ts +1 -0
  138. package/dist/Layout/index.d.ts.map +1 -1
  139. package/dist/Layout/index.js +4 -0
  140. package/dist/Layout/padding.stylex.d.ts +315 -12
  141. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  142. package/dist/Layout/padding.stylex.js +389 -12
  143. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  144. package/dist/Lightbox/Lightbox.js +5 -4
  145. package/dist/Link/Link.d.ts.map +1 -1
  146. package/dist/Link/Link.js +6 -6
  147. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  148. package/dist/MetadataList/MetadataList.js +1 -1
  149. package/dist/MobileNav/MobileNav.d.ts +1 -0
  150. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  151. package/dist/MobileNav/MobileNav.js +24 -6
  152. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  153. package/dist/MultiSelector/MultiSelector.js +8 -8
  154. package/dist/NavItem/navItemStyles.stylex.d.ts +4 -4
  155. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  156. package/dist/NavItem/navItemStyles.stylex.js +4 -4
  157. package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
  158. package/dist/NavMenu/NavHeadingMenuItem.js +3 -3
  159. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  160. package/dist/NumberInput/NumberInput.js +36 -8
  161. package/dist/Outline/Outline.d.ts.map +1 -1
  162. package/dist/Outline/Outline.js +3 -3
  163. package/dist/Pagination/Pagination.d.ts.map +1 -1
  164. package/dist/Pagination/Pagination.js +2 -2
  165. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  166. package/dist/PowerSearch/PowerSearch.js +11 -12
  167. package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
  168. package/dist/PowerSearch/PowerSearchToken.js +3 -1
  169. package/dist/PowerSearch/formatFilterValue.d.ts +11 -2
  170. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  171. package/dist/PowerSearch/formatFilterValue.js +23 -8
  172. package/dist/RadioList/RadioListItem.d.ts +2 -2
  173. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  174. package/dist/RadioList/RadioListItem.js +70 -40
  175. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  176. package/dist/Resizable/ResizeHandle.js +7 -7
  177. package/dist/Section/Section.d.ts +36 -1
  178. package/dist/Section/Section.d.ts.map +1 -1
  179. package/dist/Section/Section.js +12 -2
  180. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  181. package/dist/SegmentedControl/SegmentedControlItem.js +7 -2
  182. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  183. package/dist/SelectableCard/SelectableCard.js +3 -3
  184. package/dist/Selector/Selector.d.ts +26 -1
  185. package/dist/Selector/Selector.d.ts.map +1 -1
  186. package/dist/Selector/Selector.js +93 -20
  187. package/dist/Selector/SelectorOption.d.ts +14 -1
  188. package/dist/Selector/SelectorOption.d.ts.map +1 -1
  189. package/dist/Selector/SelectorOption.js +6 -0
  190. package/dist/Selector/SelectorRowLayoutContext.d.ts +8 -0
  191. package/dist/Selector/SelectorRowLayoutContext.d.ts.map +1 -0
  192. package/dist/Selector/SelectorRowLayoutContext.js +27 -0
  193. package/dist/Selector/types.d.ts +1 -0
  194. package/dist/Selector/types.d.ts.map +1 -1
  195. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  196. package/dist/SideNav/SideNavHeading.js +4 -4
  197. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  198. package/dist/SideNav/SideNavItem.js +3 -3
  199. package/dist/Slider/Slider.d.ts.map +1 -1
  200. package/dist/Slider/Slider.js +7 -7
  201. package/dist/Stack/Stack.d.ts +23 -1
  202. package/dist/Stack/Stack.d.ts.map +1 -1
  203. package/dist/Stack/Stack.js +12 -6
  204. package/dist/Switch/Switch.d.ts.map +1 -1
  205. package/dist/Switch/Switch.js +2 -2
  206. package/dist/TabList/Tab.d.ts.map +1 -1
  207. package/dist/TabList/Tab.js +1 -1
  208. package/dist/TabList/TabMenu.d.ts.map +1 -1
  209. package/dist/TabList/TabMenu.js +3 -3
  210. package/dist/Table/TableRow.js +4 -4
  211. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  212. package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
  213. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  214. package/dist/Table/plugins/filtering/useTableFiltering.js +2 -2
  215. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  216. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +2 -2
  217. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  218. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +2 -2
  219. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  220. package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
  221. package/dist/Table/plugins/sortable/useTableSortableState.d.ts +5 -3
  222. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  223. package/dist/Table/plugins/sortable/useTableSortableState.js +12 -9
  224. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  225. package/dist/Table/plugins/tree/useTableTreeData.js +3 -3
  226. package/dist/TextArea/TextArea.js +1 -1
  227. package/dist/TextInput/TextInput.js +1 -1
  228. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  229. package/dist/Thumbnail/Thumbnail.js +3 -3
  230. package/dist/TimeInput/TimeInput.js +2 -2
  231. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  232. package/dist/Timestamp/Timestamp.js +6 -4
  233. package/dist/Timestamp/formatInstant.d.ts +3 -2
  234. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  235. package/dist/Timestamp/formatInstant.js +8 -8
  236. package/dist/Timestamp/tooltipEntries.d.ts +2 -1
  237. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
  238. package/dist/Timestamp/tooltipEntries.js +5 -3
  239. package/dist/Toast/Toast.d.ts +4 -3
  240. package/dist/Toast/Toast.d.ts.map +1 -1
  241. package/dist/Toast/Toast.js +10 -7
  242. package/dist/Token/Token.d.ts.map +1 -1
  243. package/dist/Token/Token.js +6 -6
  244. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  245. package/dist/Tokenizer/Tokenizer.js +1 -1
  246. package/dist/Tooltip/Tooltip.d.ts +16 -3
  247. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  248. package/dist/Tooltip/Tooltip.js +2 -0
  249. package/dist/Tooltip/index.d.ts +1 -1
  250. package/dist/Tooltip/index.d.ts.map +1 -1
  251. package/dist/Tooltip/useTooltip.d.ts +21 -2
  252. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  253. package/dist/Tooltip/useTooltip.js +71 -13
  254. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  255. package/dist/TopNav/TopNavHeading.js +4 -4
  256. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  257. package/dist/TopNav/TopNavItem.js +3 -3
  258. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  259. package/dist/TopNav/TopNavMegaMenu.js +2 -2
  260. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  261. package/dist/TopNav/TopNavMegaMenuItem.js +2 -2
  262. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  263. package/dist/TopNav/TopNavMenu.js +4 -4
  264. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  265. package/dist/TreeList/TreeListItem.js +7 -7
  266. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  267. package/dist/Typeahead/BaseTypeahead.js +2 -2
  268. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  269. package/dist/Typeahead/Typeahead.js +1 -1
  270. package/dist/astryx.css +124 -62
  271. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
  272. package/dist/hooks/containerReveal.stylex.js +18 -18
  273. package/dist/hooks/scrollbarGutter.d.ts +28 -0
  274. package/dist/hooks/scrollbarGutter.d.ts.map +1 -0
  275. package/dist/hooks/scrollbarGutter.js +103 -0
  276. package/dist/hooks/useAutoMediaMode.d.ts +31 -0
  277. package/dist/hooks/useAutoMediaMode.d.ts.map +1 -0
  278. package/dist/hooks/useAutoMediaMode.js +201 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  280. package/dist/hooks/useInputStatusIcon.js +1 -1
  281. package/dist/hooks/useListFocus.d.ts +5 -2
  282. package/dist/hooks/useListFocus.d.ts.map +1 -1
  283. package/dist/hooks/useListFocus.js +12 -6
  284. package/dist/hooks/useScrollLock.d.ts +4 -0
  285. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  286. package/dist/hooks/useScrollLock.js +13 -1
  287. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  288. package/dist/i18n/InternationalizationContext.js +2 -0
  289. package/dist/i18n/index.d.ts +6 -0
  290. package/dist/i18n/index.d.ts.map +1 -1
  291. package/dist/i18n/index.js +6 -0
  292. package/dist/i18n/useCollator.d.ts +12 -0
  293. package/dist/i18n/useCollator.d.ts.map +1 -0
  294. package/dist/i18n/useCollator.js +40 -0
  295. package/dist/i18n/useLocale.d.ts +3 -0
  296. package/dist/i18n/useLocale.d.ts.map +1 -0
  297. package/dist/i18n/useLocale.js +31 -0
  298. package/dist/theme/MediaTheme.d.ts +25 -5
  299. package/dist/theme/MediaTheme.d.ts.map +1 -1
  300. package/dist/theme/MediaTheme.js +31 -6
  301. package/dist/theme/defineTheme.d.ts +5 -0
  302. package/dist/theme/defineTheme.d.ts.map +1 -1
  303. package/dist/theme/defineTheme.js +5 -0
  304. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  305. package/dist/theme/derivedVarRegistry.js +7 -0
  306. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  307. package/dist/theme/generateThemeRules.js +66 -5
  308. package/dist/theme/index.d.ts +1 -1
  309. package/dist/theme/index.d.ts.map +1 -1
  310. package/dist/theme/syntax/index.d.ts +6 -0
  311. package/dist/theme/syntax/index.d.ts.map +1 -1
  312. package/dist/theme/syntax/index.js +7 -2
  313. package/dist/utils/dateParser.d.ts +4 -3
  314. package/dist/utils/dateParser.d.ts.map +1 -1
  315. package/dist/utils/dateParser.js +8 -8
  316. package/dist/utils/index.d.ts +1 -1
  317. package/dist/utils/index.d.ts.map +1 -1
  318. package/dist/utils/index.js +1 -1
  319. package/dist/utils/plainDate.d.ts +5 -2
  320. package/dist/utils/plainDate.d.ts.map +1 -1
  321. package/dist/utils/plainDate.js +20 -4
  322. package/locales/en.json +28 -0
  323. package/locales/pseudo.json +21 -0
  324. package/package.json +3 -3
  325. package/src/Avatar/Avatar.doc.mjs +10 -6
  326. package/src/Avatar/Avatar.test.tsx +219 -1
  327. package/src/Avatar/Avatar.tsx +179 -37
  328. package/src/Avatar/AvatarStatusDot.tsx +30 -4
  329. package/src/Avatar/AvatarStatusLabelContext.ts +47 -0
  330. package/src/AvatarGroup/AvatarGroup.doc.mjs +10 -6
  331. package/src/AvatarGroup/AvatarGroup.test.tsx +62 -0
  332. package/src/AvatarGroup/AvatarGroup.tsx +3 -1
  333. package/src/AvatarGroup/AvatarGroupOverflow.tsx +31 -8
  334. package/src/Banner/Banner.doc.mjs +2 -0
  335. package/src/Blockquote/Blockquote.doc.mjs +30 -7
  336. package/src/Blockquote/Blockquote.test.tsx +34 -3
  337. package/src/Blockquote/Blockquote.tsx +11 -9
  338. package/src/BottomSheet/BottomSheet.doc.mjs +3 -3
  339. package/src/BottomSheet/BottomSheet.test.tsx +24 -0
  340. package/src/BottomSheet/BottomSheetPanel.test.tsx +91 -0
  341. package/src/BottomSheet/BottomSheetPanel.tsx +58 -3
  342. package/src/Breadcrumbs/BreadcrumbItem.tsx +36 -23
  343. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +26 -2
  344. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -0
  345. package/src/Button/Button.tsx +12 -9
  346. package/src/Calendar/Calendar.tsx +15 -9
  347. package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
  348. package/src/Calendar/styles.ts +8 -5
  349. package/src/Center/Center.doc.mjs +48 -0
  350. package/src/Center/Center.test.tsx +158 -0
  351. package/src/Center/Center.tsx +50 -9
  352. package/src/Chat/ChatComposer.tsx +11 -5
  353. package/src/Chat/ChatComposerDrawer.tsx +4 -1
  354. package/src/Chat/ChatToolCalls.tsx +13 -4
  355. package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
  356. package/src/Chat/useChatDictation.ts +1 -1
  357. package/src/Chat/useSpeechRecognition.ts +5 -3
  358. package/src/Chat/useTriggerMenu.tsx +4 -1
  359. package/src/CheckboxInput/CheckboxInput.tsx +5 -2
  360. package/src/Citation/Citation.test.tsx +6 -1
  361. package/src/Citation/Citation.tsx +11 -5
  362. package/src/ClickableCard/ClickableCard.tsx +7 -4
  363. package/src/CodeBlock/CodeBlock.tsx +4 -1
  364. package/src/Collapsible/Collapsible.tsx +5 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +6 -3
  366. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
  367. package/src/ComplexSelector/ComplexSelector.tsx +14 -7
  368. package/src/DateInput/DateInput.test.tsx +17 -1
  369. package/src/DateInput/DateInput.tsx +93 -21
  370. package/src/DateInput/DateInputTouch.test.tsx +2582 -0
  371. package/src/DateInput/MonthScroller.tsx +878 -0
  372. package/src/DateInput/MonthYearWheels.tsx +147 -0
  373. package/src/DateInput/TouchDateField.tsx +1223 -0
  374. package/src/DateInput/Wheel.tsx +473 -0
  375. package/src/DateInput/monthGeometry.ts +118 -0
  376. package/src/DateInput/tokens.stylex.ts +99 -0
  377. package/src/DateInput/useOwnScrollGesture.ts +227 -0
  378. package/src/DateInput/usePointerDragScroll.ts +220 -0
  379. package/src/DateInput/useScrollSettle.ts +164 -0
  380. package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
  381. package/src/DateRangeInput/DateRangeInput.tsx +31 -13
  382. package/src/DateTimeInput/DateTimeInput.tsx +25 -9
  383. package/src/Dialog/Dialog.test.tsx +24 -0
  384. package/src/Dialog/Dialog.tsx +3 -0
  385. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -2
  386. package/src/DropdownMenu/DropdownMenuItem.tsx +5 -2
  387. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -2
  389. package/src/Field/Field.doc.mjs +2 -0
  390. package/src/Field/FieldLabel.tsx +9 -3
  391. package/src/Field/InputClearButton.test.tsx +110 -3
  392. package/src/Field/InputClearButton.tsx +38 -0
  393. package/src/Field/inputStyles.stylex.ts +17 -13
  394. package/src/FileInput/FileInput.tsx +15 -8
  395. package/src/FormLayout/FormLayout.doc.mjs +1 -1
  396. package/src/HoverCard/HoverCard.doc.mjs +18 -0
  397. package/src/HoverCard/HoverCard.test.tsx +189 -0
  398. package/src/HoverCard/HoverCard.tsx +22 -2
  399. package/src/HoverCard/index.ts +1 -0
  400. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  401. package/src/HoverCard/useHoverCard.tsx +103 -7
  402. package/src/InputGroup/InputGroup.tsx +1 -1
  403. package/src/Item/Item.doc.mjs +1 -0
  404. package/src/Item/Item.test.tsx +50 -0
  405. package/src/Item/Item.tsx +54 -6
  406. package/src/Layer/index.ts +10 -0
  407. package/src/Layer/useLayer.tsx +13 -2
  408. package/src/Layer/useTouchTrigger.test.ts +70 -0
  409. package/src/Layer/useTouchTrigger.ts +366 -0
  410. package/src/Layout/Layout.doc.mjs +18 -0
  411. package/src/Layout/container.stylex.ts +11 -3
  412. package/src/Layout/index.ts +4 -0
  413. package/src/Layout/overlayPaddingReset.test.tsx +222 -0
  414. package/src/Layout/padding.stylex.ts +192 -12
  415. package/src/Lightbox/Lightbox.tsx +11 -3
  416. package/src/Link/Link.tsx +9 -6
  417. package/src/MetadataList/MetadataList.tsx +4 -1
  418. package/src/MobileNav/MobileNav.tsx +61 -5
  419. package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
  420. package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
  421. package/src/MultiSelector/MultiSelector.test.tsx +1 -1
  422. package/src/MultiSelector/MultiSelector.tsx +26 -10
  423. package/src/NavItem/navItemStyles.stylex.ts +7 -4
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +6 -3
  425. package/src/NumberInput/NumberInput.doc.mjs +24 -0
  426. package/src/NumberInput/NumberInput.test.tsx +138 -0
  427. package/src/NumberInput/NumberInput.tsx +47 -8
  428. package/src/Outline/Outline.tsx +5 -2
  429. package/src/Pagination/Pagination.tsx +5 -2
  430. package/src/Popover/Popover.test.tsx +27 -1
  431. package/src/PowerSearch/PowerSearch.tsx +17 -8
  432. package/src/PowerSearch/PowerSearchToken.tsx +3 -0
  433. package/src/PowerSearch/formatFilterValue.test.ts +22 -2
  434. package/src/PowerSearch/formatFilterValue.ts +31 -6
  435. package/src/RadioList/RadioList.doc.mjs +1 -1
  436. package/src/RadioList/RadioList.test.tsx +170 -2
  437. package/src/RadioList/RadioListItem.tsx +88 -53
  438. package/src/Resizable/ResizeHandle.test.tsx +22 -0
  439. package/src/Resizable/ResizeHandle.tsx +25 -7
  440. package/src/Section/Section.doc.mjs +55 -0
  441. package/src/Section/Section.test.tsx +201 -4
  442. package/src/Section/Section.tsx +69 -0
  443. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  444. package/src/SegmentedControl/SegmentedControlItem.tsx +15 -3
  445. package/src/SelectableCard/SelectableCard.tsx +6 -3
  446. package/src/Selector/Selector.doc.mjs +16 -2
  447. package/src/Selector/Selector.test.tsx +525 -1
  448. package/src/Selector/Selector.tsx +180 -25
  449. package/src/Selector/SelectorOption.doc.mjs +7 -0
  450. package/src/Selector/SelectorOption.tsx +21 -0
  451. package/src/Selector/SelectorRowLayoutContext.ts +32 -0
  452. package/src/Selector/types.ts +4 -4
  453. package/src/SideNav/SideNavHeading.tsx +14 -5
  454. package/src/SideNav/SideNavItem.tsx +9 -3
  455. package/src/Slider/Slider.tsx +25 -6
  456. package/src/Stack/Stack.doc.mjs +72 -0
  457. package/src/Stack/Stack.test.tsx +156 -0
  458. package/src/Stack/Stack.tsx +50 -8
  459. package/src/Switch/Switch.tsx +5 -2
  460. package/src/TabList/Tab.tsx +4 -1
  461. package/src/TabList/TabMenu.tsx +9 -3
  462. package/src/Table/TableRow.tsx +4 -4
  463. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +6 -2
  464. package/src/Table/plugins/filtering/useTableFiltering.tsx +4 -1
  465. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +10 -3
  466. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +6 -3
  467. package/src/Table/plugins/sortable/useTableSortable.tsx +4 -1
  468. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
  469. package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
  470. package/src/Table/plugins/tree/useTableTreeData.tsx +10 -4
  471. package/src/TextArea/TextArea.doc.mjs +1 -1
  472. package/src/TextArea/TextArea.tsx +1 -1
  473. package/src/TextInput/TextInput.tsx +1 -1
  474. package/src/Thumbnail/Thumbnail.tsx +9 -3
  475. package/src/TimeInput/TimeInput.tsx +1 -1
  476. package/src/Timestamp/Timestamp.test.tsx +27 -7
  477. package/src/Timestamp/Timestamp.tsx +8 -4
  478. package/src/Timestamp/formatInstant.ts +9 -7
  479. package/src/Timestamp/tooltipEntries.test.ts +11 -6
  480. package/src/Timestamp/tooltipEntries.ts +6 -2
  481. package/src/Toast/Toast.tsx +9 -7
  482. package/src/Token/Token.tsx +14 -5
  483. package/src/Tokenizer/Tokenizer.tsx +4 -1
  484. package/src/Tooltip/Tooltip.doc.mjs +18 -0
  485. package/src/Tooltip/Tooltip.test.tsx +254 -1
  486. package/src/Tooltip/Tooltip.tsx +22 -2
  487. package/src/Tooltip/index.ts +1 -0
  488. package/src/Tooltip/useTooltip.doc.mjs +2 -0
  489. package/src/Tooltip/useTooltip.tsx +99 -21
  490. package/src/TopNav/TopNavHeading.tsx +13 -4
  491. package/src/TopNav/TopNavItem.tsx +6 -3
  492. package/src/TopNav/TopNavMegaMenu.tsx +5 -2
  493. package/src/TopNav/TopNavMegaMenuItem.tsx +5 -2
  494. package/src/TopNav/TopNavMenu.tsx +10 -4
  495. package/src/TreeList/TreeListItem.tsx +22 -7
  496. package/src/Typeahead/BaseTypeahead.tsx +5 -2
  497. package/src/Typeahead/Typeahead.tsx +4 -1
  498. package/src/hooks/containerReveal.stylex.ts +54 -18
  499. package/src/hooks/scrollbarGutter.test.ts +167 -0
  500. package/src/hooks/scrollbarGutter.ts +121 -0
  501. package/src/hooks/useAutoMediaMode.test.ts +109 -0
  502. package/src/hooks/useAutoMediaMode.ts +233 -0
  503. package/src/hooks/useInputStatusIcon.tsx +4 -1
  504. package/src/hooks/useListFocus.doc.mjs +2 -2
  505. package/src/hooks/useListFocus.test.tsx +65 -3
  506. package/src/hooks/useListFocus.ts +15 -7
  507. package/src/hooks/useScrollLock.doc.mjs +4 -4
  508. package/src/hooks/useScrollLock.test.ts +73 -0
  509. package/src/hooks/useScrollLock.ts +14 -0
  510. package/src/i18n/InternationalizationContext.ts +2 -0
  511. package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
  512. package/src/i18n/__tests__/useCollator.test.tsx +115 -0
  513. package/src/i18n/__tests__/useLocale.test.tsx +52 -0
  514. package/src/i18n/index.ts +6 -0
  515. package/src/i18n/useCollator.doc.mjs +84 -0
  516. package/src/i18n/useCollator.ts +44 -0
  517. package/src/i18n/useLocale.doc.mjs +70 -0
  518. package/src/i18n/useLocale.ts +34 -0
  519. package/src/i18n/useTranslator.doc.mjs +1 -1
  520. package/src/reset.css +9 -2
  521. package/src/reset.test.ts +22 -0
  522. package/src/theme/MediaTheme.doc.mjs +23 -4
  523. package/src/theme/MediaTheme.dom.test.tsx +97 -0
  524. package/src/theme/MediaTheme.tsx +42 -7
  525. package/src/theme/defineTheme.test.ts +9 -3
  526. package/src/theme/defineTheme.ts +5 -0
  527. package/src/theme/derivedVarRegistry.test.ts +19 -5
  528. package/src/theme/derivedVarRegistry.ts +4 -0
  529. package/src/theme/generateThemeRules.test.ts +118 -0
  530. package/src/theme/generateThemeRules.ts +83 -5
  531. package/src/theme/index.ts +1 -1
  532. package/src/theme/syntax/index.ts +6 -2
  533. package/src/theme/syntax/serverSafeSyntax.test.ts +68 -0
  534. package/src/utils/dateParser.test.ts +20 -0
  535. package/src/utils/dateParser.ts +14 -7
  536. package/src/utils/index.ts +2 -0
  537. package/src/utils/plainDate.test.ts +6 -0
  538. package/src/utils/plainDate.ts +21 -4
@@ -0,0 +1,473 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file Wheel.tsx
7
+ * @input Option list, selected value, change callback
8
+ * @output Exports Wheel — a snap-scrolling picker column
9
+ * @position Internal component; consumed by MonthYearWheels.tsx
10
+ *
11
+ * A wheel is a scroll container, not a custom gesture surface. Momentum,
12
+ * rubber-banding, and the settle animation are the platform's; all this adds
13
+ * is `scroll-snap-align: center` on each option, half a viewport of padding at
14
+ * each end so the first and last option can reach the middle, and a commit
15
+ * when the scrolling stops.
16
+ *
17
+ * The falloff (rows fading and tipping away from the centre) is a CSS
18
+ * scroll-driven animation on a `view()` timeline — the browser interpolates it
19
+ * against each row's own position in the scrollport, so it stays glued to the
20
+ * finger with no JS in the frame loop. It is guarded by `@supports`, because a
21
+ * browser that does not understand `animation-timeline` would otherwise run
22
+ * the same keyframes on the document timeline and simply play them once.
23
+ *
24
+ * Accessibility: this is a listbox, not a novel widget. It is one tab stop
25
+ * with arrow/Home/End/PageUp/PageDown keys, `aria-activedescendant` tracking
26
+ * the active row, and every row reachable by tap — none of which depends on
27
+ * the scroll-driven decoration.
28
+ *
29
+ * SYNC: When modified, update:
30
+ * - /packages/core/src/DateInput/TouchDateField.tsx
31
+ * - /packages/core/src/DateInput/DateInput.doc.mjs
32
+ * - /packages/core/src/DateInput/DateInputTouch.test.tsx
33
+ */
34
+
35
+ import {useCallback, useEffect, useId, useRef, useState} from 'react';
36
+ import * as stylex from '@stylexjs/stylex';
37
+ import {
38
+ colorVars,
39
+ radiusVars,
40
+ fontWeightVars,
41
+ typeScaleVars,
42
+ spacingVars,
43
+ durationVars,
44
+ } from '../theme/tokens.stylex';
45
+ import {focusOutlineStyles} from '../utils';
46
+ import {dateInputTouchSizes, dateInputTouchGeometry} from './tokens.stylex';
47
+ import {useOwnScrollGesture} from './useOwnScrollGesture';
48
+ import {usePointerDragScroll} from './usePointerDragScroll';
49
+ import {useScrollSettle} from './useScrollSettle';
50
+
51
+ const ITEM_BLOCK_SIZE = dateInputTouchSizes.wheelItemSize;
52
+
53
+ /**
54
+ * Rows tip away from the centre of the wheel. 0% is a row just entering at the
55
+ * bottom of the scrollport, 50% is a row centred in it, 100% is a row leaving
56
+ * at the top — which is what a `view()` timeline over the `cover` range means,
57
+ * and why this reads as a cylinder rotating under the finger.
58
+ */
59
+ const falloff = stylex.keyframes({
60
+ '0%': {
61
+ opacity: 0.3,
62
+ transform: 'rotateX(52deg) scale(0.86)',
63
+ },
64
+ '50%': {
65
+ opacity: 1,
66
+ transform: 'rotateX(0deg) scale(1)',
67
+ },
68
+ '100%': {
69
+ opacity: 0.3,
70
+ transform: 'rotateX(-52deg) scale(0.86)',
71
+ },
72
+ });
73
+
74
+ /** Motion-free equivalent: the depth cue survives as opacity alone. */
75
+ const fadeOnly = stylex.keyframes({
76
+ '0%': {opacity: 0.3},
77
+ '50%': {opacity: 1},
78
+ '100%': {opacity: 0.3},
79
+ });
80
+
81
+ const styles = stylex.create({
82
+ column: {
83
+ position: 'relative',
84
+ flex: '1 1 0',
85
+ minWidth: 0,
86
+ },
87
+ scroller: {
88
+ blockSize: dateInputTouchGeometry.paneBlockSize,
89
+ // Load-bearing, and stated rather than inherited from the reset (whose
90
+ // rule is zero-specificity `:where`): with content-box the end padding
91
+ // below would be added to the scrollport instead of sitting inside it,
92
+ // and every snap position would be wrong.
93
+ boxSizing: 'border-box',
94
+ overflowY: 'auto',
95
+ overflowX: 'hidden',
96
+ // Snap every row to the middle of the scrollport, where the selection band
97
+ // sits. `mandatory` (not `proximity`) because a wheel has no valid resting
98
+ // position between two options.
99
+ scrollSnapType: 'y mandatory',
100
+ overscrollBehavior: 'contain',
101
+ // Room for row 0 and row n-1 to reach the centre.
102
+ paddingBlock: dateInputTouchGeometry.wheelEdgePadding,
103
+ // Gives the rotateX falloff somewhere to recede to.
104
+ perspective: '520px',
105
+ transformStyle: 'preserve-3d',
106
+ scrollbarWidth: 'none',
107
+ outline: 'none',
108
+ touchAction: 'pan-y',
109
+ },
110
+ item: {
111
+ display: 'flex',
112
+ alignItems: 'center',
113
+ justifyContent: 'center',
114
+ blockSize: ITEM_BLOCK_SIZE,
115
+ paddingInline: spacingVars['--spacing-2'],
116
+ scrollSnapAlign: 'center',
117
+ borderWidth: 0,
118
+ borderStyle: 'none',
119
+ backgroundColor: 'transparent',
120
+ // Larger than body text, and larger than the calendar's day numbers: on
121
+ // a wheel the value under the band is the whole interface, and the rows
122
+ // above and below are read at a glance while moving.
123
+ fontSize: typeScaleVars['--text-large-size'],
124
+ fontWeight: fontWeightVars['--font-weight-normal'],
125
+ color: colorVars['--color-text-primary'],
126
+ whiteSpace: 'nowrap',
127
+ // NO `overflow: hidden` here. It would make the row itself a scroll
128
+ // container, and `view()` binds to the subject's nearest ancestor scroll
129
+ // container — the falloff would measure the row against itself and sit
130
+ // frozen at 50% forever. Clipping belongs on the inner element.
131
+ cursor: {
132
+ default: 'pointer',
133
+ ':is(:disabled,[aria-disabled="true"])': 'default',
134
+ },
135
+ userSelect: 'none',
136
+ transitionProperty: 'color, font-weight',
137
+ transitionDuration: durationVars['--duration-fast'],
138
+ },
139
+ /**
140
+ * The falloff rides an inner element, never the row itself.
141
+ *
142
+ * A snap area is the element's TRANSFORMED border box, so animating the row
143
+ * would move the very positions the scroller is snapping to — the wheel
144
+ * settles a few pixels off, and the offset feeds back into the animation.
145
+ * Transforming a child leaves the row's box, and every snap offset, exact.
146
+ */
147
+ itemInner: {
148
+ display: 'flex',
149
+ alignItems: 'center',
150
+ justifyContent: 'center',
151
+ inlineSize: '100%',
152
+ blockSize: '100%',
153
+ overflow: 'hidden',
154
+ textOverflow: 'ellipsis',
155
+ // The wheel look. Held behind @supports so browsers without scroll-driven
156
+ // animations get a plain, fully legible list instead of these keyframes
157
+ // playing themselves out once on the document timeline. Under reduced
158
+ // motion the same timeline drives opacity only — the depth cue survives,
159
+ // the tipping does not.
160
+ animationName: {
161
+ default: null,
162
+ '@supports (animation-timeline: view())': {
163
+ default: falloff,
164
+ '@media (prefers-reduced-motion: reduce)': fadeOnly,
165
+ },
166
+ },
167
+ animationTimeline: 'view(y)',
168
+ animationRange: 'cover 0% cover 100%',
169
+ animationFillMode: 'both',
170
+ animationDuration: 'auto',
171
+ animationTimingFunction: 'linear',
172
+ backfaceVisibility: 'hidden',
173
+ },
174
+ itemActive: {
175
+ color: colorVars['--color-text-accent'],
176
+ fontWeight: fontWeightVars['--font-weight-semibold'],
177
+ },
178
+ itemDisabled: {
179
+ color: colorVars['--color-text-disabled'],
180
+ cursor: 'default',
181
+ },
182
+ /**
183
+ * The selection band: a single centred row-height plate behind the options.
184
+ * Purely decorative — the committed value is announced by `aria-selected`.
185
+ */
186
+ band: {
187
+ position: 'absolute',
188
+ insetInline: 0,
189
+ insetBlockStart: `calc(50% - (${ITEM_BLOCK_SIZE} / 2))`,
190
+ blockSize: ITEM_BLOCK_SIZE,
191
+ borderRadius: radiusVars['--radius-element'],
192
+ // `--color-neutral`, not `--color-background-muted`. Muted is 4.7% alpha,
193
+ // which puts the whole plate 17 units of colour away from the sheet
194
+ // behind it — so when the wheels fade in, the band's animation has 17
195
+ // units to happen in while the text beside it travels 412. It did not
196
+ // read as fading, it read as appearing. Neutral is 10%, which doubles the
197
+ // range to 36 and is still quiet enough to sit under text.
198
+ backgroundColor: colorVars['--color-neutral'],
199
+ pointerEvents: 'none',
200
+ },
201
+ });
202
+
203
+ export interface WheelOption {
204
+ /** Stable numeric identity of the row (a month 1-12, or a year). */
205
+ value: number;
206
+ /** Row text. */
207
+ label: string;
208
+ /** Rows outside min/max stay visible but cannot be committed. */
209
+ isDisabled?: boolean;
210
+ }
211
+
212
+ export interface WheelProps {
213
+ /** Accessible name for the column, e.g. "Month". */
214
+ label: string;
215
+ /** Rows, top to bottom. */
216
+ options: ReadonlyArray<WheelOption>;
217
+ /** Committed value; must match one option's `value`. */
218
+ value: number;
219
+ /** Fired when the wheel comes to rest on a different, enabled row. */
220
+ onChange: (value: number) => void;
221
+ /**
222
+ * False while the wheel is hidden — scroll offsets of a display:none
223
+ * scroller are meaningless, so listeners and the initial scroll wait.
224
+ */
225
+ isActive?: boolean;
226
+ }
227
+
228
+ /**
229
+ * One snap-scrolling picker column.
230
+ *
231
+ * @example
232
+ * ```
233
+ * <Wheel
234
+ * label="Month"
235
+ * options={monthOptions}
236
+ * value={month}
237
+ * onChange={setMonth}
238
+ * />
239
+ * ```
240
+ */
241
+ export function Wheel({
242
+ label,
243
+ options,
244
+ value,
245
+ onChange,
246
+ isActive = true,
247
+ }: WheelProps) {
248
+ const id = useId();
249
+ const scrollerRef = useRef<HTMLDivElement | null>(null);
250
+ const selectedIndex = Math.max(
251
+ 0,
252
+ options.findIndex(option => option.value === value),
253
+ );
254
+ // Which row is under the band right now. Tracks the finger during a scroll;
255
+ // `selectedIndex` only catches up when the wheel settles.
256
+ const [activeIndex, setActiveIndex] = useState(selectedIndex);
257
+
258
+ // Row height in px, read from layout rather than assumed, so a theme that
259
+ // retunes --date-input-touch-wheel-item-size still lands on the right row.
260
+ const itemBlockSize = useCallback((): number => {
261
+ const first = scrollerRef.current?.firstElementChild;
262
+ return first instanceof HTMLElement && first.offsetHeight > 0
263
+ ? first.offsetHeight
264
+ : 0;
265
+ }, []);
266
+
267
+ const scrollToIndex = useCallback(
268
+ (index: number, behavior: ScrollBehavior) => {
269
+ const scroller = scrollerRef.current;
270
+ const size = itemBlockSize();
271
+ if (scroller == null || size === 0) {
272
+ return;
273
+ }
274
+ scroller.scrollTo({top: index * size, behavior});
275
+ },
276
+ [itemBlockSize],
277
+ );
278
+
279
+ // Commit on rest. A disabled row is bounced back to the committed one
280
+ // rather than silently keeping a value the wheel is not showing.
281
+ //
282
+ // Declared here, above the park effect, because that effect consults the
283
+ // `isAtRestRef` this returns before it repositions anything.
284
+ const {isAtRestRef} = useScrollSettle(
285
+ scrollerRef,
286
+ scroller => {
287
+ const size = itemBlockSize();
288
+ if (size === 0) {
289
+ return;
290
+ }
291
+ const index = Math.min(
292
+ options.length - 1,
293
+ Math.max(0, Math.round(scroller.scrollTop / size)),
294
+ );
295
+ const option = options[index];
296
+ if (option == null || option.isDisabled) {
297
+ scrollToIndex(selectedIndex, 'smooth');
298
+ return;
299
+ }
300
+ if (option.value !== value) {
301
+ onChange(option.value);
302
+ }
303
+ },
304
+ isActive,
305
+ );
306
+
307
+ // Park the committed row under the band whenever the wheel is shown, or the
308
+ // value is changed from outside (the calendar scrolled to another month).
309
+ //
310
+ // NEVER while the wheel is still moving. A scroller that is mid-gesture or
311
+ // still carrying momentum is the user's, and repositioning it does not stop
312
+ // the momentum — on iOS it feeds a cycle where the scroll this causes reads
313
+ // as a new settle, commits the next row along, and parks again. See
314
+ // useScrollSettle. The settle handler re-checks the position afterwards, so
315
+ // a correction that is genuinely needed still happens, just at rest.
316
+ useEffect(() => {
317
+ if (!isActive || !isAtRestRef.current) {
318
+ return;
319
+ }
320
+ const size = itemBlockSize();
321
+ const scroller = scrollerRef.current;
322
+ if (scroller == null || size === 0) {
323
+ return;
324
+ }
325
+ if (Math.round(scroller.scrollTop / size) !== selectedIndex) {
326
+ scroller.scrollTo({top: selectedIndex * size, behavior: 'auto'});
327
+ }
328
+ // eslint-disable-next-line @eslint-react/set-state-in-effect -- the highlight follows a scroll position, which only exists after layout
329
+ setActiveIndex(selectedIndex);
330
+ }, [isActive, selectedIndex, itemBlockSize, isAtRestRef]);
331
+
332
+ // Highlight follows the finger. rAF-throttled: a scroll can fire far more
333
+ // often than the display refreshes, and this only feeds a repaint.
334
+ const frameRef = useRef<number | undefined>(undefined);
335
+ useEffect(() => {
336
+ const scroller = scrollerRef.current;
337
+ if (scroller == null || !isActive) {
338
+ return;
339
+ }
340
+ const onScroll = () => {
341
+ if (frameRef.current != null) {
342
+ return;
343
+ }
344
+ frameRef.current = requestAnimationFrame(() => {
345
+ frameRef.current = undefined;
346
+ const size = itemBlockSize();
347
+ if (size === 0) {
348
+ return;
349
+ }
350
+ const index = Math.min(
351
+ options.length - 1,
352
+ Math.max(0, Math.round(scroller.scrollTop / size)),
353
+ );
354
+ setActiveIndex(index);
355
+ });
356
+ };
357
+ scroller.addEventListener('scroll', onScroll, {passive: true});
358
+ return () => {
359
+ scroller.removeEventListener('scroll', onScroll);
360
+ if (frameRef.current != null) {
361
+ cancelAnimationFrame(frameRef.current);
362
+ frameRef.current = undefined;
363
+ }
364
+ };
365
+ }, [isActive, options.length, itemBlockSize]);
366
+
367
+ // Keep the finger. Inside a BottomSheet the sheet would otherwise read a
368
+ // downward drag here as swipe-to-dismiss; see useOwnScrollGesture. Gated on
369
+ // isActive because the hidden panel keeps its layout box.
370
+ // 'all': a wheel scrolls vertically, the same axis the sheet wants, so
371
+ // there is no way to share — it takes every touch that lands on it.
372
+ useOwnScrollGesture(scrollerRef, 'all', {isEnabled: isActive});
373
+
374
+ // A mouse cannot drag a scroll container, so without this the wheel ignores
375
+ // the one gesture its shape invites. Touch is untouched — it pans natively,
376
+ // with momentum this could not match. See usePointerDragScroll.
377
+ usePointerDragScroll(scrollerRef, isActive);
378
+
379
+ const moveBy = useCallback(
380
+ (delta: number) => {
381
+ const next = Math.min(
382
+ options.length - 1,
383
+ Math.max(0, activeIndex + delta),
384
+ );
385
+ const option = options[next];
386
+ if (option == null || option.isDisabled) {
387
+ return;
388
+ }
389
+ setActiveIndex(next);
390
+ scrollToIndex(next, 'smooth');
391
+ if (option.value !== value) {
392
+ onChange(option.value);
393
+ }
394
+ },
395
+ [activeIndex, options, scrollToIndex, value, onChange],
396
+ );
397
+
398
+ const handleKeyDown = useCallback(
399
+ (event: React.KeyboardEvent) => {
400
+ switch (event.key) {
401
+ case 'ArrowDown':
402
+ event.preventDefault();
403
+ moveBy(1);
404
+ break;
405
+ case 'ArrowUp':
406
+ event.preventDefault();
407
+ moveBy(-1);
408
+ break;
409
+ case 'PageDown':
410
+ event.preventDefault();
411
+ moveBy(10);
412
+ break;
413
+ case 'PageUp':
414
+ event.preventDefault();
415
+ moveBy(-10);
416
+ break;
417
+ case 'Home':
418
+ event.preventDefault();
419
+ moveBy(-activeIndex);
420
+ break;
421
+ case 'End':
422
+ event.preventDefault();
423
+ moveBy(options.length - 1 - activeIndex);
424
+ break;
425
+ default:
426
+ break;
427
+ }
428
+ },
429
+ [moveBy, activeIndex, options.length],
430
+ );
431
+
432
+ return (
433
+ <div {...stylex.props(styles.column)}>
434
+ <div aria-hidden="true" {...stylex.props(styles.band)} />
435
+ <div
436
+ ref={scrollerRef}
437
+ role="listbox"
438
+ aria-label={label}
439
+ aria-activedescendant={`${id}-${options[activeIndex]?.value}`}
440
+ tabIndex={0}
441
+ onKeyDown={handleKeyDown}
442
+ {...stylex.props(styles.scroller, focusOutlineStyles.focusVisible)}>
443
+ {options.map((option, index) => (
444
+ <div
445
+ key={option.value}
446
+ id={`${id}-${option.value}`}
447
+ role="option"
448
+ aria-selected={option.value === value}
449
+ aria-disabled={option.isDisabled || undefined}
450
+ onClick={() => {
451
+ if (option.isDisabled) {
452
+ return;
453
+ }
454
+ setActiveIndex(index);
455
+ scrollToIndex(index, 'smooth');
456
+ if (option.value !== value) {
457
+ onChange(option.value);
458
+ }
459
+ }}
460
+ {...stylex.props(
461
+ styles.item,
462
+ index === activeIndex && styles.itemActive,
463
+ option.isDisabled && styles.itemDisabled,
464
+ )}>
465
+ <span {...stylex.props(styles.itemInner)}>{option.label}</span>
466
+ </div>
467
+ ))}
468
+ </div>
469
+ </div>
470
+ );
471
+ }
472
+
473
+ Wheel.displayName = 'Wheel';
@@ -0,0 +1,118 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file monthGeometry.ts
5
+ * @input PlainDate values, scroll offsets, pane heights
6
+ * @output Pure month-index arithmetic and the virtualized pane window
7
+ * @position Internal helper; consumed by MonthScroller.tsx and DateInput.tsx
8
+ *
9
+ * A "month index" is a single integer that totally orders calendar months:
10
+ * `year * 12 + (month - 1)`. The scroller is a list of equal-width panes, so
11
+ * one integer converts freely between a month, a row in that list, and a
12
+ * scroll offset — which is the whole trick behind continuous scrolling.
13
+ *
14
+ * These are pure functions on purpose: the scroll math is the part most likely
15
+ * to be wrong, and it is testable without a layout engine.
16
+ *
17
+ * SYNC: When modified, update DateInputTouch.test.tsx.
18
+ */
19
+
20
+ import type {PlainDate} from '../utils';
21
+
22
+ /** How many months are reachable in either direction when unbounded. */
23
+ export const DEFAULT_MONTH_REACH = 600;
24
+
25
+ /** `year * 12 + (month - 1)`, for a 1-based month. */
26
+ export function toMonthIndex(year: number, month: number): number {
27
+ return year * 12 + (month - 1);
28
+ }
29
+
30
+ /** The month index of a PlainDate. */
31
+ export function monthIndexOf(date: PlainDate): number {
32
+ return toMonthIndex(date.year, date.month);
33
+ }
34
+
35
+ /** Inverse of {@link toMonthIndex}; month comes back 1-based. */
36
+ export function fromMonthIndex(index: number): {year: number; month: number} {
37
+ return {
38
+ year: Math.floor(index / 12),
39
+ month: (((index % 12) + 12) % 12) + 1,
40
+ };
41
+ }
42
+
43
+ /** `value` limited to `[min, max]`. */
44
+ export function clampIndex(value: number, min: number, max: number): number {
45
+ return Math.min(Math.max(value, min), max);
46
+ }
47
+
48
+ /**
49
+ * The month a scrollport is resting on (or nearest to, mid-scroll).
50
+ *
51
+ * Returns a row offset from the start of the list, not a month index — add
52
+ * `minMonthIndex` for that.
53
+ *
54
+ * `scrollOffset` is `scrollLeft` for the horizontal scroller. Under RTL that
55
+ * is NEGATIVE — the spec has the inline start at 0 and scrolling run down
56
+ * from there — so the sign is normalized away here rather than at each call
57
+ * site, where forgetting it would silently pin the calendar to month zero.
58
+ */
59
+ export function rowAtScrollOffset(
60
+ scrollOffset: number,
61
+ paneSize: number,
62
+ rowCount: number,
63
+ isRTL: boolean = false,
64
+ ): number {
65
+ if (paneSize <= 0) {
66
+ return 0;
67
+ }
68
+ const distance = isRTL ? -scrollOffset : scrollOffset;
69
+ return clampIndex(Math.round(distance / paneSize), 0, rowCount - 1);
70
+ }
71
+
72
+ /**
73
+ * The scroll offset that brings `row` to rest, ready for `scrollTo`. The
74
+ * inverse of {@link rowAtScrollOffset}, including its RTL sign.
75
+ */
76
+ export function scrollOffsetForRow(
77
+ row: number,
78
+ paneSize: number,
79
+ isRTL: boolean = false,
80
+ ): number {
81
+ const distance = row * paneSize;
82
+ return isRTL ? -distance : distance;
83
+ }
84
+
85
+ export interface PaneWindow {
86
+ /** First rendered row, inclusive. */
87
+ start: number;
88
+ /** Last rendered row, inclusive. */
89
+ end: number;
90
+ }
91
+
92
+ /**
93
+ * The slice of rows to actually mount around `centerRow`.
94
+ *
95
+ * Only a handful of panes exist in the DOM at a time; the rest of the list is
96
+ * a spacer. `overscan` is how many extra panes ride along on each side so a
97
+ * fast fling never lands on an unmounted one — which under `scroll-snap-type:
98
+ * mandatory` would be visible as a jump.
99
+ */
100
+ export function paneWindow(
101
+ centerRow: number,
102
+ rowCount: number,
103
+ overscan: number,
104
+ ): PaneWindow {
105
+ return {
106
+ start: Math.max(0, centerRow - overscan),
107
+ end: Math.min(rowCount - 1, centerRow + overscan),
108
+ };
109
+ }
110
+
111
+ /** `[start, end]` inclusive, as an array of rows. */
112
+ export function rowsIn({start, end}: PaneWindow): number[] {
113
+ const rows: number[] = [];
114
+ for (let row = start; row <= end; row++) {
115
+ rows.push(row);
116
+ }
117
+ return rows;
118
+ }
@@ -0,0 +1,99 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file tokens.stylex.ts
5
+ * @input StyleX variable and constant definitions
6
+ * @output Exports DateInput layout constants and derived geometry
7
+ * @position Lab component tokens consumed by DateInput.tsx, MonthScroller.tsx, Wheel.tsx, MonthYearWheels.tsx
8
+ *
9
+ * The picker's whole geometry derives from two numbers, which is what makes
10
+ * the fixed height and the scroll snapping exact:
11
+ *
12
+ * pane height = weeks x day size (one month, always 6 rows)
13
+ * body height = pane height (calendar body AND wheel body)
14
+ * wheel pad = (body height - item) / 2 (so row 0 can rest centered)
15
+ *
16
+ * Every month pane is the same height as the scrollport, so `scroll-snap-align:
17
+ * start` produces a paged scroller with no partial rest positions, and the
18
+ * wheel overlay swaps in without the component changing size.
19
+ *
20
+ * The derived values are `defineConsts`, not plain exported strings: StyleX
21
+ * inlines a constant at build time only through `defineVars`/`defineConsts`
22
+ * from a `.stylex.ts` file. A plain `export const` fails the build with
23
+ * "a style value can only contain an array, string or number".
24
+ *
25
+ */
26
+
27
+ import * as stylex from '@stylexjs/stylex';
28
+
29
+ /**
30
+ * Week rows per month pane. Fixed at 6 — the most any month needs — so every
31
+ * pane is identical in height and no snap offset ever shifts. Not a themeable
32
+ * variable: it is a fact about the Gregorian calendar, and `repeat()` cannot
33
+ * take a custom property as its count.
34
+ */
35
+ export const WEEKS_PER_PANE = 6;
36
+
37
+ /**
38
+ * The picker's two size inputs.
39
+ *
40
+ * `defineConsts`, not `defineVars`: these are inlined at build time and emit
41
+ * no CSS custom property, so they are internal geometry rather than public
42
+ * theming API. That is deliberate for both of them.
43
+ *
44
+ * The day size is an accessibility floor — 44px is the comfortable minimum
45
+ * tap target on iOS and Android, and every other target in the sheet is held
46
+ * to it. Exposing it as a variable would let a theme quietly lower the floor,
47
+ * which is not a knob a design system should hand out.
48
+ *
49
+ * The wheel row height is only meaningful against the day size (see below),
50
+ * so tuning one without the other produces a picker whose panes no longer
51
+ * agree — a variable that is only safe to change in lockstep with another is
52
+ * not really a variable.
53
+ *
54
+ * If a real need to theme either appears, promoting a const to a var is an
55
+ * additive change; removing a var that consumers have started setting is not.
56
+ */
57
+ export const dateInputTouchSizes = stylex.defineConsts({
58
+ /**
59
+ * Height of one day row, and the minimum touch target of a day. 44px is the
60
+ * comfortable minimum tap target on both iOS and Android.
61
+ */
62
+ daySize: '44px',
63
+ /**
64
+ * Height of one wheel option row, and so the spacing between options and
65
+ * the height of the selection band.
66
+ *
67
+ * 28px against a day's 44px: a wheel row is scroll-first — a value is
68
+ * chosen by bringing it under the band, and tapping one is a shortcut
69
+ * rather than the mechanism — so it does not carry a day cell's tap-target
70
+ * duty, and a mis-tap costs nothing, because the neighbouring rows are the
71
+ * same control and the result is visible immediately.
72
+ *
73
+ * The row is deliberately close to the text it holds (17px), the way a
74
+ * platform picker packs them: the list should read as one continuous
75
+ * column of values, not as a stack of buttons with air between them. At
76
+ * this height ~9 options are on screen.
77
+ */
78
+ wheelItemSize: '28px',
79
+ });
80
+
81
+ // Local aliases for the derived values below. StyleX resolves a `defineConsts`
82
+ // member through an import, so consumers read `dateInputTouchSizes.*` directly
83
+ // rather than a re-exported plain const, which its static analysis cannot
84
+ // follow across files.
85
+ const DAY_SIZE = dateInputTouchSizes.daySize;
86
+ const WHEEL_ITEM_SIZE = dateInputTouchSizes.wheelItemSize;
87
+
88
+ /** Geometry derived from the sizes above, shared across the component. */
89
+ export const dateInputTouchGeometry = stylex.defineConsts({
90
+ /** One month pane — and therefore the scrollport, and the wheel body. */
91
+ paneBlockSize: `calc(${WEEKS_PER_PANE} * ${DAY_SIZE})`,
92
+ /** The six equal week rows inside a pane. */
93
+ paneRows: `repeat(${WEEKS_PER_PANE}, 1fr)`,
94
+ /**
95
+ * Padding above the first and below the last wheel option, so either end
96
+ * can come to rest under the centered selection band.
97
+ */
98
+ wheelEdgePadding: `calc((${WEEKS_PER_PANE} * ${DAY_SIZE} - ${WHEEL_ITEM_SIZE}) / 2)`,
99
+ });