@astryxdesign/core 0.5.0 → 0.5.1-canary.f64186a

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 (597) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +45 -20
  5. package/dist/AspectRatio/AspectRatio.d.ts +6 -6
  6. package/dist/Avatar/Avatar.d.ts +25 -1
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +43 -9
  9. package/dist/Avatar/index.d.ts +2 -2
  10. package/dist/Avatar/index.d.ts.map +1 -1
  11. package/dist/Avatar/index.js +1 -1
  12. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  13. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroup.js +5 -2
  15. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  16. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  17. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.js +11 -7
  19. package/dist/Badge/Badge.d.ts.map +1 -1
  20. package/dist/Badge/Badge.js +26 -2
  21. package/dist/Banner/Banner.d.ts +10 -1
  22. package/dist/Banner/Banner.d.ts.map +1 -1
  23. package/dist/Banner/Banner.js +11 -2
  24. package/dist/Button/Button.d.ts.map +1 -1
  25. package/dist/Button/Button.js +8 -7
  26. package/dist/Calendar/Calendar.d.ts +4 -2
  27. package/dist/Calendar/Calendar.d.ts.map +1 -1
  28. package/dist/Calendar/Calendar.js +15 -21
  29. package/dist/Calendar/hooks/useCalendarDays.js +4 -6
  30. package/dist/Calendar/hooks/useCalendarNavigation.js +4 -6
  31. package/dist/Calendar/styles.d.ts +5 -20
  32. package/dist/Calendar/styles.d.ts.map +1 -1
  33. package/dist/Calendar/styles.js +15 -17
  34. package/dist/Card/Card.d.ts +12 -12
  35. package/dist/Card/Card.d.ts.map +1 -1
  36. package/dist/Card/Card.js +17 -9
  37. package/dist/Card/index.d.ts +35 -1
  38. package/dist/Card/index.d.ts.map +1 -1
  39. package/dist/Card/index.js +21 -1
  40. package/dist/Carousel/Carousel.d.ts.map +1 -1
  41. package/dist/Carousel/Carousel.js +108 -7
  42. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  43. package/dist/CheckboxInput/CheckboxInput.js +7 -1
  44. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  45. package/dist/CodeBlock/CodeBlock.js +15 -4
  46. package/dist/CodeBlock/highlightRanges.js +1 -1
  47. package/dist/CodeBlock/highlightStyles.js +13 -13
  48. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  49. package/dist/CommandPalette/CommandPaletteItem.js +2 -7
  50. package/dist/ComplexSelector/ComplexSelector.d.ts +10 -2
  51. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  52. package/dist/ComplexSelector/ComplexSelector.js +14 -7
  53. package/dist/DateInput/DateInput.d.ts +43 -0
  54. package/dist/DateInput/DateInput.d.ts.map +1 -1
  55. package/dist/DateInput/DateInput.js +22 -5
  56. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  57. package/dist/DateInput/MonthScroller.js +5 -3
  58. package/dist/DateInput/MonthYearWheels.d.ts.map +1 -1
  59. package/dist/DateInput/MonthYearWheels.js +6 -4
  60. package/dist/DateInput/NativeDateField.d.ts +10 -0
  61. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  62. package/dist/DateInput/NativeDateField.js +494 -0
  63. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  64. package/dist/DateInput/TouchDateField.js +52 -25
  65. package/dist/DateInput/Wheel.d.ts.map +1 -1
  66. package/dist/DateInput/Wheel.js +1 -0
  67. package/dist/DateInput/index.d.ts +1 -1
  68. package/dist/DateInput/index.d.ts.map +1 -1
  69. package/dist/DateInput/nativeDateSegments.d.ts +22 -0
  70. package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
  71. package/dist/DateInput/nativeDateSegments.js +111 -0
  72. package/dist/DateRangeInput/DateRangeInput.d.ts +4 -2
  73. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  74. package/dist/DateRangeInput/DateRangeInput.js +3 -5
  75. package/dist/DateTimeInput/DateTimeInput.d.ts +23 -24
  76. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  77. package/dist/DateTimeInput/DateTimeInput.js +35 -10
  78. package/dist/DateTimeInput/TouchDateTimeField.d.ts +6 -0
  79. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -0
  80. package/dist/DateTimeInput/TouchDateTimeField.js +1021 -0
  81. package/dist/Dialog/Dialog.d.ts +16 -2
  82. package/dist/Dialog/Dialog.d.ts.map +1 -1
  83. package/dist/Dialog/Dialog.js +28 -7
  84. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  85. package/dist/DropdownMenu/DropdownMenu.js +4 -13
  86. package/dist/Field/FieldLabel.d.ts.map +1 -1
  87. package/dist/Field/FieldLabel.js +9 -1
  88. package/dist/Field/InputClearButton.d.ts +9 -3
  89. package/dist/Field/InputClearButton.d.ts.map +1 -1
  90. package/dist/Field/InputClearButton.js +14 -3
  91. package/dist/Heading/Heading.d.ts.map +1 -1
  92. package/dist/Heading/Heading.js +0 -1
  93. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  94. package/dist/HoverCard/HoverCard.js +104 -3
  95. package/dist/HoverCard/useHoverCard.d.ts +14 -2
  96. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  97. package/dist/HoverCard/useHoverCard.js +7 -1
  98. package/dist/Icon/Icon.d.ts +4 -2
  99. package/dist/Icon/Icon.d.ts.map +1 -1
  100. package/dist/Icon/defaultIcons.d.ts +3 -2
  101. package/dist/Icon/defaultIcons.d.ts.map +1 -1
  102. package/dist/Icon/defaultIcons.js +7 -0
  103. package/dist/Icon/globalIconRegistry.d.ts +13 -0
  104. package/dist/Icon/globalIconRegistry.d.ts.map +1 -1
  105. package/dist/Icon/globalIconRegistry.js +34 -6
  106. package/dist/Icon/index.d.ts +1 -1
  107. package/dist/Icon/index.d.ts.map +1 -1
  108. package/dist/Icon/useIcon.d.ts +5 -2
  109. package/dist/Icon/useIcon.d.ts.map +1 -1
  110. package/dist/Icon/useIcon.js +3 -0
  111. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  112. package/dist/Indicator/CheckboxIndicator.js +6 -2
  113. package/dist/Item/Item.d.ts.map +1 -1
  114. package/dist/Item/Item.js +2 -2
  115. package/dist/Kbd/Kbd.d.ts.map +1 -1
  116. package/dist/Kbd/Kbd.js +2 -26
  117. package/dist/Layer/gestureCounter.d.ts +8 -0
  118. package/dist/Layer/gestureCounter.d.ts.map +1 -0
  119. package/dist/Layer/gestureCounter.js +53 -0
  120. package/dist/Layer/useLayer.d.ts +23 -13
  121. package/dist/Layer/useLayer.d.ts.map +1 -1
  122. package/dist/Layer/useLayer.js +146 -24
  123. package/dist/List/List.d.ts +1 -1
  124. package/dist/List/List.d.ts.map +1 -1
  125. package/dist/List/List.js +15 -4
  126. package/dist/Markdown/Markdown.d.ts +1 -1
  127. package/dist/Markdown/Markdown.d.ts.map +1 -1
  128. package/dist/Markdown/Markdown.js +20 -7
  129. package/dist/Markdown/parser.d.ts.map +1 -1
  130. package/dist/Markdown/parser.js +5 -3
  131. package/dist/MetadataList/MetadataListItem.d.ts +1 -1
  132. package/dist/MetadataList/MetadataListItem.d.ts.map +1 -1
  133. package/dist/MetadataList/MetadataListItem.js +4 -1
  134. package/dist/MultiSelector/MultiSelector.d.ts +18 -1
  135. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  136. package/dist/MultiSelector/MultiSelector.js +58 -11
  137. package/dist/MultiSelector/hooks.d.ts +7 -1
  138. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  139. package/dist/MultiSelector/hooks.js +3 -2
  140. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  141. package/dist/NavIcon/NavIcon.js +5 -1
  142. package/dist/NavItem/navItemStyles.stylex.d.ts +0 -8
  143. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  144. package/dist/NavItem/navItemStyles.stylex.js +0 -2
  145. package/dist/NavMenu/NavHeadingMenu.d.ts +1 -1
  146. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  147. package/dist/NavMenu/NavHeadingMenu.js +7 -4
  148. package/dist/NumberInput/NumberInput.d.ts +6 -1
  149. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  150. package/dist/NumberInput/NumberInput.js +95 -126
  151. package/dist/NumberInput/numberInputCommit.d.ts +34 -0
  152. package/dist/NumberInput/numberInputCommit.d.ts.map +1 -0
  153. package/dist/NumberInput/numberInputCommit.js +71 -0
  154. package/dist/NumberInput/numberParser.d.ts +64 -0
  155. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  156. package/dist/NumberInput/numberParser.js +350 -0
  157. package/dist/Outline/Outline.d.ts.map +1 -1
  158. package/dist/Outline/Outline.js +2 -3
  159. package/dist/OverflowList/OverflowList.d.ts +41 -1
  160. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  161. package/dist/OverflowList/OverflowList.js +43 -2
  162. package/dist/Popover/Popover.d.ts.map +1 -1
  163. package/dist/Popover/Popover.js +8 -14
  164. package/dist/Popover/usePopover.d.ts +15 -41
  165. package/dist/Popover/usePopover.d.ts.map +1 -1
  166. package/dist/Popover/usePopover.js +26 -7
  167. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  168. package/dist/PowerSearch/PowerSearchValueEditor.js +91 -18
  169. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  170. package/dist/PowerSearch/formatFilterValue.js +4 -2
  171. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  172. package/dist/ProgressBar/ProgressBar.js +18 -5
  173. package/dist/Resizable/useResizable.d.ts +19 -1
  174. package/dist/Resizable/useResizable.d.ts.map +1 -1
  175. package/dist/Resizable/useResizable.js +104 -38
  176. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  177. package/dist/SelectableCard/SelectableCard.js +9 -0
  178. package/dist/Selector/Selector.d.ts +18 -1
  179. package/dist/Selector/Selector.d.ts.map +1 -1
  180. package/dist/Selector/Selector.js +92 -21
  181. package/dist/Selector/hooks.d.ts +7 -1
  182. package/dist/Selector/hooks.d.ts.map +1 -1
  183. package/dist/Selector/hooks.js +3 -2
  184. package/dist/SideNav/SideNav.d.ts +6 -1
  185. package/dist/SideNav/SideNav.d.ts.map +1 -1
  186. package/dist/SideNav/SideNav.js +57 -27
  187. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  188. package/dist/SideNav/SideNavHeading.js +5 -4
  189. package/dist/SideNav/SideNavItem.d.ts +29 -4
  190. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  191. package/dist/SideNav/SideNavItem.js +75 -20
  192. package/dist/Slider/Slider.d.ts.map +1 -1
  193. package/dist/Slider/Slider.js +34 -2
  194. package/dist/Spinner/Spinner.d.ts +3 -1
  195. package/dist/Spinner/Spinner.d.ts.map +1 -1
  196. package/dist/Spinner/Spinner.js +199 -20
  197. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  198. package/dist/StatusDot/StatusDot.js +6 -1
  199. package/dist/Stepper/Step.d.ts.map +1 -1
  200. package/dist/Stepper/Step.js +7 -6
  201. package/dist/Switch/Switch.d.ts.map +1 -1
  202. package/dist/Switch/Switch.js +5 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +6 -1
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +3 -6
  207. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  208. package/dist/Table/plugins/filtering/useTableFiltering.js +73 -4
  209. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +26 -0
  210. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  211. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +34 -3
  212. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  213. package/dist/Table/plugins/selection/useTableSelection.js +25 -1
  214. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  215. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  216. package/dist/Text/Text.d.ts.map +1 -1
  217. package/dist/Text/Text.js +0 -1
  218. package/dist/TextArea/TextArea.d.ts.map +1 -1
  219. package/dist/TextArea/TextArea.js +13 -8
  220. package/dist/Timestamp/Timestamp.d.ts +1 -1
  221. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  222. package/dist/Timestamp/Timestamp.js +10 -18
  223. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  224. package/dist/Timestamp/formatInstant.js +10 -5
  225. package/dist/Toast/Toast.d.ts +17 -2
  226. package/dist/Toast/Toast.d.ts.map +1 -1
  227. package/dist/Toast/Toast.js +77 -11
  228. package/dist/Toast/ToastViewport.d.ts +1 -0
  229. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  230. package/dist/Toast/ToastViewport.js +76 -21
  231. package/dist/Toast/index.d.ts +1 -1
  232. package/dist/Toast/index.d.ts.map +1 -1
  233. package/dist/Toast/types.d.ts +38 -0
  234. package/dist/Toast/types.d.ts.map +1 -1
  235. package/dist/Toast/useToastGesture.d.ts +26 -0
  236. package/dist/Toast/useToastGesture.d.ts.map +1 -0
  237. package/dist/Toast/useToastGesture.js +223 -0
  238. package/dist/Token/Token.d.ts +1 -1
  239. package/dist/Token/Token.d.ts.map +1 -1
  240. package/dist/Token/Token.js +23 -12
  241. package/dist/Token/TokenLink.d.ts +1 -1
  242. package/dist/Token/TokenLink.d.ts.map +1 -1
  243. package/dist/Token/TokenLink.js +5 -2
  244. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  245. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  246. package/dist/Tokenizer/Tokenizer.js +31 -21
  247. package/dist/TopNav/TopNavHeading.js +2 -2
  248. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  249. package/dist/TopNav/TopNavItem.js +3 -3
  250. package/dist/TopNav/TopNavMegaMenu.d.ts +1 -1
  251. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  252. package/dist/TopNav/TopNavMegaMenu.js +30 -9
  253. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  254. package/dist/TopNav/TopNavMegaMenuItem.js +3 -3
  255. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  256. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  257. package/dist/TopNav/TopNavMenu.js +21 -6
  258. package/dist/TreeList/TreeList.d.ts +1 -1
  259. package/dist/TreeList/TreeList.d.ts.map +1 -1
  260. package/dist/TreeList/TreeList.js +7 -2
  261. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  262. package/dist/TreeList/TreeListItem.js +2 -2
  263. package/dist/Typeahead/BaseTypeahead.d.ts +33 -0
  264. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  265. package/dist/Typeahead/BaseTypeahead.js +51 -8
  266. package/dist/Typeahead/Typeahead.d.ts +8 -1
  267. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  268. package/dist/Typeahead/Typeahead.js +2 -0
  269. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  270. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  271. package/dist/Typeahead/TypeaheadItem.js +7 -2
  272. package/dist/astryx.css +87 -22
  273. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  274. package/dist/hooks/useHotkeys.js +3 -25
  275. package/dist/hooks/useOverflow.d.ts.map +1 -1
  276. package/dist/hooks/useOverflow.js +14 -4
  277. package/dist/index.d.ts +1 -1
  278. package/dist/index.d.ts.map +1 -1
  279. package/dist/theme/Theme.d.ts.map +1 -1
  280. package/dist/theme/Theme.js +46 -10
  281. package/dist/theme/defineTheme.d.ts +9 -3
  282. package/dist/theme/defineTheme.d.ts.map +1 -1
  283. package/dist/theme/defineTheme.js +5 -0
  284. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  285. package/dist/theme/derivedVarRegistry.js +24 -4
  286. package/dist/theme/generateThemeRules.d.ts +21 -0
  287. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  288. package/dist/theme/generateThemeRules.js +26 -0
  289. package/dist/theme/syntax/tokens.d.ts +1 -1
  290. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  291. package/dist/theme/syntax/tokens.js +5 -2
  292. package/dist/theme/useTheme.d.ts.map +1 -1
  293. package/dist/theme/useTheme.js +6 -7
  294. package/dist/utils/dateParser.d.ts +5 -8
  295. package/dist/utils/dateParser.d.ts.map +1 -1
  296. package/dist/utils/dateParser.js +10 -11
  297. package/dist/utils/focusOutline.stylex.d.ts +25 -0
  298. package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
  299. package/dist/utils/focusOutline.stylex.js +15 -0
  300. package/dist/utils/interactionOverlay.stylex.d.ts +26 -0
  301. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -0
  302. package/dist/utils/interactionOverlay.stylex.js +42 -0
  303. package/dist/utils/isApplePlatform.d.ts +18 -0
  304. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  305. package/dist/utils/isApplePlatform.js +37 -0
  306. package/dist/utils/plainDate.d.ts +11 -0
  307. package/dist/utils/plainDate.d.ts.map +1 -1
  308. package/dist/utils/plainDate.js +20 -4
  309. package/locales/af-ZA.json +7 -7
  310. package/locales/ar-SA.json +7 -7
  311. package/locales/ca-ES.json +6 -6
  312. package/locales/cs-CZ.json +7 -7
  313. package/locales/da-DK.json +7 -7
  314. package/locales/de-DE.json +7 -7
  315. package/locales/el-GR.json +7 -7
  316. package/locales/en.json +178 -6
  317. package/locales/es-ES.json +6 -6
  318. package/locales/fi-FI.json +7 -7
  319. package/locales/fr-FR.json +7 -7
  320. package/locales/he-IL.json +7 -7
  321. package/locales/hu-HU.json +7 -7
  322. package/locales/it-IT.json +7 -7
  323. package/locales/ja-JP.json +7 -7
  324. package/locales/ko-KR.json +7 -7
  325. package/locales/nl-NL.json +7 -7
  326. package/locales/no-NO.json +7 -7
  327. package/locales/pl-PL.json +7 -7
  328. package/locales/pseudo.json +130 -1
  329. package/locales/pt-BR.json +7 -7
  330. package/locales/pt-PT.json +7 -7
  331. package/locales/ro-RO.json +7 -7
  332. package/locales/ru-RU.json +7 -7
  333. package/locales/sr-SP.json +7 -7
  334. package/locales/sv-SE.json +7 -7
  335. package/locales/tr-TR.json +7 -7
  336. package/locales/uk-UA.json +7 -7
  337. package/locales/vi-VN.json +7 -7
  338. package/locales/zh-CN.json +7 -7
  339. package/locales/zh-TW.json +7 -7
  340. package/package.json +4 -5
  341. package/src/AlertDialog/AlertDialog.doc.mjs +114 -30
  342. package/src/AlertDialog/AlertDialog.test.tsx +211 -2
  343. package/src/AlertDialog/AlertDialog.tsx +62 -20
  344. package/src/AspectRatio/AspectRatio.doc.mjs +3 -3
  345. package/src/AspectRatio/AspectRatio.tsx +6 -6
  346. package/src/Avatar/Avatar.doc.mjs +12 -2
  347. package/src/Avatar/Avatar.test.tsx +80 -0
  348. package/src/Avatar/Avatar.tsx +64 -14
  349. package/src/Avatar/index.ts +2 -2
  350. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  351. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  352. package/src/AvatarGroup/AvatarGroup.tsx +12 -4
  353. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  354. package/src/AvatarGroup/AvatarGroupOverflow.tsx +12 -12
  355. package/src/Badge/Badge.doc.mjs +3 -2
  356. package/src/Badge/Badge.test.tsx +32 -3
  357. package/src/Badge/Badge.tsx +42 -1
  358. package/src/Banner/Banner.doc.mjs +9 -0
  359. package/src/Banner/Banner.test.tsx +76 -11
  360. package/src/Banner/Banner.tsx +21 -2
  361. package/src/Button/Button.doc.mjs +2 -1
  362. package/src/Button/Button.test.tsx +19 -4
  363. package/src/Button/Button.tsx +9 -29
  364. package/src/ButtonGroup/ButtonGroup.doc.mjs +2 -2
  365. package/src/ButtonGroup/ButtonGroup.test.tsx +13 -0
  366. package/src/Calendar/Calendar.doc.mjs +3 -3
  367. package/src/Calendar/Calendar.test.tsx +112 -0
  368. package/src/Calendar/Calendar.tsx +18 -16
  369. package/src/Calendar/hooks/useCalendarDays.ts +4 -4
  370. package/src/Calendar/hooks/useCalendarNavigation.ts +4 -4
  371. package/src/Calendar/styles.ts +48 -42
  372. package/src/Card/Card.doc.mjs +10 -7
  373. package/src/Card/Card.test.tsx +113 -19
  374. package/src/Card/Card.tsx +67 -89
  375. package/src/Card/index.ts +36 -1
  376. package/src/Carousel/Carousel.doc.mjs +3 -0
  377. package/src/Carousel/Carousel.test.tsx +360 -0
  378. package/src/Carousel/Carousel.tsx +116 -15
  379. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  380. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  381. package/src/CheckboxInput/CheckboxInput.tsx +5 -0
  382. package/src/CodeBlock/CodeBlock.doc.mjs +10 -4
  383. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  384. package/src/CodeBlock/CodeBlock.tsx +20 -4
  385. package/src/CodeBlock/highlightRanges.ts +1 -1
  386. package/src/CodeBlock/highlightStyles.ts +13 -13
  387. package/src/CommandPalette/CommandPaletteItem.tsx +2 -12
  388. package/src/ComplexSelector/ComplexSelector.doc.mjs +7 -0
  389. package/src/ComplexSelector/ComplexSelector.test.tsx +183 -0
  390. package/src/ComplexSelector/ComplexSelector.tsx +22 -12
  391. package/src/DateInput/DateInput.doc.mjs +16 -0
  392. package/src/DateInput/DateInput.test.tsx +9 -4
  393. package/src/DateInput/DateInput.tsx +54 -3
  394. package/src/DateInput/DateInputTouch.test.tsx +146 -32
  395. package/src/DateInput/MonthScroller.tsx +5 -1
  396. package/src/DateInput/MonthYearWheels.tsx +7 -4
  397. package/src/DateInput/NativeDateField.test.tsx +635 -0
  398. package/src/DateInput/NativeDateField.tsx +641 -0
  399. package/src/DateInput/TouchDateField.tsx +83 -33
  400. package/src/DateInput/Wheel.tsx +1 -0
  401. package/src/DateInput/index.ts +1 -0
  402. package/src/DateInput/nativeDateSegments.ts +127 -0
  403. package/src/DateRangeInput/DateRangeInput.doc.mjs +2 -2
  404. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -0
  405. package/src/DateRangeInput/DateRangeInput.tsx +8 -13
  406. package/src/DateTimeInput/DateTimeInput.doc.mjs +38 -21
  407. package/src/DateTimeInput/DateTimeInput.test.tsx +76 -0
  408. package/src/DateTimeInput/DateTimeInput.tsx +54 -20
  409. package/src/DateTimeInput/DateTimeInputTouch.test.tsx +980 -0
  410. package/src/DateTimeInput/TouchDateTimeField.tsx +1437 -0
  411. package/src/Dialog/Dialog.test.tsx +185 -18
  412. package/src/Dialog/Dialog.tsx +35 -11
  413. package/src/DropdownMenu/DropdownMenu.test.tsx +44 -10
  414. package/src/DropdownMenu/DropdownMenu.tsx +4 -13
  415. package/src/Field/FieldLabel.test.tsx +29 -0
  416. package/src/Field/FieldLabel.tsx +7 -0
  417. package/src/Field/InputClearButton.test.tsx +15 -1
  418. package/src/Field/InputClearButton.tsx +28 -4
  419. package/src/Heading/Heading.tsx +0 -1
  420. package/src/HoverCard/HoverCard.doc.mjs +8 -2
  421. package/src/HoverCard/HoverCard.test.tsx +154 -4
  422. package/src/HoverCard/HoverCard.tsx +137 -3
  423. package/src/HoverCard/useHoverCard.tsx +23 -3
  424. package/src/Icon/Icon.test.tsx +55 -1
  425. package/src/Icon/Icon.tsx +5 -3
  426. package/src/Icon/defaultIcons.tsx +11 -2
  427. package/src/Icon/globalIconRegistry.test.tsx +16 -3
  428. package/src/Icon/globalIconRegistry.tsx +37 -6
  429. package/src/Icon/index.ts +1 -0
  430. package/src/Icon/useIcon.ts +9 -2
  431. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  432. package/src/Indicator/Indicator.doc.mjs +2 -0
  433. package/src/Item/Item.tsx +2 -7
  434. package/src/Kbd/Kbd.test.tsx +0 -17
  435. package/src/Kbd/Kbd.tsx +2 -26
  436. package/src/Layer/gestureCounter.ts +57 -0
  437. package/src/Layer/useLayer.test.tsx +368 -16
  438. package/src/Layer/useLayer.tsx +208 -35
  439. package/src/Layout/__tests__/edgeCompensation.test.tsx +1 -1
  440. package/src/List/List.test.tsx +38 -0
  441. package/src/List/List.tsx +8 -1
  442. package/src/Markdown/Markdown.test.tsx +37 -0
  443. package/src/Markdown/Markdown.tsx +16 -5
  444. package/src/Markdown/parser.test.ts +33 -0
  445. package/src/Markdown/parser.ts +9 -3
  446. package/src/MetadataList/MetadataList.test.tsx +75 -2
  447. package/src/MetadataList/MetadataListItem.tsx +5 -2
  448. package/src/MultiSelector/MultiSelector.doc.mjs +22 -1
  449. package/src/MultiSelector/MultiSelector.test.tsx +205 -0
  450. package/src/MultiSelector/MultiSelector.tsx +103 -16
  451. package/src/MultiSelector/hooks.ts +9 -2
  452. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  453. package/src/NavIcon/NavIcon.test.tsx +9 -0
  454. package/src/NavIcon/NavIcon.tsx +5 -1
  455. package/src/NavItem/navItemStyles.stylex.ts +0 -8
  456. package/src/NavMenu/NavHeadingMenu.test.tsx +30 -0
  457. package/src/NavMenu/NavHeadingMenu.tsx +7 -4
  458. package/src/NumberInput/NumberInput.doc.mjs +26 -7
  459. package/src/NumberInput/NumberInput.test.tsx +563 -8
  460. package/src/NumberInput/NumberInput.tsx +77 -131
  461. package/src/NumberInput/numberInputCommit.test.ts +72 -0
  462. package/src/NumberInput/numberInputCommit.ts +103 -0
  463. package/src/NumberInput/numberParser.docblock.test.ts +234 -0
  464. package/src/NumberInput/numberParser.test.ts +222 -0
  465. package/src/NumberInput/numberParser.ts +403 -0
  466. package/src/Outline/Outline.tsx +2 -4
  467. package/src/OverflowList/OverflowList.doc.mjs +102 -21
  468. package/src/OverflowList/OverflowList.test.tsx +550 -4
  469. package/src/OverflowList/OverflowList.tsx +100 -2
  470. package/src/Pagination/Pagination.test.tsx +3 -2
  471. package/src/Popover/Popover.doc.mjs +2 -2
  472. package/src/Popover/Popover.test.tsx +38 -0
  473. package/src/Popover/Popover.tsx +28 -66
  474. package/src/Popover/usePopover.tsx +44 -5
  475. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +87 -0
  476. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +64 -0
  477. package/src/PowerSearch/PowerSearchValueEditor.tsx +109 -10
  478. package/src/PowerSearch/formatFilterValue.test.ts +16 -1
  479. package/src/PowerSearch/formatFilterValue.ts +2 -0
  480. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  481. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  482. package/src/ProgressBar/ProgressBar.tsx +28 -8
  483. package/src/Resizable/Resizable.doc.mjs +2 -2
  484. package/src/Resizable/useResizable.doc.mjs +21 -2
  485. package/src/Resizable/useResizable.test.ts +421 -0
  486. package/src/Resizable/useResizable.ts +133 -48
  487. package/src/SelectableCard/SelectableCard.doc.mjs +3 -0
  488. package/src/SelectableCard/SelectableCard.tsx +16 -0
  489. package/src/Selector/Selector.doc.mjs +14 -0
  490. package/src/Selector/Selector.source-build.test.mjs +65 -0
  491. package/src/Selector/Selector.test.tsx +301 -0
  492. package/src/Selector/Selector.tsx +145 -32
  493. package/src/Selector/hooks.ts +17 -2
  494. package/src/SideNav/SideNav.doc.mjs +2 -2
  495. package/src/SideNav/SideNav.test.tsx +1069 -6
  496. package/src/SideNav/SideNav.tsx +109 -38
  497. package/src/SideNav/SideNavHeading.tsx +5 -2
  498. package/src/SideNav/SideNavItem.doc.mjs +24 -3
  499. package/src/SideNav/SideNavItem.tsx +134 -44
  500. package/src/Slider/Slider.test.tsx +106 -1
  501. package/src/Slider/Slider.tsx +52 -1
  502. package/src/Spinner/Spinner.doc.mjs +17 -5
  503. package/src/Spinner/Spinner.test.tsx +164 -0
  504. package/src/Spinner/Spinner.tsx +252 -22
  505. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  506. package/src/StatusDot/StatusDot.test.tsx +9 -0
  507. package/src/StatusDot/StatusDot.tsx +7 -1
  508. package/src/Stepper/Step.doc.mjs +1 -1
  509. package/src/Stepper/Step.tsx +7 -14
  510. package/src/Switch/Switch.doc.mjs +2 -0
  511. package/src/Switch/Switch.test.tsx +10 -0
  512. package/src/Switch/Switch.tsx +3 -0
  513. package/src/Table/BaseTable.tsx +6 -1
  514. package/src/Table/Table.doc.mjs +5 -3
  515. package/src/Table/Table.tsx +3 -8
  516. package/src/Table/TableBody.doc.mjs +2 -2
  517. package/src/Table/TableFooter.doc.mjs +1 -1
  518. package/src/Table/TableHeader.doc.mjs +1 -1
  519. package/src/Table/plugins/filtering/useTableFiltering.test.tsx +44 -6
  520. package/src/Table/plugins/filtering/useTableFiltering.tsx +100 -4
  521. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +50 -4
  522. package/src/Table/plugins/selection/useTableSelection.test.tsx +62 -0
  523. package/src/Table/plugins/selection/useTableSelection.tsx +25 -2
  524. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  525. package/src/Table/useTableGroupedRows.doc.mjs +3 -3
  526. package/src/Table/useTablePagination.doc.mjs +1 -1
  527. package/src/Table/useTableSelection.doc.mjs +1 -1
  528. package/src/Text/Text.test.tsx +44 -0
  529. package/src/Text/Text.tsx +0 -1
  530. package/src/TextArea/TextArea.doc.mjs +12 -2
  531. package/src/TextArea/TextArea.test.tsx +11 -0
  532. package/src/TextArea/TextArea.tsx +34 -22
  533. package/src/Timestamp/Timestamp.test.tsx +89 -0
  534. package/src/Timestamp/Timestamp.tsx +8 -8
  535. package/src/Timestamp/formatInstant.ts +5 -0
  536. package/src/Toast/Toast.doc.mjs +213 -44
  537. package/src/Toast/Toast.tsx +118 -23
  538. package/src/Toast/ToastViewport.test.tsx +1434 -8
  539. package/src/Toast/ToastViewport.tsx +85 -14
  540. package/src/Toast/index.ts +2 -0
  541. package/src/Toast/types.ts +43 -5
  542. package/src/Toast/useToastGesture.ts +361 -0
  543. package/src/Token/Token.test.tsx +95 -0
  544. package/src/Token/Token.tsx +19 -17
  545. package/src/Token/TokenLink.tsx +12 -3
  546. package/src/Tokenizer/Tokenizer.doc.mjs +16 -3
  547. package/src/Tokenizer/Tokenizer.test.tsx +161 -0
  548. package/src/Tokenizer/Tokenizer.tsx +49 -24
  549. package/src/TopNav/TopNavHeading.tsx +2 -2
  550. package/src/TopNav/TopNavItem.tsx +3 -7
  551. package/src/TopNav/TopNavMegaMenu.test.tsx +104 -0
  552. package/src/TopNav/TopNavMegaMenu.tsx +47 -4
  553. package/src/TopNav/TopNavMegaMenuItem.tsx +10 -9
  554. package/src/TopNav/TopNavMenu.test.tsx +133 -1
  555. package/src/TopNav/TopNavMenu.tsx +38 -5
  556. package/src/TreeList/TreeList.test.tsx +49 -0
  557. package/src/TreeList/TreeList.tsx +7 -2
  558. package/src/TreeList/TreeListItem.tsx +2 -7
  559. package/src/Typeahead/BaseTypeahead.doc.mjs +13 -0
  560. package/src/Typeahead/BaseTypeahead.tsx +96 -6
  561. package/src/Typeahead/Typeahead.doc.mjs +6 -0
  562. package/src/Typeahead/Typeahead.test.tsx +154 -0
  563. package/src/Typeahead/Typeahead.tsx +9 -0
  564. package/src/Typeahead/TypeaheadItem.test.tsx +43 -0
  565. package/src/Typeahead/TypeaheadItem.tsx +8 -1
  566. package/src/hooks/useAnnounce.doc.mjs +96 -0
  567. package/src/hooks/useDevWarning.doc.mjs +96 -0
  568. package/src/hooks/useHotkeys.test.ts +0 -18
  569. package/src/hooks/useHotkeys.ts +3 -25
  570. package/src/hooks/useIndicatorFocusRing.doc.mjs +104 -0
  571. package/src/hooks/useInputStatusIcon.doc.mjs +142 -0
  572. package/src/hooks/useInteractiveRole.doc.mjs +119 -0
  573. package/src/hooks/useLongPress.doc.mjs +123 -0
  574. package/src/hooks/useOverflow.doc.mjs +58 -17
  575. package/src/hooks/useOverflow.ts +16 -4
  576. package/src/hooks/useTypeahead.doc.mjs +153 -0
  577. package/src/index.ts +2 -0
  578. package/src/raw-import.d.ts +14 -0
  579. package/src/theme/Theme.test.tsx +104 -2
  580. package/src/theme/Theme.tsx +49 -14
  581. package/src/theme/defineTheme.test.ts +23 -0
  582. package/src/theme/defineTheme.ts +12 -3
  583. package/src/theme/derivedVarRegistry.test.ts +87 -3
  584. package/src/theme/derivedVarRegistry.ts +23 -4
  585. package/src/theme/generateThemeRules.test.ts +99 -1
  586. package/src/theme/generateThemeRules.ts +28 -0
  587. package/src/theme/syntax/tokens.ts +5 -2
  588. package/src/theme/useTheme.test.tsx +10 -0
  589. package/src/theme/useTheme.ts +16 -10
  590. package/src/utils/dateParser.test.ts +34 -2
  591. package/src/utils/dateParser.ts +11 -14
  592. package/src/utils/focusOutline.stylex.ts +50 -0
  593. package/src/utils/interactionOverlay.stylex.ts +74 -0
  594. package/src/utils/isApplePlatform.test.ts +61 -0
  595. package/src/utils/isApplePlatform.ts +37 -0
  596. package/src/utils/plainDate.test.ts +46 -0
  597. package/src/utils/plainDate.ts +18 -3
@@ -13,7 +13,10 @@ import {useState} from 'react';
13
13
  import {describe, it, expect, vi, beforeEach} from 'vitest';
14
14
  import {render, screen, act, fireEvent, waitFor} from '@testing-library/react';
15
15
  import userEvent from '@testing-library/user-event';
16
+ import * as stylex from '@stylexjs/stylex';
16
17
  import {Slider} from './Slider';
18
+ import {focusOutlineStyles} from '../utils/focusOutline.stylex';
19
+ import {__resetInteractionModalityForTest} from '../utils/interactionModality';
17
20
 
18
21
  // Mock showPopover/hidePopover (not implemented in jsdom) so the tooltip layer
19
22
  // reflects its open state via a `popover-open` attribute the tests can assert.
@@ -854,7 +857,7 @@ describe('Slider', () => {
854
857
  />,
855
858
  );
856
859
  const thumb = screen.getByRole('slider');
857
- thumb.focus();
860
+ act(() => thumb.focus());
858
861
  await user.keyboard('{ArrowRight}');
859
862
  expect(onChange).not.toHaveBeenCalled();
860
863
  });
@@ -902,4 +905,106 @@ describe('Slider', () => {
902
905
  ]).toEqual([]);
903
906
  });
904
907
  });
908
+
909
+ describe('focus ring modality', () => {
910
+ beforeEach(() => {
911
+ __resetInteractionModalityForTest();
912
+ });
913
+
914
+ // The ring is a stylex class, so derive it from the same source the
915
+ // component applies rather than hardcoding a hash.
916
+ const RING = stylex
917
+ .props(focusOutlineStyles.focusVisible)
918
+ .className!.split(' ');
919
+ const isRinged = (el: HTMLElement) =>
920
+ RING.every(c => el.classList.contains(c));
921
+
922
+ // The track has no layout in jsdom; pointer maths needs a real rect.
923
+ const grabTrack = (thumb: HTMLElement) => {
924
+ const track = thumb.parentElement!;
925
+ track.getBoundingClientRect = () => ({
926
+ left: 0,
927
+ top: 0,
928
+ right: 200,
929
+ bottom: 20,
930
+ width: 200,
931
+ height: 20,
932
+ x: 0,
933
+ y: 0,
934
+ toJSON: () => {},
935
+ });
936
+ fireEvent.pointerDown(track, {clientX: 100, clientY: 10, pointerId: 1});
937
+ };
938
+
939
+ it('rings when the thumb is reached with the keyboard', async () => {
940
+ const user = userEvent.setup();
941
+ render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
942
+ await user.tab();
943
+ expect(isRinged(screen.getByRole('slider'))).toBe(true);
944
+ });
945
+
946
+ it('does not ring when the thumb is grabbed with the mouse', () => {
947
+ render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
948
+ const thumb = screen.getByRole('slider');
949
+ grabTrack(thumb);
950
+ expect(thumb).toHaveFocus();
951
+ expect(isRinged(thumb)).toBe(false);
952
+ });
953
+
954
+ it('drops the ring when the mouse grabs a thumb that already had it', async () => {
955
+ const user = userEvent.setup();
956
+ render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
957
+ await user.tab();
958
+ const thumb = screen.getByRole('slider');
959
+ expect(isRinged(thumb)).toBe(true);
960
+ // Already focused, so focus() fires no focus event.
961
+ grabTrack(thumb);
962
+ expect(isRinged(thumb)).toBe(false);
963
+ });
964
+
965
+ it('brings the ring back when an arrow key follows a mouse drag', () => {
966
+ render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
967
+ const thumb = screen.getByRole('slider');
968
+ grabTrack(thumb);
969
+ expect(isRinged(thumb)).toBe(false);
970
+ fireEvent.keyDown(thumb, {key: 'ArrowRight'});
971
+ expect(isRinged(thumb)).toBe(true);
972
+ });
973
+
974
+ it('leaves the ring off for a modifier chord after a mouse drag', () => {
975
+ render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
976
+ const thumb = screen.getByRole('slider');
977
+ grabTrack(thumb);
978
+ // Copying or reloading is not navigation, and the mouse is still on it.
979
+ fireEvent.keyDown(thumb, {key: 'c', metaKey: true});
980
+ expect(isRinged(thumb)).toBe(false);
981
+ });
982
+
983
+ it('drops the ring on blur', async () => {
984
+ const user = userEvent.setup();
985
+ render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
986
+ await user.tab();
987
+ const thumb = screen.getByRole('slider');
988
+ expect(isRinged(thumb)).toBe(true);
989
+ act(() => thumb.blur());
990
+ expect(isRinged(thumb)).toBe(false);
991
+ });
992
+
993
+ it('rings the range thumb the keyboard reached, not its sibling', async () => {
994
+ const user = userEvent.setup();
995
+ render(
996
+ <Slider
997
+ label="Price"
998
+ value={[20, 80] as [number, number]}
999
+ onChange={vi.fn()}
1000
+ />,
1001
+ );
1002
+ await user.tab();
1003
+ await user.tab();
1004
+ const thumbs = screen.getAllByRole('slider');
1005
+ expect(thumbs[1]).toHaveFocus();
1006
+ expect(isRinged(thumbs[1])).toBe(true);
1007
+ expect(isRinged(thumbs[0])).toBe(false);
1008
+ });
1009
+ });
905
1010
  });
@@ -17,11 +17,13 @@
17
17
  */
18
18
 
19
19
  import {
20
+ useEffect,
20
21
  useId,
21
22
  useMemo,
22
23
  useRef,
23
24
  useState,
24
25
  useCallback,
26
+ type FocusEvent,
25
27
  type KeyboardEvent,
26
28
  type PointerEvent,
27
29
  } from 'react';
@@ -42,6 +44,10 @@ import {VisuallyHidden} from '../VisuallyHidden';
42
44
  import type {InputStatus} from '../Field/types';
43
45
  import {mergeProps, rtlStyles} from '../utils';
44
46
  import {focusOutlineStyles} from '../utils/focusOutline.stylex';
47
+ import {
48
+ getInteractionModality,
49
+ trackInteractionModality,
50
+ } from '../utils/interactionModality';
45
51
  import {isRtlElement} from '../hooks/isRtlElement';
46
52
  import type {BaseProps} from '../BaseProps';
47
53
  import type {SizeValue} from '../utils/types';
@@ -493,6 +499,36 @@ export function Slider({ref, ...props}: SliderProps) {
493
499
  const draggingThumbRef = useRef<number | null>(null);
494
500
  const [draggingThumb, setDraggingThumb] = useState<number | null>(null);
495
501
 
502
+ // A thumb is a div[role="slider"], and `handlePointerDown` focuses it from
503
+ // script after preventDefault — which Chromium treats as focus-visible, so
504
+ // dragging with a mouse drew the keyboard ring (measured: `:focus-visible`
505
+ // true on pointerdown). Gate the ring on how the user last interacted; the
506
+ // CSS condition stays `:focus-visible`, this only narrows it.
507
+ const [keyboardFocusThumb, setKeyboardFocusThumb] = useState<number | null>(
508
+ null,
509
+ );
510
+
511
+ useEffect(() => {
512
+ trackInteractionModality();
513
+ }, []);
514
+
515
+ const handleThumbFocus = useCallback(
516
+ (thumbIndex: number, _e: FocusEvent<HTMLDivElement>) => {
517
+ // A disabled thumb draws no ring even when it stays focusable for its
518
+ // reason tooltip, so there is nothing to track for one.
519
+ setKeyboardFocusThumb(
520
+ !isDisabled && getInteractionModality() === 'keyboard'
521
+ ? thumbIndex
522
+ : null,
523
+ );
524
+ },
525
+ [isDisabled],
526
+ );
527
+
528
+ const handleThumbBlur = useCallback((_e: FocusEvent<HTMLDivElement>) => {
529
+ setKeyboardFocusThumb(null);
530
+ }, []);
531
+
496
532
  // Disabled-reason tooltip. This is a *separate* useTooltip instance from the
497
533
  // per-thumb value bubble (the `<Tooltip>` component below): it anchors to the
498
534
  // track container and fires on hover/focus of the whole control. Disabled
@@ -679,6 +715,10 @@ export function Slider({ref, ...props}: SliderProps) {
679
715
  const thumbs = track.querySelectorAll<HTMLElement>('[role="slider"]');
680
716
  thumbs[thumbIndex]?.focus();
681
717
  }
718
+ // Also clear it explicitly: focusing an already-focused thumb fires no
719
+ // focus event, so a thumb the user had tabbed to would keep its ring
720
+ // through the drag.
721
+ setKeyboardFocusThumb(null);
682
722
 
683
723
  if (
684
724
  typeof (e.currentTarget as HTMLElement).setPointerCapture === 'function'
@@ -717,6 +757,13 @@ export function Slider({ref, ...props}: SliderProps) {
717
757
  if (isDisabled) {
718
758
  return;
719
759
  }
760
+ // Unlike a text field, a thumb has no caret to show where input is
761
+ // going, so a keypress after a mouse drag must bring the ring back.
762
+ // Ask the utility rather than assuming: a modifier chord (⌘R, ⌃C) is
763
+ // not navigation and must not re-ring a thumb the mouse is holding.
764
+ if (getInteractionModality() === 'keyboard') {
765
+ setKeyboardFocusThumb(thumbIndex);
766
+ }
720
767
  const currentVal = values[thumbIndex];
721
768
  let newVal: number;
722
769
 
@@ -843,6 +890,8 @@ export function Slider({ref, ...props}: SliderProps) {
843
890
  aria-labelledby={!isRange ? labelID : undefined}
844
891
  aria-describedby={ariaDescribedBy}
845
892
  onKeyDown={e => handleKeyDown(thumbIndex, e)}
893
+ onFocus={e => handleThumbFocus(thumbIndex, e)}
894
+ onBlur={handleThumbBlur}
846
895
  {...mergeProps(
847
896
  themeProps('slider-thumb', {
848
897
  orientation,
@@ -854,7 +903,9 @@ export function Slider({ref, ...props}: SliderProps) {
854
903
  ? styles.thumbHorizontal
855
904
  : rtlStyles.centerInline('50%'),
856
905
  !isDisabled && styles.thumbHover,
857
- !isDisabled && focusOutlineStyles.focusVisible,
906
+ !isDisabled &&
907
+ keyboardFocusThumb === thumbIndex &&
908
+ focusOutlineStyles.focusVisible,
858
909
  isDisabled && styles.thumbDisabled,
859
910
  ),
860
911
  undefined,
@@ -10,8 +10,8 @@ export const docs = {
10
10
  props: [
11
11
  {
12
12
  name: 'size',
13
- type: "'sm' | 'md' | 'lg'",
14
- description: 'Spinner size (10px, 14px, 18px).',
13
+ type: "'sm' | 'md' | 'lg' | 'xl'",
14
+ description: 'Spinner size — ring diameter (10px, 14px, 18px, 28px).',
15
15
  default: "'md'",
16
16
  },
17
17
  {
@@ -43,6 +43,12 @@ export const docs = {
43
43
  targets: [
44
44
  {className: 'astryx-spinner', visualProps: ['size', 'shade']},
45
45
  ],
46
+ vars: [
47
+ {name: '--spinner-diameter', description: "Diameter of the drawn ring. Set it on a size-variant target to retheme what each named size resolves to, e.g. spinner: { 'size:xl': { '--spinner-diameter': '2.5rem' } }. The rendered box is this plus the stroke width on each side, and follows automatically. Any length works — rem, em and calc() are resolved before the ring is drawn.", default: '10px (sm), 14px (md), 18px (lg), 28px (xl)'},
48
+ {name: '--spinner-stroke-width', description: 'Stroke width of both circles the ring is drawn from — the moving arc and the track behind it. Set it per size alongside the diameter. One stroke width drives both, so 0 is honoured as a zero-width stroke and paints nothing at all rather than falling back to the default — for an arc with no track behind it, set --spinner-track-color to transparent instead.', default: '2px (sm), 3px (md), 3px (lg), 4px (xl)'},
49
+ {name: '--spinner-color', description: "Color of the moving arc. Defaults to the shade's token, so set it on a shade-variant target to retheme one shade — spinner: { 'shade:subtle': { '--spinner-color': 'var(--color-text-tertiary)' } } — or on the base target to retheme all four. Accepts any color notation, including var(), color-mix() and currentColor.", default: 'var(--color-accent) (default), var(--color-text-secondary) (subtle), var(--color-on-dark) (onMedia), currentColor (inherit)'},
50
+ {name: '--spinner-track-color', description: 'Color of the track the arc travels on. Set it to `transparent` for an arc with no track. The onMedia and inherit shades draw the track at reduced alpha (30%) so it reads against an arbitrary backdrop; that fade applies to a themed color too.', default: 'var(--color-track) (default, subtle), var(--color-on-dark) (onMedia), currentColor (inherit)'},
51
+ ],
46
52
  },
47
53
  usage: {
48
54
  description:
@@ -63,8 +69,8 @@ export const docsZh = {
63
69
  props: [
64
70
  {
65
71
  name: 'size',
66
- type: "'sm' | 'md' | 'lg'",
67
- description: '旋转器尺寸(10px、14px、18px)。',
72
+ type: "'sm' | 'md' | 'lg' | 'xl'",
73
+ description: '旋转器尺寸——环直径(10px、14px、18px、28px)。',
68
74
  default: "'md'",
69
75
  },
70
76
  {
@@ -95,6 +101,12 @@ export const docsZh = {
95
101
  targets: [
96
102
  {className: 'astryx-spinner', visualProps: ['size', 'shade']},
97
103
  ],
104
+ vars: [
105
+ {name: '--spinner-diameter', description: "绘制环的直径。在尺寸变体目标上设置,以重新定义每个命名尺寸的解析值,例如 spinner: { 'size:xl': { '--spinner-diameter': '2.5rem' } }。渲染盒子的尺寸为该值加上两侧的描边宽度,并自动跟随。支持任意长度单位——rem、em 与 calc() 会在绘制前解析。", default: '10px (sm), 14px (md), 18px (lg), 28px (xl)'},
106
+ {name: '--spinner-stroke-width', description: '绘制环的两个圆——移动圆弧与其后的轨道——的描边宽度。与直径一起按尺寸设置。同一个描边宽度同时驱动两者,因此 0 会被采纳为零宽描边——什么都不绘制,而不会回退到默认值;若想要没有轨道的圆弧,请改将 --spinner-track-color 设为 transparent。', default: '2px (sm), 3px (md), 3px (lg), 4px (xl)'},
107
+ {name: '--spinner-color', description: "运动圆弧的颜色。默认取所在 shade 的令牌,因此可在 shade 变体目标上设置以重新定义单个 shade——spinner: { 'shade:subtle': { '--spinner-color': 'var(--color-text-tertiary)' } }——或在 base 目标上设置以覆盖全部四种。接受任意颜色写法,包括 var()、color-mix() 与 currentColor。", default: 'var(--color-accent)(default)、var(--color-text-secondary)(subtle)、var(--color-on-dark)(onMedia)、currentColor(inherit)'},
108
+ {name: '--spinner-track-color', description: '圆弧所在轨道的颜色。设为 `transparent` 可得到无轨道的圆弧。onMedia 与 inherit 两种 shade 会以降低的透明度(30%)绘制轨道,以便在任意背景上可辨;该淡化同样作用于主题化的颜色。', default: 'var(--color-track)(default、subtle)、var(--color-on-dark)(onMedia)、currentColor(inherit)'},
109
+ ],
98
110
  },
99
111
  usage: {
100
112
  description:
@@ -121,7 +133,7 @@ export const docsDense = {
121
133
  ],
122
134
  },
123
135
  propDescriptions: {
124
- size: 'Spinner size (10px, 14px, 18px).',
136
+ size: 'Spinner size — ring diameter (10px, 14px, 18px, 28px).',
125
137
  shade: 'Color shade for light or dark backgrounds.',
126
138
  label: 'Visible content below spinner. String auto-sets aria-label.',
127
139
  'aria-label': 'A11y name for screen readers. Defaults to label or "Loading".',
@@ -12,6 +12,8 @@
12
12
  import {describe, it, expect, vi, afterEach} from 'vitest';
13
13
  import {render, screen} from '@testing-library/react';
14
14
  import {Spinner} from './Spinner';
15
+ import {defineTheme} from '../theme/defineTheme';
16
+ import {generateThemeCSS} from '../theme/generateThemeRules';
15
17
 
16
18
  /** sm/md/lg/xl, as Spinner.tsx defines them. */
17
19
  const SIZES = {
@@ -140,6 +142,85 @@ describe('Spinner', () => {
140
142
  const spinner = screen.getByTestId('spinner');
141
143
  expect(spinner.tagName.toLowerCase()).toBe('span');
142
144
  });
145
+
146
+ // The box has always been sized by an inline width/height written after the
147
+ // caller's `style`, so the component's own size wins over a `style={{width}}`
148
+ // passed in. Making the geometry themeable changed what that value is made
149
+ // of — a composed var rather than a number — and deliberately not where it is
150
+ // written: moving the sizing into a rule would have handed a caller's inline
151
+ // width a precedence over the box it has never had. This pins the precedence
152
+ // itself, which is the part a consumer could be depending on.
153
+ describe('box sizing', () => {
154
+ it.each(Object.entries(SIZES))(
155
+ 'falls back to the %s frame where no stylesheet has declared the var',
156
+ (size, {diameter, border}) => {
157
+ render(
158
+ <Spinner size={size as keyof typeof SIZES} data-testid="spinner" />,
159
+ );
160
+ const box = screen.getByTestId('spinner');
161
+ const expected = `var(--_spinner-box-size, ${diameter + border * 2}px)`;
162
+ expect(box.style.width).toBe(expected);
163
+ expect(box.style.height).toBe(expected);
164
+ },
165
+ );
166
+
167
+ it('keeps its own size over a width the caller passes in style', () => {
168
+ render(
169
+ <Spinner
170
+ size="xl"
171
+ style={{width: 999, height: 999, opacity: 0.5}}
172
+ data-testid="spinner"
173
+ />,
174
+ );
175
+ const box = screen.getByTestId('spinner');
176
+ expect(box.style.width).toBe('var(--_spinner-box-size, 36px)');
177
+ expect(box.style.height).toBe('var(--_spinner-box-size, 36px)');
178
+ // Everything else the caller passed still applies — only the two
179
+ // properties the box owns are taken back.
180
+ expect(box.style.opacity).toBe('0.5');
181
+ });
182
+ });
183
+
184
+ // The themed geometry resolves in the cascade — jsdom implements no layout
185
+ // and no custom-property registration, so no test here can reach what a
186
+ // theme actually draws; that is verified in a browser and the numbers are in
187
+ // the PR. What IS a contract a unit test can hold is the routing: a theme
188
+ // writes an override against the documented key, and it has to come out on
189
+ // the selector the component reads from.
190
+ describe('a theme reaches the spinner through its public vars', () => {
191
+ const cssFor = (
192
+ components: Parameters<typeof defineTheme>[0]['components'],
193
+ ) =>
194
+ generateThemeCSS(defineTheme({name: 'spinner-theming', components}))
195
+ .component;
196
+
197
+ it('scopes a themed size to that size variant', () => {
198
+ // Asserting the whole rule, not just the declaration: the same var on
199
+ // the bare `.astryx-spinner` would resize every size at once, which is
200
+ // the bug a size-variant key exists to avoid.
201
+ expect(
202
+ cssFor({spinner: {'size:xl': {'--spinner-diameter': '2.5rem'}}}),
203
+ ).toContain('.astryx-spinner.xl {\n --spinner-diameter: 2.5rem;');
204
+ });
205
+
206
+ it('scopes a themed color to that shade variant', () => {
207
+ expect(
208
+ cssFor({
209
+ spinner: {'shade:subtle': {'--spinner-track-color': 'transparent'}},
210
+ }),
211
+ ).toContain(
212
+ '.astryx-spinner.subtle {\n --spinner-track-color: transparent;',
213
+ );
214
+ });
215
+
216
+ it('lets the base target set a value for every size and shade', () => {
217
+ expect(
218
+ cssFor({spinner: {base: {'--spinner-color': 'var(--color-brand)'}}}),
219
+ ).toContain(
220
+ '.astryx-spinner {\n --spinner-color: var(--color-brand);',
221
+ );
222
+ });
223
+ });
143
224
  });
144
225
 
145
226
  describe('Spinner ring', () => {
@@ -196,6 +277,28 @@ describe('Spinner ring', () => {
196
277
  );
197
278
  });
198
279
 
280
+ // The authored dash is the size's own absolute pattern, and stays that way:
281
+ // it is what a render with no stylesheet draws, and it is byte-for-byte the
282
+ // pattern this component drew before the geometry became themeable. Scaling
283
+ // with a themed diameter is the rule's job — it composes the same two
284
+ // lengths out of the resolved diameter — and deliberately not `pathLength`'s,
285
+ // which rescales against the path length the UA measures on its own
286
+ // approximation of the circle and shortens the default arc by 0.64%.
287
+ it.each(Object.entries(SIZES))(
288
+ 'leaves the %s arc its own absolute dash, with no pathLength',
289
+ (size, {diameter}) => {
290
+ render(
291
+ <Spinner size={size as keyof typeof SIZES} data-testid="spinner" />,
292
+ );
293
+ const {track, arc} = circles();
294
+ const [on, off] = dashOf(arc);
295
+ expect(on + off).toBeCloseTo(Math.PI * diameter, 6);
296
+ expect(arc.getAttribute('pathLength')).toBeNull();
297
+ // The track is a full ring, so it needs neither.
298
+ expect(track.getAttribute('pathLength')).toBeNull();
299
+ },
300
+ );
301
+
199
302
  // The whole point of the SVG ring: the colours come off the cascade, so
200
303
  // nothing has to resolve them in JS. A read reaching the paint path again
201
304
  // fails here.
@@ -268,3 +371,64 @@ describe('Spinner ring', () => {
268
371
  }
269
372
  });
270
373
  });
374
+
375
+ describe('geometry var registration', () => {
376
+ /** The shape `registerSpinnerVars` passes; jsdom implements no real one. */
377
+ type Descriptor = {
378
+ name: string;
379
+ syntax: string;
380
+ inherits: boolean;
381
+ initialValue: string;
382
+ };
383
+ const stubRegisterProperty = (fn: (d: Descriptor) => void) => {
384
+ (
385
+ CSS as unknown as {registerProperty: (d: Descriptor) => void}
386
+ ).registerProperty = fn;
387
+ };
388
+
389
+ afterEach(() => {
390
+ delete (CSS as unknown as {registerProperty?: unknown}).registerProperty;
391
+ vi.resetModules();
392
+ });
393
+
394
+ it('registers when the module is imported, not when a spinner mounts', async () => {
395
+ // Registering an inherited property with an `initial-value` invalidates
396
+ // style for the whole document. A spinner mounts onto a page that has
397
+ // already rendered, so paying it there is paying it on the full tree —
398
+ // measured at 29ms against 12ms on an 11k-element page. At import the page
399
+ // is whatever has rendered so far, which for a bundle in the head is
400
+ // nothing. This pins the timing: it fails if the call moves back into a
401
+ // ref callback, an effect, or the component body.
402
+ const registerProperty = vi.fn<(d: Descriptor) => void>();
403
+ stubRegisterProperty(registerProperty);
404
+
405
+ vi.resetModules();
406
+ await import('./Spinner');
407
+
408
+ expect(registerProperty.mock.calls.map(([d]) => d.name)).toEqual([
409
+ '--_spinner-ring-diameter',
410
+ '--_spinner-ring-stroke',
411
+ ]);
412
+ // Both are `<length>` with an initial value, which is what makes a themed
413
+ // `0` mean `0px` inside the `calc()` rather than poisoning it.
414
+ for (const [descriptor] of registerProperty.mock.calls) {
415
+ expect(descriptor.syntax).toBe('<length>');
416
+ expect(descriptor.inherits).toBe(true);
417
+ expect(descriptor.initialValue).toBe('0px');
418
+ }
419
+ });
420
+
421
+ it('survives a second evaluation, and does not need the DOM', async () => {
422
+ // Two copies of the package on one page, or a fast-refresh re-evaluation:
423
+ // registerProperty throws on a duplicate rather than replacing, and the
424
+ // existing registration is this same one.
425
+ const registerProperty = vi.fn<(d: Descriptor) => void>(() => {
426
+ throw new Error('InvalidModificationError');
427
+ });
428
+ stubRegisterProperty(registerProperty);
429
+
430
+ vi.resetModules();
431
+ await expect(import('./Spinner')).resolves.toBeDefined();
432
+ expect(registerProperty).toHaveBeenCalledTimes(2);
433
+ });
434
+ });