@astryxdesign/core 0.1.9-canary.d2d84f8 → 0.1.9-canary.e00a241

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 (414) hide show
  1. package/dist/Avatar/Avatar.d.ts +6 -1
  2. package/dist/Avatar/Avatar.d.ts.map +1 -1
  3. package/dist/Avatar/Avatar.js +46 -12
  4. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  5. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  8. package/dist/Breadcrumbs/index.d.ts +1 -0
  9. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  10. package/dist/Breadcrumbs/index.js +2 -1
  11. package/dist/Calendar/Calendar.d.ts.map +1 -1
  12. package/dist/Calendar/Calendar.js +50 -9
  13. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  14. package/dist/Calendar/dayCellUtils.js +2 -2
  15. package/dist/Calendar/styles.d.ts +30 -31
  16. package/dist/Calendar/styles.d.ts.map +1 -1
  17. package/dist/Calendar/styles.js +30 -31
  18. package/dist/Card/Card.js +1 -1
  19. package/dist/Carousel/Carousel.d.ts.map +1 -1
  20. package/dist/Carousel/Carousel.js +22 -12
  21. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  22. package/dist/Chat/ChatDictationButton.js +3 -1
  23. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  24. package/dist/Chat/ChatMessageBubble.js +8 -8
  25. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  26. package/dist/Chat/ChatToolCalls.js +10 -3
  27. package/dist/CheckboxInput/CheckboxInput.js +4 -4
  28. package/dist/Citation/Citation.d.ts +22 -1
  29. package/dist/Citation/Citation.d.ts.map +1 -1
  30. package/dist/Citation/Citation.js +20 -6
  31. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  32. package/dist/CodeBlock/CodeBlock.js +13 -7
  33. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  34. package/dist/Collapsible/Collapsible.js +3 -1
  35. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  36. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  37. package/dist/CommandPalette/CommandPalette.js +42 -3
  38. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  39. package/dist/ContextMenu/ContextMenu.js +14 -5
  40. package/dist/ContextMenu/index.d.ts +1 -0
  41. package/dist/ContextMenu/index.d.ts.map +1 -1
  42. package/dist/ContextMenu/index.js +3 -1
  43. package/dist/DateInput/DateInput.d.ts +1 -0
  44. package/dist/DateInput/DateInput.d.ts.map +1 -1
  45. package/dist/DateInput/DateInput.js +31 -19
  46. package/dist/DateRangeInput/DateRangeInput.d.ts +1 -0
  47. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  48. package/dist/DateRangeInput/DateRangeInput.js +20 -19
  49. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  50. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  51. package/dist/Divider/Divider.d.ts +2 -2
  52. package/dist/Divider/Divider.d.ts.map +1 -1
  53. package/dist/Divider/Divider.js +13 -0
  54. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  55. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  56. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  57. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  58. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  59. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  60. package/dist/DropdownMenu/DropdownMenuItem.js +4 -1
  61. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  62. package/dist/DropdownMenu/DropdownMenuRadioItem.js +4 -1
  63. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  64. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  65. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  66. package/dist/DropdownMenu/index.d.ts +10 -0
  67. package/dist/DropdownMenu/index.d.ts.map +1 -1
  68. package/dist/DropdownMenu/index.js +13 -0
  69. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  70. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  71. package/dist/DropdownMenu/menuItemHover.js +33 -0
  72. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  73. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  74. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  75. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  76. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  77. package/dist/Field/Field.d.ts +3 -1
  78. package/dist/Field/Field.d.ts.map +1 -1
  79. package/dist/Field/Field.js +4 -1
  80. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  81. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  82. package/dist/Field/inputStyles.stylex.js +1 -0
  83. package/dist/FieldStatus/FieldStatus.d.ts +4 -1
  84. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  85. package/dist/FieldStatus/FieldStatus.js +4 -2
  86. package/dist/FileInput/FileInput.d.ts +1 -0
  87. package/dist/FileInput/FileInput.d.ts.map +1 -1
  88. package/dist/FileInput/FileInput.js +91 -65
  89. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  90. package/dist/HoverCard/useHoverCard.js +10 -4
  91. package/dist/Icon/Icon.d.ts +15 -1
  92. package/dist/Icon/Icon.d.ts.map +1 -1
  93. package/dist/Icon/Icon.js +19 -3
  94. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  95. package/dist/Lightbox/Lightbox.js +15 -9
  96. package/dist/List/List.d.ts.map +1 -1
  97. package/dist/List/List.js +6 -3
  98. package/dist/Markdown/Markdown.d.ts.map +1 -1
  99. package/dist/Markdown/Markdown.js +7 -0
  100. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  101. package/dist/MultiSelector/MultiSelector.js +65 -40
  102. package/dist/NumberInput/NumberInput.d.ts +1 -0
  103. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  104. package/dist/NumberInput/NumberInput.js +20 -17
  105. package/dist/Outline/Outline.js +2 -2
  106. package/dist/RadioList/RadioList.d.ts +2 -2
  107. package/dist/RadioList/RadioListItem.js +4 -4
  108. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  109. package/dist/Resizable/ResizeHandle.js +49 -19
  110. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  111. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  112. package/dist/SegmentedControl/SegmentedControlItem.js +2 -1
  113. package/dist/Selector/Selector.d.ts.map +1 -1
  114. package/dist/Selector/Selector.js +58 -16
  115. package/dist/SideNav/SideNav.d.ts.map +1 -1
  116. package/dist/SideNav/SideNav.js +5 -5
  117. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  118. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  119. package/dist/SideNav/SideNavItem.d.ts +1 -1
  120. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  121. package/dist/SideNav/SideNavItem.js +12 -10
  122. package/dist/Slider/Slider.d.ts.map +1 -1
  123. package/dist/Slider/Slider.js +87 -34
  124. package/dist/Spinner/Spinner.d.ts +3 -2
  125. package/dist/Spinner/Spinner.d.ts.map +1 -1
  126. package/dist/Spinner/Spinner.js +11 -2
  127. package/dist/Switch/Switch.d.ts +8 -1
  128. package/dist/Switch/Switch.d.ts.map +1 -1
  129. package/dist/Switch/Switch.js +160 -91
  130. package/dist/TabList/TabList.d.ts +2 -11
  131. package/dist/TabList/TabList.d.ts.map +1 -1
  132. package/dist/TabList/TabList.js +5 -9
  133. package/dist/TabList/TabListContext.d.ts +0 -5
  134. package/dist/TabList/TabListContext.d.ts.map +1 -1
  135. package/dist/TabList/TabListContext.js +0 -5
  136. package/dist/Table/BaseTable.d.ts.map +1 -1
  137. package/dist/Table/BaseTable.js +30 -4
  138. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  139. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  140. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  141. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +36 -26
  142. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  143. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  144. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  145. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +14 -6
  146. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  147. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  148. package/dist/Table/types.d.ts +32 -0
  149. package/dist/Table/types.d.ts.map +1 -1
  150. package/dist/TextArea/TextArea.d.ts +1 -0
  151. package/dist/TextArea/TextArea.d.ts.map +1 -1
  152. package/dist/TextArea/TextArea.js +16 -19
  153. package/dist/TextInput/TextInput.d.ts +1 -0
  154. package/dist/TextInput/TextInput.d.ts.map +1 -1
  155. package/dist/TextInput/TextInput.js +18 -17
  156. package/dist/TimeInput/TimeInput.d.ts +1 -0
  157. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  158. package/dist/TimeInput/TimeInput.js +15 -17
  159. package/dist/Timestamp/Timestamp.d.ts +31 -3
  160. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  161. package/dist/Timestamp/Timestamp.js +47 -76
  162. package/dist/Timestamp/formatInstant.d.ts +41 -0
  163. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  164. package/dist/Timestamp/formatInstant.js +177 -0
  165. package/dist/Timestamp/index.d.ts +1 -0
  166. package/dist/Timestamp/index.d.ts.map +1 -1
  167. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  168. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  169. package/dist/Timestamp/tooltipEntries.js +163 -0
  170. package/dist/Token/Token.d.ts +30 -3
  171. package/dist/Token/Token.d.ts.map +1 -1
  172. package/dist/Token/Token.js +17 -2
  173. package/dist/Token/index.d.ts +1 -1
  174. package/dist/Token/index.d.ts.map +1 -1
  175. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  176. package/dist/Tokenizer/Tokenizer.js +2 -2
  177. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  178. package/dist/Tooltip/useTooltip.js +17 -1
  179. package/dist/TopNav/TopNavItem.d.ts +3 -1
  180. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  181. package/dist/TopNav/TopNavItem.js +30 -3
  182. package/dist/TreeList/TreeList.d.ts.map +1 -1
  183. package/dist/TreeList/TreeList.js +1 -0
  184. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  185. package/dist/TreeList/TreeListBranches.js +8 -6
  186. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  187. package/dist/TreeList/TreeListItem.js +32 -14
  188. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  189. package/dist/Typeahead/BaseTypeahead.js +19 -10
  190. package/dist/Typeahead/Typeahead.js +1 -1
  191. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  192. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  193. package/dist/Typeahead/TypeaheadItem.js +20 -10
  194. package/dist/astryx.css +38 -14
  195. package/dist/astryx.umd.js +52 -52
  196. package/dist/astryx.umd.js.map +4 -4
  197. package/dist/hooks/index.d.ts +2 -0
  198. package/dist/hooks/index.d.ts.map +1 -1
  199. package/dist/hooks/index.js +1 -0
  200. package/dist/hooks/isRtlElement.d.ts +18 -0
  201. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  202. package/dist/hooks/isRtlElement.js +27 -0
  203. package/dist/hooks/useAnnounce.d.ts +3 -0
  204. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  205. package/dist/hooks/useAnnounce.js +39 -1
  206. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  207. package/dist/hooks/useFocusTrap.js +52 -11
  208. package/dist/hooks/useGridFocus.d.ts +6 -1
  209. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  210. package/dist/hooks/useGridFocus.js +12 -9
  211. package/dist/hooks/useHotkeys.d.ts +9 -0
  212. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  213. package/dist/hooks/useHotkeys.js +9 -0
  214. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  215. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  216. package/dist/hooks/useInputStatusIcon.js +158 -0
  217. package/dist/hooks/useListFocus.d.ts +39 -1
  218. package/dist/hooks/useListFocus.d.ts.map +1 -1
  219. package/dist/hooks/useListFocus.js +54 -9
  220. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  221. package/dist/theme/derivedVarRegistry.js +13 -0
  222. package/dist/utils/index.d.ts +1 -0
  223. package/dist/utils/index.d.ts.map +1 -1
  224. package/dist/utils/index.js +2 -1
  225. package/dist/utils/plainDate.d.ts +17 -0
  226. package/dist/utils/plainDate.d.ts.map +1 -1
  227. package/dist/utils/plainDate.js +23 -4
  228. package/dist/utils/rtlStyles.d.ts +24 -0
  229. package/dist/utils/rtlStyles.d.ts.map +1 -0
  230. package/dist/utils/rtlStyles.js +27 -0
  231. package/locales/en.json +76 -0
  232. package/locales/pseudo.json +57 -0
  233. package/package.json +6 -1
  234. package/src/Avatar/Avatar.doc.mjs +18 -4
  235. package/src/Avatar/Avatar.test.tsx +106 -0
  236. package/src/Avatar/Avatar.tsx +62 -10
  237. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  238. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  239. package/src/AvatarGroup/AvatarGroup.test.tsx +16 -1
  240. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  241. package/src/Breadcrumbs/Breadcrumbs.test.tsx +51 -0
  242. package/src/Breadcrumbs/index.ts +4 -0
  243. package/src/Button/Button.doc.mjs +0 -4
  244. package/src/Calendar/Calendar.test.tsx +170 -20
  245. package/src/Calendar/Calendar.tsx +73 -16
  246. package/src/Calendar/dayCellUtils.test.ts +18 -18
  247. package/src/Calendar/dayCellUtils.ts +2 -2
  248. package/src/Calendar/styles.ts +33 -39
  249. package/src/Card/Card.tsx +1 -1
  250. package/src/Carousel/Carousel.tsx +25 -6
  251. package/src/Chat/ChatComposer.doc.mjs +2 -2
  252. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  253. package/src/Chat/ChatDictationButton.tsx +7 -1
  254. package/src/Chat/ChatMessageBubble.tsx +15 -10
  255. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  256. package/src/Chat/ChatToolCalls.tsx +9 -2
  257. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  258. package/src/CheckboxInput/CheckboxInput.tsx +4 -4
  259. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  260. package/src/Citation/Citation.doc.mjs +4 -3
  261. package/src/Citation/Citation.test.tsx +97 -0
  262. package/src/Citation/Citation.tsx +43 -11
  263. package/src/CodeBlock/CodeBlock.tsx +35 -8
  264. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  265. package/src/Collapsible/Collapsible.test.tsx +7 -0
  266. package/src/Collapsible/Collapsible.tsx +16 -6
  267. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  268. package/src/CommandPalette/CommandPalette.tsx +41 -2
  269. package/src/ContextMenu/ContextMenu.test.tsx +42 -0
  270. package/src/ContextMenu/ContextMenu.tsx +13 -11
  271. package/src/ContextMenu/index.ts +5 -0
  272. package/src/DateInput/DateInput.doc.mjs +15 -5
  273. package/src/DateInput/DateInput.test.tsx +210 -3
  274. package/src/DateInput/DateInput.tsx +41 -29
  275. package/src/DateRangeInput/DateRangeInput.doc.mjs +11 -3
  276. package/src/DateRangeInput/DateRangeInput.test.tsx +120 -3
  277. package/src/DateRangeInput/DateRangeInput.tsx +29 -27
  278. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  279. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  280. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  281. package/src/Divider/Divider.test.tsx +32 -8
  282. package/src/Divider/Divider.tsx +14 -1
  283. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  284. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  285. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  286. package/src/DropdownMenu/DropdownMenuItem.tsx +8 -2
  287. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +8 -2
  288. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  289. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  290. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  291. package/src/DropdownMenu/index.ts +16 -0
  292. package/src/DropdownMenu/menuItemHover.ts +39 -0
  293. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  294. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  295. package/src/Field/Field.tsx +14 -9
  296. package/src/Field/inputStyles.stylex.ts +2 -0
  297. package/src/FieldStatus/FieldStatus.tsx +10 -4
  298. package/src/FileInput/FileInput.doc.mjs +13 -7
  299. package/src/FileInput/FileInput.test.tsx +84 -16
  300. package/src/FileInput/FileInput.tsx +96 -79
  301. package/src/HoverCard/useHoverCard.tsx +15 -3
  302. package/src/Icon/Icon.doc.mjs +13 -0
  303. package/src/Icon/Icon.test.tsx +89 -0
  304. package/src/Icon/Icon.tsx +51 -3
  305. package/src/Lightbox/Lightbox.tsx +11 -3
  306. package/src/List/List.test.tsx +17 -29
  307. package/src/List/List.tsx +6 -1
  308. package/src/Markdown/Markdown.test.tsx +21 -0
  309. package/src/Markdown/Markdown.tsx +4 -0
  310. package/src/MultiSelector/MultiSelector.doc.mjs +11 -0
  311. package/src/MultiSelector/MultiSelector.test.tsx +306 -3
  312. package/src/MultiSelector/MultiSelector.tsx +88 -57
  313. package/src/NumberInput/NumberInput.doc.mjs +11 -5
  314. package/src/NumberInput/NumberInput.test.tsx +48 -0
  315. package/src/NumberInput/NumberInput.tsx +25 -26
  316. package/src/Outline/Outline.test.tsx +12 -12
  317. package/src/Outline/Outline.tsx +2 -2
  318. package/src/RadioList/RadioList.doc.mjs +6 -0
  319. package/src/RadioList/RadioList.tsx +2 -2
  320. package/src/RadioList/RadioListItem.tsx +4 -4
  321. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  322. package/src/Resizable/ResizeHandle.tsx +70 -20
  323. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -0
  324. package/src/Selector/Selector.doc.mjs +8 -0
  325. package/src/Selector/Selector.test.tsx +328 -3
  326. package/src/Selector/Selector.tsx +70 -19
  327. package/src/SideNav/SideNav.tsx +1 -8
  328. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  329. package/src/SideNav/SideNavItem.tsx +5 -3
  330. package/src/Slider/Slider.doc.mjs +6 -0
  331. package/src/Slider/Slider.test.tsx +240 -12
  332. package/src/Slider/Slider.tsx +96 -23
  333. package/src/Spinner/Spinner.test.tsx +10 -8
  334. package/src/Spinner/Spinner.tsx +15 -5
  335. package/src/Switch/Switch.doc.mjs +142 -42
  336. package/src/Switch/Switch.test.tsx +22 -0
  337. package/src/Switch/Switch.tsx +90 -33
  338. package/src/TabList/TabList.doc.mjs +0 -6
  339. package/src/TabList/TabList.test.tsx +3 -15
  340. package/src/TabList/TabList.tsx +6 -17
  341. package/src/TabList/TabListContext.ts +0 -6
  342. package/src/Table/BaseTable.tsx +34 -2
  343. package/src/Table/Table.doc.mjs +11 -0
  344. package/src/Table/Table.test.tsx +85 -0
  345. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  346. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +24 -17
  347. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  348. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  349. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +37 -8
  350. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  351. package/src/Table/types.ts +33 -0
  352. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  353. package/src/TextArea/TextArea.doc.mjs +11 -5
  354. package/src/TextArea/TextArea.tsx +17 -26
  355. package/src/TextInput/TextInput.doc.mjs +11 -5
  356. package/src/TextInput/TextInput.test.tsx +130 -3
  357. package/src/TextInput/TextInput.tsx +18 -25
  358. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  359. package/src/TimeInput/TimeInput.doc.mjs +11 -5
  360. package/src/TimeInput/TimeInput.tsx +14 -25
  361. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  362. package/src/Timestamp/Timestamp.test.tsx +371 -0
  363. package/src/Timestamp/Timestamp.tsx +96 -93
  364. package/src/Timestamp/formatInstant.ts +218 -0
  365. package/src/Timestamp/index.ts +4 -0
  366. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  367. package/src/Timestamp/tooltipEntries.ts +210 -0
  368. package/src/Token/Token.test.tsx +21 -15
  369. package/src/Token/Token.tsx +31 -14
  370. package/src/Token/index.ts +1 -1
  371. package/src/Tokenizer/Tokenizer.doc.mjs +8 -1
  372. package/src/Tokenizer/Tokenizer.tsx +4 -2
  373. package/src/Tooltip/Tooltip.test.tsx +46 -0
  374. package/src/Tooltip/useTooltip.tsx +20 -0
  375. package/src/TopNav/TopNav.test.tsx +93 -2
  376. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  377. package/src/TopNav/TopNavItem.tsx +35 -6
  378. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  379. package/src/TreeList/TreeList.doc.mjs +6 -0
  380. package/src/TreeList/TreeList.test.tsx +50 -10
  381. package/src/TreeList/TreeList.tsx +5 -0
  382. package/src/TreeList/TreeListBranches.tsx +8 -6
  383. package/src/TreeList/TreeListItem.tsx +33 -11
  384. package/src/Typeahead/BaseTypeahead.tsx +29 -14
  385. package/src/Typeahead/Typeahead.doc.mjs +6 -0
  386. package/src/Typeahead/Typeahead.test.tsx +27 -0
  387. package/src/Typeahead/Typeahead.tsx +1 -1
  388. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  389. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  390. package/src/doc-mjs.d.ts +19 -0
  391. package/src/hooks/index.ts +6 -0
  392. package/src/hooks/isRtlElement.ts +27 -0
  393. package/src/hooks/useAnnounce.test.tsx +75 -2
  394. package/src/hooks/useAnnounce.ts +47 -1
  395. package/src/hooks/useFocusTrap.test.tsx +104 -0
  396. package/src/hooks/useFocusTrap.ts +61 -12
  397. package/src/hooks/useGridFocus.doc.mjs +3 -3
  398. package/src/hooks/useGridFocus.test.tsx +41 -0
  399. package/src/hooks/useGridFocus.ts +21 -10
  400. package/src/hooks/useHotkeys.ts +9 -0
  401. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  402. package/src/hooks/useInputStatusIcon.tsx +224 -0
  403. package/src/hooks/useListFocus.doc.mjs +25 -3
  404. package/src/hooks/useListFocus.test.tsx +153 -1
  405. package/src/hooks/useListFocus.ts +102 -9
  406. package/src/reset.css +0 -26
  407. package/src/reset.test.ts +60 -0
  408. package/src/theme/derivedVarRegistry.test.ts +1 -0
  409. package/src/theme/derivedVarRegistry.ts +6 -0
  410. package/src/theme/generateThemeRules.test.ts +42 -0
  411. package/src/utils/index.ts +2 -0
  412. package/src/utils/plainDate.test.ts +24 -6
  413. package/src/utils/plainDate.ts +18 -4
  414. package/src/utils/rtlStyles.ts +27 -0
@@ -131,6 +131,8 @@ export interface TopNavItemProps extends BaseProps<HTMLAnchorElement> {
131
131
  isSelected?: boolean;
132
132
  /**
133
133
  * Whether the nav item is disabled.
134
+ * A disabled item renders as a plain anchor without an href (and without
135
+ * target), so it cannot navigate and is removed from the tab order.
134
136
  * @default false
135
137
  */
136
138
  isDisabled?: boolean;
@@ -157,6 +159,15 @@ export interface TopNavItemProps extends BaseProps<HTMLAnchorElement> {
157
159
  size?: NavItemSize;
158
160
  }
159
161
 
162
+ /**
163
+ * Click handler for disabled items. The disabled anchor renders without an
164
+ * href, so there is no navigation to block in practice; preventDefault is a
165
+ * defensive guard against synthetic/programmatic clicks.
166
+ */
167
+ function preventDefaultClick(event: React.MouseEvent<HTMLAnchorElement>): void {
168
+ event.preventDefault();
169
+ }
170
+
160
171
  /**
161
172
  * A navigation item for use within TopNav startContent.
162
173
  *
@@ -178,6 +189,9 @@ export interface TopNavItemProps extends BaseProps<HTMLAnchorElement> {
178
189
  */
179
190
  export function TopNavItem({
180
191
  as,
192
+ href,
193
+ target,
194
+ onClick,
181
195
  label,
182
196
  isSelected = false,
183
197
  isDisabled = false,
@@ -195,22 +209,34 @@ export function TopNavItem({
195
209
  const renderMode = useTopNavRenderMode();
196
210
  const {closeMobileNav} = useAppShellMobile();
197
211
 
212
+ // A disabled item renders as a plain <a> with no href: an href-less anchor
213
+ // is not focusable and exposes no link affordance, so programmatic focus +
214
+ // Enter, AT activation commands, and middle-click cannot navigate or fire
215
+ // the consumer onClick. The router LinkComponent is deliberately skipped —
216
+ // a disabled item performs no navigation, and custom router links may
217
+ // require a live href. target is omitted with the href. (Mirrors the
218
+ // disabled paths of Link and SideNavItem.)
219
+ const Root = isDisabled ? 'a' : LinkComponent;
220
+
198
221
  // =========================================================================
199
222
  // Drawer mode — render as a SideNavItem-style vertical list element
200
223
  // =========================================================================
201
224
  if (renderMode === 'drawer') {
202
- const handleDrawerClick = (e: React.MouseEvent) => {
225
+ const handleDrawerClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
203
226
  if (isDisabled) {
227
+ preventDefaultClick(e);
204
228
  return;
205
229
  }
206
230
  // Forward the original onClick if present
207
- (props as {onClick?: (e: React.MouseEvent) => void}).onClick?.(e);
231
+ onClick?.(e);
208
232
  closeMobileNav();
209
233
  };
210
234
 
211
235
  return (
212
- <LinkComponent
236
+ <Root
213
237
  ref={ref}
238
+ href={isDisabled ? undefined : href}
239
+ target={isDisabled ? undefined : target}
214
240
  aria-label={isIconOnly ? label : undefined}
215
241
  aria-current={isSelected ? 'page' : undefined}
216
242
  aria-disabled={isDisabled || undefined}
@@ -235,7 +261,7 @@ export function TopNavItem({
235
261
  onClick={handleDrawerClick}>
236
262
  {icon}
237
263
  {!isIconOnly && (children ?? label)}
238
- </LinkComponent>
264
+ </Root>
239
265
  );
240
266
  }
241
267
 
@@ -244,8 +270,11 @@ export function TopNavItem({
244
270
  // =========================================================================
245
271
 
246
272
  return (
247
- <LinkComponent
273
+ <Root
248
274
  ref={ref}
275
+ href={isDisabled ? undefined : href}
276
+ target={isDisabled ? undefined : target}
277
+ onClick={isDisabled ? preventDefaultClick : onClick}
249
278
  aria-label={isIconOnly ? label : undefined}
250
279
  aria-current={isSelected ? 'page' : undefined}
251
280
  aria-disabled={isDisabled || undefined}
@@ -267,7 +296,7 @@ export function TopNavItem({
267
296
  {...props}>
268
297
  {icon}
269
298
  {!isIconOnly && (children ?? label)}
270
- </LinkComponent>
299
+ </Root>
271
300
  );
272
301
  }
273
302
 
@@ -27,7 +27,7 @@ export const docs = {
27
27
  {
28
28
  name: 'imageAlt',
29
29
  type: 'string',
30
- description: 'Alt text for the image. When omitted, the image is explicitly decorative (alt="", role="presentation", aria-hidden) — usually correct since the card already has a visible title. Provide it only when the image conveys information beyond the title and description.',
30
+ description: 'Alt text for the image. When omitted, the image is explicitly decorative (alt="", role="presentation", aria-hidden), usually correct since the card already has a visible title. Provide it only when the image conveys information beyond the title and description.',
31
31
  },
32
32
  {
33
33
  name: 'linkLabel',
@@ -30,6 +30,9 @@ export const docs = {
30
30
  {className: 'astryx-tree-list-item-label', states: ['selected']},
31
31
  {className: 'astryx-tree-list-guide'},
32
32
  ],
33
+ vars: [
34
+ {name: '--tree-list-indent', description: 'Per-level indentation step. Each nesting level indents its rows by this distance, and the guide lines follow it so they stay aligned. Set it on the `tree-list` target to retune the metric (e.g. `var(--spacing-5)` for a wider indent).', default: 'var(--spacing-4)'},
35
+ ],
33
36
  },
34
37
  components: [
35
38
  {
@@ -104,6 +107,9 @@ export const docsZh = {
104
107
  {className: 'astryx-tree-list-item-label', states: ['selected']},
105
108
  {className: 'astryx-tree-list-guide'},
106
109
  ],
110
+ vars: [
111
+ {name: '--tree-list-indent', description: '每级缩进步长。每个嵌套层级的行按此距离缩进,引导线随之对齐。在 `tree-list` 目标上设置以调整该度量(例如用 `var(--spacing-5)` 获得更宽的缩进)。', default: 'var(--spacing-4)'},
112
+ ],
107
113
  },
108
114
  components: [
109
115
  {
@@ -432,25 +432,28 @@ describe('TreeList', () => {
432
432
 
433
433
  it("variant='noGuides' preserves per-level indentation on the rows", () => {
434
434
  // Indentation lives on the row's margin-inline-start (not the guide
435
- // element), so it must survive when the connectors are suppressed. A
436
- // deeper row is indented more than a shallower one.
435
+ // element), so it must survive when the connectors are suppressed. The
436
+ // per-row distance is published as the `--_tree-indent` custom property
437
+ // (not an inline longhand — see #4308), so the theme layer can override
438
+ // the `margin-inline-start` declaration. A deeper row is indented more
439
+ // than a shallower one.
437
440
  const {container} = render(
438
441
  <TreeList items={deepItems} variant="noGuides" />,
439
442
  );
440
- const marginOf = (text: string): string => {
443
+ const indentOf = (text: string): string => {
441
444
  const li = screen.getByText(text).closest('li')!;
442
- const styled = li.querySelector('[style*="margin-inline-start"]');
445
+ const styled = li.querySelector('[style*="--_tree-indent"]');
443
446
  return styled?.getAttribute('style') ?? '';
444
447
  };
445
448
  // Guides are gone…
446
449
  expect(container.querySelector('.astryx-tree-list-guide')).toBeNull();
447
- // …but each level still carries an inline margin-inline-start, and the
448
- // level multiplier grows with depth (0, 1, 2).
449
- expect(marginOf('Root')).toContain('margin-inline-start');
450
- expect(marginOf('Mid')).toContain('margin-inline-start');
451
- expect(marginOf('Leaf')).toContain('margin-inline-start');
450
+ // …but each level still publishes an indent distance, and the level
451
+ // multiplier grows with depth (0, 1, 2).
452
+ expect(indentOf('Root')).toContain('--_tree-indent');
453
+ expect(indentOf('Mid')).toContain('--_tree-indent');
454
+ expect(indentOf('Leaf')).toContain('--_tree-indent');
452
455
  const level = (text: string): number => {
453
- const m = /calc\((\d+)/.exec(marginOf(text));
456
+ const m = /calc\((\d+)/.exec(indentOf(text));
454
457
  return m ? Number(m[1]) : NaN;
455
458
  };
456
459
  expect(level('Mid')).toBeGreaterThan(level('Root'));
@@ -504,6 +507,43 @@ describe('TreeList', () => {
504
507
  });
505
508
  });
506
509
 
510
+ // ===========================================================================
511
+ // Indent lever (--tree-list-indent)
512
+ // ===========================================================================
513
+
514
+ describe('indent lever', () => {
515
+ it('lets a theme retune the indent step via the tree-list target', () => {
516
+ // The per-level step is a public, themeable var (default --spacing-4) set
517
+ // on the tree-list root, so a theme can retune the indent metric (e.g. to
518
+ // --spacing-5) via defineTheme. jsdom cannot resolve the @layer cascade,
519
+ // so the generated CSS is what proves the override reaches the lever.
520
+ const theme = defineTheme({
521
+ name: 'tree-list-indent-test',
522
+ components: {
523
+ 'tree-list': {
524
+ base: {'--tree-list-indent': 'var(--spacing-5)'},
525
+ },
526
+ },
527
+ });
528
+ const css = generateThemeCSSFlat(theme);
529
+ expect(css).toContain('.astryx-tree-list {');
530
+ expect(css).toContain('--tree-list-indent: var(--spacing-5)');
531
+ });
532
+
533
+ it('rows consume the indent step in their published indent distance', () => {
534
+ // Each row's --_tree-indent is calc(level * var(--tree-list-indent)),
535
+ // so retuning the step scales every level uniformly.
536
+ render(<TreeList items={deepItems} variant="noGuides" />);
537
+ const indentOf = (text: string): string => {
538
+ const li = screen.getByText(text).closest('li')!;
539
+ const styled = li.querySelector('[style*="--_tree-indent"]');
540
+ return styled?.getAttribute('style') ?? '';
541
+ };
542
+ expect(indentOf('Mid')).toContain('var(--tree-list-indent)');
543
+ expect(indentOf('Leaf')).toContain('var(--tree-list-indent)');
544
+ });
545
+ });
546
+
507
547
  // ===========================================================================
508
548
  // xds class name
509
549
  // ===========================================================================
@@ -83,6 +83,11 @@ export interface TreeListProps extends BaseProps<HTMLDivElement> {
83
83
  const styles = stylex.create({
84
84
  root: {
85
85
  position: 'relative',
86
+ // Per-level indentation step. Public, themeable lever: a theme can retune
87
+ // the tree's indent metric (e.g. to `var(--spacing-5)`) via `defineTheme`
88
+ // on the `tree-list` target, and both the row margins (TreeListItem) and
89
+ // the guide-line offsets (TreeListBranches) read it so they stay aligned.
90
+ '--tree-list-indent': spacingVars['--spacing-4'],
86
91
  },
87
92
  list: {
88
93
  margin: 0,
@@ -26,8 +26,10 @@ const LINE_WIDTH = 1;
26
26
  */
27
27
  const BRANCH_MARGIN = `calc(${spacingVars['--spacing-2']} + ${spacingVars['--spacing-0-5']})`;
28
28
 
29
- /** Per-level indent width, matching --spacing-4 (16px). */
30
- const LEVEL_INDENT = spacingVars['--spacing-4'];
29
+ /** Per-level indent width. Reads the public, themeable `--tree-list-indent`
30
+ * lever set on the tree-list root (default `--spacing-4`, 16px), so the guide
31
+ * lines stay aligned with the row indent when a theme retunes the step. */
32
+ const LEVEL_INDENT = 'var(--tree-list-indent)';
31
33
 
32
34
  const styles = stylex.create({
33
35
  container: {
@@ -37,10 +39,10 @@ const styles = stylex.create({
37
39
  },
38
40
  verticalLine: {
39
41
  borderRadius: 1,
40
- left: 0,
42
+ insetInlineStart: 0,
41
43
  margin: 'auto',
42
44
  position: 'absolute',
43
- right: 0,
45
+ insetInlineEnd: 0,
44
46
  width: LINE_WIDTH,
45
47
  backgroundColor: colorVars['--color-border-emphasized'],
46
48
  },
@@ -92,7 +94,7 @@ export function TreeListBranches({
92
94
  key={level}
93
95
  {...mergeProps(stylex.props(styles.container), {
94
96
  style: {
95
- left: branchOffset,
97
+ insetInlineStart: branchOffset,
96
98
  },
97
99
  })}>
98
100
  <div
@@ -109,7 +111,7 @@ export function TreeListBranches({
109
111
  <div
110
112
  {...mergeProps(stylex.props(styles.container), {
111
113
  style: {
112
- left: `calc(${BRANCH_MARGIN} + ${nestedLevel - 1} * ${LEVEL_INDENT})`,
114
+ insetInlineStart: `calc(${BRANCH_MARGIN} + ${nestedLevel - 1} * ${LEVEL_INDENT})`,
113
115
  },
114
116
  })}>
115
117
  <div
@@ -25,7 +25,7 @@ import {
25
25
  typeScaleVars,
26
26
  } from '../theme/tokens.stylex';
27
27
  import {getIcon} from '../Icon/globalIconRegistry';
28
- import {mergeProps} from '../utils';
28
+ import {mergeProps, rtlStyles} from '../utils';
29
29
  import {useLinkComponent} from '../Link/useLinkComponent';
30
30
  import {TreeListBranches} from './TreeListBranches';
31
31
  import type {TreeListDensity, TreeListVariant} from './TreeListTypes';
@@ -82,6 +82,12 @@ const styles = stylex.create({
82
82
  position: 'relative',
83
83
  boxSizing: 'border-box',
84
84
  textAlign: 'start',
85
+ // Per-level indent. Declared here (not inline) so it lives in
86
+ // `@layer astryx-base` and the theme layer can override it in normal
87
+ // cascade order — an inline longhand would outrank every layer. The row
88
+ // publishes only the computed distance as `--_tree-indent`; the per-level
89
+ // step is the public `--tree-list-indent` lever (see TreeList `root`).
90
+ marginInlineStart: 'var(--_tree-indent, 0px)',
85
91
  },
86
92
  interactive: {
87
93
  cursor: 'pointer',
@@ -246,6 +252,11 @@ const descriptionSizeStyles = stylex.create({
246
252
  },
247
253
  });
248
254
 
255
+ // `CSSProperties` has no index signature for custom properties, so the row's
256
+ // inline style — which publishes the computed indent distance as the private
257
+ // `--_tree-indent` — needs this augmentation to typecheck.
258
+ type IndentStyle = React.CSSProperties & Record<'--_tree-indent', string>;
259
+
249
260
  // =============================================================================
250
261
  // Types
251
262
  // =============================================================================
@@ -353,9 +364,18 @@ export function TreeListItem({
353
364
  return undefined;
354
365
  }, [onClick, hasChildren, onToggle, id, isDisabled]);
355
366
 
356
- const computedMarginLeft = hasChildren
357
- ? `calc(${nestedLevel} * ${spacingVars['--spacing-4']})`
358
- : `calc(${nestedLevel} * ${spacingVars['--spacing-4']} + ${spacingVars['--spacing-4']} + ${spacingVars['--spacing-2']})`;
367
+ // Per-level indent distance. The per-level step is the public, themeable
368
+ // `--tree-list-indent` lever (default `--spacing-4`, set on the tree-list
369
+ // root). Leaves add a fixed chevron-column offset (chevron width + gap) so
370
+ // their labels line up with sibling parents' labels; that offset is tied to
371
+ // the chevron's own dimensions, not the indent step, so it does not scale
372
+ // with the lever. Published as the private `--_tree-indent` and consumed by
373
+ // `contentWrapper`'s stylesheet `margin-inline-start` (kept out of the inline
374
+ // style so the theme layer can override it — see #4308).
375
+ const indentDistance = hasChildren
376
+ ? `calc(${nestedLevel} * var(--tree-list-indent))`
377
+ : `calc(${nestedLevel} * var(--tree-list-indent) + ${spacingVars['--spacing-4']} + ${spacingVars['--spacing-2']})`;
378
+ const indentStyle: IndentStyle = {'--_tree-indent': indentDistance};
359
379
 
360
380
  const labelAndDescription = (
361
381
  <>
@@ -386,12 +406,14 @@ export function TreeListItem({
386
406
  );
387
407
 
388
408
  const chevronIcon = (
389
- <span
390
- {...stylex.props(
391
- styles.chevronSvg,
392
- isExpanded ? styles.chevronExpanded : styles.chevronCollapsed,
393
- )}>
394
- {getIcon('chevronRight')}
409
+ <span {...stylex.props(rtlStyles.mirror)}>
410
+ <span
411
+ {...stylex.props(
412
+ styles.chevronSvg,
413
+ isExpanded ? styles.chevronExpanded : styles.chevronCollapsed,
414
+ )}>
415
+ {getIcon('chevronRight')}
416
+ </span>
395
417
  </span>
396
418
  );
397
419
 
@@ -527,7 +549,7 @@ export function TreeListItem({
527
549
  isSelected && styles.selected,
528
550
  ),
529
551
  )}
530
- style={{marginInlineStart: computedMarginLeft}}
552
+ style={indentStyle}
531
553
  onClick={handleClick}>
532
554
  {innerContent}
533
555
  </div>
@@ -431,7 +431,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
431
431
  resultsGenRef.current = gen;
432
432
  const shown = searchResults.slice(0, maxMenuItems);
433
433
  setResults(shown);
434
- setHighlightedIndex(searchResults.length > 0 ? 0 : -1);
434
+ setHighlightedIndex(shown.length > 0 ? 0 : -1);
435
435
  if (searchResults.length > 0 || searchQuery.length > 0) {
436
436
  showLayer();
437
437
  }
@@ -469,8 +469,9 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
469
469
  return;
470
470
  }
471
471
  resultsGenRef.current = gen;
472
- setResults(bootstrapResults.slice(0, maxMenuItems));
473
- setHighlightedIndex(bootstrapResults.length > 0 ? 0 : -1);
472
+ const shown = bootstrapResults.slice(0, maxMenuItems);
473
+ setResults(shown);
474
+ setHighlightedIndex(shown.length > 0 ? 0 : -1);
474
475
  if (bootstrapResults.length > 0) {
475
476
  showLayer();
476
477
  }
@@ -637,15 +638,19 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
637
638
  switch (e.key) {
638
639
  case 'ArrowDown':
639
640
  e.preventDefault();
640
- setHighlightedIndex(prev =>
641
- prev < results.length - 1 ? prev + 1 : 0,
642
- );
641
+ if (results.length > 0) {
642
+ setHighlightedIndex(prev =>
643
+ prev < results.length - 1 ? prev + 1 : 0,
644
+ );
645
+ }
643
646
  break;
644
647
  case 'ArrowUp':
645
648
  e.preventDefault();
646
- setHighlightedIndex(prev =>
647
- prev > 0 ? prev - 1 : results.length - 1,
648
- );
649
+ if (results.length > 0) {
650
+ setHighlightedIndex(prev =>
651
+ prev > 0 ? prev - 1 : results.length - 1,
652
+ );
653
+ }
649
654
  break;
650
655
  case 'Enter':
651
656
  e.preventDefault();
@@ -660,13 +665,17 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
660
665
  case 'Home':
661
666
  if (popover.isOpen) {
662
667
  e.preventDefault();
663
- setHighlightedIndex(0);
668
+ if (results.length > 0) {
669
+ setHighlightedIndex(0);
670
+ }
664
671
  }
665
672
  break;
666
673
  case 'End':
667
674
  if (popover.isOpen) {
668
675
  e.preventDefault();
669
- setHighlightedIndex(results.length - 1);
676
+ if (results.length > 0) {
677
+ setHighlightedIndex(results.length - 1);
678
+ }
670
679
  }
671
680
  break;
672
681
  }
@@ -694,13 +703,17 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
694
703
  // cursor walks off-screen once navigation passes the visible window. Mirrors
695
704
  // CommandPaletteItem's scrollIntoView({block: 'nearest'}) behavior.
696
705
  useEffect(() => {
697
- if (!popover.isOpen || highlightedIndex < 0) {
706
+ if (
707
+ !popover.isOpen ||
708
+ highlightedIndex < 0 ||
709
+ highlightedIndex >= results.length
710
+ ) {
698
711
  return;
699
712
  }
700
713
  document
701
714
  .getElementById(getItemId(highlightedIndex))
702
715
  ?.scrollIntoView?.({block: 'nearest'});
703
- }, [popover.isOpen, highlightedIndex, getItemId]);
716
+ }, [popover.isOpen, highlightedIndex, getItemId, results.length]);
704
717
 
705
718
  const selectedKey =
706
719
  value == null ? null : getKey(value.id, () => results.indexOf(value));
@@ -725,7 +738,9 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
725
738
  aria-expanded={popover.isOpen}
726
739
  aria-controls={listboxId}
727
740
  aria-activedescendant={
728
- popover.isOpen && highlightedIndex >= 0
741
+ popover.isOpen &&
742
+ highlightedIndex >= 0 &&
743
+ highlightedIndex < results.length
729
744
  ? getItemId(highlightedIndex)
730
745
  : undefined
731
746
  }
@@ -165,6 +165,12 @@ export const docs = {
165
165
  type: '(isOpen: boolean) => void',
166
166
  description: 'Callback when the dropdown opens or closes.',
167
167
  },
168
+ {
169
+ name: 'width',
170
+ type: 'SizeValue',
171
+ description:
172
+ 'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
173
+ },
168
174
  {
169
175
  name: 'xstyle',
170
176
  type: 'StyleXStyles',
@@ -171,6 +171,33 @@ describe('BaseTypeahead', () => {
171
171
  });
172
172
  });
173
173
 
174
+ describe('empty results active descendant (#4059)', () => {
175
+ it('does not set aria-activedescendant when search has 0 results', async () => {
176
+ render(
177
+ <BaseTypeahead
178
+ searchSource={fruitSource}
179
+ value={null}
180
+ onChange={() => {}}
181
+ debounceMs={0}
182
+ />,
183
+ );
184
+ const input = screen.getByRole('combobox');
185
+ fireEvent.change(input, {target: {value: 'zzzzz'}});
186
+
187
+ await waitFor(() => {
188
+ expect(input).not.toHaveAttribute('aria-activedescendant');
189
+ });
190
+
191
+ // Press ArrowDown — should NOT set aria-activedescendant to option-0
192
+ fireEvent.keyDown(input, {key: 'ArrowDown'});
193
+ expect(input).not.toHaveAttribute('aria-activedescendant');
194
+
195
+ // Press Home — should NOT set aria-activedescendant
196
+ fireEvent.keyDown(input, {key: 'Home'});
197
+ expect(input).not.toHaveAttribute('aria-activedescendant');
198
+ });
199
+ });
200
+
174
201
  it('disables input when isDisabled', () => {
175
202
  render(
176
203
  <BaseTypeahead
@@ -421,7 +421,7 @@ export function Typeahead<T extends SearchableItem>({
421
421
  styles.wrapper,
422
422
  sizeStyle,
423
423
  status && inputStatusBorderStyles[status.type],
424
- status && inputStatusHoverShadowStyles[status.type],
424
+ status && !isDisabled && inputStatusHoverShadowStyles[status.type],
425
425
  status && inputStatusFocusWithinStyles[status.type],
426
426
  isDisabled && inputWrapperStyles.disabled,
427
427
  inputGroup && groupStyles.inGroup,
@@ -132,6 +132,7 @@ export function TypeaheadItem<T extends SearchableItem>({
132
132
  icon,
133
133
  description,
134
134
  isDisabled = false,
135
+ xstyle,
135
136
  }: TypeaheadItemProps<T>) {
136
137
  // If item has a pre-rendered element, use it
137
138
  if (item.element) {
@@ -143,7 +144,7 @@ export function TypeaheadItem<T extends SearchableItem>({
143
144
  ref={ref}
144
145
  {...mergeProps(
145
146
  themeProps('typeahead-item'),
146
- stylex.props(styles.container, isDisabled && styles.disabled),
147
+ stylex.props(styles.container, isDisabled && styles.disabled, xstyle),
147
148
  )}>
148
149
  {icon}
149
150
  <div {...stylex.props(styles.content)}>
@@ -0,0 +1,62 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+
5
+ import {docs as CheckboxInputDocs} from '../CheckboxInput/CheckboxInput.doc.mjs';
6
+ import {docs as CheckboxListDocs} from '../CheckboxList/CheckboxList.doc.mjs';
7
+ import {docs as DateInputDocs} from '../DateInput/DateInput.doc.mjs';
8
+ import {docs as DateRangeInputDocs} from '../DateRangeInput/DateRangeInput.doc.mjs';
9
+ import {docs as DateTimeInputDocs} from '../DateTimeInput/DateTimeInput.doc.mjs';
10
+ import {docs as FileInputDocs} from '../FileInput/FileInput.doc.mjs';
11
+ import {docs as MultiSelectorDocs} from '../MultiSelector/MultiSelector.doc.mjs';
12
+ import {docs as NumberInputDocs} from '../NumberInput/NumberInput.doc.mjs';
13
+ import {docs as RadioListDocs} from '../RadioList/RadioList.doc.mjs';
14
+ import {docs as SelectorDocs} from '../Selector/Selector.doc.mjs';
15
+ import {docs as SliderDocs} from '../Slider/Slider.doc.mjs';
16
+ import {docs as SwitchDocs} from '../Switch/Switch.doc.mjs';
17
+ import {docs as TextAreaDocs} from '../TextArea/TextArea.doc.mjs';
18
+ import {docs as TextInputDocs} from '../TextInput/TextInput.doc.mjs';
19
+ import {docs as TimeInputDocs} from '../TimeInput/TimeInput.doc.mjs';
20
+ import {docs as TokenizerDocs} from '../Tokenizer/Tokenizer.doc.mjs';
21
+ import {docs as TypeaheadDocs} from '../Typeahead/Typeahead.doc.mjs';
22
+
23
+ const inputDocsSuite = [
24
+ {name: 'CheckboxInput', docs: CheckboxInputDocs},
25
+ {name: 'CheckboxList', docs: CheckboxListDocs},
26
+ {name: 'DateInput', docs: DateInputDocs},
27
+ {name: 'DateRangeInput', docs: DateRangeInputDocs},
28
+ {name: 'DateTimeInput', docs: DateTimeInputDocs},
29
+ {name: 'FileInput', docs: FileInputDocs},
30
+ {name: 'MultiSelector', docs: MultiSelectorDocs},
31
+ {name: 'NumberInput', docs: NumberInputDocs},
32
+ {name: 'RadioList', docs: RadioListDocs},
33
+ {name: 'Selector', docs: SelectorDocs},
34
+ {name: 'Slider', docs: SliderDocs},
35
+ {name: 'Switch', docs: SwitchDocs},
36
+ {name: 'TextArea', docs: TextAreaDocs},
37
+ {name: 'TextInput', docs: TextInputDocs},
38
+ {name: 'TimeInput', docs: TimeInputDocs},
39
+ {name: 'Tokenizer', docs: TokenizerDocs},
40
+ {name: 'Typeahead', docs: TypeaheadDocs},
41
+ ];
42
+
43
+ describe('Input Family width Prop Contract (#4163)', () => {
44
+ inputDocsSuite.forEach(({name, docs}) => {
45
+ it(`documents the width prop for ${name}`, () => {
46
+ const propsList = (docs.props || docs.components?.[0]?.props) as {
47
+ name: string;
48
+ type: string;
49
+ }[];
50
+ expect(
51
+ propsList,
52
+ `${name}.doc.mjs must expose a props array`,
53
+ ).toBeDefined();
54
+ const widthProp = propsList.find(p => p.name === 'width');
55
+ expect(
56
+ widthProp,
57
+ `${name}.doc.mjs must document the width prop`,
58
+ ).toBeDefined();
59
+ expect(widthProp?.type).toBe('SizeValue');
60
+ });
61
+ });
62
+ });
@@ -0,0 +1,19 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Ambient module declaration for `*.doc.mjs` imports.
5
+ *
6
+ * Component contract tests (packages/core/src/__tests__/*Contract.test.tsx)
7
+ * import `{Name}.doc.mjs` files directly to assert their documented props
8
+ * stay in sync with the component's real API. Without this declaration,
9
+ * TypeScript has no way to type a `.mjs` import and fails with
10
+ * TS7016 ("Could not find a declaration file for module").
11
+ */
12
+
13
+ declare module '*.doc.mjs' {
14
+ import type {ComponentDoc, TranslationDoc} from './docs-types';
15
+
16
+ export const docs: ComponentDoc;
17
+ export const docsZh: ComponentDoc;
18
+ export const docsDense: TranslationDoc;
19
+ }
@@ -76,6 +76,12 @@ export type {
76
76
  export {useInputContainer} from './useInputContainer';
77
77
  export type {UseInputContainerOptions} from './useInputContainer';
78
78
 
79
+ export {useInputStatusIcon} from './useInputStatusIcon';
80
+ export type {
81
+ UseInputStatusIconOptions,
82
+ UseInputStatusIconReturn,
83
+ } from './useInputStatusIcon';
84
+
79
85
  export {useInteractiveRole} from './useInteractiveRole';
80
86
  export type {
81
87
  InteractiveRole,
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file isRtlElement.ts
7
+ * @input DOM element (nullable)
8
+ * @output Exports isRtlElement helper resolving an element's computed text
9
+ * direction
10
+ * @position Internal hook utility; used by useListFocus and useGridFocus to
11
+ * auto-detect RTL for arrow-key navigation (same getComputedStyle precedent
12
+ * as Resizable's ResizeHandle drag deltas).
13
+ */
14
+
15
+ /**
16
+ * Whether `el` renders right-to-left, per its computed `direction`.
17
+ *
18
+ * SSR-safe: returns false when `el` is null or no DOM is available. Callers
19
+ * should invoke this lazily (on keydown, not on render) so getComputedStyle
20
+ * runs only when a horizontal arrow key is actually handled.
21
+ */
22
+ export function isRtlElement(el: HTMLElement | null): boolean {
23
+ if (!el || typeof window === 'undefined') {
24
+ return false;
25
+ }
26
+ return window.getComputedStyle(el).direction === 'rtl';
27
+ }