@astryxdesign/core 0.4.4 → 0.4.5-canary.013e728

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 (469) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/AvatarGroup/AvatarGroupOverflow.js +1 -1
  3. package/dist/BottomSheet/BottomSheet.d.ts +11 -2
  4. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  5. package/dist/BottomSheet/BottomSheet.js +4 -0
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts +5 -2
  7. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheetPanel.js +65 -15
  9. package/dist/BottomSheet/index.d.ts +1 -1
  10. package/dist/BottomSheet/index.d.ts.map +1 -1
  11. package/dist/BottomSheet/snapOffsets.d.ts +57 -20
  12. package/dist/BottomSheet/snapOffsets.d.ts.map +1 -1
  13. package/dist/BottomSheet/snapOffsets.js +106 -30
  14. package/dist/BottomSheet/useSheetGestures.d.ts +13 -8
  15. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  16. package/dist/BottomSheet/useSheetGestures.js +160 -64
  17. package/dist/Breadcrumbs/BreadcrumbItem.js +6 -6
  18. package/dist/Button/Button.js +5 -5
  19. package/dist/Calendar/Calendar.d.ts +17 -0
  20. package/dist/Calendar/Calendar.d.ts.map +1 -1
  21. package/dist/Calendar/Calendar.js +55 -15
  22. package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts +14 -0
  23. package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts.map +1 -0
  24. package/dist/Calendar/getStandaloneShortWeekdayNames.js +26 -0
  25. package/dist/Calendar/hooks/useCalendarConstraints.d.ts +22 -1
  26. package/dist/Calendar/hooks/useCalendarConstraints.d.ts.map +1 -1
  27. package/dist/Calendar/hooks/useCalendarConstraints.js +26 -4
  28. package/dist/Calendar/hooks/useCalendarDays.d.ts.map +1 -1
  29. package/dist/Calendar/hooks/useCalendarDays.js +14 -10
  30. package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
  31. package/dist/Calendar/hooks/useCalendarNavigation.js +8 -4
  32. package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts +47 -0
  33. package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts.map +1 -0
  34. package/dist/Calendar/standaloneShortWeekdayNames.generated.js +40 -0
  35. package/dist/Calendar/styles.js +3 -3
  36. package/dist/Center/Center.d.ts +23 -1
  37. package/dist/Center/Center.d.ts.map +1 -1
  38. package/dist/Center/Center.js +12 -6
  39. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  40. package/dist/Chat/ChatComposer.js +2 -2
  41. package/dist/Chat/ChatToolCalls.js +1 -1
  42. package/dist/Chat/useChatDictation.d.ts +1 -1
  43. package/dist/Chat/useChatDictation.d.ts.map +1 -1
  44. package/dist/Chat/useSpeechRecognition.d.ts +1 -1
  45. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  46. package/dist/Chat/useSpeechRecognition.js +4 -3
  47. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  48. package/dist/CheckboxInput/CheckboxInput.js +9 -0
  49. package/dist/Citation/Citation.js +3 -3
  50. package/dist/ClickableCard/ClickableCard.js +2 -2
  51. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  52. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  53. package/dist/ComplexSelector/ComplexSelector.js +8 -3
  54. package/dist/DateInput/DateInput.d.ts.map +1 -1
  55. package/dist/DateInput/DateInput.js +17 -9
  56. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  57. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  58. package/dist/DateRangeInput/DateRangeInput.js +47 -9
  59. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  60. package/dist/DateTimeInput/DateTimeInput.js +18 -10
  61. package/dist/Dialog/Dialog.d.ts.map +1 -1
  62. package/dist/Dialog/Dialog.js +3 -3
  63. package/dist/Field/FieldLabel.d.ts +2 -1
  64. package/dist/Field/FieldLabel.d.ts.map +1 -1
  65. package/dist/Field/FieldLabel.js +21 -3
  66. package/dist/Field/InputClearButton.d.ts +2 -1
  67. package/dist/Field/InputClearButton.d.ts.map +1 -1
  68. package/dist/Field/InputClearButton.js +12 -1
  69. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  70. package/dist/Field/inputStyles.stylex.js +4 -4
  71. package/dist/FileInput/FileInput.d.ts.map +1 -1
  72. package/dist/FileInput/FileInput.js +2 -2
  73. package/dist/FormLayout/FormLayout.d.ts +24 -2
  74. package/dist/FormLayout/FormLayout.d.ts.map +1 -1
  75. package/dist/FormLayout/FormLayout.js +7 -2
  76. package/dist/FormLayout/FormLayoutContext.d.ts +15 -2
  77. package/dist/FormLayout/FormLayoutContext.d.ts.map +1 -1
  78. package/dist/FormLayout/FormLayoutContext.js +19 -4
  79. package/dist/FormLayout/index.d.ts +1 -1
  80. package/dist/FormLayout/index.d.ts.map +1 -1
  81. package/dist/HoverCard/HoverCard.d.ts +13 -3
  82. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  83. package/dist/HoverCard/HoverCard.js +2 -0
  84. package/dist/HoverCard/index.d.ts +1 -1
  85. package/dist/HoverCard/index.d.ts.map +1 -1
  86. package/dist/HoverCard/useHoverCard.d.ts +17 -2
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +78 -9
  89. package/dist/Item/Item.d.ts +9 -1
  90. package/dist/Item/Item.d.ts.map +1 -1
  91. package/dist/Item/Item.js +46 -8
  92. package/dist/Layer/index.d.ts +4 -2
  93. package/dist/Layer/index.d.ts.map +1 -1
  94. package/dist/Layer/index.js +2 -0
  95. package/dist/Layer/useLayer.d.ts.map +1 -1
  96. package/dist/Layer/useLayer.js +3 -2
  97. package/dist/Layer/useTouchTrigger.d.ts +113 -0
  98. package/dist/Layer/useTouchTrigger.d.ts.map +1 -0
  99. package/dist/Layer/useTouchTrigger.js +241 -0
  100. package/dist/Layout/container.stylex.d.ts +4 -2
  101. package/dist/Layout/container.stylex.d.ts.map +1 -1
  102. package/dist/Layout/container.stylex.js +19 -11
  103. package/dist/Layout/index.d.ts +1 -0
  104. package/dist/Layout/index.d.ts.map +1 -1
  105. package/dist/Layout/index.js +4 -0
  106. package/dist/Layout/padding.stylex.d.ts +315 -12
  107. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  108. package/dist/Layout/padding.stylex.js +389 -12
  109. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  110. package/dist/Lightbox/Lightbox.js +2 -1
  111. package/dist/Link/Link.js +4 -4
  112. package/dist/Markdown/Markdown.d.ts +8 -0
  113. package/dist/Markdown/Markdown.d.ts.map +1 -1
  114. package/dist/Markdown/Markdown.js +31 -3
  115. package/dist/Markdown/parser.d.ts +14 -2
  116. package/dist/Markdown/parser.d.ts.map +1 -1
  117. package/dist/Markdown/parser.js +50 -2
  118. package/dist/MobileNav/MobileNav.d.ts +1 -0
  119. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  120. package/dist/MobileNav/MobileNav.js +24 -6
  121. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  122. package/dist/MultiSelector/MultiSelector.js +8 -3
  123. package/dist/NavItem/navItemStyles.stylex.d.ts +2 -2
  124. package/dist/NavItem/navItemStyles.stylex.js +2 -2
  125. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  126. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  127. package/dist/NumberInput/NumberInput.js +38 -5
  128. package/dist/Outline/Outline.js +2 -2
  129. package/dist/Outline/parseOutlineFromMarkdown.d.ts +2 -0
  130. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  131. package/dist/Outline/parseOutlineFromMarkdown.js +5 -31
  132. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  133. package/dist/PowerSearch/PowerSearch.js +11 -12
  134. package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
  135. package/dist/PowerSearch/PowerSearchToken.js +3 -1
  136. package/dist/PowerSearch/formatFilterValue.d.ts +11 -2
  137. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  138. package/dist/PowerSearch/formatFilterValue.js +23 -8
  139. package/dist/RadioList/RadioList.d.ts.map +1 -1
  140. package/dist/RadioList/RadioList.js +12 -1
  141. package/dist/RadioList/RadioListItem.d.ts +2 -2
  142. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  143. package/dist/RadioList/RadioListItem.js +68 -38
  144. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  145. package/dist/Resizable/ResizeHandle.js +3 -3
  146. package/dist/Section/Section.d.ts +36 -1
  147. package/dist/Section/Section.d.ts.map +1 -1
  148. package/dist/Section/Section.js +12 -2
  149. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  150. package/dist/SegmentedControl/SegmentedControlItem.js +6 -1
  151. package/dist/SelectableCard/SelectableCard.js +1 -1
  152. package/dist/Selector/Selector.d.ts +26 -1
  153. package/dist/Selector/Selector.d.ts.map +1 -1
  154. package/dist/Selector/Selector.js +95 -17
  155. package/dist/Selector/SelectorOption.d.ts +14 -1
  156. package/dist/Selector/SelectorOption.d.ts.map +1 -1
  157. package/dist/Selector/SelectorOption.js +6 -0
  158. package/dist/Selector/SelectorRowLayoutContext.d.ts +8 -0
  159. package/dist/Selector/SelectorRowLayoutContext.d.ts.map +1 -0
  160. package/dist/Selector/SelectorRowLayoutContext.js +27 -0
  161. package/dist/Selector/types.d.ts +1 -0
  162. package/dist/Selector/types.d.ts.map +1 -1
  163. package/dist/SideNav/SideNavHeading.js +1 -1
  164. package/dist/SideNav/SideNavItem.js +1 -1
  165. package/dist/Slider/Slider.d.ts.map +1 -1
  166. package/dist/Slider/Slider.js +3 -3
  167. package/dist/Stack/Stack.d.ts +23 -1
  168. package/dist/Stack/Stack.d.ts.map +1 -1
  169. package/dist/Stack/Stack.js +12 -6
  170. package/dist/StatusDot/StatusDot.d.ts +43 -3
  171. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  172. package/dist/StatusDot/StatusDot.js +42 -5
  173. package/dist/Switch/Switch.d.ts.map +1 -1
  174. package/dist/Switch/Switch.js +9 -0
  175. package/dist/TabList/TabMenu.js +1 -1
  176. package/dist/Table/TableRow.js +4 -4
  177. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  178. package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
  179. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  180. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +1 -1
  181. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  182. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  183. package/dist/Table/plugins/sortable/useTableSortableState.d.ts +5 -3
  184. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  185. package/dist/Table/plugins/sortable/useTableSortableState.js +12 -9
  186. package/dist/Table/plugins/tree/useTableTreeData.js +2 -2
  187. package/dist/TextArea/TextArea.d.ts.map +1 -1
  188. package/dist/TextArea/TextArea.js +6 -1
  189. package/dist/TextInput/TextInput.d.ts.map +1 -1
  190. package/dist/TextInput/TextInput.js +6 -1
  191. package/dist/Thumbnail/Thumbnail.js +1 -1
  192. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  193. package/dist/TimeInput/TimeInput.js +6 -1
  194. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  195. package/dist/Timestamp/Timestamp.js +6 -4
  196. package/dist/Timestamp/formatInstant.d.ts +3 -2
  197. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  198. package/dist/Timestamp/formatInstant.js +8 -8
  199. package/dist/Timestamp/tooltipEntries.d.ts +2 -1
  200. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
  201. package/dist/Timestamp/tooltipEntries.js +5 -3
  202. package/dist/Token/Token.js +1 -1
  203. package/dist/Tooltip/Tooltip.d.ts +16 -3
  204. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  205. package/dist/Tooltip/Tooltip.js +2 -0
  206. package/dist/Tooltip/index.d.ts +1 -1
  207. package/dist/Tooltip/index.d.ts.map +1 -1
  208. package/dist/Tooltip/useTooltip.d.ts +21 -2
  209. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  210. package/dist/Tooltip/useTooltip.js +71 -13
  211. package/dist/TopNav/TopNavHeading.js +2 -2
  212. package/dist/TopNav/TopNavItem.js +2 -2
  213. package/dist/TopNav/TopNavMegaMenu.js +1 -1
  214. package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
  215. package/dist/TopNav/TopNavMenu.js +2 -2
  216. package/dist/TreeList/TreeListItem.js +1 -1
  217. package/dist/astryx.css +85 -60
  218. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
  219. package/dist/hooks/containerReveal.stylex.js +18 -18
  220. package/dist/hooks/scrollbarGutter.d.ts +28 -0
  221. package/dist/hooks/scrollbarGutter.d.ts.map +1 -0
  222. package/dist/hooks/scrollbarGutter.js +103 -0
  223. package/dist/hooks/useResolvedRequired.d.ts +19 -0
  224. package/dist/hooks/useResolvedRequired.d.ts.map +1 -0
  225. package/dist/hooks/useResolvedRequired.js +40 -0
  226. package/dist/hooks/useScrollLock.d.ts +4 -0
  227. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  228. package/dist/hooks/useScrollLock.js +13 -1
  229. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  230. package/dist/i18n/InternationalizationContext.js +2 -0
  231. package/dist/i18n/index.d.ts +6 -0
  232. package/dist/i18n/index.d.ts.map +1 -1
  233. package/dist/i18n/index.js +6 -0
  234. package/dist/i18n/useCollator.d.ts +12 -0
  235. package/dist/i18n/useCollator.d.ts.map +1 -0
  236. package/dist/i18n/useCollator.js +40 -0
  237. package/dist/i18n/useLocale.d.ts +3 -0
  238. package/dist/i18n/useLocale.d.ts.map +1 -0
  239. package/dist/i18n/useLocale.js +31 -0
  240. package/dist/theme/defineTheme.d.ts +5 -0
  241. package/dist/theme/defineTheme.d.ts.map +1 -1
  242. package/dist/theme/defineTheme.js +5 -0
  243. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  244. package/dist/theme/derivedVarRegistry.js +7 -0
  245. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  246. package/dist/theme/generateThemeRules.js +66 -5
  247. package/dist/utils/dateParser.d.ts +4 -3
  248. package/dist/utils/dateParser.d.ts.map +1 -1
  249. package/dist/utils/dateParser.js +8 -8
  250. package/dist/utils/plainDate.d.ts +9 -2
  251. package/dist/utils/plainDate.d.ts.map +1 -1
  252. package/dist/utils/plainDate.js +16 -4
  253. package/locales/af-ZA.json +958 -0
  254. package/locales/ar-SA.json +998 -0
  255. package/locales/ca-ES.json +974 -0
  256. package/locales/cs-CZ.json +990 -0
  257. package/locales/da-DK.json +966 -0
  258. package/locales/de-DE.json +966 -0
  259. package/locales/el-GR.json +990 -0
  260. package/locales/en.json +8 -0
  261. package/locales/es-ES.json +978 -0
  262. package/locales/fi-FI.json +990 -0
  263. package/locales/fr-FR.json +928 -0
  264. package/locales/he-IL.json +990 -0
  265. package/locales/hu-HU.json +990 -0
  266. package/locales/it-IT.json +978 -0
  267. package/locales/ja-JP.json +998 -0
  268. package/locales/ko-KR.json +990 -0
  269. package/locales/nl-NL.json +954 -0
  270. package/locales/no-NO.json +978 -0
  271. package/locales/pl-PL.json +982 -0
  272. package/locales/pseudo.json +6 -0
  273. package/locales/pt-BR.json +982 -0
  274. package/locales/pt-PT.json +982 -0
  275. package/locales/ro-RO.json +982 -0
  276. package/locales/ru-RU.json +990 -0
  277. package/locales/sr-SP.json +990 -0
  278. package/locales/sv-SE.json +986 -0
  279. package/locales/tr-TR.json +990 -0
  280. package/locales/uk-UA.json +990 -0
  281. package/locales/vi-VN.json +986 -0
  282. package/locales/zh-CN.json +998 -0
  283. package/locales/zh-TW.json +998 -0
  284. package/package.json +3 -3
  285. package/src/AvatarGroup/AvatarGroupOverflow.tsx +1 -1
  286. package/src/Banner/Banner.doc.mjs +2 -0
  287. package/src/BottomSheet/BottomSheet.doc.mjs +36 -3
  288. package/src/BottomSheet/BottomSheet.test.tsx +177 -2
  289. package/src/BottomSheet/BottomSheet.tsx +16 -2
  290. package/src/BottomSheet/BottomSheetPanel.test.tsx +41 -0
  291. package/src/BottomSheet/BottomSheetPanel.tsx +95 -15
  292. package/src/BottomSheet/index.ts +5 -1
  293. package/src/BottomSheet/snapOffsets.test.ts +114 -47
  294. package/src/BottomSheet/snapOffsets.ts +119 -33
  295. package/src/BottomSheet/useSheetGestures.test.ts +113 -15
  296. package/src/BottomSheet/useSheetGestures.ts +196 -100
  297. package/src/Breadcrumbs/BreadcrumbItem.tsx +1 -1
  298. package/src/Button/Button.tsx +7 -7
  299. package/src/Calendar/Calendar.doc.mjs +16 -0
  300. package/src/Calendar/Calendar.test.tsx +137 -0
  301. package/src/Calendar/Calendar.tsx +80 -11
  302. package/src/Calendar/getStandaloneShortWeekdayNames.test.ts +54 -0
  303. package/src/Calendar/getStandaloneShortWeekdayNames.ts +38 -0
  304. package/src/Calendar/hooks/useCalendarConstraints.ts +54 -3
  305. package/src/Calendar/hooks/useCalendarDays.ts +13 -10
  306. package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
  307. package/src/Calendar/standaloneShortWeekdayNames.generated.ts +50 -0
  308. package/src/Calendar/styles.ts +3 -3
  309. package/src/Center/Center.doc.mjs +48 -0
  310. package/src/Center/Center.test.tsx +158 -0
  311. package/src/Center/Center.tsx +50 -9
  312. package/src/Chat/ChatComposer.tsx +7 -4
  313. package/src/Chat/ChatToolCalls.tsx +1 -1
  314. package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
  315. package/src/Chat/useChatDictation.ts +1 -1
  316. package/src/Chat/useSpeechRecognition.ts +5 -3
  317. package/src/CheckboxInput/CheckboxInput.tsx +6 -0
  318. package/src/Citation/Citation.tsx +3 -3
  319. package/src/ClickableCard/ClickableCard.tsx +2 -2
  320. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  321. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
  322. package/src/ComplexSelector/ComplexSelector.tsx +8 -5
  323. package/src/DateInput/DateInput.test.tsx +17 -1
  324. package/src/DateInput/DateInput.tsx +13 -9
  325. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  326. package/src/DateRangeInput/DateRangeInput.test.tsx +81 -1
  327. package/src/DateRangeInput/DateRangeInput.tsx +88 -8
  328. package/src/DateTimeInput/DateTimeInput.tsx +23 -8
  329. package/src/Dialog/Dialog.test.tsx +24 -0
  330. package/src/Dialog/Dialog.tsx +3 -0
  331. package/src/Field/Field.doc.mjs +3 -0
  332. package/src/Field/FieldLabel.tsx +20 -4
  333. package/src/Field/InputClearButton.test.tsx +134 -2
  334. package/src/Field/InputClearButton.tsx +42 -1
  335. package/src/Field/inputStyles.stylex.ts +16 -12
  336. package/src/FileInput/FileInput.tsx +5 -4
  337. package/src/FormLayout/FormLayout.doc.mjs +13 -0
  338. package/src/FormLayout/FormLayout.test.tsx +181 -4
  339. package/src/FormLayout/FormLayout.tsx +33 -2
  340. package/src/FormLayout/FormLayoutContext.ts +22 -7
  341. package/src/FormLayout/index.ts +1 -1
  342. package/src/HoverCard/HoverCard.doc.mjs +18 -0
  343. package/src/HoverCard/HoverCard.test.tsx +189 -0
  344. package/src/HoverCard/HoverCard.tsx +22 -2
  345. package/src/HoverCard/index.ts +1 -0
  346. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  347. package/src/HoverCard/useHoverCard.tsx +103 -7
  348. package/src/Item/Item.doc.mjs +1 -0
  349. package/src/Item/Item.test.tsx +50 -0
  350. package/src/Item/Item.tsx +41 -2
  351. package/src/Layer/index.ts +10 -0
  352. package/src/Layer/useLayer.tsx +13 -2
  353. package/src/Layer/useTouchTrigger.test.ts +70 -0
  354. package/src/Layer/useTouchTrigger.ts +366 -0
  355. package/src/Layout/Layout.doc.mjs +18 -0
  356. package/src/Layout/container.stylex.ts +11 -3
  357. package/src/Layout/index.ts +4 -0
  358. package/src/Layout/overlayPaddingReset.test.tsx +222 -0
  359. package/src/Layout/padding.stylex.ts +192 -12
  360. package/src/Lightbox/Lightbox.tsx +2 -1
  361. package/src/Link/Link.tsx +4 -4
  362. package/src/Markdown/Markdown.doc.mjs +3 -0
  363. package/src/Markdown/Markdown.test.tsx +78 -0
  364. package/src/Markdown/Markdown.tsx +41 -1
  365. package/src/Markdown/parser.ts +60 -2
  366. package/src/MobileNav/MobileNav.tsx +61 -5
  367. package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
  368. package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
  369. package/src/MultiSelector/MultiSelector.test.tsx +1 -1
  370. package/src/MultiSelector/MultiSelector.tsx +8 -5
  371. package/src/NavItem/navItemStyles.stylex.ts +2 -2
  372. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  373. package/src/NumberInput/NumberInput.doc.mjs +24 -0
  374. package/src/NumberInput/NumberInput.test.tsx +138 -0
  375. package/src/NumberInput/NumberInput.tsx +44 -6
  376. package/src/Outline/Outline.doc.mjs +2 -0
  377. package/src/Outline/Outline.tsx +1 -1
  378. package/src/Outline/parseOutlineFromMarkdown.ts +10 -40
  379. package/src/PowerSearch/PowerSearch.tsx +17 -8
  380. package/src/PowerSearch/PowerSearchToken.tsx +3 -0
  381. package/src/PowerSearch/formatFilterValue.test.ts +22 -2
  382. package/src/PowerSearch/formatFilterValue.ts +31 -6
  383. package/src/RadioList/RadioList.doc.mjs +1 -1
  384. package/src/RadioList/RadioList.test.tsx +170 -2
  385. package/src/RadioList/RadioList.tsx +9 -1
  386. package/src/RadioList/RadioListItem.tsx +83 -51
  387. package/src/Resizable/ResizeHandle.test.tsx +22 -0
  388. package/src/Resizable/ResizeHandle.tsx +9 -3
  389. package/src/Section/Section.doc.mjs +55 -0
  390. package/src/Section/Section.test.tsx +201 -4
  391. package/src/Section/Section.tsx +69 -0
  392. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  393. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -2
  394. package/src/SelectableCard/SelectableCard.tsx +1 -1
  395. package/src/Selector/Selector.doc.mjs +16 -2
  396. package/src/Selector/Selector.test.tsx +525 -1
  397. package/src/Selector/Selector.tsx +166 -21
  398. package/src/Selector/SelectorOption.doc.mjs +7 -0
  399. package/src/Selector/SelectorOption.tsx +21 -0
  400. package/src/Selector/SelectorRowLayoutContext.ts +32 -0
  401. package/src/Selector/types.ts +4 -4
  402. package/src/SideNav/SideNavHeading.tsx +2 -2
  403. package/src/SideNav/SideNavItem.tsx +1 -1
  404. package/src/Slider/Slider.tsx +11 -1
  405. package/src/Stack/Stack.doc.mjs +72 -0
  406. package/src/Stack/Stack.test.tsx +156 -0
  407. package/src/Stack/Stack.tsx +50 -8
  408. package/src/StatusDot/StatusDot.doc.mjs +13 -0
  409. package/src/StatusDot/StatusDot.test.tsx +115 -8
  410. package/src/StatusDot/StatusDot.tsx +74 -9
  411. package/src/Switch/Switch.tsx +6 -0
  412. package/src/TabList/TabMenu.tsx +1 -1
  413. package/src/Table/TableRow.tsx +4 -4
  414. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +2 -1
  415. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +2 -1
  416. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +22 -4
  417. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
  418. package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
  419. package/src/Table/plugins/tree/useTableTreeData.tsx +2 -2
  420. package/src/TextArea/TextArea.doc.mjs +1 -1
  421. package/src/TextArea/TextArea.tsx +3 -1
  422. package/src/TextInput/TextInput.tsx +3 -1
  423. package/src/Thumbnail/Thumbnail.tsx +1 -1
  424. package/src/TimeInput/TimeInput.tsx +3 -1
  425. package/src/Timestamp/Timestamp.test.tsx +27 -7
  426. package/src/Timestamp/Timestamp.tsx +8 -4
  427. package/src/Timestamp/formatInstant.ts +9 -7
  428. package/src/Timestamp/tooltipEntries.test.ts +11 -6
  429. package/src/Timestamp/tooltipEntries.ts +6 -2
  430. package/src/Token/Token.tsx +1 -1
  431. package/src/Tooltip/Tooltip.doc.mjs +18 -0
  432. package/src/Tooltip/Tooltip.test.tsx +254 -1
  433. package/src/Tooltip/Tooltip.tsx +22 -2
  434. package/src/Tooltip/index.ts +1 -0
  435. package/src/Tooltip/useTooltip.doc.mjs +2 -0
  436. package/src/Tooltip/useTooltip.tsx +99 -21
  437. package/src/TopNav/TopNavHeading.tsx +1 -1
  438. package/src/TopNav/TopNavItem.tsx +2 -2
  439. package/src/TopNav/TopNavMegaMenu.tsx +1 -1
  440. package/src/TopNav/TopNavMegaMenuItem.tsx +1 -1
  441. package/src/TopNav/TopNavMenu.tsx +2 -2
  442. package/src/TreeList/TreeListItem.tsx +1 -1
  443. package/src/hooks/containerReveal.stylex.ts +54 -18
  444. package/src/hooks/scrollbarGutter.test.ts +167 -0
  445. package/src/hooks/scrollbarGutter.ts +121 -0
  446. package/src/hooks/useResolvedRequired.ts +42 -0
  447. package/src/hooks/useScrollLock.doc.mjs +4 -4
  448. package/src/hooks/useScrollLock.test.ts +73 -0
  449. package/src/hooks/useScrollLock.ts +14 -0
  450. package/src/i18n/InternationalizationContext.ts +2 -0
  451. package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
  452. package/src/i18n/__tests__/useCollator.test.tsx +115 -0
  453. package/src/i18n/__tests__/useLocale.test.tsx +52 -0
  454. package/src/i18n/index.ts +6 -0
  455. package/src/i18n/useCollator.doc.mjs +84 -0
  456. package/src/i18n/useCollator.ts +44 -0
  457. package/src/i18n/useLocale.doc.mjs +70 -0
  458. package/src/i18n/useLocale.ts +34 -0
  459. package/src/i18n/useTranslator.doc.mjs +1 -1
  460. package/src/theme/defineTheme.test.ts +9 -3
  461. package/src/theme/defineTheme.ts +5 -0
  462. package/src/theme/derivedVarRegistry.test.ts +19 -5
  463. package/src/theme/derivedVarRegistry.ts +4 -0
  464. package/src/theme/generateThemeRules.test.ts +118 -0
  465. package/src/theme/generateThemeRules.ts +83 -5
  466. package/src/utils/dateParser.test.ts +20 -0
  467. package/src/utils/dateParser.ts +14 -7
  468. package/src/utils/plainDate.test.ts +56 -0
  469. package/src/utils/plainDate.ts +17 -4
@@ -158,7 +158,7 @@ const itemStyles = stylex.create({
158
158
  paddingBlock: spacingVars['--spacing-1'],
159
159
  textDecoration: {
160
160
  default: 'none',
161
- ':hover': {
161
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
162
162
  '@media (hover: hover)': 'underline',
163
163
  },
164
164
  },
@@ -107,11 +107,11 @@ const styles = stylex.create({
107
107
  },
108
108
  },
109
109
  ariaDisabled: {
110
+ // The variants' hover treatment already steps aside for
111
+ // `[aria-disabled]`; `:active` still matches a press on an aria-disabled
112
+ // button, so that one is suppressed here.
110
113
  backgroundImage: {
111
114
  default: 'none',
112
- ':hover': {
113
- '@media (hover: hover)': 'none',
114
- },
115
115
  ':active': 'none',
116
116
  },
117
117
  },
@@ -198,7 +198,7 @@ const variants = stylex.create({
198
198
  color: colorVars['--color-on-accent'],
199
199
  backgroundImage: {
200
200
  default: null,
201
- ':hover': {
201
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
202
202
  '@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']})`,
203
203
  },
204
204
  ':active': `linear-gradient(${colorVars['--color-overlay-pressed']}, ${colorVars['--color-overlay-pressed']})`,
@@ -209,7 +209,7 @@ const variants = stylex.create({
209
209
  color: colorVars['--color-text-primary'],
210
210
  backgroundImage: {
211
211
  default: null,
212
- ':hover': {
212
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
213
213
  '@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']})`,
214
214
  },
215
215
  ':active': `linear-gradient(${colorVars['--color-overlay-pressed']}, ${colorVars['--color-overlay-pressed']})`,
@@ -220,7 +220,7 @@ const variants = stylex.create({
220
220
  color: colorVars['--color-text-primary'],
221
221
  backgroundImage: {
222
222
  default: null,
223
- ':hover': {
223
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
224
224
  '@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']})`,
225
225
  },
226
226
  ':active': `linear-gradient(${colorVars['--color-overlay-pressed']}, ${colorVars['--color-overlay-pressed']})`,
@@ -235,7 +235,7 @@ const variants = stylex.create({
235
235
  outlineColor: {default: null, ':focus-visible': colorVars['--color-error']},
236
236
  backgroundImage: {
237
237
  default: null,
238
- ':hover': {
238
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
239
239
  '@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']})`,
240
240
  },
241
241
  ':active': `linear-gradient(${colorVars['--color-overlay-pressed']}, ${colorVars['--color-overlay-pressed']})`,
@@ -68,6 +68,18 @@ export const docs = {
68
68
  type: 'Array<(date: Date) => boolean>',
69
69
  description: 'Custom constraint functions.',
70
70
  },
71
+ {
72
+ name: 'maxRangeSpan',
73
+ type: 'number',
74
+ description:
75
+ 'Range mode: max days a range may span, both endpoints counted (7 = a 7-day window). Caps the window from the picked start; before a start is picked every day stays selectable.',
76
+ },
77
+ {
78
+ name: 'minRangeSpan',
79
+ type: 'number',
80
+ description:
81
+ 'Range mode: min days a range must span, both endpoints counted (2 forbids a single-day range). Default 1.',
82
+ },
71
83
  {
72
84
  name: 'focusDate',
73
85
  type: 'ISODateString',
@@ -142,6 +154,8 @@ export const docsZh = {
142
154
  {name: 'min', type: 'ISODateString', description: '可选择的最早日期。'},
143
155
  {name: 'max', type: 'ISODateString', description: '可选择的最晚日期。'},
144
156
  {name: 'dateConstraints', type: 'Array<(date: Date) => boolean>', description: '自定义约束函数。'},
157
+ {name: 'maxRangeSpan', type: 'number', description: '范围模式:范围最多可跨越的天数,含首尾两端(7 = 7 天窗口)。选定起始日后限制窗口大小。'},
158
+ {name: 'minRangeSpan', type: 'number', description: '范围模式:范围最少需跨越的天数,含首尾两端(2 表示禁止单日范围)。默认为 1。'},
145
159
  {name: 'focusDate', type: 'ISODateString', description: '受控可见月份。'},
146
160
  {name: 'onFocusDateChange', type: '(focusDate: ISODateString) => void', description: '导航回调函数。'},
147
161
  {name: 'handleRef', type: 'React.Ref<CalendarHandle>', description: '日历导航的命令式句柄,包括 navigateTo()。'},
@@ -205,6 +219,8 @@ export const docsDense = {
205
219
  min: 'minimum selectable date',
206
220
  max: 'maximum selectable date',
207
221
  dateConstraints: 'custom constraint fns',
222
+ maxRangeSpan: 'range mode: max days a range may span, both ends counted (7 = 7-day window)',
223
+ minRangeSpan: 'range mode: min days a range must span, both ends counted (default 1)',
208
224
  focusDate: 'controlled visible month',
209
225
  onFocusDateChange: 'navigation callback',
210
226
  handleRef: 'imperative navigation handle',
@@ -21,6 +21,8 @@ import {calendarStyles} from './styles';
21
21
  import {defineTheme} from '../theme/defineTheme';
22
22
  import {generateThemeCSS} from '../theme/generateThemeRules';
23
23
  import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
24
+ import {InternationalizationProvider} from '../i18n/InternationalizationProvider';
25
+ import {standaloneShortWeekdayNamesByLocale} from './standaloneShortWeekdayNames.generated';
24
26
 
25
27
  function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
26
28
  const {prose, component} = generateThemeCSS(theme);
@@ -120,6 +122,45 @@ describe('Calendar', () => {
120
122
  expect(screen.getByText('Sa')).toBeInTheDocument();
121
123
  });
122
124
 
125
+ it('uses the provider locale for stand-alone short day names', () => {
126
+ const localizedNames = standaloneShortWeekdayNamesByLocale.es;
127
+ const englishNames = standaloneShortWeekdayNamesByLocale.en;
128
+ expect(localizedNames).not.toEqual(englishNames);
129
+
130
+ const {rerender} = render(
131
+ <InternationalizationProvider locale="es-ES">
132
+ <Calendar />
133
+ </InternationalizationProvider>,
134
+ );
135
+
136
+ expect(
137
+ screen.getAllByRole('columnheader').map(header => header.textContent),
138
+ ).toEqual(localizedNames);
139
+
140
+ rerender(
141
+ <InternationalizationProvider locale="en">
142
+ <Calendar />
143
+ </InternationalizationProvider>,
144
+ );
145
+ expect(
146
+ screen.getAllByRole('columnheader').map(header => header.textContent),
147
+ ).toEqual(englishNames);
148
+ });
149
+
150
+ it('rotates localized day names by numeric weekday index', () => {
151
+ const localizedNames = standaloneShortWeekdayNamesByLocale.es;
152
+
153
+ render(
154
+ <InternationalizationProvider locale="es-ES">
155
+ <Calendar weekStartsOn={1} />
156
+ </InternationalizationProvider>,
157
+ );
158
+
159
+ expect(
160
+ screen.getAllByRole('columnheader').map(header => header.textContent),
161
+ ).toEqual([...localizedNames.slice(1), localizedNames[0]]);
162
+ });
163
+
123
164
  it('displays correct number of day cells', () => {
124
165
  render(<Calendar />);
125
166
 
@@ -236,6 +277,102 @@ describe('Calendar', () => {
236
277
  expect(sunday).toBeDisabled();
237
278
  });
238
279
 
280
+ it('caps the end date to maxRangeSpan once a start is picked', async () => {
281
+ const user = userEvent.setup();
282
+
283
+ render(<Calendar mode="range" focusDate="2026-01-01" maxRangeSpan={7} />);
284
+
285
+ // Before a start is picked every day is selectable.
286
+ expect(getDayButton(20)).not.toBeDisabled();
287
+
288
+ // Pick Jan 10 as the start. A 7-day window spans Jan 4–16 (start ± 6).
289
+ await user.click(getDayButton(10));
290
+
291
+ expect(getDayButton(16)).not.toBeDisabled(); // 6 days after — the edge
292
+ expect(getDayButton(17)).toBeDisabled(); // 7 days after — beyond the cap
293
+ expect(getDayButton(4)).not.toBeDisabled(); // 6 days before — symmetric
294
+ expect(getDayButton(3)).toBeDisabled(); // 7 days before — beyond the cap
295
+ });
296
+
297
+ it('enforces minRangeSpan once a start is picked', async () => {
298
+ const user = userEvent.setup();
299
+
300
+ render(<Calendar mode="range" focusDate="2026-01-01" minRangeSpan={3} />);
301
+
302
+ // Pick Jan 10. A 3-day minimum forbids ends closer than 2 days away.
303
+ await user.click(getDayButton(10));
304
+
305
+ // The picked start itself stays enabled — it is the active selection
306
+ // anchor, not an unreachable end.
307
+ expect(getDayButton(10)).not.toBeDisabled();
308
+ expect(getDayButton(11)).toBeDisabled(); // 1 day apart — too short
309
+ expect(getDayButton(12)).not.toBeDisabled(); // 3-day span — allowed
310
+ expect(getDayButton(8)).not.toBeDisabled(); // 3-day span the other way
311
+ expect(getDayButton(9)).toBeDisabled(); // 2-day span — too short
312
+ });
313
+
314
+ it('clears the in-progress start when the anchor is clicked again', async () => {
315
+ const user = userEvent.setup();
316
+ const handleChange = vi.fn();
317
+
318
+ render(
319
+ <Calendar
320
+ mode="range"
321
+ focusDate="2026-01-01"
322
+ onChange={handleChange}
323
+ minRangeSpan={3}
324
+ />,
325
+ );
326
+
327
+ // Pick Jan 10 as the start, then click it again.
328
+ await user.click(getDayButton(10));
329
+ await user.click(getDayButton(10));
330
+
331
+ // No zero-length range is committed…
332
+ expect(handleChange).not.toHaveBeenCalled();
333
+
334
+ // …and the start is cleared: the days that minRangeSpan disabled around
335
+ // the old anchor are selectable again, so a new start can be placed there.
336
+ expect(getDayButton(11)).not.toBeDisabled();
337
+ expect(getDayButton(9)).not.toBeDisabled();
338
+ });
339
+
340
+ it('does not apply range-span constraints in single mode', async () => {
341
+ const user = userEvent.setup();
342
+
343
+ render(<Calendar focusDate="2026-01-01" maxRangeSpan={7} />);
344
+
345
+ // A picked single date must not disable far-away days.
346
+ await user.click(getDayButton(10));
347
+ expect(getDayButton(25)).not.toBeDisabled();
348
+ });
349
+
350
+ it('keeps a controlled value that is wider than maxRangeSpan (selection-only)', () => {
351
+ // The span constraint governs which days are pickable, not validation of
352
+ // an already-set value — so a 15-day value under a 7-day cap stays
353
+ // rendered and is never silently rewritten.
354
+ render(
355
+ <Calendar
356
+ mode="range"
357
+ value={{start: '2026-01-05', end: '2026-01-20'}}
358
+ focusDate="2026-01-01"
359
+ maxRangeSpan={7}
360
+ />,
361
+ );
362
+
363
+ // No selection is in progress (both endpoints are set), so no anchor →
364
+ // every day is pickable and both endpoints render selected.
365
+ expect(getDayButton(20)).not.toBeDisabled();
366
+ expect(getDayButton(5).closest('[role="gridcell"]')).toHaveAttribute(
367
+ 'aria-selected',
368
+ 'true',
369
+ );
370
+ expect(getDayButton(20).closest('[role="gridcell"]')).toHaveAttribute(
371
+ 'aria-selected',
372
+ 'true',
373
+ );
374
+ });
375
+
239
376
  // ─── Multi-Month ─────────────────────────────────────────────
240
377
 
241
378
  it('renders two months when numberOfMonths={2}', () => {
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  import {
21
+ use,
21
22
  useState,
22
23
  useMemo,
23
24
  useCallback,
@@ -86,7 +87,7 @@ import type {
86
87
  } from '../utils/dateTypes';
87
88
  import {normalizeDayOfWeek} from '../utils/dateTypes';
88
89
  import {themeProps} from '../utils/themeProps';
89
- import {useTranslator} from '../i18n';
90
+ import {useTranslator, InternationalizationContext} from '../i18n';
90
91
 
91
92
  /** Imperative handle for Calendar handleRef */
92
93
 
@@ -120,6 +121,25 @@ interface CalendarBaseProps extends Omit<
120
121
  */
121
122
  dateConstraints?: ReadonlyArray<(date: Date) => boolean>;
122
123
 
124
+ /**
125
+ * Range mode only. Maximum number of days a selected range may span,
126
+ * counting both endpoints — `maxRangeSpan={7}` allows a 7-day window
127
+ * (start + 6 days). Once a start date is picked, days beyond this distance
128
+ * from it are disabled in either direction; before a start is picked every
129
+ * otherwise-valid day stays selectable. Use for rolling windows like "at
130
+ * most a week from the chosen day". For fixed calendar bounds use min/max.
131
+ */
132
+ maxRangeSpan?: number;
133
+
134
+ /**
135
+ * Range mode only. Minimum number of days a selected range must span,
136
+ * counting both endpoints — `minRangeSpan={2}` forbids a single-day range.
137
+ * Once a start date is picked, days closer than this to it are disabled —
138
+ * except the start itself, which stays selectable as the active anchor.
139
+ * Defaults to 1 (a same-day start and end is allowed).
140
+ */
141
+ minRangeSpan?: number;
142
+
123
143
  /**
124
144
  * Controlled focus date (which month is visible).
125
145
  * If not provided, defaults to selected date or today.
@@ -201,6 +221,7 @@ export type CalendarProps = CalendarSingleProps | CalendarRangeProps;
201
221
  */
202
222
  export function Calendar({ref, ...props}: CalendarProps) {
203
223
  const t = useTranslator();
224
+ const {locale} = use(InternationalizationContext);
204
225
  const {
205
226
  handleRef,
206
227
  mode = 'single',
@@ -211,6 +232,8 @@ export function Calendar({ref, ...props}: CalendarProps) {
211
232
  min,
212
233
  max,
213
234
  dateConstraints,
235
+ maxRangeSpan,
236
+ minRangeSpan,
214
237
  focusDate: focusDateProp,
215
238
  onFocusDateChange,
216
239
  hasOutsideDays = true,
@@ -306,12 +329,12 @@ export function Calendar({ref, ...props}: CalendarProps) {
306
329
  // Format month header
307
330
  const monthYearLabel = useMemo(() => {
308
331
  if (numberOfMonths === 1) {
309
- return plainDateFormat(visibleMonths[0], DATE_FORMAT_MONTH_YEAR);
332
+ return plainDateFormat(visibleMonths[0], DATE_FORMAT_MONTH_YEAR, locale);
310
333
  }
311
334
  return visibleMonths
312
- .map(m => plainDateFormat(m, DATE_FORMAT_MONTH_YEAR))
335
+ .map(m => plainDateFormat(m, DATE_FORMAT_MONTH_YEAR, locale))
313
336
  .join(' – ');
314
- }, [visibleMonths, numberOfMonths]);
337
+ }, [visibleMonths, numberOfMonths, locale]);
315
338
 
316
339
  // Announce the newly visible month to screen readers whenever it changes.
317
340
  // The visible month label (`<span>`) carries no live semantics, so paging the
@@ -419,12 +442,29 @@ export function Calendar({ref, ...props}: CalendarProps) {
419
442
  setRangeSelectionStart(iso);
420
443
  announce(
421
444
  t('@astryx.calendar.rangeStartAnnounce', {
422
- date: plainDateFormat(date, DATE_FORMAT_WITH_WEEKDAY),
445
+ date: plainDateFormat(date, DATE_FORMAT_WITH_WEEKDAY, locale),
423
446
  }),
424
447
  );
425
448
  } else {
426
449
  // Second click - complete the range
427
450
  const startPd = plainDateFromISO(rangeSelectionStart);
451
+
452
+ // Clicking the anchor again clears the in-progress start rather than
453
+ // committing a zero-length range. This is also the escape hatch when
454
+ // `minRangeSpan` disables the days around the anchor: without it the
455
+ // anchor would be the only clickable day left and the start could
456
+ // never be moved. `minRangeSpan` leaves the anchor itself enabled
457
+ // precisely so this toggle stays reachable.
458
+ if (plainDateIsEqual(date, startPd)) {
459
+ setRangeSelectionStart(null);
460
+ announce(
461
+ t('@astryx.calendar.rangeClearedAnnounce', {
462
+ date: plainDateFormat(date, DATE_FORMAT_WITH_WEEKDAY),
463
+ }),
464
+ );
465
+ return;
466
+ }
467
+
428
468
  let start: ISODateString;
429
469
  let end: ISODateString;
430
470
 
@@ -448,17 +488,19 @@ export function Calendar({ref, ...props}: CalendarProps) {
448
488
  start: plainDateFormat(
449
489
  plainDateFromISO(start),
450
490
  DATE_FORMAT_WITH_WEEKDAY,
491
+ locale,
451
492
  ),
452
493
  end: plainDateFormat(
453
494
  plainDateFromISO(end),
454
495
  DATE_FORMAT_WITH_WEEKDAY,
496
+ locale,
455
497
  ),
456
498
  }),
457
499
  );
458
500
  }
459
501
  }
460
502
  },
461
- [mode, onChange, rangeSelectionStart, announce, t],
503
+ [mode, onChange, rangeSelectionStart, announce, t, locale],
462
504
  );
463
505
 
464
506
  return (
@@ -528,6 +570,8 @@ export function Calendar({ref, ...props}: CalendarProps) {
528
570
  min={min}
529
571
  max={max}
530
572
  dateConstraints={dateConstraints}
573
+ maxRangeSpan={maxRangeSpan}
574
+ minRangeSpan={minRangeSpan}
531
575
  hasOutsideDays={hasOutsideDays}
532
576
  hasWeekNumbers={hasWeekNumbers}
533
577
  hasVariableRowCount={hasVariableRowCount}
@@ -567,6 +611,8 @@ interface MonthGridProps {
567
611
  min?: ISODateString;
568
612
  max?: ISODateString;
569
613
  dateConstraints?: ReadonlyArray<(date: Date) => boolean>;
614
+ maxRangeSpan?: number;
615
+ minRangeSpan?: number;
570
616
  hasOutsideDays: boolean;
571
617
  hasWeekNumbers: boolean;
572
618
  hasVariableRowCount: boolean;
@@ -589,6 +635,8 @@ function MonthGrid({
589
635
  min,
590
636
  max,
591
637
  dateConstraints,
638
+ maxRangeSpan,
639
+ minRangeSpan,
592
640
  hasOutsideDays,
593
641
  hasWeekNumbers,
594
642
  hasVariableRowCount,
@@ -601,6 +649,7 @@ function MonthGrid({
601
649
  pendingFocus,
602
650
  onPendingFocusHandled,
603
651
  }: MonthGridProps) {
652
+ const {locale} = use(InternationalizationContext);
604
653
  const year = month.year;
605
654
 
606
655
  // Use hooks for days generation and constraints
@@ -610,11 +659,30 @@ function MonthGrid({
610
659
  weekStartsOn,
611
660
  hasVariableRowCount,
612
661
  });
662
+ const dayNameHeaders = useMemo(
663
+ () =>
664
+ dayNames.map((name, offset) => ({
665
+ dayOfWeek: ((weekStartsOn + offset) % 7) as DayOfWeek,
666
+ name,
667
+ })),
668
+ [dayNames, weekStartsOn],
669
+ );
670
+
671
+ const rangeAnchor = useMemo(
672
+ () =>
673
+ mode === 'range' && rangeSelectionStart
674
+ ? plainDateFromISO(rangeSelectionStart)
675
+ : null,
676
+ [mode, rangeSelectionStart],
677
+ );
613
678
 
614
679
  const {isDateDisabled} = useCalendarConstraints({
615
680
  min,
616
681
  max,
617
682
  dateConstraints,
683
+ maxRangeSpan,
684
+ minRangeSpan,
685
+ rangeAnchor,
618
686
  });
619
687
 
620
688
  // Parse selected date for roving tabindex priority
@@ -803,8 +871,8 @@ function MonthGrid({
803
871
 
804
872
  // Month label for announcements
805
873
  const monthLabel = useMemo(() => {
806
- return plainDateFormat(month, DATE_FORMAT_MONTH_YEAR);
807
- }, [month]);
874
+ return plainDateFormat(month, DATE_FORMAT_MONTH_YEAR, locale);
875
+ }, [month, locale]);
808
876
 
809
877
  return (
810
878
  <div {...stylex.props(monthGridStyles.monthGrid)}>
@@ -833,9 +901,9 @@ function MonthGrid({
833
901
  )}
834
902
  />
835
903
  )}
836
- {dayNames.map(name => (
904
+ {dayNameHeaders.map(({dayOfWeek, name}) => (
837
905
  <div
838
- key={name}
906
+ key={dayOfWeek}
839
907
  role="columnheader"
840
908
  {...stylex.props(monthGridStyles.dayName)}>
841
909
  {name}
@@ -962,6 +1030,7 @@ function DayCell({
962
1030
  onDayHover,
963
1031
  }: DayCellProps) {
964
1032
  const t = useTranslator();
1033
+ const {locale} = use(InternationalizationContext);
965
1034
  const {date, isOutside, dayNumber} = day;
966
1035
 
967
1036
  if (isOutside && !hasOutsideDays) {
@@ -1007,7 +1076,7 @@ function DayCell({
1007
1076
  // params rather than string concatenation. A mid-flight first pick (where
1008
1077
  // rangeStart === rangeEnd) reads as "range start" only; a completed one-day
1009
1078
  // range reads as both start and end.
1010
- const dateLabel = plainDateFormat(date, DATE_FORMAT_WITH_WEEKDAY);
1079
+ const dateLabel = plainDateFormat(date, DATE_FORMAT_WITH_WEEKDAY, locale);
1011
1080
  const dayLabel = state.isSelected
1012
1081
  ? t('@astryx.calendar.daySelected', {date: dateLabel})
1013
1082
  : state.isRangeStart && state.isRangeEnd
@@ -0,0 +1,54 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file getStandaloneShortWeekdayNames.test.ts
5
+ * @input Private Calendar weekday locale resolver and generated CLDR data
6
+ * @output Regression coverage for exact, language, extension, and safe fallback
7
+ * @position Tests for getStandaloneShortWeekdayNames.ts
8
+ */
9
+
10
+ import {describe, expect, it} from 'vitest';
11
+ import {getStandaloneShortWeekdayNames} from './getStandaloneShortWeekdayNames';
12
+ import {standaloneShortWeekdayNamesByLocale} from './standaloneShortWeekdayNames.generated';
13
+
14
+ describe('getStandaloneShortWeekdayNames', () => {
15
+ it('preserves the default English weekday names', () => {
16
+ expect(getStandaloneShortWeekdayNames('en')).toEqual([
17
+ 'Su',
18
+ 'Mo',
19
+ 'Tu',
20
+ 'We',
21
+ 'Th',
22
+ 'Fr',
23
+ 'Sa',
24
+ ]);
25
+ });
26
+
27
+ it('returns an exact generated locale entry', () => {
28
+ expect(getStandaloneShortWeekdayNames('ar-SA')).toBe(
29
+ standaloneShortWeekdayNamesByLocale['ar-SA'],
30
+ );
31
+ });
32
+
33
+ it('ignores Unicode extensions when resolving an exact locale', () => {
34
+ const expected = standaloneShortWeekdayNamesByLocale['zh-TW'];
35
+
36
+ expect(getStandaloneShortWeekdayNames('zh-TW')).toBe(expected);
37
+ expect(getStandaloneShortWeekdayNames('zh-TW-u-ca-chinese')).toBe(expected);
38
+ });
39
+
40
+ it('falls back from a regional locale to its base language', () => {
41
+ expect(getStandaloneShortWeekdayNames('es-ES')).toBe(
42
+ standaloneShortWeekdayNamesByLocale.es,
43
+ );
44
+ });
45
+
46
+ it('safely falls back to English for malformed and unsupported locales', () => {
47
+ expect(getStandaloneShortWeekdayNames('not_a_locale')).toBe(
48
+ standaloneShortWeekdayNamesByLocale.en,
49
+ );
50
+ expect(getStandaloneShortWeekdayNames('xx-ZZ')).toBe(
51
+ standaloneShortWeekdayNamesByLocale.en,
52
+ );
53
+ });
54
+ });
@@ -0,0 +1,38 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file getStandaloneShortWeekdayNames.ts
5
+ * @input Generated CLDR stand-alone-short weekday table
6
+ * @output Private locale resolver for Calendar weekday headers
7
+ * @position Calendar-internal pure utility; not exported from the package
8
+ */
9
+
10
+ import {
11
+ standaloneShortWeekdayNamesByLocale,
12
+ type StandaloneShortWeekdayNames,
13
+ } from './standaloneShortWeekdayNames.generated';
14
+
15
+ const weekdayNamesByLocale: Readonly<
16
+ Record<string, StandaloneShortWeekdayNames>
17
+ > = standaloneShortWeekdayNamesByLocale;
18
+ const englishWeekdayNames = weekdayNamesByLocale.en;
19
+
20
+ /**
21
+ * Resolve generated weekday reference data as exact locale -> language -> English.
22
+ * Unicode extensions are ignored through Intl.Locale.baseName. Invalid tags and
23
+ * valid but unsupported languages safely use English.
24
+ */
25
+ export function getStandaloneShortWeekdayNames(
26
+ locale: string,
27
+ ): StandaloneShortWeekdayNames {
28
+ try {
29
+ const parsed = new Intl.Locale(locale);
30
+ return (
31
+ weekdayNamesByLocale[parsed.baseName] ??
32
+ weekdayNamesByLocale[parsed.language] ??
33
+ englishWeekdayNames
34
+ );
35
+ } catch {
36
+ return englishWeekdayNames;
37
+ }
38
+ }
@@ -20,6 +20,7 @@ import {
20
20
  plainDateToDate,
21
21
  plainDateIsBefore,
22
22
  plainDateIsAfter,
23
+ plainDateDiffDays,
23
24
  } from '../../utils/plainDate';
24
25
 
25
26
  /**
@@ -35,6 +36,25 @@ export interface UseCalendarConstraintsOptions {
35
36
  * Date is disabled if ANY function returns false.
36
37
  */
37
38
  dateConstraints?: ReadonlyArray<(date: Date) => boolean>;
39
+ /**
40
+ * Maximum number of days a range may span, counting both endpoints
41
+ * (a value of 7 allows a 7-day window). In range mode, once a start is
42
+ * picked, dates further than this from it are disabled. No effect until a
43
+ * range anchor exists.
44
+ */
45
+ maxRangeSpan?: number;
46
+ /**
47
+ * Minimum number of days a range must span, counting both endpoints
48
+ * (a value of 2 forbids a single-day range). In range mode, once a start
49
+ * is picked, dates closer than this to it are disabled.
50
+ */
51
+ minRangeSpan?: number;
52
+ /**
53
+ * The in-progress range start (first click, awaiting the second). Span
54
+ * constraints are measured from this date. Null when no selection is
55
+ * underway.
56
+ */
57
+ rangeAnchor?: PlainDate | null;
38
58
  }
39
59
 
40
60
  /**
@@ -53,13 +73,15 @@ export interface UseCalendarConstraintsReturn {
53
73
  * Hook for managing calendar date validation constraints.
54
74
  *
55
75
  * Provides a function to check if a date is disabled based on
56
- * min/max bounds and custom constraint functions.
76
+ * min/max bounds, range-span bounds, and custom constraint functions.
57
77
  *
58
78
  * @example
59
79
  * ```
60
80
  * const {isDateDisabled} = useCalendarConstraints({
61
81
  * min: '2026-01-01',
62
82
  * max: '2026-12-31',
83
+ * maxRangeSpan: 7, // once a start is picked, cap the window at 7 days
84
+ * rangeAnchor, // the in-progress start (null before the first click)
63
85
  * dateConstraints: [
64
86
  * (date) => date.getDay() !== 0, // No Sundays (receives native Date)
65
87
  * ],
@@ -74,7 +96,8 @@ export interface UseCalendarConstraintsReturn {
74
96
  export function useCalendarConstraints(
75
97
  options: UseCalendarConstraintsOptions,
76
98
  ): UseCalendarConstraintsReturn {
77
- const {min, max, dateConstraints} = options;
99
+ const {min, max, dateConstraints, maxRangeSpan, minRangeSpan, rangeAnchor} =
100
+ options;
78
101
 
79
102
  // Parse min/max dates
80
103
  const minDate = useMemo(() => (min ? plainDateFromISO(min) : null), [min]);
@@ -93,6 +116,27 @@ export function useCalendarConstraints(
93
116
  return true;
94
117
  }
95
118
 
119
+ // Range-span bounds, measured from the in-progress start. Spans count
120
+ // both endpoints (a span of 7 spans a 7-day window), so the reachable
121
+ // distance from the anchor is `span - 1` days in either direction. Only
122
+ // active once a start is picked — before that, every day stays pickable.
123
+ if (rangeAnchor) {
124
+ const distance = Math.abs(plainDateDiffDays(rangeAnchor, date));
125
+ if (maxRangeSpan != null && distance > maxRangeSpan - 1) {
126
+ return true;
127
+ }
128
+ // The anchor itself (distance 0) is never disabled by minRangeSpan —
129
+ // it is the picked start, and disabling it would render the active
130
+ // selection start as aria-disabled to keyboard and screen-reader users.
131
+ if (
132
+ minRangeSpan != null &&
133
+ distance > 0 &&
134
+ distance < minRangeSpan - 1
135
+ ) {
136
+ return true;
137
+ }
138
+ }
139
+
96
140
  // Check custom constraints (convert to Date for public API compatibility)
97
141
  if (dateConstraints) {
98
142
  for (const constraint of dateConstraints) {
@@ -104,7 +148,14 @@ export function useCalendarConstraints(
104
148
 
105
149
  return false;
106
150
  },
107
- [minDate, maxDate, dateConstraints],
151
+ [
152
+ minDate,
153
+ maxDate,
154
+ dateConstraints,
155
+ maxRangeSpan,
156
+ minRangeSpan,
157
+ rangeAnchor,
158
+ ],
108
159
  );
109
160
 
110
161
  return {