@astryxdesign/core 0.1.2 → 0.1.3-canary.6d57c8d

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 (567) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +12 -12
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +2 -4
  10. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  11. package/dist/ButtonGroup/ButtonGroup.js +0 -1
  12. package/dist/Calendar/Calendar.d.ts +5 -4
  13. package/dist/Calendar/Calendar.d.ts.map +1 -1
  14. package/dist/Calendar/Calendar.js +82 -43
  15. package/dist/Calendar/hooks/index.d.ts +0 -2
  16. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  17. package/dist/Calendar/hooks/index.js +1 -2
  18. package/dist/Calendar/index.d.ts +3 -3
  19. package/dist/Calendar/index.d.ts.map +1 -1
  20. package/dist/Calendar/index.js +1 -1
  21. package/dist/Calendar/styles.d.ts +3 -9
  22. package/dist/Calendar/styles.d.ts.map +1 -1
  23. package/dist/Calendar/styles.js +3 -11
  24. package/dist/Carousel/Carousel.d.ts.map +1 -1
  25. package/dist/Carousel/Carousel.js +17 -3
  26. package/dist/Center/Center.d.ts +11 -1
  27. package/dist/Center/Center.d.ts.map +1 -1
  28. package/dist/Center/Center.js +9 -3
  29. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  30. package/dist/Chat/ChatComposerInput.js +0 -1
  31. package/dist/Chat/ChatMessageList.d.ts +14 -1
  32. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  33. package/dist/Chat/ChatMessageList.js +2 -0
  34. package/dist/Chat/useTriggerMenu.d.ts +8 -1
  35. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  36. package/dist/Chat/useTriggerMenu.js +15 -3
  37. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  38. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  39. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  40. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  41. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  42. package/dist/CheckboxList/CheckboxList.js +39 -9
  43. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  44. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  45. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  46. package/dist/CheckboxList/CheckboxListItem.js +0 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +9 -5
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +51 -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 +167 -99
  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/Field/Field.d.ts +19 -2
  71. package/dist/Field/Field.d.ts.map +1 -1
  72. package/dist/Field/Field.js +4 -0
  73. package/dist/Field/FieldLabel.d.ts +21 -2
  74. package/dist/Field/FieldLabel.d.ts.map +1 -1
  75. package/dist/Field/FieldLabel.js +41 -26
  76. package/dist/FileInput/FileInput.d.ts +22 -1
  77. package/dist/FileInput/FileInput.d.ts.map +1 -1
  78. package/dist/FileInput/FileInput.js +56 -10
  79. package/dist/Grid/Grid.d.ts +11 -1
  80. package/dist/Grid/Grid.d.ts.map +1 -1
  81. package/dist/Grid/Grid.js +32 -6
  82. package/dist/InputGroup/InputGroup.d.ts +4 -3
  83. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  84. package/dist/InputGroup/InputGroup.js +16 -6
  85. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  86. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  87. package/dist/InputGroup/InputGroupContext.js +2 -2
  88. package/dist/InputGroup/groupStyles.d.ts +1 -1
  89. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  90. package/dist/InputGroup/groupStyles.js +3 -3
  91. package/dist/InputGroup/index.d.ts +1 -0
  92. package/dist/InputGroup/index.d.ts.map +1 -1
  93. package/dist/Kbd/Kbd.d.ts.map +1 -1
  94. package/dist/Kbd/Kbd.js +33 -1
  95. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  96. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  97. package/dist/Layer/layerAnimations.stylex.js +8 -4
  98. package/dist/Layer/useLayer.d.ts +10 -0
  99. package/dist/Layer/useLayer.d.ts.map +1 -1
  100. package/dist/Layer/useLayer.js +64 -9
  101. package/dist/Layout/Layout.d.ts +4 -11
  102. package/dist/Layout/Layout.d.ts.map +1 -1
  103. package/dist/Layout/Layout.js +10 -11
  104. package/dist/Layout/LayoutFooter.d.ts +2 -2
  105. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  106. package/dist/Layout/LayoutHeader.d.ts +2 -2
  107. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  108. package/dist/Layout/LayoutPanel.d.ts +2 -2
  109. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  110. package/dist/Layout/padding.stylex.d.ts +51 -0
  111. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  112. package/dist/Layout/padding.stylex.js +63 -0
  113. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  114. package/dist/Lightbox/Lightbox.js +4 -2
  115. package/dist/Link/Link.d.ts +16 -1
  116. package/dist/Link/Link.d.ts.map +1 -1
  117. package/dist/Link/Link.js +18 -7
  118. package/dist/Markdown/Markdown.d.ts.map +1 -1
  119. package/dist/Markdown/Markdown.js +7 -0
  120. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  121. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  122. package/dist/MoreMenu/MoreMenu.js +0 -2
  123. package/dist/MultiSelector/MultiSelector.d.ts +27 -2
  124. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  125. package/dist/MultiSelector/MultiSelector.js +141 -42
  126. package/dist/MultiSelector/hooks.d.ts +12 -1
  127. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  128. package/dist/MultiSelector/hooks.js +14 -1
  129. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  130. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  131. package/dist/NumberInput/NumberInput.d.ts +23 -2
  132. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  133. package/dist/NumberInput/NumberInput.js +62 -12
  134. package/dist/Pagination/Pagination.d.ts +1 -1
  135. package/dist/Pagination/Pagination.d.ts.map +1 -1
  136. package/dist/Pagination/Pagination.js +15 -1
  137. package/dist/Popover/Popover.d.ts +18 -2
  138. package/dist/Popover/Popover.d.ts.map +1 -1
  139. package/dist/Popover/Popover.js +5 -2
  140. package/dist/Popover/usePopover.d.ts +38 -3
  141. package/dist/Popover/usePopover.d.ts.map +1 -1
  142. package/dist/Popover/usePopover.js +24 -9
  143. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  144. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  145. package/dist/PowerSearch/PowerSearch.js +6 -1
  146. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  147. package/dist/ProgressBar/ProgressBar.js +3 -5
  148. package/dist/RadioList/RadioList.d.ts +20 -2
  149. package/dist/RadioList/RadioList.d.ts.map +1 -1
  150. package/dist/RadioList/RadioList.js +105 -9
  151. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  152. package/dist/RadioList/RadioListItem.js +13 -2
  153. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  154. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  155. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  156. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  157. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  159. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  160. package/dist/Selector/Selector.d.ts +24 -1
  161. package/dist/Selector/Selector.d.ts.map +1 -1
  162. package/dist/Selector/Selector.js +122 -43
  163. package/dist/Selector/hooks.d.ts +8 -1
  164. package/dist/Selector/hooks.d.ts.map +1 -1
  165. package/dist/Selector/hooks.js +14 -1
  166. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  167. package/dist/SideNav/SideNavHeading.js +2 -0
  168. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  169. package/dist/SideNav/SideNavItem.js +1 -1
  170. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  171. package/dist/Skeleton/Skeleton.js +8 -2
  172. package/dist/Slider/Slider.d.ts +21 -0
  173. package/dist/Slider/Slider.d.ts.map +1 -1
  174. package/dist/Slider/Slider.js +36 -9
  175. package/dist/Spinner/Spinner.d.ts.map +1 -1
  176. package/dist/Spinner/Spinner.js +1 -1
  177. package/dist/Stack/Stack.d.ts +38 -23
  178. package/dist/Stack/Stack.d.ts.map +1 -1
  179. package/dist/Stack/Stack.js +25 -1
  180. package/dist/Stack/StackItem.d.ts +12 -23
  181. package/dist/Stack/StackItem.d.ts.map +1 -1
  182. package/dist/Stack/StackItem.js +8 -1
  183. package/dist/Switch/Switch.d.ts +23 -2
  184. package/dist/Switch/Switch.d.ts.map +1 -1
  185. package/dist/Switch/Switch.js +41 -11
  186. package/dist/TabList/Tab.d.ts.map +1 -1
  187. package/dist/TabList/Tab.js +6 -0
  188. package/dist/TabList/TabList.d.ts +16 -4
  189. package/dist/TabList/TabList.d.ts.map +1 -1
  190. package/dist/TabList/TabList.js +87 -10
  191. package/dist/TabList/TabListContext.d.ts +5 -0
  192. package/dist/TabList/TabListContext.d.ts.map +1 -1
  193. package/dist/TabList/TabListContext.js +5 -0
  194. package/dist/TabList/TabMenu.d.ts.map +1 -1
  195. package/dist/TabList/TabMenu.js +6 -1
  196. package/dist/Table/BaseTable.d.ts.map +1 -1
  197. package/dist/Table/BaseTable.js +11 -8
  198. package/dist/Table/Table.d.ts.map +1 -1
  199. package/dist/Table/Table.js +10 -1
  200. package/dist/Table/TableCell.d.ts +8 -1
  201. package/dist/Table/TableCell.d.ts.map +1 -1
  202. package/dist/Table/TableCell.js +10 -10
  203. package/dist/Table/TableHeaderCell.d.ts +8 -1
  204. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  205. package/dist/Table/TableHeaderCell.js +16 -16
  206. package/dist/Table/index.d.ts +2 -1
  207. package/dist/Table/index.d.ts.map +1 -1
  208. package/dist/Table/index.js +2 -2
  209. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  210. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  211. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  212. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  213. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  214. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  215. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  216. package/dist/Table/tableContextMenu.d.ts +33 -0
  217. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  218. package/dist/Table/tableContextMenu.js +133 -0
  219. package/dist/Table/types.d.ts +66 -0
  220. package/dist/Table/types.d.ts.map +1 -1
  221. package/dist/TextArea/TextArea.d.ts +22 -1
  222. package/dist/TextArea/TextArea.d.ts.map +1 -1
  223. package/dist/TextArea/TextArea.js +41 -9
  224. package/dist/TextInput/TextInput.d.ts +23 -2
  225. package/dist/TextInput/TextInput.d.ts.map +1 -1
  226. package/dist/TextInput/TextInput.js +55 -10
  227. package/dist/TimeInput/TimeInput.d.ts +23 -1
  228. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  229. package/dist/TimeInput/TimeInput.js +147 -76
  230. package/dist/Toast/Toast.d.ts.map +1 -1
  231. package/dist/Toast/Toast.js +14 -0
  232. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  233. package/dist/Toast/ToastViewport.js +131 -3
  234. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  235. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.js +26 -4
  237. package/dist/Toolbar/Toolbar.d.ts +3 -3
  238. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  239. package/dist/Toolbar/Toolbar.js +51 -7
  240. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  241. package/dist/Tooltip/useTooltip.js +56 -9
  242. package/dist/TopNav/TopNavHeading.d.ts +7 -1
  243. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  244. package/dist/TopNav/TopNavHeading.js +8 -0
  245. package/dist/TreeList/TreeList.d.ts +1 -1
  246. package/dist/TreeList/TreeList.d.ts.map +1 -1
  247. package/dist/TreeList/TreeList.js +72 -2
  248. package/dist/TreeList/TreeListItem.d.ts +11 -1
  249. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  250. package/dist/TreeList/TreeListItem.js +51 -12
  251. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  252. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  253. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  254. package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
  255. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  256. package/dist/Typeahead/BaseTypeahead.js +69 -6
  257. package/dist/Typeahead/Typeahead.d.ts +26 -3
  258. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  259. package/dist/Typeahead/Typeahead.js +65 -29
  260. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  261. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  262. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  263. package/dist/VisuallyHidden/index.d.ts +11 -0
  264. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  265. package/dist/VisuallyHidden/index.js +13 -0
  266. package/dist/astryx.css +18 -0
  267. package/dist/astryx.umd.js +55 -55
  268. package/dist/astryx.umd.js.map +4 -4
  269. package/dist/hooks/focusableSelector.d.ts +22 -0
  270. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  271. package/dist/hooks/focusableSelector.js +24 -0
  272. package/dist/hooks/index.d.ts +12 -2
  273. package/dist/hooks/index.d.ts.map +1 -1
  274. package/dist/hooks/index.js +7 -2
  275. package/dist/hooks/useAnnounce.d.ts +40 -0
  276. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  277. package/dist/hooks/useAnnounce.js +142 -0
  278. package/dist/hooks/useClickableContainer.d.ts +16 -1
  279. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  280. package/dist/hooks/useClickableContainer.js +16 -4
  281. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  282. package/dist/hooks/useEntryAnimation.js +4 -4
  283. package/dist/hooks/useFocusTrap.d.ts +18 -0
  284. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  285. package/dist/hooks/useFocusTrap.js +87 -3
  286. package/dist/hooks/useGridFocus.d.ts +88 -1
  287. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  288. package/dist/hooks/useGridFocus.js +261 -62
  289. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  290. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  291. package/dist/hooks/useKeyboardHint.js +220 -0
  292. package/dist/hooks/useListFocus.d.ts +78 -7
  293. package/dist/hooks/useListFocus.d.ts.map +1 -1
  294. package/dist/hooks/useListFocus.js +305 -52
  295. package/dist/hooks/useLongPress.d.ts +21 -0
  296. package/dist/hooks/useLongPress.d.ts.map +1 -0
  297. package/dist/hooks/useLongPress.js +89 -0
  298. package/dist/hooks/useTreeFocus.d.ts +157 -0
  299. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  300. package/dist/hooks/useTreeFocus.js +337 -0
  301. package/dist/hooks/useTypeahead.d.ts +63 -0
  302. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  303. package/dist/hooks/useTypeahead.js +117 -0
  304. package/dist/index.d.ts +1 -0
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +1 -0
  307. package/dist/utils/dateTypes.d.ts +12 -0
  308. package/dist/utils/dateTypes.d.ts.map +1 -1
  309. package/dist/utils/dateTypes.js +41 -1
  310. package/dist/utils/index.d.ts +4 -0
  311. package/dist/utils/index.d.ts.map +1 -1
  312. package/dist/utils/index.js +3 -1
  313. package/dist/utils/inputAria.d.ts +29 -0
  314. package/dist/utils/inputAria.d.ts.map +1 -0
  315. package/dist/utils/inputAria.js +30 -0
  316. package/dist/utils/timeParser.d.ts.map +1 -1
  317. package/dist/utils/timeParser.js +5 -2
  318. package/docs.mjs +2 -2
  319. package/package.json +12 -8
  320. package/src/Avatar/Avatar.test.tsx +37 -0
  321. package/src/Avatar/Avatar.tsx +10 -5
  322. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  323. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  324. package/src/Button/Button.tsx +3 -16
  325. package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
  326. package/src/ButtonGroup/ButtonGroup.tsx +0 -1
  327. package/src/Calendar/Calendar.doc.mjs +4 -4
  328. package/src/Calendar/Calendar.test.tsx +168 -30
  329. package/src/Calendar/Calendar.tsx +121 -66
  330. package/src/Calendar/hooks/index.ts +0 -6
  331. package/src/Calendar/index.ts +1 -3
  332. package/src/Calendar/styles.ts +4 -9
  333. package/src/Carousel/Carousel.doc.mjs +7 -1
  334. package/src/Carousel/Carousel.test.tsx +20 -0
  335. package/src/Carousel/Carousel.tsx +16 -1
  336. package/src/Center/Center.doc.mjs +12 -2
  337. package/src/Center/Center.tsx +28 -3
  338. package/src/Chat/ChatComposerInput.test.tsx +26 -7
  339. package/src/Chat/ChatComposerInput.tsx +0 -1
  340. package/src/Chat/ChatDictationButton.doc.mjs +1 -1
  341. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  342. package/src/Chat/ChatMessageList.test.tsx +18 -0
  343. package/src/Chat/ChatMessageList.tsx +16 -0
  344. package/src/Chat/useTriggerMenu.tsx +22 -4
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  347. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  348. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  349. package/src/CheckboxList/CheckboxList.test.tsx +192 -26
  350. package/src/CheckboxList/CheckboxList.tsx +56 -3
  351. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  352. package/src/CheckboxList/CheckboxListItem.tsx +0 -3
  353. package/src/Citation/Citation.doc.mjs +10 -0
  354. package/src/Citation/Citation.test.tsx +121 -0
  355. package/src/Citation/Citation.tsx +15 -3
  356. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  357. package/src/CodeBlock/CodeBlock.tsx +63 -33
  358. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  359. package/src/CommandPalette/CommandPalette.tsx +9 -21
  360. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  361. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  362. package/src/ContextMenu/ContextMenu.tsx +113 -16
  363. package/src/DateInput/DateInput.doc.mjs +286 -56
  364. package/src/DateInput/DateInput.test.tsx +248 -2
  365. package/src/DateInput/DateInput.tsx +196 -100
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  368. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  371. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  372. package/src/Dialog/Dialog.doc.mjs +2 -2
  373. package/src/Dialog/Dialog.tsx +12 -0
  374. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  375. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  376. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  377. package/src/Field/Field.doc.mjs +59 -18
  378. package/src/Field/Field.test.tsx +32 -0
  379. package/src/Field/Field.tsx +23 -5
  380. package/src/Field/FieldLabel.tsx +55 -17
  381. package/src/FileInput/FileInput.doc.mjs +17 -0
  382. package/src/FileInput/FileInput.test.tsx +283 -49
  383. package/src/FileInput/FileInput.tsx +76 -4
  384. package/src/Grid/Grid.doc.mjs +17 -4
  385. package/src/Grid/Grid.test.tsx +43 -30
  386. package/src/Grid/Grid.tsx +38 -3
  387. package/src/Icon/Icon.doc.mjs +3 -3
  388. package/src/InputGroup/InputGroup.doc.mjs +111 -23
  389. package/src/InputGroup/InputGroup.test.tsx +257 -60
  390. package/src/InputGroup/InputGroup.tsx +23 -5
  391. package/src/InputGroup/InputGroupContext.ts +9 -4
  392. package/src/InputGroup/groupStyles.ts +7 -1
  393. package/src/InputGroup/index.ts +1 -0
  394. package/src/Kbd/Kbd.test.tsx +22 -4
  395. package/src/Kbd/Kbd.tsx +35 -3
  396. package/src/Layer/layerAnimations.stylex.ts +20 -4
  397. package/src/Layer/useLayer.doc.mjs +5 -0
  398. package/src/Layer/useLayer.test.tsx +111 -0
  399. package/src/Layer/useLayer.tsx +90 -9
  400. package/src/Layout/Layout.tsx +8 -16
  401. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  402. package/src/Layout/LayoutFooter.tsx +3 -3
  403. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  404. package/src/Layout/LayoutHeader.tsx +3 -3
  405. package/src/Layout/LayoutPanel.tsx +3 -3
  406. package/src/Layout/padding.stylex.ts +52 -0
  407. package/src/Lightbox/Lightbox.doc.mjs +9 -0
  408. package/src/Lightbox/Lightbox.test.tsx +53 -5
  409. package/src/Lightbox/Lightbox.tsx +9 -4
  410. package/src/Link/Link.doc.mjs +14 -0
  411. package/src/Link/Link.test.tsx +50 -0
  412. package/src/Link/Link.tsx +39 -4
  413. package/src/Markdown/Markdown.test.tsx +11 -0
  414. package/src/Markdown/Markdown.tsx +7 -0
  415. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  416. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  417. package/src/MoreMenu/MoreMenu.tsx +0 -9
  418. package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
  419. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  420. package/src/MultiSelector/MultiSelector.tsx +194 -48
  421. package/src/MultiSelector/hooks.ts +27 -0
  422. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  423. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  424. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  425. package/src/NumberInput/NumberInput.test.tsx +242 -40
  426. package/src/NumberInput/NumberInput.tsx +89 -11
  427. package/src/Pagination/Pagination.doc.mjs +3 -3
  428. package/src/Pagination/Pagination.test.tsx +117 -5
  429. package/src/Pagination/Pagination.tsx +21 -1
  430. package/src/Popover/Popover.doc.mjs +26 -0
  431. package/src/Popover/Popover.test.tsx +121 -0
  432. package/src/Popover/Popover.tsx +23 -2
  433. package/src/Popover/usePopover.doc.mjs +9 -3
  434. package/src/Popover/usePopover.tsx +70 -12
  435. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  436. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  437. package/src/PowerSearch/PowerSearch.tsx +16 -3
  438. package/src/ProgressBar/ProgressBar.test.tsx +34 -26
  439. package/src/ProgressBar/ProgressBar.tsx +3 -4
  440. package/src/RadioList/RadioList.doc.mjs +9 -0
  441. package/src/RadioList/RadioList.test.tsx +252 -16
  442. package/src/RadioList/RadioList.tsx +166 -10
  443. package/src/RadioList/RadioListItem.tsx +14 -2
  444. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  445. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  446. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  447. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  448. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  449. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  450. package/src/Selector/Selector.doc.mjs +26 -0
  451. package/src/Selector/Selector.test.tsx +387 -15
  452. package/src/Selector/Selector.tsx +175 -67
  453. package/src/Selector/hooks.ts +23 -0
  454. package/src/SideNav/SideNav.test.tsx +48 -0
  455. package/src/SideNav/SideNavHeading.tsx +8 -2
  456. package/src/SideNav/SideNavItem.tsx +2 -4
  457. package/src/Skeleton/Skeleton.test.tsx +25 -0
  458. package/src/Skeleton/Skeleton.tsx +11 -1
  459. package/src/Slider/Slider.doc.mjs +15 -0
  460. package/src/Slider/Slider.test.tsx +155 -2
  461. package/src/Slider/Slider.tsx +53 -5
  462. package/src/Spinner/Spinner.tsx +7 -1
  463. package/src/Stack/Stack.doc.mjs +77 -0
  464. package/src/Stack/Stack.test.tsx +55 -5
  465. package/src/Stack/Stack.tsx +73 -24
  466. package/src/Stack/StackItem.test.tsx +23 -0
  467. package/src/Stack/StackItem.tsx +20 -23
  468. package/src/Switch/Switch.doc.mjs +15 -0
  469. package/src/Switch/Switch.test.tsx +162 -27
  470. package/src/Switch/Switch.tsx +61 -21
  471. package/src/TabList/Tab.tsx +6 -0
  472. package/src/TabList/TabList.doc.mjs +6 -0
  473. package/src/TabList/TabList.test.tsx +251 -0
  474. package/src/TabList/TabList.tsx +103 -12
  475. package/src/TabList/TabListContext.ts +7 -3
  476. package/src/TabList/TabMenu.tsx +6 -3
  477. package/src/Table/BaseTable.tsx +21 -17
  478. package/src/Table/Table.doc.mjs +2 -0
  479. package/src/Table/Table.test.tsx +10 -0
  480. package/src/Table/Table.tsx +8 -0
  481. package/src/Table/TableCell.tsx +25 -23
  482. package/src/Table/TableHeaderCell.tsx +28 -29
  483. package/src/Table/index.ts +3 -2
  484. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  485. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  486. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  487. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  488. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  489. package/src/Table/tableContextMenu.test.tsx +157 -0
  490. package/src/Table/tableContextMenu.tsx +142 -0
  491. package/src/Table/types.ts +74 -0
  492. package/src/Table/useTableColumnResize.doc.mjs +3 -3
  493. package/src/Table/useTableStickyColumns.doc.mjs +2 -2
  494. package/src/Text/Text.doc.mjs +18 -18
  495. package/src/TextArea/TextArea.doc.mjs +17 -0
  496. package/src/TextArea/TextArea.test.tsx +167 -35
  497. package/src/TextArea/TextArea.tsx +62 -15
  498. package/src/TextInput/TextInput.doc.mjs +17 -0
  499. package/src/TextInput/TextInput.test.tsx +168 -34
  500. package/src/TextInput/TextInput.tsx +77 -9
  501. package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
  502. package/src/TimeInput/TimeInput.doc.mjs +340 -60
  503. package/src/TimeInput/TimeInput.test.tsx +255 -7
  504. package/src/TimeInput/TimeInput.tsx +176 -70
  505. package/src/Timestamp/Timestamp.doc.mjs +19 -19
  506. package/src/Toast/Toast.tsx +14 -0
  507. package/src/Toast/ToastViewport.test.tsx +214 -0
  508. package/src/Toast/ToastViewport.tsx +167 -21
  509. package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
  510. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  511. package/src/Tokenizer/Tokenizer.tsx +37 -1
  512. package/src/Toolbar/Toolbar.test.tsx +109 -6
  513. package/src/Toolbar/Toolbar.tsx +59 -5
  514. package/src/Tooltip/Tooltip.test.tsx +68 -0
  515. package/src/Tooltip/useTooltip.tsx +55 -8
  516. package/src/TopNav/TopNav.test.tsx +50 -0
  517. package/src/TopNav/TopNavHeading.tsx +17 -1
  518. package/src/TreeList/TreeList.test.tsx +295 -6
  519. package/src/TreeList/TreeList.tsx +73 -1
  520. package/src/TreeList/TreeListItem.tsx +51 -5
  521. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  522. package/src/Typeahead/BaseTypeahead.tsx +93 -9
  523. package/src/Typeahead/Typeahead.doc.mjs +141 -30
  524. package/src/Typeahead/Typeahead.test.tsx +331 -1
  525. package/src/Typeahead/Typeahead.tsx +105 -33
  526. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  527. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  528. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  529. package/src/VisuallyHidden/index.ts +15 -0
  530. package/src/docs-script.test.mjs +50 -0
  531. package/src/hooks/focusableSelector.ts +25 -0
  532. package/src/hooks/index.ts +28 -2
  533. package/src/hooks/useAnnounce.test.tsx +101 -0
  534. package/src/hooks/useAnnounce.ts +168 -0
  535. package/src/hooks/useClickableContainer.ts +16 -4
  536. package/src/hooks/useEntryAnimation.ts +16 -4
  537. package/src/hooks/useFocusTrap.test.tsx +132 -0
  538. package/src/hooks/useFocusTrap.ts +98 -4
  539. package/src/hooks/useGridFocus.doc.mjs +40 -2
  540. package/src/hooks/useGridFocus.test.tsx +132 -0
  541. package/src/hooks/useGridFocus.ts +343 -67
  542. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  543. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  544. package/src/hooks/useKeyboardHint.tsx +332 -0
  545. package/src/hooks/useListFocus.doc.mjs +50 -10
  546. package/src/hooks/useListFocus.test.tsx +375 -0
  547. package/src/hooks/useListFocus.ts +395 -57
  548. package/src/hooks/useLongPress.test.tsx +134 -0
  549. package/src/hooks/useLongPress.ts +119 -0
  550. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  551. package/src/hooks/useTreeFocus.test.tsx +214 -0
  552. package/src/hooks/useTreeFocus.ts +559 -0
  553. package/src/hooks/useTypeahead.test.tsx +109 -0
  554. package/src/hooks/useTypeahead.ts +183 -0
  555. package/src/index.ts +1 -0
  556. package/src/theme/derivedVarRegistry.test.ts +10 -5
  557. package/src/utils/dateTypes.test.ts +40 -0
  558. package/src/utils/dateTypes.ts +40 -0
  559. package/src/utils/index.ts +9 -0
  560. package/src/utils/inputAria.test.ts +39 -0
  561. package/src/utils/inputAria.ts +58 -0
  562. package/src/utils/timeParser.test.ts +10 -0
  563. package/src/utils/timeParser.ts +5 -2
  564. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  565. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  566. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  567. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -103,8 +103,8 @@ export const docs = {
103
103
  },
104
104
  {
105
105
  name: 'weekStartsOn',
106
- type: '0 | 1 | 2 | 3 | 4 | 5 | 6',
107
- description: 'First day of week (0=Sunday).',
106
+ type: "0 | 1 | 2 | 3 | 4 | 5 | 6 | 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'",
107
+ description: 'First day of week. Accepts a number (0=Sunday) or a three-letter day name (e.g. "mon").',
108
108
  default: '0',
109
109
  },
110
110
  ],
@@ -147,7 +147,7 @@ export const docsZh = {
147
147
  {name: 'hasOutsideDays', type: 'boolean', description: '显示相邻月份的日期。', default: 'true'},
148
148
  {name: 'hasWeekNumbers', type: 'boolean', description: '显示 ISO 周数。', default: 'false'},
149
149
  {name: 'hasVariableRowCount', type: 'boolean', description: '可变行数与固定 6 行网格。', default: 'false'},
150
- {name: 'weekStartsOn', type: '0 | 1 | 2 | 3 | 4 | 5 | 6', description: '每周起始日(0=周日)。', default: '0'},
150
+ {name: 'weekStartsOn', type: "0 | 1 | 2 | 3 | 4 | 5 | 6 | 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'", description: '每周起始日。可为数字(0=周日)或三字母星期缩写(如 "mon")。', default: '0'},
151
151
  ],
152
152
  theming: {
153
153
  targets: [
@@ -200,6 +200,6 @@ export const docsDense = {
200
200
  hasOutsideDays: 'show days from adjacent months',
201
201
  hasWeekNumbers: 'show ISO week numbers',
202
202
  hasVariableRowCount: 'variable vs fixed 6-row grid',
203
- weekStartsOn: 'first day of week (0=Sunday)',
203
+ weekStartsOn: 'first day of week (0=Sunday, or name e.g. "mon")',
204
204
  },
205
205
  };
@@ -10,19 +10,21 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi} from 'vitest';
13
- import {act, render, screen} from '@testing-library/react';
13
+ import {act, render, screen, within} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {Calendar} from './Calendar';
16
16
  import type {CalendarHandle} from './Calendar';
17
17
 
18
18
  /**
19
19
  * Helper to find a day button by its day number.
20
- * Day buttons have aria-labels like "Thursday, January 15, 2026".
20
+ * Day buttons are native <button> elements with aria-labels like
21
+ * "Thursday, January 15, 2026". Each button is the sole child of a
22
+ * role="gridcell" wrapper.
21
23
  */
22
24
  function getDayButton(day: number, month = 'January', year = 2026) {
23
25
  // Match the full date pattern with the day number
24
26
  const pattern = new RegExp(`${month}\\s+${day},\\s+${year}`);
25
- return screen.getByRole('gridcell', {name: pattern});
27
+ return screen.getByRole('button', {name: pattern});
26
28
  }
27
29
 
28
30
  describe('Calendar', () => {
@@ -146,10 +148,7 @@ describe('Calendar', () => {
146
148
  const handleFocusChange = vi.fn();
147
149
 
148
150
  render(
149
- <Calendar
150
- focusDate="2026-01-01"
151
- onFocusDateChange={handleFocusChange}
152
- />,
151
+ <Calendar focusDate="2026-01-01" onFocusDateChange={handleFocusChange} />,
153
152
  );
154
153
 
155
154
  const nextButton = screen.getByRole('button', {name: 'Next month'});
@@ -189,9 +188,7 @@ describe('Calendar', () => {
189
188
  return day !== 0 && day !== 6;
190
189
  };
191
190
 
192
- render(
193
- <Calendar focusDate="2026-01-01" dateConstraints={[isWeekday]} />,
194
- );
191
+ render(<Calendar focusDate="2026-01-01" dateConstraints={[isWeekday]} />);
195
192
 
196
193
  // January 4, 2026 is a Sunday - should be disabled
197
194
  const sunday = getDayButton(4);
@@ -238,6 +235,21 @@ describe('Calendar', () => {
238
235
  expect(dayNames[0]).toHaveTextContent('Mo');
239
236
  });
240
237
 
238
+ it('accepts a three-letter day name for weekStartsOn', () => {
239
+ render(<Calendar weekStartsOn="mon" />);
240
+
241
+ // "mon" should behave exactly like the numeric 1 (Monday first).
242
+ const dayNames = screen.getAllByText(/^(Mo|Tu|We|Th|Fr|Sa|Su)$/);
243
+ expect(dayNames[0]).toHaveTextContent('Mo');
244
+ });
245
+
246
+ it('treats weekStartsOn day names case-insensitively', () => {
247
+ render(<Calendar weekStartsOn={'WED' as 'wed'} />);
248
+
249
+ const dayNames = screen.getAllByText(/^(Mo|Tu|We|Th|Fr|Sa|Su)$/);
250
+ expect(dayNames[0]).toHaveTextContent('We');
251
+ });
252
+
241
253
  // ─── Range Mode ──────────────────────────────────────────────
242
254
 
243
255
  it('supports range selection mode', async () => {
@@ -245,11 +257,7 @@ describe('Calendar', () => {
245
257
  const handleChange = vi.fn();
246
258
 
247
259
  render(
248
- <Calendar
249
- mode="range"
250
- onChange={handleChange}
251
- focusDate="2026-01-01"
252
- />,
260
+ <Calendar mode="range" onChange={handleChange} focusDate="2026-01-01" />,
253
261
  );
254
262
 
255
263
  // Click start date
@@ -271,11 +279,7 @@ describe('Calendar', () => {
271
279
  const handleChange = vi.fn();
272
280
 
273
281
  render(
274
- <Calendar
275
- mode="range"
276
- onChange={handleChange}
277
- focusDate="2026-01-01"
278
- />,
282
+ <Calendar mode="range" onChange={handleChange} focusDate="2026-01-01" />,
279
283
  );
280
284
 
281
285
  // Click later date first
@@ -321,6 +325,75 @@ describe('Calendar', () => {
321
325
  expect(screen.getAllByRole('gridcell').length).toBeGreaterThan(0);
322
326
  });
323
327
 
328
+ it('renders a valid APG grid: one grid, header row of columnheaders inside it, week rows of gridcells', () => {
329
+ render(<Calendar focusDate="2026-01-01" />);
330
+
331
+ const grids = screen.getAllByRole('grid');
332
+ expect(grids.length).toBe(1);
333
+ const grid = grids[0];
334
+
335
+ // The columnheaders live INSIDE the grid.
336
+ const columnHeaders = screen.getAllByRole('columnheader');
337
+ expect(columnHeaders.length).toBe(7);
338
+ for (const header of columnHeaders) {
339
+ expect(grid.contains(header)).toBe(true);
340
+ }
341
+
342
+ // The grid's rows: first is the header row of columnheaders, the rest are
343
+ // week rows whose direct children are gridcells.
344
+ const rows = within(grid).getAllByRole('row');
345
+ // 1 header row + 6 week rows (fixed 6-row grid).
346
+ expect(rows.length).toBe(7);
347
+
348
+ const [headerRow, ...weekRows] = rows;
349
+
350
+ // Header row's direct children are the 7 columnheaders.
351
+ const headerChildren = Array.from(headerRow.children);
352
+ const headerColHeaders = headerChildren.filter(
353
+ child => child.getAttribute('role') === 'columnheader',
354
+ );
355
+ expect(headerColHeaders.length).toBe(7);
356
+
357
+ // Each week row's direct children are gridcells (7 per row).
358
+ for (const row of weekRows) {
359
+ const gridcellChildren = Array.from(row.children).filter(
360
+ child => child.getAttribute('role') === 'gridcell',
361
+ );
362
+ expect(gridcellChildren.length).toBe(7);
363
+ }
364
+ });
365
+
366
+ it('renders week-number cells as rowheader when hasWeekNumbers is set', () => {
367
+ render(<Calendar hasWeekNumbers focusDate="2026-01-01" />);
368
+
369
+ const grid = screen.getByRole('grid');
370
+ const rowHeaders = within(grid).getAllByRole('rowheader');
371
+ // One rowheader (week number) per week row.
372
+ expect(rowHeaders.length).toBeGreaterThanOrEqual(5);
373
+ // Week numbers are numeric.
374
+ for (const header of rowHeaders) {
375
+ expect(header.textContent).toMatch(/^\d+$/);
376
+ }
377
+ });
378
+
379
+ it('gridcell wrappers are direct children of week rows, and the button is inside the gridcell', () => {
380
+ render(<Calendar focusDate="2026-01-01" />);
381
+
382
+ const grid = screen.getByRole('grid');
383
+ const gridcells = within(grid).getAllByRole('gridcell');
384
+ for (const cell of gridcells) {
385
+ // The gridcell's parent is a role="row".
386
+ const parent = cell.parentElement;
387
+ expect(parent?.getAttribute('role')).toBe('row');
388
+ // The day button (if present) is a descendant of the gridcell.
389
+ const button = cell.querySelector('button');
390
+ if (button) {
391
+ expect(cell.contains(button)).toBe(true);
392
+ expect(button).not.toHaveAttribute('role', 'gridcell');
393
+ }
394
+ }
395
+ });
396
+
324
397
  it('has navigation buttons with accessible labels', () => {
325
398
  render(<Calendar />);
326
399
 
@@ -346,16 +419,13 @@ describe('Calendar', () => {
346
419
  const handleFocusChange = vi.fn();
347
420
 
348
421
  render(
349
- <Calendar
350
- focusDate="2026-01-01"
351
- onFocusDateChange={handleFocusChange}
352
- />,
422
+ <Calendar focusDate="2026-01-01" onFocusDateChange={handleFocusChange} />,
353
423
  );
354
424
 
355
425
  // Focus Jan 28
356
426
  const day28 = getDayButton(28);
357
427
  await user.click(day28);
358
- day28.querySelector('button')?.focus();
428
+ day28.focus();
359
429
 
360
430
  // Press ArrowDown — should move to Feb 4 (+7 days), not Feb 28
361
431
  await user.keyboard('{ArrowDown}');
@@ -365,6 +435,78 @@ describe('Calendar', () => {
365
435
  expect(focusedElement).toHaveAttribute('data-date', '2026-02-04');
366
436
  });
367
437
 
438
+ it('ArrowDown lands on the same weekday +7 days even when earlier days are disabled (complex-2)', async () => {
439
+ const user = userEvent.setup();
440
+
441
+ // min disables Jan 1–4 (HTML-disabled). Jan 8 is a Thursday; ArrowDown must
442
+ // land on Jan 15 (the same weekday, +7 days), not a shifted date caused by
443
+ // the removed enabled cells.
444
+ render(<Calendar focusDate="2026-01-01" min="2026-01-05" />);
445
+
446
+ const day1 = getDayButton(1);
447
+ expect(day1).toBeDisabled();
448
+
449
+ const day8 = getDayButton(8);
450
+ day8.focus();
451
+
452
+ await user.keyboard('{ArrowDown}');
453
+ expect(document.activeElement).toHaveAttribute('data-date', '2026-01-15');
454
+
455
+ await user.keyboard('{ArrowDown}');
456
+ expect(document.activeElement).toHaveAttribute('data-date', '2026-01-22');
457
+ });
458
+
459
+ it('ArrowUp skips a disabled cell in the same column to the next enabled row (complex-2)', async () => {
460
+ const user = userEvent.setup();
461
+
462
+ // max disables Jan 22 onward. Focus Feb 5 handling is out of scope; instead
463
+ // use dateConstraints to disable a single mid-grid day and verify column
464
+ // geometry is preserved (ArrowUp from Jan 15 skips disabled Jan 8 → Jan 1).
465
+ const disableJan8 = (date: Date) =>
466
+ !(
467
+ date.getFullYear() === 2026 &&
468
+ date.getMonth() === 0 &&
469
+ date.getDate() === 8
470
+ );
471
+
472
+ render(<Calendar focusDate="2026-01-01" dateConstraints={[disableJan8]} />);
473
+
474
+ const day8 = getDayButton(8);
475
+ expect(day8).toBeDisabled();
476
+
477
+ const day15 = getDayButton(15);
478
+ day15.focus();
479
+
480
+ // ArrowUp: same column one row up is Jan 8 (disabled) → skip to Jan 1.
481
+ await user.keyboard('{ArrowUp}');
482
+ expect(document.activeElement).toHaveAttribute('data-date', '2026-01-01');
483
+ });
484
+
485
+ it('cross-month arrow nav resolves the focused date from data-date (locale-safe)', async () => {
486
+ // Regression for complex-4: getFocusedDate must read the machine-readable
487
+ // data-date attribute, not parse the human-readable aria-label with
488
+ // new Date() (which is locale-dependent). We prove the resolution path by
489
+ // corrupting the aria-label to something new Date() cannot parse — cross-
490
+ // month navigation must still report the correct ISO date.
491
+ const user = userEvent.setup();
492
+ const handleFocusChange = vi.fn();
493
+
494
+ render(
495
+ <Calendar focusDate="2026-01-01" onFocusDateChange={handleFocusChange} />,
496
+ );
497
+
498
+ const day28 = getDayButton(28);
499
+ await user.click(day28);
500
+ day28.focus();
501
+ // Simulate a non-English/unparseable aria-label while keeping data-date.
502
+ day28.setAttribute('aria-label', '2026年1月28日 水曜日');
503
+
504
+ await user.keyboard('{ArrowDown}');
505
+
506
+ // Feb 4 (+7 days) — resolved via data-date despite the unparseable label.
507
+ expect(document.activeElement).toHaveAttribute('data-date', '2026-02-04');
508
+ });
509
+
368
510
  it('prev button is disabled when focusDate month contains min', () => {
369
511
  render(<Calendar focusDate="2026-01-01" min="2026-01-15" />);
370
512
 
@@ -414,11 +556,7 @@ describe('Calendar', () => {
414
556
  const handleChange = vi.fn();
415
557
 
416
558
  render(
417
- <Calendar
418
- mode="range"
419
- onChange={handleChange}
420
- focusDate="2026-01-01"
421
- />,
559
+ <Calendar mode="range" onChange={handleChange} focusDate="2026-01-01" />,
422
560
  );
423
561
 
424
562
  // Click start date to begin range selection
@@ -32,7 +32,6 @@ import {useGridFocus} from '../hooks';
32
32
  import {
33
33
  useCalendarDays,
34
34
  useCalendarConstraints,
35
- useCalendarRovingTabindex,
36
35
  type CalendarDay,
37
36
  } from './hooks';
38
37
  import {
@@ -46,7 +45,6 @@ import {
46
45
  plainDateFromISO,
47
46
  plainDateToISO,
48
47
  plainDateToDate,
49
- plainDateFromDate,
50
48
  plainDateToday,
51
49
  plainDateSetFirstOfMonth,
52
50
  plainDateAddMonths,
@@ -70,8 +68,19 @@ import {
70
68
  // Types
71
69
  // =============================================================================
72
70
 
73
- export type {ISODateString, DayOfWeek, DateRange} from '../utils/dateTypes';
74
- import type {ISODateString, DayOfWeek, DateRange} from '../utils/dateTypes';
71
+ export type {
72
+ ISODateString,
73
+ DayOfWeek,
74
+ DayOfWeekName,
75
+ DateRange,
76
+ } from '../utils/dateTypes';
77
+ import type {
78
+ ISODateString,
79
+ DayOfWeek,
80
+ DayOfWeekName,
81
+ DateRange,
82
+ } from '../utils/dateTypes';
83
+ import {normalizeDayOfWeek} from '../utils/dateTypes';
75
84
  import {themeProps} from '../utils/themeProps';
76
85
 
77
86
  /** Imperative handle for Calendar handleRef */
@@ -134,10 +143,11 @@ interface CalendarBaseProps extends Omit<
134
143
  hasVariableRowCount?: boolean;
135
144
 
136
145
  /**
137
- * First day of week.
146
+ * First day of week. Accepts a number (0 = Sunday … 6 = Saturday) or a
147
+ * three-letter day name ('sun'–'sat', case-insensitive) for readability.
138
148
  * Default: 0 (Sunday)
139
149
  */
140
- weekStartsOn?: DayOfWeek;
150
+ weekStartsOn?: DayOfWeek | DayOfWeekName;
141
151
  }
142
152
 
143
153
  // ─── Mode-specific Props (discriminated union) ────────────────
@@ -200,13 +210,17 @@ export function Calendar({ref, ...props}: CalendarProps) {
200
210
  hasOutsideDays = true,
201
211
  hasWeekNumbers = false,
202
212
  hasVariableRowCount = false,
203
- weekStartsOn = 0,
213
+ weekStartsOn: weekStartsOnProp = 0,
204
214
  xstyle,
205
215
  className,
206
216
  style,
207
217
  ...rest
208
218
  } = props;
209
219
 
220
+ // Normalize `weekStartsOn` (number or three-letter day name) to a numeric
221
+ // DayOfWeek so all downstream date math keeps working with an index.
222
+ const weekStartsOn = normalizeDayOfWeek(weekStartsOnProp);
223
+
210
224
  // Today's date (memoized)
211
225
  const today = useMemo(() => plainDateToday(), []);
212
226
 
@@ -539,33 +553,52 @@ function MonthGrid({
539
553
  return null;
540
554
  }, [mode, value]);
541
555
 
542
- const {isTabbable} = useCalendarRovingTabindex({
543
- days,
544
- today,
545
- year,
546
- month: month.month,
547
- isDateDisabled,
548
- selectedDate: selectedDateForTabindex,
549
- });
556
+ // Seed the initial roving tab stop for this month. useGridFocus owns the
557
+ // live tab stop (see `hasRovingTabIndex` below) — it honors an existing
558
+ // `tabindex="0"` and repairs/moves it thereafter — so this only decides
559
+ // which day button starts tabbable. Priority: selected date (if visible and
560
+ // enabled) > today (if visible and enabled) > first enabled in-month day.
561
+ const seedTabbableIso = useMemo((): ISODateString | null => {
562
+ if (selectedDateForTabindex) {
563
+ const isSelectedInMonth =
564
+ selectedDateForTabindex.year === year &&
565
+ selectedDateForTabindex.month === month.month;
566
+ if (isSelectedInMonth && !isDateDisabled(selectedDateForTabindex)) {
567
+ return plainDateToISO(selectedDateForTabindex);
568
+ }
569
+ }
570
+
571
+ const isTodayInMonth = today.year === year && today.month === month.month;
572
+ if (isTodayInMonth && !isDateDisabled(today)) {
573
+ return plainDateToISO(today);
574
+ }
550
575
 
551
- // Helper to get the focused date from the currently focused element
576
+ for (const day of days) {
577
+ if (!day.isOutside && !isDateDisabled(day.date)) {
578
+ return day.iso;
579
+ }
580
+ }
581
+
582
+ return null;
583
+ }, [days, today, year, month.month, isDateDisabled, selectedDateForTabindex]);
584
+
585
+ // Helper to get the focused date from the currently focused element.
586
+ // Reads the machine-readable `data-date` (ISO) attribute rather than parsing
587
+ // the human-readable `aria-label` with `new Date()`, which is locale/format
588
+ // dependent and returns Invalid Date in non-English locales (e.g. fr-FR,
589
+ // ja-JP), silently swallowing month-boundary arrow navigation (complex-4).
552
590
  const getFocusedDate = useCallback((): ISODateString | null => {
553
591
  const activeElement = document.activeElement as HTMLElement | null;
554
592
  if (!activeElement) {
555
593
  return null;
556
594
  }
557
595
 
558
- const ariaLabel = activeElement.getAttribute('aria-label');
559
- if (!ariaLabel) {
596
+ const iso = activeElement.getAttribute('data-date');
597
+ if (!iso) {
560
598
  return null;
561
599
  }
562
600
 
563
- const parsed = new Date(ariaLabel);
564
- if (isNaN(parsed.getTime())) {
565
- return null;
566
- }
567
-
568
- return plainDateToISO(plainDateFromDate(parsed));
601
+ return iso as ISODateString;
569
602
  }, []);
570
603
 
571
604
  // Handle navigation to previous month
@@ -605,16 +638,31 @@ function MonthGrid({
605
638
  }
606
639
  }, [getFocusedDate, onNavigateNext]);
607
640
 
608
- // Grid focus navigation
609
- const {gridRef, handleKeyDown: handleGridKeyDown} =
610
- useGridFocus<HTMLDivElement>({
611
- columns: 7,
612
- cellSelector: 'button:not([disabled])',
613
- onNavigateBefore: handleNavigatePrevious,
614
- onNavigateAfter: handleNavigateNext,
615
- onPageUp: handlePageUp,
616
- onPageDown: handlePageDown,
617
- });
641
+ // Grid focus navigation.
642
+ //
643
+ // The hook enumerates ALL grid cells (every `role="gridcell"`, including
644
+ // disabled days and empty placeholder cells) so the true 7-column geometry is
645
+ // preserved. `isCellFocusable` / `getFocusTarget` tell the hook which cells
646
+ // can take focus (those containing an enabled day button) and where to send
647
+ // focus (the day button inside the cell). Arrow keys move to the target
648
+ // row/column and, if that cell is disabled, continue in the same direction to
649
+ // the next enabled cell.
650
+ const {
651
+ gridRef,
652
+ handleKeyDown: handleGridKeyDown,
653
+ handleFocus: handleGridFocus,
654
+ } = useGridFocus<HTMLDivElement>({
655
+ columns: 7,
656
+ cellSelector: '[role="gridcell"]',
657
+ isCellFocusable: cell =>
658
+ cell.querySelector('button:not([disabled])') !== null,
659
+ getFocusTarget: cell => cell.querySelector<HTMLElement>('button'),
660
+ hasRovingTabIndex: true,
661
+ onNavigateBefore: handleNavigatePrevious,
662
+ onNavigateAfter: handleNavigateNext,
663
+ onPageUp: handlePageUp,
664
+ onPageDown: handlePageDown,
665
+ });
618
666
 
619
667
  // Handle pending focus after month navigation
620
668
  useEffect(() => {
@@ -627,11 +675,11 @@ function MonthGrid({
627
675
  );
628
676
 
629
677
  const targetPd = plainDateFromISO(pendingFocus);
630
- const targetLabel = plainDateFormat(targetPd, DATE_FORMAT_WITH_WEEKDAY);
678
+ const targetIso = plainDateToISO(targetPd);
631
679
 
632
680
  let targetButton: HTMLElement | null = null;
633
681
  for (const button of buttons) {
634
- if (button.getAttribute('aria-label') === targetLabel) {
682
+ if (button.getAttribute('data-date') === targetIso) {
635
683
  targetButton = button;
636
684
  break;
637
685
  }
@@ -688,40 +736,38 @@ function MonthGrid({
688
736
 
689
737
  return (
690
738
  <div {...stylex.props(monthGridStyles.monthGrid)}>
691
- {/* Day names header */}
692
- <div
693
- {...stylex.props(
694
- monthGridStyles.weekHeader,
695
- hasWeekNumbers && monthGridStyles.weekHeaderWithNumbers,
696
- )}>
697
- {hasWeekNumbers && (
698
- <div
699
- {...stylex.props(
700
- monthGridStyles.dayName,
701
- monthGridStyles.weekNumberHeader,
702
- )}
703
- />
704
- )}
705
- {dayNames.map(name => (
706
- <div
707
- key={name}
708
- role="columnheader"
709
- {...stylex.props(monthGridStyles.dayName)}>
710
- {name}
711
- </div>
712
- ))}
713
- </div>
714
-
715
- {/* Days grid */}
739
+ {/* Days grid (APG grid: header row of columnheaders + week rows) */}
716
740
  <div
717
741
  ref={gridRef}
718
742
  role="grid"
719
743
  aria-label={monthLabel}
720
744
  onKeyDown={handleGridKeyDown}
745
+ onFocus={handleGridFocus}
721
746
  {...stylex.props(
722
747
  monthGridStyles.daysGrid,
723
748
  hasWeekNumbers && monthGridStyles.daysGridWithNumbers,
724
749
  )}>
750
+ {/* Day names header row (columnheaders live inside the grid). Uses the
751
+ same display:contents row so its cells align to the grid columns. */}
752
+ <div role="row" {...stylex.props(monthGridStyles.weekRow)}>
753
+ {hasWeekNumbers && (
754
+ <div
755
+ {...stylex.props(
756
+ monthGridStyles.dayName,
757
+ monthGridStyles.weekNumberHeader,
758
+ )}
759
+ />
760
+ )}
761
+ {dayNames.map(name => (
762
+ <div
763
+ key={name}
764
+ role="columnheader"
765
+ {...stylex.props(monthGridStyles.dayName)}>
766
+ {name}
767
+ </div>
768
+ ))}
769
+ </div>
770
+
725
771
  {weeks.map(week => {
726
772
  const weekDate = week.find(d => !d.isOutside)?.date || week[0].date;
727
773
  const weekNum = plainDateGetWeekNumber(weekDate);
@@ -732,7 +778,9 @@ function MonthGrid({
732
778
  role="row"
733
779
  {...stylex.props(monthGridStyles.weekRow)}>
734
780
  {hasWeekNumbers && (
735
- <div {...stylex.props(monthGridStyles.weekNumber)}>
781
+ <div
782
+ role="rowheader"
783
+ {...stylex.props(monthGridStyles.weekNumber)}>
736
784
  {weekNum}
737
785
  </div>
738
786
  )}
@@ -750,7 +798,7 @@ function MonthGrid({
750
798
  today={today}
751
799
  hasOutsideDays={hasOutsideDays}
752
800
  isDisabled={isDateDisabled(day.date)}
753
- isTabbable={isTabbable(day.iso)}
801
+ isTabbable={day.iso === seedTabbableIso}
754
802
  onDayClick={onDayClick}
755
803
  onDayHover={onDayHover}
756
804
  />
@@ -779,6 +827,11 @@ interface DayCellProps {
779
827
  today: PlainDate;
780
828
  hasOutsideDays: boolean;
781
829
  isDisabled: boolean;
830
+ /**
831
+ * Whether this day seeds the initial roving tab stop. useGridFocus
832
+ * (`hasRovingTabIndex`) owns the live tab stop thereafter — it honors an
833
+ * existing `tabindex="0"` and repairs/moves it on navigation and focus.
834
+ */
782
835
  isTabbable: boolean;
783
836
  onDayClick: (date: PlainDate) => void;
784
837
  onDayHover: (date: PlainDate | null) => void;
@@ -803,7 +856,9 @@ function DayCell({
803
856
  const {date, isOutside, dayNumber} = day;
804
857
 
805
858
  if (isOutside && !hasOutsideDays) {
806
- return <div {...stylex.props(dayCellStyles.cell)} />;
859
+ // Empty placeholder cell — still a gridcell so the grid geometry stays a
860
+ // clean 7-per-row set for keyboard navigation.
861
+ return <div role="gridcell" {...stylex.props(dayCellStyles.cell)} />;
807
862
  }
808
863
 
809
864
  const state = computeDayCellState({
@@ -825,7 +880,7 @@ function DayCell({
825
880
  const previewRounding = computePreviewRounding(state);
826
881
 
827
882
  return (
828
- <div {...stylex.props(dayCellStyles.cell)}>
883
+ <div role="gridcell" {...stylex.props(dayCellStyles.cell)}>
829
884
  {/* Range background */}
830
885
  {state.isInRange && (
831
886
  <div
@@ -863,12 +918,12 @@ function DayCell({
863
918
  {/* Day button */}
864
919
  <button
865
920
  type="button"
866
- role="gridcell"
867
921
  data-date={day.iso}
868
922
  aria-label={plainDateFormat(date, DATE_FORMAT_WITH_WEEKDAY)}
869
923
  aria-selected={state.isSelected || state.isInRange || undefined}
870
924
  aria-disabled={state.effectivelyDisabled || undefined}
871
925
  disabled={isDisabled}
926
+ // Initial roving tab-stop seed; useGridFocus owns it after mount.
872
927
  tabIndex={isTabbableDay ? 0 : -1}
873
928
  onClick={() => !state.effectivelyDisabled && onDayClick(date)}
874
929
  onMouseEnter={() => !state.effectivelyDisabled && onDayHover(date)}
@@ -27,9 +27,3 @@ export type {
27
27
  UseCalendarNavigationOptions,
28
28
  UseCalendarNavigationReturn,
29
29
  } from './useCalendarNavigation';
30
-
31
- export {useCalendarRovingTabindex} from './useCalendarRovingTabindex';
32
- export type {
33
- UseCalendarRovingTabindexOptions,
34
- UseCalendarRovingTabindexReturn,
35
- } from './useCalendarRovingTabindex';
@@ -17,6 +17,7 @@ export type {
17
17
  CalendarHandle,
18
18
  ISODateString,
19
19
  DayOfWeek,
20
+ DayOfWeekName,
20
21
  DateRange,
21
22
  } from './Calendar';
22
23
 
@@ -25,7 +26,6 @@ export {
25
26
  useCalendarDays,
26
27
  useCalendarConstraints,
27
28
  useCalendarNavigation,
28
- useCalendarRovingTabindex,
29
29
  } from './hooks';
30
30
  export type {
31
31
  CalendarDay,
@@ -35,8 +35,6 @@ export type {
35
35
  UseCalendarConstraintsReturn,
36
36
  UseCalendarNavigationOptions,
37
37
  UseCalendarNavigationReturn,
38
- UseCalendarRovingTabindexOptions,
39
- UseCalendarRovingTabindexReturn,
40
38
  } from './hooks';
41
39
 
42
40
  // Re-export calendar-specific utilities for advanced usage
@@ -78,17 +78,12 @@ export const monthGridStyles = stylex.create({
78
78
  monthGrid: {
79
79
  flex: '1 1 0',
80
80
  },
81
- weekHeader: {
82
- display: 'grid',
83
- gridTemplateColumns: 'repeat(7, 1fr)',
84
- marginBottom: spacingVars['--spacing-1'],
85
- },
86
- weekHeaderWithNumbers: {
87
- gridTemplateColumns: 'auto repeat(7, 1fr)',
88
- },
89
81
  dayName: {
90
82
  width: sizeVars['--size-element-md'],
91
- height: sizeVars['--size-element-md'],
83
+ // Restores the small gap the standalone header used to have below it.
84
+ height: `calc(${sizeVars['--size-element-md']} + ${spacingVars['--spacing-1']})`,
85
+ paddingBottom: spacingVars['--spacing-1'],
86
+ boxSizing: 'border-box',
92
87
  display: 'flex',
93
88
  alignItems: 'center',
94
89
  justifyContent: 'center',