@astryxdesign/core 0.1.1 → 0.1.2-canary.04e71b2

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 (631) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +131 -13
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  8. package/dist/Banner/Banner.d.ts +7 -0
  9. package/dist/Banner/Banner.d.ts.map +1 -1
  10. package/dist/Banner/Banner.js +9 -2
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  14. package/dist/Button/Button.d.ts +12 -2
  15. package/dist/Button/Button.d.ts.map +1 -1
  16. package/dist/Button/Button.js +53 -17
  17. package/dist/Calendar/Calendar.d.ts +5 -4
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +82 -43
  20. package/dist/Calendar/hooks/index.d.ts +0 -2
  21. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  22. package/dist/Calendar/hooks/index.js +1 -2
  23. package/dist/Calendar/index.d.ts +3 -3
  24. package/dist/Calendar/index.d.ts.map +1 -1
  25. package/dist/Calendar/index.js +1 -1
  26. package/dist/Calendar/styles.d.ts +3 -9
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +3 -11
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +17 -3
  31. package/dist/Center/Center.d.ts +11 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +9 -3
  34. package/dist/Chat/ChatMessageList.d.ts +14 -1
  35. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageList.js +2 -0
  37. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  38. package/dist/Chat/useTriggerMenu.js +4 -1
  39. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  42. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  43. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxList.js +39 -9
  45. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  46. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +3 -3
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +49 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +59 -8
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  71. package/dist/EmptyState/EmptyState.js +7 -1
  72. package/dist/Field/Field.d.ts +19 -2
  73. package/dist/Field/Field.d.ts.map +1 -1
  74. package/dist/Field/Field.js +4 -0
  75. package/dist/Field/FieldLabel.d.ts +21 -2
  76. package/dist/Field/FieldLabel.d.ts.map +1 -1
  77. package/dist/Field/FieldLabel.js +41 -26
  78. package/dist/FileInput/FileInput.d.ts +22 -1
  79. package/dist/FileInput/FileInput.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.js +56 -10
  81. package/dist/Grid/Grid.d.ts +11 -1
  82. package/dist/Grid/Grid.d.ts.map +1 -1
  83. package/dist/Grid/Grid.js +8 -0
  84. package/dist/HoverCard/HoverCard.d.ts +2 -2
  85. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  86. package/dist/HoverCard/HoverCard.js +18 -6
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +7 -3
  89. package/dist/InputGroup/InputGroup.d.ts +2 -2
  90. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  91. package/dist/InputGroup/InputGroup.js +14 -5
  92. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  93. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  94. package/dist/InputGroup/InputGroupContext.js +2 -2
  95. package/dist/InputGroup/index.d.ts +1 -0
  96. package/dist/InputGroup/index.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.d.ts.map +1 -1
  98. package/dist/Kbd/Kbd.js +34 -2
  99. package/dist/Layer/anchorName.d.ts +20 -0
  100. package/dist/Layer/anchorName.d.ts.map +1 -0
  101. package/dist/Layer/anchorName.js +36 -0
  102. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  103. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  104. package/dist/Layer/layerAnimations.stylex.js +8 -4
  105. package/dist/Layer/useLayer.d.ts +29 -0
  106. package/dist/Layer/useLayer.d.ts.map +1 -1
  107. package/dist/Layer/useLayer.js +55 -11
  108. package/dist/Layout/Layout.d.ts +4 -11
  109. package/dist/Layout/Layout.d.ts.map +1 -1
  110. package/dist/Layout/Layout.js +10 -11
  111. package/dist/Layout/LayoutFooter.d.ts +2 -2
  112. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  113. package/dist/Layout/LayoutHeader.d.ts +2 -2
  114. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  115. package/dist/Layout/LayoutPanel.d.ts +2 -2
  116. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  117. package/dist/Layout/padding.stylex.d.ts +51 -0
  118. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  119. package/dist/Layout/padding.stylex.js +63 -0
  120. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  121. package/dist/Lightbox/Lightbox.js +4 -2
  122. package/dist/Link/Link.d.ts +17 -2
  123. package/dist/Link/Link.d.ts.map +1 -1
  124. package/dist/Link/Link.js +20 -9
  125. package/dist/Link/LinkProvider.d.ts +1 -0
  126. package/dist/Link/LinkProvider.d.ts.map +1 -1
  127. package/dist/Link/LinkProvider.js +1 -0
  128. package/dist/Markdown/Markdown.d.ts.map +1 -1
  129. package/dist/Markdown/Markdown.js +18 -3
  130. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  131. package/dist/MobileNav/MobileNav.js +13 -0
  132. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  133. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  134. package/dist/MoreMenu/MoreMenu.js +0 -2
  135. package/dist/MultiSelector/MultiSelector.d.ts +25 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  137. package/dist/MultiSelector/MultiSelector.js +103 -22
  138. package/dist/MultiSelector/hooks.d.ts +12 -1
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +14 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  143. package/dist/NumberInput/NumberInput.d.ts +23 -2
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +62 -12
  146. package/dist/Pagination/Pagination.d.ts +1 -1
  147. package/dist/Pagination/Pagination.d.ts.map +1 -1
  148. package/dist/Pagination/Pagination.js +46 -28
  149. package/dist/Popover/usePopover.d.ts +25 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +18 -6
  152. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  153. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  154. package/dist/PowerSearch/PowerSearch.js +6 -1
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +3 -5
  157. package/dist/RadioList/RadioList.d.ts +20 -2
  158. package/dist/RadioList/RadioList.d.ts.map +1 -1
  159. package/dist/RadioList/RadioList.js +105 -9
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +13 -2
  162. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  163. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  164. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  165. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  166. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  167. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  168. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  169. package/dist/Selector/Selector.d.ts +22 -1
  170. package/dist/Selector/Selector.d.ts.map +1 -1
  171. package/dist/Selector/Selector.js +84 -23
  172. package/dist/Selector/hooks.d.ts +8 -1
  173. package/dist/Selector/hooks.d.ts.map +1 -1
  174. package/dist/Selector/hooks.js +14 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +1 -1
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +8 -2
  179. package/dist/Slider/Slider.d.ts +21 -0
  180. package/dist/Slider/Slider.d.ts.map +1 -1
  181. package/dist/Slider/Slider.js +38 -11
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +1 -1
  184. package/dist/Stack/Stack.d.ts +38 -23
  185. package/dist/Stack/Stack.d.ts.map +1 -1
  186. package/dist/Stack/Stack.js +25 -1
  187. package/dist/Stack/StackItem.d.ts +12 -23
  188. package/dist/Stack/StackItem.d.ts.map +1 -1
  189. package/dist/Stack/StackItem.js +8 -1
  190. package/dist/Switch/Switch.d.ts +23 -2
  191. package/dist/Switch/Switch.d.ts.map +1 -1
  192. package/dist/Switch/Switch.js +41 -11
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +6 -0
  195. package/dist/TabList/TabList.d.ts +16 -4
  196. package/dist/TabList/TabList.d.ts.map +1 -1
  197. package/dist/TabList/TabList.js +86 -10
  198. package/dist/TabList/TabListContext.d.ts +5 -0
  199. package/dist/TabList/TabListContext.d.ts.map +1 -1
  200. package/dist/TabList/TabListContext.js +5 -0
  201. package/dist/TabList/TabMenu.d.ts.map +1 -1
  202. package/dist/TabList/TabMenu.js +6 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +38 -16
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +36 -7
  207. package/dist/Table/TableCell.d.ts +8 -1
  208. package/dist/Table/TableCell.d.ts.map +1 -1
  209. package/dist/Table/TableCell.js +10 -10
  210. package/dist/Table/TableHeaderCell.d.ts +8 -1
  211. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  212. package/dist/Table/TableHeaderCell.js +16 -16
  213. package/dist/Table/TableRow.d.ts +6 -1
  214. package/dist/Table/TableRow.d.ts.map +1 -1
  215. package/dist/Table/TableRow.js +15 -7
  216. package/dist/Table/index.d.ts +4 -1
  217. package/dist/Table/index.d.ts.map +1 -1
  218. package/dist/Table/index.js +3 -2
  219. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  220. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  221. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  222. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  223. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  225. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  226. package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
  227. package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
  228. package/dist/Table/plugins/stickyColumns/index.js +3 -0
  229. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
  230. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
  231. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
  232. package/dist/Table/tableContextMenu.d.ts +33 -0
  233. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  234. package/dist/Table/tableContextMenu.js +133 -0
  235. package/dist/Table/types.d.ts +161 -5
  236. package/dist/Table/types.d.ts.map +1 -1
  237. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  238. package/dist/Table/useBaseTablePlugins.js +1 -1
  239. package/dist/Text/text.stylex.d.ts +2 -2
  240. package/dist/Text/text.stylex.js +2 -2
  241. package/dist/TextArea/TextArea.d.ts +22 -1
  242. package/dist/TextArea/TextArea.d.ts.map +1 -1
  243. package/dist/TextArea/TextArea.js +41 -9
  244. package/dist/TextInput/TextInput.d.ts +23 -2
  245. package/dist/TextInput/TextInput.d.ts.map +1 -1
  246. package/dist/TextInput/TextInput.js +55 -10
  247. package/dist/TimeInput/TimeInput.d.ts +23 -1
  248. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  249. package/dist/TimeInput/TimeInput.js +53 -10
  250. package/dist/Timestamp/Timestamp.d.ts +1 -1
  251. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  252. package/dist/Timestamp/Timestamp.js +28 -3
  253. package/dist/Toast/Toast.d.ts.map +1 -1
  254. package/dist/Toast/Toast.js +14 -0
  255. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  256. package/dist/Toast/ToastViewport.js +132 -3
  257. package/dist/ToggleButton/ToggleButton.d.ts +7 -9
  258. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  259. package/dist/ToggleButton/ToggleButton.js +34 -57
  260. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  261. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  262. package/dist/Tokenizer/Tokenizer.js +27 -5
  263. package/dist/Toolbar/Toolbar.d.ts +3 -3
  264. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  265. package/dist/Toolbar/Toolbar.js +51 -7
  266. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  267. package/dist/Tooltip/useTooltip.js +57 -9
  268. package/dist/TreeList/TreeList.d.ts +1 -1
  269. package/dist/TreeList/TreeList.d.ts.map +1 -1
  270. package/dist/TreeList/TreeList.js +72 -2
  271. package/dist/TreeList/TreeListItem.d.ts +11 -1
  272. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  273. package/dist/TreeList/TreeListItem.js +51 -12
  274. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  275. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  276. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  277. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  278. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  279. package/dist/Typeahead/BaseTypeahead.js +67 -6
  280. package/dist/Typeahead/Typeahead.d.ts +24 -1
  281. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/Typeahead.js +29 -5
  283. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  284. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  285. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  286. package/dist/VisuallyHidden/index.d.ts +11 -0
  287. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  288. package/dist/VisuallyHidden/index.js +13 -0
  289. package/dist/astryx.css +40 -1
  290. package/dist/astryx.umd.js +147 -0
  291. package/dist/astryx.umd.js.map +7 -0
  292. package/dist/hooks/focusableSelector.d.ts +22 -0
  293. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  294. package/dist/hooks/focusableSelector.js +24 -0
  295. package/dist/hooks/index.d.ts +12 -2
  296. package/dist/hooks/index.d.ts.map +1 -1
  297. package/dist/hooks/index.js +7 -2
  298. package/dist/hooks/useAnnounce.d.ts +40 -0
  299. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  300. package/dist/hooks/useAnnounce.js +142 -0
  301. package/dist/hooks/useClickableContainer.d.ts +16 -1
  302. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  303. package/dist/hooks/useClickableContainer.js +16 -4
  304. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  305. package/dist/hooks/useEntryAnimation.js +4 -4
  306. package/dist/hooks/useFocusTrap.d.ts +18 -0
  307. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  308. package/dist/hooks/useFocusTrap.js +87 -3
  309. package/dist/hooks/useGridFocus.d.ts +88 -1
  310. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  311. package/dist/hooks/useGridFocus.js +261 -62
  312. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  313. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  314. package/dist/hooks/useKeyboardHint.js +220 -0
  315. package/dist/hooks/useListFocus.d.ts +78 -7
  316. package/dist/hooks/useListFocus.d.ts.map +1 -1
  317. package/dist/hooks/useListFocus.js +305 -52
  318. package/dist/hooks/useLongPress.d.ts +21 -0
  319. package/dist/hooks/useLongPress.d.ts.map +1 -0
  320. package/dist/hooks/useLongPress.js +89 -0
  321. package/dist/hooks/useTreeFocus.d.ts +157 -0
  322. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  323. package/dist/hooks/useTreeFocus.js +337 -0
  324. package/dist/hooks/useTypeahead.d.ts +63 -0
  325. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  326. package/dist/hooks/useTypeahead.js +117 -0
  327. package/dist/index.d.ts +1 -0
  328. package/dist/index.d.ts.map +1 -1
  329. package/dist/index.js +1 -0
  330. package/dist/theme/generateThemeRules.js +1 -1
  331. package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
  332. package/dist/theme/syntax/SyntaxTheme.js +1 -1
  333. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  334. package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
  335. package/dist/theme/syntax/presets.d.ts +1 -1
  336. package/dist/theme/syntax/presets.d.ts.map +1 -1
  337. package/dist/theme/syntax/presets.js +1 -1
  338. package/dist/theme/syntax/tokens.d.ts +1 -1
  339. package/dist/theme/syntax/tokens.js +1 -1
  340. package/dist/theme/types.d.ts +1 -1
  341. package/dist/utils/dateTypes.d.ts +12 -0
  342. package/dist/utils/dateTypes.d.ts.map +1 -1
  343. package/dist/utils/dateTypes.js +41 -1
  344. package/dist/utils/index.d.ts +4 -0
  345. package/dist/utils/index.d.ts.map +1 -1
  346. package/dist/utils/index.js +3 -1
  347. package/dist/utils/inputAria.d.ts +29 -0
  348. package/dist/utils/inputAria.d.ts.map +1 -0
  349. package/dist/utils/inputAria.js +30 -0
  350. package/dist/utils/timeParser.d.ts.map +1 -1
  351. package/dist/utils/timeParser.js +5 -2
  352. package/package.json +15 -5
  353. package/src/Avatar/Avatar.test.tsx +37 -0
  354. package/src/Avatar/Avatar.tsx +10 -5
  355. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
  356. package/src/Banner/Banner.doc.mjs +8 -0
  357. package/src/Banner/Banner.test.tsx +16 -7
  358. package/src/Banner/Banner.tsx +9 -2
  359. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  360. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  361. package/src/Button/Button.doc.mjs +6 -0
  362. package/src/Button/Button.test.tsx +105 -11
  363. package/src/Button/Button.tsx +76 -21
  364. package/src/Calendar/Calendar.doc.mjs +4 -4
  365. package/src/Calendar/Calendar.test.tsx +168 -30
  366. package/src/Calendar/Calendar.tsx +121 -66
  367. package/src/Calendar/hooks/index.ts +0 -6
  368. package/src/Calendar/index.ts +1 -3
  369. package/src/Calendar/styles.ts +4 -9
  370. package/src/Carousel/Carousel.doc.mjs +7 -1
  371. package/src/Carousel/Carousel.test.tsx +20 -0
  372. package/src/Carousel/Carousel.tsx +16 -1
  373. package/src/Center/Center.doc.mjs +12 -2
  374. package/src/Center/Center.tsx +28 -3
  375. package/src/Chat/ChatComposer.doc.mjs +3 -0
  376. package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
  377. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  378. package/src/Chat/ChatMessageList.test.tsx +18 -0
  379. package/src/Chat/ChatMessageList.tsx +16 -0
  380. package/src/Chat/useTriggerMenu.tsx +3 -0
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  383. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  384. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  385. package/src/CheckboxList/CheckboxList.test.tsx +169 -16
  386. package/src/CheckboxList/CheckboxList.tsx +56 -3
  387. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  388. package/src/Citation/Citation.doc.mjs +10 -0
  389. package/src/Citation/Citation.test.tsx +117 -0
  390. package/src/Citation/Citation.tsx +9 -1
  391. package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
  392. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  393. package/src/CodeBlock/CodeBlock.tsx +61 -33
  394. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  395. package/src/CommandPalette/CommandPalette.tsx +9 -21
  396. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  397. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  398. package/src/ContextMenu/ContextMenu.tsx +113 -16
  399. package/src/DateInput/DateInput.doc.mjs +281 -56
  400. package/src/DateInput/DateInput.test.tsx +188 -2
  401. package/src/DateInput/DateInput.tsx +89 -10
  402. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  403. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  404. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  405. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  406. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  407. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  408. package/src/Dialog/Dialog.doc.mjs +2 -2
  409. package/src/Dialog/Dialog.tsx +12 -0
  410. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  411. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  412. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  413. package/src/EmptyState/EmptyState.test.tsx +4 -2
  414. package/src/EmptyState/EmptyState.tsx +6 -2
  415. package/src/Field/Field.test.tsx +32 -0
  416. package/src/Field/Field.tsx +23 -5
  417. package/src/Field/FieldLabel.tsx +55 -17
  418. package/src/FileInput/FileInput.doc.mjs +17 -0
  419. package/src/FileInput/FileInput.test.tsx +283 -49
  420. package/src/FileInput/FileInput.tsx +76 -4
  421. package/src/Grid/Grid.doc.mjs +14 -4
  422. package/src/Grid/Grid.tsx +20 -0
  423. package/src/HoverCard/HoverCard.doc.mjs +3 -0
  424. package/src/HoverCard/HoverCard.test.tsx +189 -2
  425. package/src/HoverCard/HoverCard.tsx +20 -16
  426. package/src/HoverCard/useHoverCard.tsx +13 -10
  427. package/src/Icon/Icon.doc.mjs +8 -0
  428. package/src/InputGroup/InputGroup.doc.mjs +2 -2
  429. package/src/InputGroup/InputGroup.test.tsx +98 -60
  430. package/src/InputGroup/InputGroup.tsx +21 -4
  431. package/src/InputGroup/InputGroupContext.ts +9 -4
  432. package/src/InputGroup/index.ts +1 -0
  433. package/src/Kbd/Kbd.test.tsx +22 -4
  434. package/src/Kbd/Kbd.tsx +37 -12
  435. package/src/Layer/anchorName.test.ts +146 -0
  436. package/src/Layer/anchorName.ts +46 -0
  437. package/src/Layer/layerAnimations.stylex.ts +20 -4
  438. package/src/Layer/useLayer.doc.mjs +12 -2
  439. package/src/Layer/useLayer.tsx +100 -15
  440. package/src/Layout/Layout.doc.mjs +1 -1
  441. package/src/Layout/Layout.tsx +8 -16
  442. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  443. package/src/Layout/LayoutFooter.tsx +3 -3
  444. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  445. package/src/Layout/LayoutHeader.tsx +3 -3
  446. package/src/Layout/LayoutPanel.tsx +3 -3
  447. package/src/Layout/padding.stylex.ts +52 -0
  448. package/src/Lightbox/Lightbox.doc.mjs +9 -2
  449. package/src/Lightbox/Lightbox.test.tsx +53 -5
  450. package/src/Lightbox/Lightbox.tsx +9 -4
  451. package/src/Link/Link.doc.mjs +14 -0
  452. package/src/Link/Link.test.tsx +52 -2
  453. package/src/Link/Link.tsx +42 -7
  454. package/src/Link/LinkProvider.tsx +1 -0
  455. package/src/List/List.doc.mjs +7 -0
  456. package/src/Markdown/Markdown.doc.mjs +2 -0
  457. package/src/Markdown/Markdown.test.tsx +28 -26
  458. package/src/Markdown/Markdown.tsx +21 -6
  459. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  460. package/src/MobileNav/MobileNav.doc.mjs +8 -8
  461. package/src/MobileNav/MobileNav.tsx +13 -0
  462. package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
  463. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  464. package/src/MoreMenu/MoreMenu.tsx +0 -9
  465. package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
  466. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  467. package/src/MultiSelector/MultiSelector.tsx +145 -21
  468. package/src/MultiSelector/hooks.ts +27 -0
  469. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  470. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  471. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  472. package/src/NumberInput/NumberInput.test.tsx +242 -40
  473. package/src/NumberInput/NumberInput.tsx +89 -11
  474. package/src/OverflowList/OverflowList.doc.mjs +17 -0
  475. package/src/Pagination/Pagination.doc.mjs +3 -3
  476. package/src/Pagination/Pagination.test.tsx +250 -14
  477. package/src/Pagination/Pagination.tsx +54 -29
  478. package/src/Popover/usePopover.doc.mjs +7 -3
  479. package/src/Popover/usePopover.tsx +49 -6
  480. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  481. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  482. package/src/PowerSearch/PowerSearch.tsx +16 -3
  483. package/src/ProgressBar/ProgressBar.test.tsx +45 -27
  484. package/src/ProgressBar/ProgressBar.tsx +3 -4
  485. package/src/RadioList/RadioList.doc.mjs +9 -0
  486. package/src/RadioList/RadioList.test.tsx +252 -16
  487. package/src/RadioList/RadioList.tsx +166 -10
  488. package/src/RadioList/RadioListItem.tsx +14 -2
  489. package/src/Resizable/Resizable.doc.mjs +1 -1
  490. package/src/Section/Section.doc.mjs +11 -0
  491. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  492. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  493. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  494. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  495. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  496. package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
  497. package/src/Selector/Selector.doc.mjs +25 -0
  498. package/src/Selector/Selector.test.tsx +322 -15
  499. package/src/Selector/Selector.tsx +126 -40
  500. package/src/Selector/hooks.ts +23 -0
  501. package/src/SideNav/SideNav.test.tsx +16 -0
  502. package/src/SideNav/SideNavItem.tsx +2 -4
  503. package/src/Skeleton/Skeleton.doc.mjs +11 -1
  504. package/src/Skeleton/Skeleton.test.tsx +25 -0
  505. package/src/Skeleton/Skeleton.tsx +11 -1
  506. package/src/Slider/Slider.doc.mjs +19 -0
  507. package/src/Slider/Slider.test.tsx +158 -18
  508. package/src/Slider/Slider.tsx +54 -6
  509. package/src/Spinner/Spinner.tsx +7 -1
  510. package/src/Stack/Stack.doc.mjs +77 -0
  511. package/src/Stack/Stack.test.tsx +55 -5
  512. package/src/Stack/Stack.tsx +73 -24
  513. package/src/Stack/StackItem.test.tsx +23 -0
  514. package/src/Stack/StackItem.tsx +20 -23
  515. package/src/Switch/Switch.doc.mjs +15 -0
  516. package/src/Switch/Switch.test.tsx +162 -27
  517. package/src/Switch/Switch.tsx +61 -21
  518. package/src/TabList/Tab.tsx +6 -0
  519. package/src/TabList/TabList.doc.mjs +12 -0
  520. package/src/TabList/TabList.test.tsx +208 -0
  521. package/src/TabList/TabList.tsx +102 -12
  522. package/src/TabList/TabListContext.ts +7 -3
  523. package/src/TabList/TabMenu.tsx +6 -3
  524. package/src/Table/BaseTable.tsx +72 -41
  525. package/src/Table/Table.doc.mjs +14 -0
  526. package/src/Table/Table.test.tsx +39 -30
  527. package/src/Table/Table.tsx +27 -1
  528. package/src/Table/TableCell.tsx +25 -23
  529. package/src/Table/TableHeaderCell.tsx +28 -29
  530. package/src/Table/TableRow.tsx +37 -7
  531. package/src/Table/index.ts +6 -2
  532. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  533. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  534. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  535. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  536. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  537. package/src/Table/plugins/stickyColumns/index.ts +4 -0
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
  539. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
  540. package/src/Table/tableContextMenu.test.tsx +157 -0
  541. package/src/Table/tableContextMenu.tsx +142 -0
  542. package/src/Table/types.ts +175 -4
  543. package/src/Table/useBaseTablePlugins.ts +1 -0
  544. package/src/Table/useTableColumnResize.doc.mjs +96 -0
  545. package/src/Table/useTableStickyColumns.doc.mjs +59 -0
  546. package/src/Text/Heading.doc.mjs +15 -2
  547. package/src/Text/Text.doc.mjs +7 -1
  548. package/src/Text/text.stylex.ts +2 -2
  549. package/src/TextArea/TextArea.doc.mjs +17 -0
  550. package/src/TextArea/TextArea.test.tsx +167 -35
  551. package/src/TextArea/TextArea.tsx +62 -15
  552. package/src/TextInput/TextInput.doc.mjs +17 -0
  553. package/src/TextInput/TextInput.test.tsx +168 -34
  554. package/src/TextInput/TextInput.tsx +77 -9
  555. package/src/TimeInput/TimeInput.doc.mjs +335 -60
  556. package/src/TimeInput/TimeInput.test.tsx +144 -7
  557. package/src/TimeInput/TimeInput.tsx +74 -5
  558. package/src/Timestamp/Timestamp.doc.mjs +9 -0
  559. package/src/Timestamp/Timestamp.test.tsx +44 -18
  560. package/src/Timestamp/Timestamp.tsx +30 -10
  561. package/src/Toast/Toast.tsx +14 -0
  562. package/src/Toast/ToastViewport.test.tsx +204 -0
  563. package/src/Toast/ToastViewport.tsx +167 -21
  564. package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
  565. package/src/ToggleButton/ToggleButton.test.tsx +39 -18
  566. package/src/ToggleButton/ToggleButton.tsx +46 -83
  567. package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
  568. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  569. package/src/Tokenizer/Tokenizer.tsx +41 -2
  570. package/src/Toolbar/Toolbar.doc.mjs +65 -5
  571. package/src/Toolbar/Toolbar.test.tsx +109 -6
  572. package/src/Toolbar/Toolbar.tsx +59 -5
  573. package/src/Tooltip/Tooltip.test.tsx +81 -0
  574. package/src/Tooltip/useTooltip.tsx +57 -11
  575. package/src/TreeList/TreeList.test.tsx +295 -6
  576. package/src/TreeList/TreeList.tsx +73 -1
  577. package/src/TreeList/TreeListItem.tsx +51 -5
  578. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  579. package/src/Typeahead/BaseTypeahead.tsx +86 -9
  580. package/src/Typeahead/Typeahead.doc.mjs +136 -30
  581. package/src/Typeahead/Typeahead.test.tsx +331 -1
  582. package/src/Typeahead/Typeahead.tsx +52 -1
  583. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  584. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  585. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  586. package/src/VisuallyHidden/index.ts +15 -0
  587. package/src/hooks/focusableSelector.ts +25 -0
  588. package/src/hooks/index.ts +28 -2
  589. package/src/hooks/useAnnounce.test.tsx +101 -0
  590. package/src/hooks/useAnnounce.ts +168 -0
  591. package/src/hooks/useClickableContainer.ts +16 -4
  592. package/src/hooks/useEntryAnimation.ts +16 -4
  593. package/src/hooks/useFocusTrap.test.tsx +132 -0
  594. package/src/hooks/useFocusTrap.ts +98 -4
  595. package/src/hooks/useGridFocus.doc.mjs +40 -2
  596. package/src/hooks/useGridFocus.test.tsx +132 -0
  597. package/src/hooks/useGridFocus.ts +343 -67
  598. package/src/hooks/useImageMode.test.ts +133 -0
  599. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  600. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  601. package/src/hooks/useKeyboardHint.tsx +332 -0
  602. package/src/hooks/useListFocus.doc.mjs +50 -10
  603. package/src/hooks/useListFocus.test.tsx +375 -0
  604. package/src/hooks/useListFocus.ts +395 -57
  605. package/src/hooks/useLongPress.test.tsx +134 -0
  606. package/src/hooks/useLongPress.ts +119 -0
  607. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  608. package/src/hooks/useTreeFocus.test.tsx +214 -0
  609. package/src/hooks/useTreeFocus.ts +559 -0
  610. package/src/hooks/useTypeahead.test.tsx +109 -0
  611. package/src/hooks/useTypeahead.ts +183 -0
  612. package/src/index.ts +1 -0
  613. package/src/theme/derivedVarRegistry.test.ts +4 -0
  614. package/src/theme/generateThemeRules.test.ts +7 -8
  615. package/src/theme/generateThemeRules.ts +1 -1
  616. package/src/theme/syntax/SyntaxTheme.tsx +1 -1
  617. package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
  618. package/src/theme/syntax/presets.ts +5 -2
  619. package/src/theme/syntax/tokens.ts +1 -1
  620. package/src/theme/types.ts +1 -1
  621. package/src/utils/dateTypes.test.ts +40 -0
  622. package/src/utils/dateTypes.ts +40 -0
  623. package/src/utils/index.ts +9 -0
  624. package/src/utils/inputAria.test.ts +39 -0
  625. package/src/utils/inputAria.ts +58 -0
  626. package/src/utils/timeParser.test.ts +10 -0
  627. package/src/utils/timeParser.ts +5 -2
  628. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  629. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  630. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  631. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -63,6 +63,7 @@ import type {BaseProps} from '../BaseProps';
63
63
  import type {SizeValue} from '../utils/types';
64
64
  import {useSize} from '../SizeContext/SizeContext';
65
65
  import {useInputContainer} from '../hooks/useInputContainer';
66
+ import {useTooltip} from '../Tooltip';
66
67
  import {themeProps} from '../utils/themeProps';
67
68
 
68
69
  const styles = stylex.create({
@@ -183,6 +184,29 @@ export interface TimeInputProps extends Omit<
183
184
  */
184
185
  isDisabled?: boolean;
185
186
 
187
+ /**
188
+ * Explains why the input is disabled. When set together with
189
+ * `isDisabled`, the input shows a tooltip with this text on hover and
190
+ * keyboard focus, and the field stays focusable (via `aria-disabled`)
191
+ * so the reason is discoverable by keyboard and assistive technology.
192
+ * Typing and arrow-key adjustment stay blocked.
193
+ *
194
+ * Use this instead of wrapping a disabled input in `Tooltip` — disabled
195
+ * controls don't emit the pointer events an external tooltip needs.
196
+ *
197
+ * @example
198
+ * ```
199
+ * <TimeInput
200
+ * label="Start time"
201
+ * value={time}
202
+ * onChange={setTime}
203
+ * isDisabled
204
+ * disabledMessage="You need the Editor role to change this"
205
+ * />
206
+ * ```
207
+ */
208
+ disabledMessage?: string;
209
+
186
210
  /**
187
211
  * The selected time in ISO format (HH:MM or HH:MM:SS).
188
212
  */
@@ -301,6 +325,7 @@ export function TimeInput({
301
325
  isOptional = false,
302
326
  isRequired = false,
303
327
  isDisabled = false,
328
+ disabledMessage,
304
329
  value,
305
330
  onChange,
306
331
  changeAction,
@@ -334,6 +359,19 @@ export function TimeInput({
334
359
  const [optimisticValue, setOptimisticValue] = useOptimistic(value);
335
360
  const isBusy = isLoading || optimisticValue !== value;
336
361
 
362
+ // Disabled-reason tooltip. Disabled controls swallow pointer events, so the
363
+ // tooltip listeners attach to the input container (which already exists) and
364
+ // the input stays perceivable via aria-disabled instead of the disabled
365
+ // attribute. Typing is blocked with readOnly and value mutation guards.
366
+ const showsDisabledMessage = isDisabled && !!disabledMessage;
367
+ const disabledMessageTooltip = useTooltip({
368
+ placement: 'above',
369
+ // The container div is not naturally focusable; focusin bubbles up from
370
+ // the input, so always attach focus listeners.
371
+ focusTrigger: 'always',
372
+ isEnabled: showsDisabledMessage,
373
+ });
374
+
337
375
  // Status icon mapping
338
376
  const statusIconMap: Record<InputStatusType, IconName> = {
339
377
  warning: 'warning',
@@ -354,6 +392,7 @@ export function TimeInput({
354
392
  [
355
393
  description ? descriptionID : null,
356
394
  status?.message ? statusMessageID : null,
395
+ showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
357
396
  ]
358
397
  .filter(Boolean)
359
398
  .join(' ') || undefined;
@@ -415,6 +454,11 @@ export function TimeInput({
415
454
  // Handle input text change - update immediately if valid
416
455
  const handleInputChange = useCallback(
417
456
  (e: React.ChangeEvent<HTMLInputElement>) => {
457
+ // With a disabledMessage the input drops `disabled` for focusability, so
458
+ // guard value mutation explicitly (readOnly also blocks typing).
459
+ if (isDisabled) {
460
+ return;
461
+ }
418
462
  const newValue = e.target.value;
419
463
  setPendingInput(newValue);
420
464
 
@@ -424,13 +468,19 @@ export function TimeInput({
424
468
  fireChange(parsed);
425
469
  }
426
470
  },
427
- [hasSeconds, min, max, value, fireChange],
471
+ [hasSeconds, min, max, value, fireChange, isDisabled],
428
472
  );
429
473
 
430
474
  // Handle focus
431
475
  const handleFocus = useCallback(() => {
476
+ // A disabled input stays focusable (via aria-disabled) so its reason is
477
+ // discoverable, but it must not present editing affordances — keep the
478
+ // static placeholder rather than swapping in the format hint.
479
+ if (isDisabled) {
480
+ return;
481
+ }
432
482
  setIsFocused(true);
433
- }, []);
483
+ }, [isDisabled]);
434
484
 
435
485
  // Handle blur - validate and clear pending input
436
486
  const handleBlur = useCallback(
@@ -466,6 +516,11 @@ export function TimeInput({
466
516
  // Handle keyboard navigation on input
467
517
  const handleInputKeyDown = useCallback(
468
518
  (e: KeyboardEvent<HTMLInputElement>) => {
519
+ // Arrow-key adjustment mutates the value; block it while showing a
520
+ // disabled reason (the input keeps focusability via aria-disabled).
521
+ if (isDisabled) {
522
+ return;
523
+ }
469
524
  if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
470
525
  e.preventDefault();
471
526
 
@@ -492,7 +547,7 @@ export function TimeInput({
492
547
  }
493
548
  }
494
549
  },
495
- [value, hasSeconds, increment, min, max, fireChange],
550
+ [value, hasSeconds, increment, min, max, fireChange, isDisabled],
496
551
  );
497
552
 
498
553
  // Handle clear button click
@@ -531,7 +586,13 @@ export function TimeInput({
531
586
  labelTooltip={labelTooltip}
532
587
  width={width}>
533
588
  <div
534
- ref={containerRef}
589
+ ref={el => {
590
+ containerRef.current = el;
591
+ // Anchor + hover/focus listeners for the disabled-message tooltip.
592
+ // Handlers are gated internally by isEnabled, so attaching
593
+ // unconditionally is safe.
594
+ disabledMessageTooltip.ref(el);
595
+ }}
535
596
  onClick={handleWrapperClick}
536
597
  onMouseUp={handleWrapperMouseUp}
537
598
  {...mergeProps(
@@ -561,7 +622,12 @@ export function TimeInput({
561
622
  onBlur={handleBlur}
562
623
  onKeyDown={handleInputKeyDown}
563
624
  placeholder={displayPlaceholder}
564
- disabled={isDisabled}
625
+ // With a disabledMessage the input keeps focusability via
626
+ // aria-disabled so the reason is focus-discoverable; typing and
627
+ // arrow-key adjustment are blocked with readOnly and the guards.
628
+ disabled={isDisabled && !showsDisabledMessage}
629
+ aria-disabled={showsDisabledMessage ? 'true' : undefined}
630
+ readOnly={showsDisabledMessage || undefined}
565
631
  autoFocus={hasAutoFocus}
566
632
  data-autofocus={hasAutoFocus || undefined}
567
633
  aria-describedby={ariaDescribedBy}
@@ -592,6 +658,9 @@ export function TimeInput({
592
658
  />
593
659
  )}
594
660
  </div>
661
+
662
+ {showsDisabledMessage &&
663
+ disabledMessageTooltip.renderTooltip(disabledMessage)}
595
664
  </Field>
596
665
  );
597
666
  }
@@ -7,6 +7,15 @@ export const docs = {
7
7
  displayName: 'Timestamp',
8
8
  category: 'Content',
9
9
  keywords: ['date', 'time', 'datetime', 'relative', 'ago', 'clock', 'format', 'duration'],
10
+ playground: {
11
+ // `value` is required but has no semantic default, so the properties-tab
12
+ // preview fell back to the literal string "value" — which
13
+ // `new Date("value")` rejects, crashing the preview with "Invalid time
14
+ // value". Seed a valid ISO 8601 date so the interactive preview renders.
15
+ defaults: {
16
+ value: '2026-02-19T17:00:00Z',
17
+ },
18
+ },
10
19
  props: [
11
20
  {
12
21
  name: 'value',
@@ -33,10 +33,30 @@ describe('Timestamp', () => {
33
33
  expect(screen.getByText('2 hours ago')).toBeInTheDocument();
34
34
  });
35
35
 
36
- it('renders "just now" for very recent times', () => {
36
+ it('renders "now" for very recent times', () => {
37
37
  const fiveSecondsAgo = Date.now() / 1000 - 5;
38
38
  render(<Timestamp value={fiveSecondsAgo} format="relative" />);
39
- expect(screen.getByText('just now')).toBeInTheDocument();
39
+ expect(screen.getByText('now')).toBeInTheDocument();
40
+ });
41
+
42
+ it('renders "now" for the current instant (not a future phrase)', () => {
43
+ // A value equal to "right now". Because the internal `now` baseline is
44
+ // captured at render time, it can lag the value by a fraction of a second,
45
+ // producing a tiny negative delta that must not be treated as the future.
46
+ render(<Timestamp value={Date.now() / 1000} format="relative" />);
47
+ expect(screen.queryByText(/^in /)).not.toBeInTheDocument();
48
+ expect(screen.getByText('now')).toBeInTheDocument();
49
+ });
50
+
51
+ it('renders "now" for a value a hair in the future (clock skew)', () => {
52
+ // Real-world clock / captured-now skew can make a current-ish value land a
53
+ // fraction of a second in the future relative to the component's internal
54
+ // `now`. This must read as the present ("now"), never "in a few
55
+ // seconds". Regression test for the right-now -> future-phrase bug.
56
+ const aHairInTheFuture = Date.now() / 1000 + 0.6;
57
+ render(<Timestamp value={aHairInTheFuture} format="relative" />);
58
+ expect(screen.queryByText(/^in /)).not.toBeInTheDocument();
59
+ expect(screen.getByText('now')).toBeInTheDocument();
40
60
  });
41
61
 
42
62
  it('renders "yesterday" for times ~1 day ago', () => {
@@ -49,11 +69,7 @@ describe('Timestamp', () => {
49
69
 
50
70
  it('renders date format', () => {
51
71
  render(
52
- <Timestamp
53
- value="2026-02-19T17:00:00Z"
54
- format="date"
55
- data-testid="ts"
56
- />,
72
+ <Timestamp value="2026-02-19T17:00:00Z" format="date" data-testid="ts" />,
57
73
  );
58
74
  const el = screen.getByTestId('ts');
59
75
  expect(el.textContent).toContain('2026');
@@ -77,11 +93,7 @@ describe('Timestamp', () => {
77
93
 
78
94
  it('renders time format', () => {
79
95
  render(
80
- <Timestamp
81
- value="2026-02-19T17:00:00Z"
82
- format="time"
83
- data-testid="ts"
84
- />,
96
+ <Timestamp value="2026-02-19T17:00:00Z" format="time" data-testid="ts" />,
85
97
  );
86
98
  const el = screen.getByTestId('ts');
87
99
  // Should contain time but not year
@@ -197,7 +209,7 @@ describe('Timestamp', () => {
197
209
  it('live updates relative time', () => {
198
210
  const now = Date.now() / 1000;
199
211
  render(<Timestamp value={now - 5} format="relative" isLive />);
200
- expect(screen.getByText('just now')).toBeInTheDocument();
212
+ expect(screen.getByText('now')).toBeInTheDocument();
201
213
 
202
214
  act(() => {
203
215
  vi.advanceTimersByTime(30_000);
@@ -210,11 +222,7 @@ describe('Timestamp', () => {
210
222
  it('forwards ref', () => {
211
223
  const ref = {current: null as HTMLTimeElement | null};
212
224
  render(
213
- <Timestamp
214
- ref={ref}
215
- value="2026-03-25T10:00:00Z"
216
- format="date_time"
217
- />,
225
+ <Timestamp ref={ref} value="2026-03-25T10:00:00Z" format="date_time" />,
218
226
  );
219
227
  expect(ref.current).toBeInstanceOf(HTMLTimeElement);
220
228
  });
@@ -240,6 +248,24 @@ describe('Timestamp', () => {
240
248
  expect(screen.getByText('in 1 hour')).toBeInTheDocument();
241
249
  });
242
250
 
251
+ it('renders "now" for a value a few seconds in the future (clock skew)', () => {
252
+ // Beyond the sub-second render lag but still within the skew tolerance: a
253
+ // value ~20s ahead of our clock is almost always skew (the value's clock
254
+ // running fast), not a genuine future event, so it should read as the
255
+ // present rather than "in a few seconds".
256
+ const twentySecondsFromNow = Date.now() / 1000 + 20;
257
+ render(<Timestamp value={twentySecondsFromNow} format="relative" />);
258
+ expect(screen.queryByText(/^in /)).not.toBeInTheDocument();
259
+ expect(screen.getByText('now')).toBeInTheDocument();
260
+ });
261
+
262
+ it('renders a genuine near-future time beyond the skew tolerance', () => {
263
+ // Past the skew window — this is a real upcoming time, not clock drift.
264
+ const fortyFiveSecondsFromNow = Date.now() / 1000 + 45;
265
+ render(<Timestamp value={fortyFiveSecondsFromNow} format="relative" />);
266
+ expect(screen.getByText('in a few seconds')).toBeInTheDocument();
267
+ });
268
+
243
269
  // --- Long-ago relative ---
244
270
 
245
271
  it('renders months ago for dates older than 30 days', () => {
@@ -19,12 +19,7 @@
19
19
  import {useEffect, useRef, useState, lazy, Suspense} from 'react';
20
20
  import * as stylex from '@stylexjs/stylex';
21
21
  import {Text} from '../Text';
22
- import type {
23
- TextType,
24
- TextSize,
25
- TextColor,
26
- TextWeight,
27
- } from '../theme/types';
22
+ import type {TextType, TextSize, TextColor, TextWeight} from '../theme/types';
28
23
  import {mergeProps, mergeRefs} from '../utils';
29
24
  import type {BaseProps} from '../BaseProps';
30
25
  import {themeProps} from '../utils/themeProps';
@@ -54,7 +49,7 @@ export interface TimestampProps extends BaseProps<HTMLTimeElement> {
54
49
  value: string | number;
55
50
  /**
56
51
  * Display format.
57
- * - `'relative'`: "2 hours ago", "yesterday", "just now"
52
+ * - `'relative'`: "2 hours ago", "yesterday", "now"
58
53
  * - `'auto'`: Relative for recent times, `date_time` for older
59
54
  * - `'date'`: "Mar 21, 2025"
60
55
  * - `'date_time'`: "Mar 21, 2025, 2:51 PM"
@@ -138,6 +133,18 @@ const YEAR = 365 * DAY;
138
133
  /** Default auto threshold: 7 days in seconds */
139
134
  const DEFAULT_AUTO_THRESHOLD = 7 * DAY;
140
135
 
136
+ /**
137
+ * Tolerance (in seconds) for treating a *future* timestamp as the present.
138
+ * A value only a handful of seconds ahead of our reference clock is almost
139
+ * always clock skew — the displayed `now` lagging the real clock, or the value
140
+ * being produced on a slightly faster clock — not a genuine future event, so
141
+ * it reads as "now" rather than a confusing "in a few seconds". The future
142
+ * side gets a wider window than the past (which only needs to absorb the
143
+ * sub-second render-time lag) because future drift is far more likely to be
144
+ * skew than real.
145
+ */
146
+ const FUTURE_SKEW_TOLERANCE = 30;
147
+
141
148
  function parseValue(value: string | number): Date {
142
149
  if (typeof value === 'number') {
143
150
  // Heuristic: if the number is less than 1e12, treat as seconds; otherwise ms.
@@ -150,9 +157,25 @@ function parseValue(value: string | number): Date {
150
157
  function getRelativeTimeString(date: Date, now: Date): string {
151
158
  const diffSeconds = Math.round((now.getTime() - date.getTime()) / 1000);
152
159
 
160
+ // Treat values at (or a hair before/after) the present as "now". The
161
+ // internal `now` reference is captured at render time, so it can lag the
162
+ // real clock; a value equal to "right now" can land a fraction of a second
163
+ // in the future and round to a small negative delta. Without this clamp,
164
+ // such values fall into the future branch and render "in a few seconds".
165
+ if (Math.abs(diffSeconds) < 10) {
166
+ return 'now';
167
+ }
168
+
153
169
  if (diffSeconds < 0) {
154
170
  // Future dates
155
171
  const absDiff = Math.abs(diffSeconds);
172
+ // A value only a few seconds ahead of our clock is almost always skew, not
173
+ // a genuine future event — render it as the present rather than a
174
+ // confusing "in a few seconds". Wider than the past window above on
175
+ // purpose (see FUTURE_SKEW_TOLERANCE).
176
+ if (absDiff <= FUTURE_SKEW_TOLERANCE) {
177
+ return 'now';
178
+ }
156
179
  if (absDiff < MINUTE) {
157
180
  return 'in a few seconds';
158
181
  }
@@ -176,9 +199,6 @@ function getRelativeTimeString(date: Date, now: Date): string {
176
199
  return `in ${years} ${years === 1 ? 'year' : 'years'}`;
177
200
  }
178
201
 
179
- if (diffSeconds < 10) {
180
- return 'just now';
181
- }
182
202
  if (diffSeconds < MINUTE) {
183
203
  return `${diffSeconds} seconds ago`;
184
204
  }
@@ -167,6 +167,20 @@ export function Toast({
167
167
  };
168
168
  }, [autoHideDuration, startTimer]);
169
169
 
170
+ // Pause the auto-hide timer while the window is not focused, so a toast
171
+ // doesn't silently expire while the user is in another window or tab.
172
+ useEffect(() => {
173
+ if (!isAutoHide) {
174
+ return;
175
+ }
176
+ window.addEventListener('blur', pauseTimer);
177
+ window.addEventListener('focus', resumeTimer);
178
+ return () => {
179
+ window.removeEventListener('blur', pauseTimer);
180
+ window.removeEventListener('focus', resumeTimer);
181
+ };
182
+ }, [isAutoHide, pauseTimer, resumeTimer]);
183
+
170
184
  const handleDismiss = useCallback(() => {
171
185
  onDismiss('manual');
172
186
  }, [onDismiss]);
@@ -0,0 +1,204 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file ToastViewport.test.tsx
5
+ * @input Uses vitest, @testing-library/react, ToastViewport + useToast
6
+ * @output Unit tests for toast keyboard reach and focus management
7
+ * @position Testing; validates ToastViewport.tsx + Toast.tsx focus behavior
8
+ *
9
+ * SYNC: When ToastViewport.tsx or Toast.tsx focus handling changes, update these tests
10
+ */
11
+
12
+ import {describe, it, expect, vi, beforeAll} from 'vitest';
13
+ import {render, screen, fireEvent, act} from '@testing-library/react';
14
+ import React from 'react';
15
+ import {ToastViewport} from './ToastViewport';
16
+ import {useToast} from './useToast';
17
+ import type {ToastOptions} from './types';
18
+
19
+ // Popover API is not implemented in jsdom.
20
+ beforeAll(() => {
21
+ if (typeof HTMLElement.prototype.showPopover === 'undefined') {
22
+ HTMLElement.prototype.showPopover = vi.fn();
23
+ HTMLElement.prototype.hidePopover = vi.fn();
24
+ }
25
+ });
26
+
27
+ // Module-level constant default props (avoids unstable-default-props lint).
28
+ const EMPTY_OPTIONS: ToastOptions = {body: 'placeholder'};
29
+
30
+ function ShowToastButton({
31
+ options = EMPTY_OPTIONS,
32
+ triggerLabel = 'Trigger',
33
+ }: {
34
+ options?: ToastOptions;
35
+ triggerLabel?: string;
36
+ }) {
37
+ const toast = useToast();
38
+ return (
39
+ <button type="button" onClick={() => toast(options)}>
40
+ {triggerLabel}
41
+ </button>
42
+ );
43
+ }
44
+
45
+ const INFO_A: ToastOptions = {body: 'Toast A'};
46
+ const INFO_B: ToastOptions = {body: 'Toast B'};
47
+ const AUTO_TOAST: ToastOptions = {body: 'Auto toast', autoHideDuration: 3000};
48
+
49
+ function renderViewport(children: React.ReactNode) {
50
+ return render(<ToastViewport isTopLayer={false}>{children}</ToastViewport>);
51
+ }
52
+
53
+ // Fire the transition-end that ToastViewport listens for to unmount an
54
+ // exiting toast (jsdom does not run CSS transitions).
55
+ function completeExit(toastId: string) {
56
+ const node = document.querySelector<HTMLElement>(
57
+ `[data-toast-id="${toastId}"]`,
58
+ );
59
+ if (node) {
60
+ fireEvent.transitionEnd(node, {propertyName: 'grid-template-rows'});
61
+ }
62
+ }
63
+
64
+ describe('ToastViewport keyboard reach + focus', () => {
65
+ it('F6 moves focus into the newest toast', () => {
66
+ renderViewport(
67
+ <ShowToastButton options={INFO_A} triggerLabel="Trigger A" />,
68
+ );
69
+ const trigger = screen.getByText('Trigger A');
70
+ trigger.focus();
71
+ act(() => {
72
+ fireEvent.click(trigger);
73
+ });
74
+
75
+ expect(screen.getByText('Toast A')).toBeInTheDocument();
76
+ expect(document.activeElement).toBe(trigger);
77
+
78
+ act(() => {
79
+ fireEvent.keyDown(document, {key: 'F6'});
80
+ });
81
+
82
+ // Focus lands on the dismiss button of the newest toast.
83
+ const dismiss = screen.getByRole('button', {name: 'Dismiss notification'});
84
+ expect(document.activeElement).toBe(dismiss);
85
+ });
86
+
87
+ it('dismissing a focused toast moves focus to a remaining toast, not body', () => {
88
+ renderViewport(
89
+ <>
90
+ <ShowToastButton options={INFO_A} triggerLabel="Trigger A" />
91
+ <ShowToastButton options={INFO_B} triggerLabel="Trigger B" />
92
+ </>,
93
+ );
94
+ act(() => {
95
+ fireEvent.click(screen.getByText('Trigger A'));
96
+ });
97
+ act(() => {
98
+ fireEvent.click(screen.getByText('Trigger B'));
99
+ });
100
+
101
+ const dismissButtons = screen.getAllByRole('button', {
102
+ name: 'Dismiss notification',
103
+ });
104
+ expect(dismissButtons).toHaveLength(2);
105
+
106
+ // Focus the first toast's dismiss button, then dismiss it.
107
+ const firstToast = document.querySelectorAll('[data-toast-id]')[0];
108
+ const firstToastId = firstToast.getAttribute('data-toast-id')!;
109
+ const firstDismiss = firstToast.querySelector<HTMLElement>(
110
+ 'button[aria-label="Dismiss notification"]',
111
+ )!;
112
+ firstDismiss.focus();
113
+ expect(document.activeElement).toBe(firstDismiss);
114
+
115
+ act(() => {
116
+ fireEvent.click(firstDismiss);
117
+ });
118
+ act(() => {
119
+ completeExit(firstToastId);
120
+ });
121
+
122
+ // Focus must NOT drop to <body>; it moves to the remaining toast.
123
+ expect(document.activeElement).not.toBe(document.body);
124
+ expect(document.activeElement?.getAttribute('aria-label')).toBe(
125
+ 'Dismiss notification',
126
+ );
127
+ });
128
+
129
+ it('dismissing the last focused toast restores the previously-focused element', () => {
130
+ renderViewport(
131
+ <ShowToastButton options={INFO_A} triggerLabel="Trigger A" />,
132
+ );
133
+ const trigger = screen.getByText('Trigger A');
134
+ trigger.focus();
135
+ act(() => {
136
+ fireEvent.click(trigger);
137
+ });
138
+
139
+ // F6 into the toast, remembering the trigger as the prior focus.
140
+ act(() => {
141
+ fireEvent.keyDown(document, {key: 'F6'});
142
+ });
143
+ const dismiss = screen.getByRole('button', {name: 'Dismiss notification'});
144
+ expect(document.activeElement).toBe(dismiss);
145
+
146
+ const toastId = document
147
+ .querySelector('[data-toast-id]')!
148
+ .getAttribute('data-toast-id')!;
149
+
150
+ act(() => {
151
+ fireEvent.click(dismiss);
152
+ });
153
+ act(() => {
154
+ completeExit(toastId);
155
+ });
156
+
157
+ // No toasts left — focus returns to the element focused before F6.
158
+ expect(document.activeElement).toBe(trigger);
159
+ });
160
+ });
161
+
162
+ describe('Toast blur timer pause', () => {
163
+ it('pauses the auto-hide timer while the window is blurred', () => {
164
+ vi.useFakeTimers();
165
+ try {
166
+ renderViewport(
167
+ <ShowToastButton options={AUTO_TOAST} triggerLabel="Trigger Auto" />,
168
+ );
169
+ act(() => {
170
+ fireEvent.click(screen.getByText('Trigger Auto'));
171
+ });
172
+ expect(screen.getByText('Auto toast')).toBeInTheDocument();
173
+
174
+ // Window loses focus — timer should pause.
175
+ act(() => {
176
+ window.dispatchEvent(new Event('blur'));
177
+ });
178
+ act(() => {
179
+ vi.advanceTimersByTime(5000);
180
+ });
181
+ // Still present because the timer was paused while blurred.
182
+ expect(screen.getByText('Auto toast')).toBeInTheDocument();
183
+
184
+ // Window regains focus — timer resumes and the toast dismisses.
185
+ act(() => {
186
+ window.dispatchEvent(new Event('focus'));
187
+ });
188
+ act(() => {
189
+ vi.advanceTimersByTime(5000);
190
+ });
191
+ const toastId = document
192
+ .querySelector('[data-toast-id]')
193
+ ?.getAttribute('data-toast-id');
194
+ if (toastId) {
195
+ act(() => {
196
+ completeExit(toastId);
197
+ });
198
+ }
199
+ expect(screen.queryByText('Auto toast')).not.toBeInTheDocument();
200
+ } finally {
201
+ vi.useRealTimers();
202
+ }
203
+ });
204
+ });