@astryxdesign/core 0.5.2-canary.2da7a39 → 0.5.2-canary.2e51213

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 (375) hide show
  1. package/dist/AppShell/AppShell.d.ts +2 -2
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +10 -7
  4. package/dist/Banner/Banner.d.ts +5 -0
  5. package/dist/Banner/Banner.d.ts.map +1 -1
  6. package/dist/Banner/Banner.js +7 -2
  7. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +3 -2
  8. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  9. package/dist/Breadcrumbs/BreadcrumbItem.js +22 -15
  10. package/dist/Carousel/Carousel.d.ts.map +1 -1
  11. package/dist/Carousel/Carousel.js +2 -2
  12. package/dist/Chat/ChatComposer.d.ts +2 -2
  13. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  14. package/dist/Chat/ChatComposer.js +21 -2
  15. package/dist/Chat/ChatToolCalls.d.ts +1 -1
  16. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  17. package/dist/Chat/ChatToolCalls.js +12 -34
  18. package/dist/Chat/useChatNewMessages.js +3 -3
  19. package/dist/CheckboxList/CheckboxListItem.d.ts +5 -3
  20. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  21. package/dist/DateTimeInput/DateTimeInput.d.ts +47 -13
  22. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  23. package/dist/DateTimeInput/DateTimeInput.js +269 -162
  24. package/dist/DateTimeInput/NativeDateSegment.d.ts +24 -0
  25. package/dist/DateTimeInput/NativeDateSegment.d.ts.map +1 -0
  26. package/dist/DateTimeInput/NativeDateSegment.js +235 -0
  27. package/dist/DateTimeInput/NativeTimeSegment.d.ts +30 -0
  28. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -0
  29. package/dist/DateTimeInput/NativeTimeSegment.js +248 -0
  30. package/dist/DateTimeInput/index.d.ts +1 -1
  31. package/dist/DateTimeInput/index.d.ts.map +1 -1
  32. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +95 -0
  33. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -0
  34. package/dist/DateTimeInput/nativePickerSegmentStyles.js +103 -0
  35. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  36. package/dist/DropdownMenu/DropdownMenu.js +2 -4
  37. package/dist/Field/Field.d.ts.map +1 -1
  38. package/dist/Field/Field.js +10 -4
  39. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  40. package/dist/Field/PanelSearchInput.js +3 -5
  41. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  42. package/dist/FieldStatus/FieldStatus.js +4 -2
  43. package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
  44. package/dist/Layer/useTouchTrigger.js +2 -4
  45. package/dist/Layout/Layout.d.ts +7 -8
  46. package/dist/Layout/Layout.d.ts.map +1 -1
  47. package/dist/Layout/Layout.js +7 -8
  48. package/dist/MultiSelector/MultiSelector.d.ts +11 -1
  49. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  50. package/dist/MultiSelector/MultiSelector.js +89 -45
  51. package/dist/RadioList/RadioListItem.d.ts +30 -5
  52. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  53. package/dist/RadioList/RadioListItem.js +17 -4
  54. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  55. package/dist/Resizable/ResizeHandle.js +102 -44
  56. package/dist/Resizable/index.d.ts +2 -0
  57. package/dist/Resizable/index.d.ts.map +1 -1
  58. package/dist/Resizable/index.js +1 -0
  59. package/dist/Resizable/useResizable.d.ts +89 -8
  60. package/dist/Resizable/useResizable.d.ts.map +1 -1
  61. package/dist/Resizable/useResizable.js +521 -38
  62. package/dist/Resizable/utils.d.ts +35 -0
  63. package/dist/Resizable/utils.d.ts.map +1 -0
  64. package/dist/Resizable/utils.js +27 -0
  65. package/dist/Selector/Selector.d.ts +10 -0
  66. package/dist/Selector/Selector.d.ts.map +1 -1
  67. package/dist/Selector/Selector.js +105 -57
  68. package/dist/Selector/useSelectorPresentation.d.ts +1 -0
  69. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  70. package/dist/Selector/useSelectorPresentation.js +17 -2
  71. package/dist/Slider/Slider.d.ts.map +1 -1
  72. package/dist/Slider/Slider.js +3 -5
  73. package/dist/Spinner/Spinner.d.ts.map +1 -1
  74. package/dist/Spinner/Spinner.js +9 -10
  75. package/dist/Stepper/Step.d.ts +2 -1
  76. package/dist/Stepper/Step.d.ts.map +1 -1
  77. package/dist/Stepper/Step.js +75 -11
  78. package/dist/Stepper/Stepper.d.ts.map +1 -1
  79. package/dist/Stepper/Stepper.js +1 -0
  80. package/dist/TabList/TabList.d.ts.map +1 -1
  81. package/dist/TabList/TabList.js +2 -3
  82. package/dist/Table/index.d.ts +16 -1
  83. package/dist/Table/index.d.ts.map +1 -1
  84. package/dist/Table/index.js +4 -0
  85. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  86. package/dist/Table/plugins/columnResize/useTableColumnResize.js +8 -9
  87. package/dist/Table/plugins/rowStatus/index.d.ts +1 -1
  88. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -1
  89. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +11 -30
  90. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  91. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +108 -62
  92. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  93. package/dist/Table/plugins/sortable/useTableSortable.js +9 -1
  94. package/dist/Text/useTruncation.d.ts +1 -1
  95. package/dist/Text/useTruncation.d.ts.map +1 -1
  96. package/dist/Text/useTruncation.js +8 -7
  97. package/dist/TimeInput/TimeInput.d.ts +16 -1
  98. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  99. package/dist/TimeInput/TimeInput.js +98 -60
  100. package/dist/TimeInput/index.d.ts +1 -1
  101. package/dist/TimeInput/index.d.ts.map +1 -1
  102. package/dist/Timestamp/Timestamp.d.ts +2 -2
  103. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  104. package/dist/Timestamp/Timestamp.js +4 -145
  105. package/dist/Timestamp/formatRelativeTime.d.ts +20 -0
  106. package/dist/Timestamp/formatRelativeTime.d.ts.map +1 -0
  107. package/dist/Timestamp/formatRelativeTime.js +81 -0
  108. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  109. package/dist/Toast/ToastViewport.js +125 -77
  110. package/dist/Tokenizer/Tokenizer.d.ts +0 -38
  111. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  112. package/dist/Tokenizer/Tokenizer.js +94 -26
  113. package/dist/Tokenizer/useEndLaneReserve.d.ts +31 -0
  114. package/dist/Tokenizer/useEndLaneReserve.d.ts.map +1 -0
  115. package/dist/Tokenizer/useEndLaneReserve.js +136 -0
  116. package/dist/Tooltip/useTooltip.d.ts +1 -1
  117. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  118. package/dist/Tooltip/useTooltip.js +53 -42
  119. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  120. package/dist/Typeahead/BaseTypeahead.js +85 -30
  121. package/dist/Typeahead/Typeahead.d.ts +0 -21
  122. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  123. package/dist/Typeahead/Typeahead.js +108 -67
  124. package/dist/Typeahead/busyIndicatorLane.d.ts +62 -0
  125. package/dist/Typeahead/busyIndicatorLane.d.ts.map +1 -0
  126. package/dist/Typeahead/busyIndicatorLane.js +98 -0
  127. package/dist/astryx.css +25 -7
  128. package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -1
  129. package/dist/hooks/useFocusReturnVisibility.js +3 -5
  130. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
  131. package/dist/hooks/useIsomorphicLayoutEffect.d.ts.map +1 -1
  132. package/dist/hooks/useOverflow.js +11 -11
  133. package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
  134. package/dist/hooks/useScrollOverflow.js +7 -4
  135. package/dist/theme/defineTheme.d.ts +13 -0
  136. package/dist/theme/defineTheme.d.ts.map +1 -1
  137. package/dist/theme/defineTheme.js +7 -0
  138. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  139. package/dist/theme/generateThemeRules.js +1 -1
  140. package/dist/theme/localTokens.d.ts +18 -0
  141. package/dist/theme/localTokens.d.ts.map +1 -0
  142. package/dist/theme/localTokens.js +162 -0
  143. package/dist/utils/interactionModality.d.ts +6 -28
  144. package/dist/utils/interactionModality.d.ts.map +1 -1
  145. package/dist/utils/interactionModality.js +72 -37
  146. package/dist/utils/sharedResizeObserver.d.ts +17 -9
  147. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  148. package/dist/utils/sharedResizeObserver.js +54 -12
  149. package/locales/en.json +8 -0
  150. package/locales/pseudo.json +6 -0
  151. package/package.json +7 -2
  152. package/src/AlertDialog/useImperativeAlertDialog.test.tsx +163 -0
  153. package/src/AppShell/AppShell.doc.mjs +4 -3
  154. package/src/AppShell/AppShell.test.tsx +21 -0
  155. package/src/AppShell/AppShell.tsx +12 -7
  156. package/src/Banner/Banner.doc.mjs +5 -1
  157. package/src/Banner/Banner.tsx +12 -1
  158. package/src/BottomSheet/BottomSheet.doc.mjs +31 -0
  159. package/src/BottomSheet/BottomSheet.spec.md +178 -0
  160. package/src/Breadcrumbs/BreadcrumbItem.tsx +18 -9
  161. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +4 -1
  162. package/src/Breadcrumbs/Breadcrumbs.test.tsx +12 -0
  163. package/src/Button/Button.doc.mjs +56 -0
  164. package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
  165. package/src/Calendar/hooks/useCalendarConstraints.test.ts +153 -0
  166. package/src/Calendar/hooks/useCalendarDays.test.ts +265 -0
  167. package/src/Calendar/hooks/useCalendarNavigation.test.ts +252 -0
  168. package/src/Carousel/Carousel.test.tsx +83 -0
  169. package/src/Carousel/Carousel.tsx +8 -2
  170. package/src/Chat/ChatComposer.doc.mjs +7 -4
  171. package/src/Chat/ChatComposer.test.tsx +84 -2
  172. package/src/Chat/ChatComposer.tsx +45 -3
  173. package/src/Chat/ChatSendButton.test.tsx +189 -0
  174. package/src/Chat/ChatToolCalls.doc.mjs +1 -1
  175. package/src/Chat/ChatToolCalls.test.tsx +33 -1
  176. package/src/Chat/ChatToolCalls.tsx +13 -34
  177. package/src/Chat/useChatNewMessages.ts +3 -3
  178. package/src/Chat/useChatPasteAsToken.test.ts +191 -0
  179. package/src/CheckboxList/CheckboxList.doc.mjs +172 -31
  180. package/src/CheckboxList/CheckboxList.spec.md +215 -0
  181. package/src/CheckboxList/CheckboxList.test.tsx +45 -0
  182. package/src/CheckboxList/CheckboxListItem.doc.mjs +11 -9
  183. package/src/CheckboxList/CheckboxListItem.tsx +5 -3
  184. package/src/Collapsible/useCollapsible.test.tsx +236 -0
  185. package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
  186. package/src/CommandPalette/CommandPalette.spec.md +224 -0
  187. package/src/CommandPalette/CommandPaletteEmpty.test.tsx +101 -0
  188. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  189. package/src/ComplexSelector/ComplexSelector.spec.md +190 -0
  190. package/src/ContextMenu/ContextMenu.doc.mjs +48 -0
  191. package/src/ContextMenu/ContextMenu.spec.md +207 -0
  192. package/src/DateTimeInput/DateTimeInput.doc.mjs +43 -23
  193. package/src/DateTimeInput/DateTimeInput.tsx +388 -181
  194. package/src/DateTimeInput/DateTimeInputTouch.test.tsx +10 -2
  195. package/src/DateTimeInput/NativeDateSegment.tsx +308 -0
  196. package/src/DateTimeInput/NativePickerSegments.test.tsx +626 -0
  197. package/src/DateTimeInput/NativeTimeSegment.tsx +331 -0
  198. package/src/DateTimeInput/index.ts +1 -0
  199. package/src/DateTimeInput/nativePickerSegmentStyles.ts +120 -0
  200. package/src/Divider/Divider.doc.mjs +24 -0
  201. package/src/Divider/Divider.spec.md +157 -0
  202. package/src/DropdownMenu/DropdownMenu.doc.mjs +104 -0
  203. package/src/DropdownMenu/DropdownMenu.spec.md +246 -0
  204. package/src/DropdownMenu/DropdownMenu.tsx +2 -4
  205. package/src/Field/Field.doc.mjs +1 -0
  206. package/src/Field/Field.test.tsx +39 -0
  207. package/src/Field/Field.tsx +18 -1
  208. package/src/Field/InputClearButton.test.tsx +68 -6
  209. package/src/Field/PanelSearchInput.tsx +3 -5
  210. package/src/FieldStatus/FieldStatus.doc.mjs +21 -0
  211. package/src/FieldStatus/FieldStatus.spec.md +213 -0
  212. package/src/FieldStatus/FieldStatus.test.tsx +17 -0
  213. package/src/FieldStatus/FieldStatus.tsx +7 -2
  214. package/src/Grid/Grid.doc.mjs +19 -0
  215. package/src/Grid/Grid.spec.md +162 -0
  216. package/src/Icon/Icon.doc.mjs +196 -48
  217. package/src/Icon/Icon.spec.md +192 -0
  218. package/src/IconButton/IconButton.doc.mjs +38 -0
  219. package/src/Kbd/Kbd.doc.mjs +18 -0
  220. package/src/Kbd/Kbd.spec.md +142 -0
  221. package/src/Layer/useTouchTrigger.ts +2 -4
  222. package/src/Layout/Layout.doc.mjs +57 -17
  223. package/src/Layout/Layout.spec.md +235 -0
  224. package/src/Layout/Layout.test.tsx +1 -1
  225. package/src/Layout/Layout.tsx +7 -8
  226. package/src/Layout/LayoutHeader.doc.mjs +9 -0
  227. package/src/Layout/LayoutPanel.doc.mjs +4 -3
  228. package/src/Lightbox/Lightbox.doc.mjs +149 -33
  229. package/src/Lightbox/Lightbox.spec.md +198 -0
  230. package/src/Lightbox/useLightbox.test.tsx +263 -0
  231. package/src/Markdown/Markdown.doc.mjs +60 -0
  232. package/src/Markdown/Markdown.spec.md +207 -0
  233. package/src/MobileNav/MobileNav.doc.mjs +140 -32
  234. package/src/MobileNav/MobileNav.spec.md +209 -0
  235. package/src/MoreMenu/MoreMenu.doc.mjs +42 -0
  236. package/src/MoreMenu/MoreMenu.spec.md +222 -0
  237. package/src/MultiSelector/MultiSelector.doc.mjs +124 -1
  238. package/src/MultiSelector/MultiSelector.spec.md +293 -0
  239. package/src/MultiSelector/MultiSelector.test.tsx +305 -2
  240. package/src/MultiSelector/MultiSelector.tsx +124 -55
  241. package/src/NavIcon/NavIcon.doc.mjs +17 -0
  242. package/src/NavIcon/NavIcon.spec.md +134 -0
  243. package/src/NavMenu/NavHeadingMenu.spec.md +210 -0
  244. package/src/NavMenu/NavMenu.doc.mjs +37 -0
  245. package/src/Outline/Outline.doc.mjs +35 -0
  246. package/src/Outline/Outline.spec.md +162 -0
  247. package/src/OverflowList/OverflowList.doc.mjs +24 -0
  248. package/src/OverflowList/OverflowList.spec.md +157 -0
  249. package/src/Pagination/Pagination.doc.mjs +74 -0
  250. package/src/Pagination/Pagination.spec.md +226 -0
  251. package/src/Popover/Popover.doc.mjs +264 -65
  252. package/src/Popover/Popover.spec.md +421 -0
  253. package/src/Popover/usePopover.doc.mjs +208 -33
  254. package/src/PowerSearch/PowerSearch.spec.md +291 -0
  255. package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +253 -0
  256. package/src/PowerSearch/PowerSearchToken.test.tsx +228 -0
  257. package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
  258. package/src/ProgressBar/ProgressBar.spec.md +261 -0
  259. package/src/RadioList/RadioList.test.tsx +136 -2
  260. package/src/RadioList/RadioListItem.doc.mjs +30 -11
  261. package/src/RadioList/RadioListItem.tsx +44 -8
  262. package/src/Resizable/Resizable.doc.mjs +130 -24
  263. package/src/Resizable/Resizable.spec.md +246 -0
  264. package/src/Resizable/ResizeHandle.test.tsx +177 -56
  265. package/src/Resizable/ResizeHandle.tsx +128 -45
  266. package/src/Resizable/index.ts +2 -0
  267. package/src/Resizable/shippedDirections.test.ts +462 -0
  268. package/src/Resizable/useResizable.doc.mjs +124 -29
  269. package/src/Resizable/useResizable.test.ts +1277 -3
  270. package/src/Resizable/useResizable.ts +760 -48
  271. package/src/Resizable/utils.test.ts +74 -0
  272. package/src/Resizable/utils.ts +35 -0
  273. package/src/Section/Section.doc.mjs +17 -0
  274. package/src/Section/Section.spec.md +139 -0
  275. package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
  276. package/src/SegmentedControl/SegmentedControl.spec.md +152 -0
  277. package/src/Selector/Selector.doc.mjs +133 -97
  278. package/src/Selector/Selector.spec.md +300 -0
  279. package/src/Selector/Selector.test.tsx +318 -1
  280. package/src/Selector/Selector.tsx +127 -60
  281. package/src/Selector/useSelectorPresentation.ts +24 -4
  282. package/src/SideNav/SideNav.test.tsx +3 -3
  283. package/src/SideNav/SideNavCollapseButton.doc.mjs +8 -0
  284. package/src/SideNav/SideNavItem.doc.mjs +1 -1
  285. package/src/Skeleton/Skeleton.doc.mjs +12 -0
  286. package/src/Skeleton/Skeleton.spec.md +127 -0
  287. package/src/Slider/Slider.doc.mjs +184 -30
  288. package/src/Slider/Slider.spec.md +224 -0
  289. package/src/Slider/Slider.tsx +2 -5
  290. package/src/Spinner/Spinner.doc.mjs +5 -5
  291. package/src/Spinner/Spinner.test.tsx +32 -7
  292. package/src/Spinner/Spinner.tsx +46 -14
  293. package/src/Stack/Stack.doc.mjs +22 -0
  294. package/src/Stack/Stack.spec.md +155 -0
  295. package/src/StatusDot/StatusDot.doc.mjs +17 -0
  296. package/src/StatusDot/StatusDot.spec.md +138 -0
  297. package/src/Stepper/Step.doc.mjs +1 -1
  298. package/src/Stepper/Step.tsx +118 -5
  299. package/src/Stepper/Stepper.doc.mjs +51 -1
  300. package/src/Stepper/Stepper.spec.md +245 -0
  301. package/src/Stepper/Stepper.test.tsx +315 -0
  302. package/src/Stepper/Stepper.tsx +8 -0
  303. package/src/Switch/Switch.doc.mjs +40 -0
  304. package/src/Switch/Switch.spec.md +175 -0
  305. package/src/TabList/TabList.tsx +2 -3
  306. package/src/Table/Table.doc.mjs +102 -24
  307. package/src/Table/Table.perf.test.tsx +51 -1
  308. package/src/Table/Table.spec.md +325 -0
  309. package/src/Table/TableHeaderCell.test.tsx +291 -0
  310. package/src/Table/index.ts +19 -4
  311. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -12
  312. package/src/Table/plugins/filtering/useTableFilterState.test.tsx +143 -0
  313. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +2 -1
  314. package/src/Table/plugins/rowStatus/index.ts +1 -4
  315. package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +92 -0
  316. package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +354 -0
  317. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +431 -56
  318. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +155 -84
  319. package/src/Table/plugins/sortable/useTableSortable.test.tsx +72 -0
  320. package/src/Table/plugins/sortable/useTableSortable.tsx +18 -1
  321. package/src/Table/useTableGroupedRows.doc.mjs +2 -2
  322. package/src/Table/useTableRowStatus.doc.mjs +5 -5
  323. package/src/Text/Text.doc.mjs +190 -43
  324. package/src/Text/Text.spec.md +206 -0
  325. package/src/Text/useTruncation.ts +8 -7
  326. package/src/TextArea/TextArea.doc.mjs +255 -45
  327. package/src/TextArea/TextArea.spec.md +205 -0
  328. package/src/TimeInput/NativeTimeInput.test.tsx +269 -0
  329. package/src/TimeInput/TimeInput.doc.mjs +47 -23
  330. package/src/TimeInput/TimeInput.tsx +109 -50
  331. package/src/TimeInput/index.ts +1 -0
  332. package/src/Timestamp/Timestamp.doc.mjs +2 -2
  333. package/src/Timestamp/Timestamp.test.tsx +75 -3
  334. package/src/Timestamp/Timestamp.tsx +7 -153
  335. package/src/Timestamp/formatRelativeTime.ts +96 -0
  336. package/src/Toast/ToastViewport.test.tsx +194 -0
  337. package/src/Toast/ToastViewport.tsx +136 -58
  338. package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
  339. package/src/Tokenizer/Tokenizer.doc.mjs +2 -0
  340. package/src/Tokenizer/Tokenizer.spec.md +293 -0
  341. package/src/Tokenizer/Tokenizer.test.tsx +335 -1
  342. package/src/Tokenizer/Tokenizer.tsx +129 -46
  343. package/src/Tokenizer/useEndLaneReserve.ts +146 -0
  344. package/src/Toolbar/Toolbar.doc.mjs +19 -0
  345. package/src/Toolbar/Toolbar.spec.md +174 -0
  346. package/src/Tooltip/Tooltip.doc.mjs +17 -0
  347. package/src/Tooltip/Tooltip.spec.md +142 -0
  348. package/src/Tooltip/Tooltip.test.tsx +21 -1
  349. package/src/Tooltip/useTooltip.test.tsx +318 -0
  350. package/src/Tooltip/useTooltip.tsx +60 -39
  351. package/src/TreeList/TreeList.doc.mjs +58 -0
  352. package/src/TreeList/TreeList.spec.md +224 -0
  353. package/src/Typeahead/BaseTypeahead.tsx +96 -25
  354. package/src/Typeahead/Typeahead.doc.mjs +89 -1
  355. package/src/Typeahead/Typeahead.spec.md +257 -0
  356. package/src/Typeahead/Typeahead.test.tsx +502 -1
  357. package/src/Typeahead/Typeahead.tsx +187 -69
  358. package/src/Typeahead/TypeaheadItem.test.tsx +93 -2
  359. package/src/Typeahead/busyIndicatorLane.tsx +109 -0
  360. package/src/__tests__/stylexDeclarations.ts +51 -0
  361. package/src/hooks/useFocusReturnVisibility.ts +3 -5
  362. package/src/hooks/useOverflow.ts +11 -11
  363. package/src/hooks/useScrollLock.test.ts +103 -0
  364. package/src/hooks/useScrollOverflow.ts +7 -4
  365. package/src/i18n/useTranslator.test.tsx +197 -0
  366. package/src/theme/defineTheme.test.ts +227 -0
  367. package/src/theme/defineTheme.ts +30 -0
  368. package/src/theme/derivedVarRegistry.test.ts +6 -0
  369. package/src/theme/generateThemeRules.ts +4 -1
  370. package/src/theme/localTokens.ts +272 -0
  371. package/src/theme/themingTargets.test.ts +126 -28
  372. package/src/utils/interactionModality.test.ts +83 -0
  373. package/src/utils/interactionModality.ts +88 -36
  374. package/src/utils/sharedResizeObserver.test.ts +124 -5
  375. package/src/utils/sharedResizeObserver.ts +60 -14
@@ -4,11 +4,14 @@
4
4
 
5
5
  /**
6
6
  * @file DateTimeInput.tsx
7
- * @input Uses React, Field, Calendar, usePopover, useAnnounce, time parsing utilities, TouchDateTimeField, StyleX intrinsic flex layout
8
- * @output Exports DateTimeInput component, DateTimeInputProps
7
+ * @input Uses React, Field, Calendar, NativeDateSegment, NativeTimeSegment, TouchDateTimeField, usePopover, useAnnounce, time parsing utilities, StyleX intrinsic flex layout
8
+ * @output Exports DateTimeInput component, DateTimeInputProps, and native picker type
9
9
  * @position Core implementation; consumed by index.ts, tested by DateTimeInput.test.tsx
10
10
  *
11
11
  * SYNC: When modified, update these files to stay in sync:
12
+ * - /packages/core/src/DateTimeInput/NativeDateSegment.tsx (native date surface)
13
+ * - /packages/core/src/DateTimeInput/NativeTimeSegment.tsx (native time surface)
14
+ * - /packages/core/src/DateTimeInput/NativePickerSegments.test.tsx (native surface tests)
12
15
  * - /packages/core/src/DateTimeInput/DateTimeInput.doc.mjs (props table, features, implementation notes)
13
16
  * - /packages/core/src/DateTimeInput/DateTimeInput.test.tsx (desktop tests for new/changed behavior)
14
17
  * - /packages/core/src/DateTimeInput/DateTimeInputTouch.test.tsx (touch-surface tests)
@@ -40,6 +43,8 @@ import { themeProps } from "../utils/themeProps.js";
40
43
  import { focusOutlineStyles } from "../utils/focusOutline.stylex.js";
41
44
  import { useLocale, useTranslator } from "../i18n/index.js";
42
45
  import { useMergedRefs } from "../hooks/useMergedRefs.js";
46
+ import { NativeDateSegment } from "./NativeDateSegment.js";
47
+ import { NativeTimeSegment } from "./NativeTimeSegment.js";
43
48
  import { TouchDateTimeField } from "./TouchDateTimeField.js";
44
49
 
45
50
  /** Supported minute increments for arrow-key stepping of the time field. */
@@ -48,7 +53,7 @@ import { TouchDateTimeField } from "./TouchDateTimeField.js";
48
53
  * Supported minute cadences for the preset-time dropdown. Every value divides
49
54
  * an hour evenly, so each option lands on a round clock time.
50
55
  */
51
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
56
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
52
57
  // Two 196px segments plus the 8px gap fit at exactly 400px. Below that,
53
58
  // flex wrapping moves each growing segment onto its own full-width row.
54
59
  const HORIZONTAL_SEGMENT_BASIS = 196;
@@ -59,6 +64,10 @@ const styles = {
59
64
  kOIVth: "x1txdalj",
60
65
  $$css: true
61
66
  },
67
+ nativeRow: {
68
+ k7Eaqz: "xfb3i0g",
69
+ $$css: true
70
+ },
62
71
  iconButton: {
63
72
  k1xSpc: "x78zum5",
64
73
  kGNEyG: "x6s0dn4",
@@ -134,6 +143,7 @@ const timeOptionSizeStyles = {
134
143
  $$css: true
135
144
  }
136
145
  };
146
+ const TOUCH_POINTER_QUERY = '(pointer: coarse)';
137
147
  const sizeStyles = {
138
148
  sm: {
139
149
  kZKoxP: "x6k0iem",
@@ -225,6 +235,7 @@ function PointerDateTimeField({
225
235
  labelTooltip,
226
236
  numberOfMonths = 1,
227
237
  weekStartsOn,
238
+ nativePicker = 'touch',
228
239
  width,
229
240
  xstyle,
230
241
  className,
@@ -234,6 +245,12 @@ function PointerDateTimeField({
234
245
  }) {
235
246
  const t = useTranslator();
236
247
  const locale = useLocale();
248
+ const isTouch = useMediaQuery(TOUCH_POINTER_QUERY);
249
+ const usesNativePicker = nativePicker === 'always' || nativePicker === 'touch' && isTouch;
250
+ // iOS's native time picker has no seconds wheel, treats step as validation
251
+ // rather than picker cadence, and cannot express our preset-time list. Keep
252
+ // the Astryx time field for those explicit contracts instead of losing them.
253
+ const usesNativeTimePicker = usesNativePicker && !hasSeconds && timeIncrement === 1 && timeOptionInterval === undefined;
237
254
  const isEffectivelyRequired = useResolvedRequired({
238
255
  isRequired,
239
256
  isOptional
@@ -250,14 +267,20 @@ function PointerDateTimeField({
250
267
  const descriptionID = useId();
251
268
  const statusMessageID = useId();
252
269
  const dateInputRef = useRef(null);
270
+ const mergedDateInputRef = useMergedRefs(ref, dateInputRef);
253
271
  const timeInputRef = useRef(null);
254
272
  const timeContainerRef = useRef(null);
255
273
  const calendarRef = useRef(null);
256
274
  const lastFiredDateRef = useRef(undefined);
257
275
  const [, startTransition] = useTransition();
258
276
  const [optimisticValue, setOptimisticValue] = useOptimistic(value);
259
- const isBusy = isLoading || optimisticValue !== value;
260
- const isEffectivelyDisabled = isDisabled || isBusy;
277
+ const isPendingChange = optimisticValue !== value;
278
+ const isBusy = isLoading || isPendingChange;
279
+ // A native wheel emits several edits while it remains open. Disabling the
280
+ // control after the first optimistic edit detaches the iOS picker, so pending
281
+ // changeAction work stays interactive just like TouchDateTimeField. Explicit
282
+ // loading still disables every surface.
283
+ const isEffectivelyDisabled = isDisabled || isLoading || !usesNativePicker && isPendingChange;
261
284
 
262
285
  // Disabled-reason tooltip. Disabled controls swallow pointer events, so the
263
286
  // tooltip listeners attach to the outer row container (which already exists)
@@ -336,6 +359,17 @@ function PointerDateTimeField({
336
359
  // --- Time input state ---
337
360
  const [timePendingInput, setTimePendingInput] = useState(null);
338
361
  const [isTimeFocused, setIsTimeFocused] = useState(false);
362
+ // Native date and time are separate OS pickers. Hold a time chosen before the
363
+ // date exists, then combine it when the date picker commits.
364
+ const [nativeTimeDraft, setNativeTimeDraft] = useState(undefined);
365
+ const previousNativeValueRef = useRef(optimisticValue);
366
+ if (optimisticValue !== previousNativeValueRef.current) {
367
+ previousNativeValueRef.current = optimisticValue;
368
+ if (nativeTimeDraft !== undefined) {
369
+ setNativeTimeDraft(undefined);
370
+ }
371
+ }
372
+ const nativeTimeValue = nativeTimeDraft ?? valueParts.time;
339
373
  const formatDisplayTime = hourFormat === '12h' ? formatDisplayTime12h : formatDisplayTime24h;
340
374
  const timeDisplayValue = useMemo(() => {
341
375
  if (timePendingInput !== null) {
@@ -371,9 +405,9 @@ function PointerDateTimeField({
371
405
  // Opt-in: without timeOptionInterval the time field keeps exactly the
372
406
  // semantics it shipped with — a plain text input, no combobox role, no
373
407
  // second listbox in the accessibility tree.
374
- const hasTimeOptions = timeOptionInterval !== undefined;
408
+ const hasTimeOptions = !usesNativeTimePicker && timeOptionInterval !== undefined;
375
409
  const timeOptions = useMemo(() => {
376
- if (timeOptionInterval === undefined) {
410
+ if (!hasTimeOptions || timeOptionInterval === undefined) {
377
411
  return [];
378
412
  }
379
413
  const options = [];
@@ -394,11 +428,11 @@ function PointerDateTimeField({
394
428
  });
395
429
  }
396
430
  return options;
397
- }, [timeOptionInterval, hasSeconds, timeMin, timeMax, formatDisplayTime]);
431
+ }, [hasTimeOptions, timeOptionInterval, hasSeconds, timeMin, timeMax, formatDisplayTime]);
398
432
 
399
433
  // --- Unified change handler ---
400
434
  const fireChange = useCallback(newValue => {
401
- if (isBusy) {
435
+ if (isEffectivelyDisabled) {
402
436
  return;
403
437
  }
404
438
  onChange(newValue);
@@ -408,7 +442,7 @@ function PointerDateTimeField({
408
442
  await changeAction(newValue);
409
443
  });
410
444
  }
411
- }, [isBusy, onChange, changeAction, startTransition, setOptimisticValue]);
445
+ }, [isEffectivelyDisabled, onChange, changeAction, startTransition, setOptimisticValue]);
412
446
 
413
447
  // --- Popover ---
414
448
  const popover = usePopover({
@@ -446,7 +480,7 @@ function PointerDateTimeField({
446
480
 
447
481
  // --- Date handlers ---
448
482
  const handleDateChange = useCallback((newDate, source) => {
449
- const currentTime = valueParts.time ?? getDefaultTime(hasSeconds);
483
+ const currentTime = valueParts.time ?? nativeTimeDraft ?? getDefaultTime(hasSeconds);
450
484
  let effectiveTime = currentTime;
451
485
  if (minParts.date && newDate === minParts.date && minParts.time) {
452
486
  if (!isTimeInRange(effectiveTime, minParts.time, undefined)) {
@@ -466,7 +500,34 @@ function PointerDateTimeField({
466
500
  setDatePendingInput(null);
467
501
  popover.hide();
468
502
  }
469
- }, [valueParts.time, hasSeconds, minParts, maxParts, fireChange, popover]);
503
+ }, [valueParts.time, nativeTimeDraft, hasSeconds, minParts, maxParts, fireChange, popover]);
504
+ const handleNativeDateChange = useCallback(newDate => {
505
+ if (newDate === undefined) {
506
+ fireChange(undefined);
507
+ return;
508
+ }
509
+ handleDateChange(newDate, 'input');
510
+ }, [fireChange, handleDateChange]);
511
+ const handleNativeTimeChange = useCallback(newTime => {
512
+ if (newTime === undefined) {
513
+ setNativeTimeDraft(undefined);
514
+ if (optimisticValue !== undefined) {
515
+ fireChange(undefined);
516
+ }
517
+ return;
518
+ }
519
+ if (valueParts.date) {
520
+ const combined = combineDateTime(valueParts.date, newTime);
521
+ if (combined) {
522
+ fireChange(combined);
523
+ }
524
+ return;
525
+ }
526
+ // A datetime cannot be emitted until its date exists, but the OS time
527
+ // picker is independently reachable. Preserve this draft for the date
528
+ // commit instead of making the user's first choice disappear.
529
+ setNativeTimeDraft(newTime);
530
+ }, [fireChange, optimisticValue, valueParts.date]);
470
531
  const handleDateInputChange = useCallback(e => {
471
532
  // With a disabledMessage the input drops `disabled` for focusability, so
472
533
  // guard value mutation explicitly (readOnly also blocks typing).
@@ -875,9 +936,12 @@ function PointerDateTimeField({
875
936
 
876
937
  // --- Clear ---
877
938
  const handleClear = useCallback(() => {
939
+ setNativeTimeDraft(undefined);
878
940
  fireChange(undefined);
879
- dateInputRef.current?.focus();
880
- }, [fireChange]);
941
+ if (!usesNativePicker) {
942
+ dateInputRef.current?.focus();
943
+ }
944
+ }, [fireChange, usesNativePicker]);
881
945
 
882
946
  // Focus time input when clicking wrapper padding/icon
883
947
  const {
@@ -912,81 +976,98 @@ function PointerDateTimeField({
912
976
  size,
913
977
  status: status?.type ?? null,
914
978
  disabled: isDisabled ? 'disabled' : null
915
- }), stylex.props(styles.row, xstyle), className, style),
979
+ }), stylex.props(styles.row, usesNativePicker && styles.nativeRow, xstyle), className, style),
916
980
  children: [/*#__PURE__*/_jsxs("div", {
917
- ref: popover.triggerRef,
981
+ ref: usesNativePicker ? undefined : popover.triggerRef,
918
982
  ...mergeProps(themeProps('date-time-input-date-segment', {
919
983
  size,
920
984
  status: status?.type ?? null
921
985
  }), stylex.props(inputWrapperStyles.base, sizeStyles[size], styles.dateWrapper, isEffectivelyDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isEffectivelyDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type])),
922
- children: [/*#__PURE__*/_jsx("button", {
923
- type: "button",
924
- onClick: handleCalendarToggle,
925
- disabled: isEffectivelyDisabled,
926
- "aria-label": popover.isOpen ? t('@astryx.dateInput.toggleCalendarClose') : t('@astryx.dateInput.openCalendar'),
927
- ...stylex.props(focusOutlineStyles.focusVisible, styles.iconButton, isEffectivelyDisabled && styles.iconButtonDisabled),
928
- children: /*#__PURE__*/_jsx(Icon, {
929
- icon: "calendar",
930
- size: "sm",
931
- color: "secondary"
932
- // Stable theme target on the calendar toggle glyph, so a theme
933
- // can restyle just this icon (color, size, hover) — and each
934
- // open/closed state — via `defineTheme`, mirroring
935
- // `date-input-toggle-icon`. Same-element rules in
936
- // @layer astryx-theme win over the icon's own base color/size,
937
- // which a segment-level target could not reach.
938
- ,
939
- ...themeProps('date-time-input-toggle-icon', {
940
- state: popover.isOpen ? 'expanded' : 'collapsed'
941
- })
942
- })
943
- }), /*#__PURE__*/_jsx("input", {
944
- ref: useMergedRefs(ref, dateInputRef),
986
+ children: [usesNativePicker ? /*#__PURE__*/_jsx(NativeDateSegment, {
945
987
  id: dateInputId,
946
- type: "text",
947
- role: "combobox",
948
- value: dateDisplayValue,
949
- onChange: handleDateInputChange,
950
- onBlur: handleDateBlur,
951
- onClick: handleDateInputClick,
952
- onKeyDown: handleDateKeyDown,
953
- placeholder: placeholder
954
- // With a disabledMessage the input keeps focusability via
955
- // aria-disabled so the reason is focus-discoverable; typing is
956
- // blocked with readOnly and the mutation guards, and calendar
957
- // activation is blocked by the isEffectivelyDisabled guards.
958
- ,
959
- disabled: isEffectivelyDisabled && !showsDisabledMessage,
960
- "aria-disabled": showsDisabledMessage ? 'true' : undefined,
961
- readOnly: showsDisabledMessage || undefined,
962
- "aria-describedby": ariaDescribedBy,
963
- "aria-required": isEffectivelyRequired ? 'true' : undefined,
964
- "aria-invalid": status?.type === 'error' || !isDateInputValid ? 'true' : undefined,
965
- "aria-busy": isBusy || undefined,
966
- "aria-expanded": popover.isOpen,
967
- "aria-haspopup": "dialog",
968
- "aria-controls": popover.isOpen ? popover.id : undefined,
969
- "aria-autocomplete": "none",
970
- autoComplete: "off",
971
- ...{
972
- 0: {
973
- className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5"
974
- },
975
- 2: {
976
- className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5 xt0e3qv"
977
- },
978
- 1: {
979
- className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx xjbqb8w x1a2a7pz xeyghm5 xv1l7n4"
980
- },
981
- 3: {
982
- className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx xjbqb8w x1a2a7pz xeyghm5 xt0e3qv xv1l7n4"
983
- }
984
- }[!!isEffectivelyDisabled << 1 | !!!isDateInputValid << 0]
985
- }), /*#__PURE__*/_jsx(VisuallyHidden, {
986
- as: "div",
987
- role: "alert",
988
- "aria-live": "assertive",
989
- children: !isDateInputValid ? t('@astryx.dateInput.invalidDate') : ''
988
+ inputRef: mergedDateInputRef,
989
+ value: valueParts.date,
990
+ onChange: handleNativeDateChange,
991
+ placeholder: placeholder,
992
+ min: calendarMin,
993
+ max: calendarMax,
994
+ isDateDisabled: isDateDisabled,
995
+ isEffectivelyDisabled: isEffectivelyDisabled,
996
+ hasDisabledMessage: showsDisabledMessage,
997
+ isEffectivelyRequired: isEffectivelyRequired,
998
+ isBusy: isBusy,
999
+ statusType: status?.type,
1000
+ ariaDescribedBy: ariaDescribedBy
1001
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
1002
+ children: [/*#__PURE__*/_jsx("button", {
1003
+ type: "button",
1004
+ onClick: handleCalendarToggle,
1005
+ disabled: isEffectivelyDisabled,
1006
+ "aria-label": popover.isOpen ? t('@astryx.dateInput.toggleCalendarClose') : t('@astryx.dateInput.openCalendar'),
1007
+ ...stylex.props(focusOutlineStyles.focusVisible, styles.iconButton, isEffectivelyDisabled && styles.iconButtonDisabled),
1008
+ children: /*#__PURE__*/_jsx(Icon, {
1009
+ icon: "calendar",
1010
+ size: "sm",
1011
+ color: "secondary"
1012
+ // Stable theme target on the calendar toggle glyph, so a theme
1013
+ // can restyle just this icon (color, size, hover) — and each
1014
+ // open/closed state — via `defineTheme`, mirroring
1015
+ // `date-input-toggle-icon`. Same-element rules in
1016
+ // @layer astryx-theme win over the icon's own base color/size,
1017
+ // which a segment-level target could not reach.
1018
+ ,
1019
+ ...themeProps('date-time-input-toggle-icon', {
1020
+ state: popover.isOpen ? 'expanded' : 'collapsed'
1021
+ })
1022
+ })
1023
+ }), /*#__PURE__*/_jsx("input", {
1024
+ ref: mergedDateInputRef,
1025
+ id: dateInputId,
1026
+ type: "text",
1027
+ role: "combobox",
1028
+ value: dateDisplayValue,
1029
+ onChange: handleDateInputChange,
1030
+ onBlur: handleDateBlur,
1031
+ onClick: handleDateInputClick,
1032
+ onKeyDown: handleDateKeyDown,
1033
+ placeholder: placeholder
1034
+ // With a disabledMessage the input keeps focusability via
1035
+ // aria-disabled so the reason is focus-discoverable; typing is
1036
+ // blocked with readOnly and the mutation guards, and calendar
1037
+ // activation is blocked by the isEffectivelyDisabled guards.
1038
+ ,
1039
+ disabled: isEffectivelyDisabled && !showsDisabledMessage,
1040
+ "aria-disabled": showsDisabledMessage ? 'true' : undefined,
1041
+ readOnly: showsDisabledMessage || undefined,
1042
+ "aria-describedby": ariaDescribedBy,
1043
+ "aria-required": isEffectivelyRequired ? 'true' : undefined,
1044
+ "aria-invalid": status?.type === 'error' || !isDateInputValid ? 'true' : undefined,
1045
+ "aria-busy": isBusy || undefined,
1046
+ "aria-expanded": popover.isOpen,
1047
+ "aria-haspopup": "dialog",
1048
+ "aria-controls": popover.isOpen ? popover.id : undefined,
1049
+ "aria-autocomplete": "none",
1050
+ autoComplete: "off",
1051
+ ...{
1052
+ 0: {
1053
+ className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5"
1054
+ },
1055
+ 2: {
1056
+ className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5 xt0e3qv"
1057
+ },
1058
+ 1: {
1059
+ className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx xjbqb8w x1a2a7pz xeyghm5 xv1l7n4"
1060
+ },
1061
+ 3: {
1062
+ className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx xjbqb8w x1a2a7pz xeyghm5 xt0e3qv xv1l7n4"
1063
+ }
1064
+ }[!!isEffectivelyDisabled << 1 | !!!isDateInputValid << 0]
1065
+ }), /*#__PURE__*/_jsx(VisuallyHidden, {
1066
+ as: "div",
1067
+ role: "alert",
1068
+ "aria-live": "assertive",
1069
+ children: !isDateInputValid ? t('@astryx.dateInput.invalidDate') : ''
1070
+ })]
990
1071
  }), hasClear && value !== undefined && !isEffectivelyDisabled && /*#__PURE__*/_jsx(InputClearButton, {
991
1072
  label: t('@astryx.dateInput.clear', {
992
1073
  label
@@ -995,10 +1076,10 @@ function PointerDateTimeField({
995
1076
  }), isBusy && /*#__PURE__*/_jsx(Spinner, {
996
1077
  size: "sm"
997
1078
  }), statusIcon]
998
- }), /*#__PURE__*/_jsxs("div", {
1079
+ }), /*#__PURE__*/_jsx("div", {
999
1080
  ref: timeContainerRef,
1000
- onClick: handleTimeWrapperClick,
1001
- onMouseUp: handleTimeWrapperMouseUp
1081
+ onClick: usesNativeTimePicker ? undefined : handleTimeWrapperClick,
1082
+ onMouseUp: usesNativeTimePicker ? undefined : handleTimeWrapperMouseUp
1002
1083
  // On the wrapper, not just the input: clicking the clock icon or the
1003
1084
  // padding is routed to the input as a synthetic click, which would
1004
1085
  // otherwise open the list mid-gesture and let light dismiss eat it.
@@ -1008,75 +1089,97 @@ function PointerDateTimeField({
1008
1089
  size,
1009
1090
  status: status?.type ?? null
1010
1091
  }), stylex.props(inputWrapperStyles.base, sizeStyles[size], styles.timeWrapper, isEffectivelyDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isEffectivelyDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type])),
1011
- children: [/*#__PURE__*/_jsx("div", {
1012
- ...{
1013
- className: "x78zum5 x6s0dn4 xl56j7k x2lah0s"
1014
- },
1015
- children: /*#__PURE__*/_jsx(Icon, {
1016
- icon: "clock",
1017
- size: "sm",
1018
- color: "secondary"
1019
- // Stable theme target on the leading clock glyph, so a theme can
1020
- // restyle just this icon (color, size) via `defineTheme`. The
1021
- // time segment has no toggle button — the clock is a static
1022
- // leading affordance — so this carries no interactive state.
1023
- ,
1024
- ...themeProps('date-time-input-clock-icon')
1025
- })
1026
- }), /*#__PURE__*/_jsx("input", {
1027
- ref: timeInputRef,
1092
+ children: usesNativeTimePicker ? /*#__PURE__*/_jsx(NativeTimeSegment, {
1028
1093
  id: timeInputId,
1029
- type: "text",
1030
- value: timeDisplayValue,
1031
- onChange: handleTimeInputChange,
1032
- onFocus: handleTimeFocus,
1033
- onBlur: handleTimeBlur,
1034
- onKeyDown: handleTimeKeyDown,
1035
- onClick: hasTimeOptions ? showTimeOptions : undefined,
1036
- onPointerDown: hasTimeOptions ? markTimePointerActive : undefined,
1037
- placeholder: resolvedTimePlaceholder
1038
- // Combobox semantics appear only with the dropdown opted in, so a
1039
- // field without it keeps a single combobox on the date half.
1040
- ,
1041
- role: hasTimeOptions ? 'combobox' : undefined,
1042
- "aria-expanded": hasTimeOptions ? timePopover.isOpen : undefined,
1043
- "aria-controls": hasTimeOptions && timePopover.isOpen ? timeListboxId : undefined,
1044
- "aria-autocomplete": hasTimeOptions ? 'list' : undefined,
1045
- "aria-activedescendant": hasTimeOptions && timePopover.isOpen && activeTimeIndex >= 0 && activeTimeIndex < timeOptions.length ? timeOptionId(activeTimeIndex) : undefined
1046
- // With a disabledMessage the input keeps focusability via
1047
- // aria-disabled so the reason is focus-discoverable; typing is
1048
- // blocked with readOnly and the mutation guards.
1049
- ,
1050
- disabled: isEffectivelyDisabled && !showsDisabledMessage,
1051
- "aria-disabled": showsDisabledMessage ? 'true' : undefined,
1052
- readOnly: showsDisabledMessage || undefined,
1053
- "aria-label": resolvedTimeLabel,
1054
- "aria-describedby": ariaDescribedBy,
1055
- "aria-required": isEffectivelyRequired ? 'true' : undefined,
1056
- "aria-invalid": status?.type === 'error' || !isTimeInputValid ? 'true' : undefined,
1057
- "aria-busy": isBusy || undefined,
1058
- ...{
1059
- 0: {
1060
- className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5"
1061
- },
1062
- 2: {
1063
- className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5 xt0e3qv"
1064
- },
1065
- 1: {
1066
- className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx xjbqb8w x1a2a7pz xeyghm5 xv1l7n4"
1094
+ inputRef: timeInputRef,
1095
+ value: nativeTimeValue,
1096
+ onChange: handleNativeTimeChange,
1097
+ placeholder: timePlaceholder,
1098
+ inputLabel: resolvedTimeLabel,
1099
+ openPickerLabel: t('@astryx.dateTimeInput.openTimePicker', {
1100
+ label: resolvedTimeLabel
1101
+ }),
1102
+ iconThemeProps: themeProps('date-time-input-clock-icon'),
1103
+ min: timeMin,
1104
+ max: timeMax,
1105
+ hourFormat: hourFormat,
1106
+ isEffectivelyDisabled: isEffectivelyDisabled,
1107
+ hasDisabledMessage: showsDisabledMessage,
1108
+ isEffectivelyRequired: isEffectivelyRequired,
1109
+ isBusy: isBusy,
1110
+ statusType: status?.type,
1111
+ ariaDescribedBy: ariaDescribedBy
1112
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
1113
+ children: [/*#__PURE__*/_jsx("div", {
1114
+ ...{
1115
+ className: "x78zum5 x6s0dn4 xl56j7k x2lah0s"
1067
1116
  },
1068
- 3: {
1069
- className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx xjbqb8w x1a2a7pz xeyghm5 xt0e3qv xv1l7n4"
1070
- }
1071
- }[!!isEffectivelyDisabled << 1 | !!!isTimeInputValid << 0]
1072
- }), /*#__PURE__*/_jsx(VisuallyHidden, {
1073
- as: "div",
1074
- role: "alert",
1075
- "aria-live": "assertive",
1076
- children: !isTimeInputValid ? t('@astryx.timeInput.invalidTime') : ''
1077
- })]
1117
+ children: /*#__PURE__*/_jsx(Icon, {
1118
+ icon: "clock",
1119
+ size: "sm",
1120
+ color: "secondary"
1121
+ // Stable theme target on the leading clock glyph, so a theme can
1122
+ // restyle just this icon (color, size) via `defineTheme`. The
1123
+ // time segment has no toggle button — the clock is a static
1124
+ // leading affordance — so this carries no interactive state.
1125
+ ,
1126
+ ...themeProps('date-time-input-clock-icon')
1127
+ })
1128
+ }), /*#__PURE__*/_jsx("input", {
1129
+ ref: timeInputRef,
1130
+ id: timeInputId,
1131
+ type: "text",
1132
+ value: timeDisplayValue,
1133
+ onChange: handleTimeInputChange,
1134
+ onFocus: handleTimeFocus,
1135
+ onBlur: handleTimeBlur,
1136
+ onKeyDown: handleTimeKeyDown,
1137
+ onClick: hasTimeOptions ? showTimeOptions : undefined,
1138
+ onPointerDown: hasTimeOptions ? markTimePointerActive : undefined,
1139
+ placeholder: resolvedTimePlaceholder
1140
+ // Combobox semantics appear only with the dropdown opted in, so a
1141
+ // field without it keeps a single combobox on the date half.
1142
+ ,
1143
+ role: hasTimeOptions ? 'combobox' : undefined,
1144
+ "aria-expanded": hasTimeOptions ? timePopover.isOpen : undefined,
1145
+ "aria-controls": hasTimeOptions && timePopover.isOpen ? timeListboxId : undefined,
1146
+ "aria-autocomplete": hasTimeOptions ? 'list' : undefined,
1147
+ "aria-activedescendant": hasTimeOptions && timePopover.isOpen && activeTimeIndex >= 0 && activeTimeIndex < timeOptions.length ? timeOptionId(activeTimeIndex) : undefined
1148
+ // With a disabledMessage the input keeps focusability via
1149
+ // aria-disabled so the reason is focus-discoverable; typing is
1150
+ // blocked with readOnly and the mutation guards.
1151
+ ,
1152
+ disabled: isEffectivelyDisabled && !showsDisabledMessage,
1153
+ "aria-disabled": showsDisabledMessage ? 'true' : undefined,
1154
+ readOnly: showsDisabledMessage || undefined,
1155
+ "aria-label": resolvedTimeLabel,
1156
+ "aria-describedby": ariaDescribedBy,
1157
+ "aria-required": isEffectivelyRequired ? 'true' : undefined,
1158
+ "aria-invalid": status?.type === 'error' || !isTimeInputValid ? 'true' : undefined,
1159
+ "aria-busy": isBusy || undefined,
1160
+ ...{
1161
+ 0: {
1162
+ className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5"
1163
+ },
1164
+ 2: {
1165
+ className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5 xt0e3qv"
1166
+ },
1167
+ 1: {
1168
+ className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx xjbqb8w x1a2a7pz xeyghm5 xv1l7n4"
1169
+ },
1170
+ 3: {
1171
+ className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 x6pjikd xw6l6zx xjbqb8w x1a2a7pz xeyghm5 xt0e3qv xv1l7n4"
1172
+ }
1173
+ }[!!isEffectivelyDisabled << 1 | !!!isTimeInputValid << 0]
1174
+ }), /*#__PURE__*/_jsx(VisuallyHidden, {
1175
+ as: "div",
1176
+ role: "alert",
1177
+ "aria-live": "assertive",
1178
+ children: !isTimeInputValid ? t('@astryx.timeInput.invalidTime') : ''
1179
+ })]
1180
+ })
1078
1181
  })]
1079
- }), popover.render(/*#__PURE__*/_jsx(Calendar, {
1182
+ }), !usesNativePicker && popover.render(/*#__PURE__*/_jsx(Calendar, {
1080
1183
  handleRef: calendarRef,
1081
1184
  mode: "single",
1082
1185
  value: valueParts.date,
@@ -1126,20 +1229,24 @@ function PointerDateTimeField({
1126
1229
  });
1127
1230
  }
1128
1231
  PointerDateTimeField.displayName = 'PointerDateTimeField';
1129
- const TOUCH_POINTER_QUERY = '(pointer: coarse)';
1130
1232
 
1131
1233
  /**
1132
- * A combined date and time picker that keeps the desktop text-entry surface on
1133
- * mouse/trackpad devices and uses Astryx's custom bottom-sheet picker on coarse
1134
- * pointers. Unlike DateInput, this never hands touch picking to the browser/OS:
1135
- * the mobile flow has to coordinate date and time together, preserve drafted
1136
- * time before a date exists, and enforce datetime min/max across both panels.
1234
+ * A combined date and time picker whose `nativePicker` prop chooses both
1235
+ * surfaces. Native modes use OS-owned date and time controls in Astryx field
1236
+ * chrome. With `nativePicker="never"`, fine pointers keep the typed fields and
1237
+ * popovers while coarse pointers get Astryx's coordinated Date/Time bottom
1238
+ * sheet.
1137
1239
  */
1138
- export function DateTimeInput(props) {
1240
+ export function DateTimeInput({
1241
+ nativePicker = 'touch',
1242
+ ...props
1243
+ }) {
1139
1244
  const isTouch = useMediaQuery(TOUCH_POINTER_QUERY);
1140
- return isTouch ? /*#__PURE__*/_jsx(TouchDateTimeField, {
1141
- ...props
1142
- }) : /*#__PURE__*/_jsx(PointerDateTimeField, {
1245
+ const usesNativePicker = nativePicker === 'always' || nativePicker === 'touch' && isTouch;
1246
+ return usesNativePicker || !isTouch ? /*#__PURE__*/_jsx(PointerDateTimeField, {
1247
+ ...props,
1248
+ nativePicker: nativePicker
1249
+ }) : /*#__PURE__*/_jsx(TouchDateTimeField, {
1143
1250
  ...props
1144
1251
  });
1145
1252
  }
@@ -0,0 +1,24 @@
1
+ import { plainDateFromISO, type ISODateString } from '../utils';
2
+ import type { InputStatusType } from '../Field';
3
+ export type NativeDateSegmentProps = {
4
+ id: string;
5
+ inputRef: React.Ref<HTMLInputElement>;
6
+ value?: ISODateString;
7
+ onChange: (value: ISODateString | undefined) => void;
8
+ placeholder: string;
9
+ min?: ISODateString;
10
+ max?: ISODateString;
11
+ isDateDisabled: (date: ReturnType<typeof plainDateFromISO>) => boolean;
12
+ isEffectivelyDisabled: boolean;
13
+ hasDisabledMessage: boolean;
14
+ isEffectivelyRequired: boolean;
15
+ isBusy: boolean;
16
+ statusType?: InputStatusType;
17
+ ariaDescribedBy?: string;
18
+ };
19
+ /** The native date half rendered inside DateTimeInput's date wrapper. */
20
+ export declare function NativeDateSegment({ id, inputRef, value, onChange, placeholder, min, max, isDateDisabled, isEffectivelyDisabled, hasDisabledMessage, isEffectivelyRequired, isBusy, statusType, ariaDescribedBy, }: NativeDateSegmentProps): import("react").JSX.Element;
21
+ export declare namespace NativeDateSegment {
22
+ var displayName: string;
23
+ }
24
+ //# sourceMappingURL=NativeDateSegment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeDateSegment.d.ts","sourceRoot":"","sources":["../../src/DateTimeInput/NativeDateSegment.tsx"],"names":[],"mappings":"AAiCA,OAAO,EAIL,gBAAgB,EAGhB,KAAK,aAAa,EACnB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,UAAU,CAAC;AAK9C,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,KAAK,IAAI,CAAC;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,cAAc,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,KAAK,OAAO,CAAC;IACvE,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,WAAW,EACX,GAAG,EACH,GAAG,EACH,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,MAAM,EACN,UAAU,EACV,eAAe,GAChB,EAAE,sBAAsB,+BAiOxB;yBAhPe,iBAAiB"}