@astryxdesign/core 0.3.0 → 0.4.0

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 (551) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +10 -2
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +22 -3
  5. package/dist/AppShell/AppShell.d.ts +2 -2
  6. package/dist/AppShell/AppShell.d.ts.map +1 -1
  7. package/dist/AppShell/AppShell.js +40 -9
  8. package/dist/AspectRatio/AspectRatio.d.ts +13 -0
  9. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  10. package/dist/AspectRatio/AspectRatio.js +9 -0
  11. package/dist/Avatar/Avatar.d.ts.map +1 -1
  12. package/dist/Avatar/Avatar.js +33 -20
  13. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroupOverflow.js +2 -3
  15. package/dist/Banner/Banner.d.ts.map +1 -1
  16. package/dist/Banner/Banner.js +15 -14
  17. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/BreadcrumbItem.js +21 -8
  19. package/dist/Breadcrumbs/index.d.ts +2 -1
  20. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  21. package/dist/Breadcrumbs/index.js +1 -0
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +11 -15
  24. package/dist/Calendar/Calendar.d.ts.map +1 -1
  25. package/dist/Calendar/Calendar.js +2 -1
  26. package/dist/Calendar/styles.d.ts +0 -2
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +0 -2
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +8 -12
  31. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  32. package/dist/Chat/ChatDictationButton.js +2 -1
  33. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatLayoutScrollButton.js +3 -1
  35. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  36. package/dist/Chat/ChatSendButton.js +4 -3
  37. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  38. package/dist/Chat/ChatToolCalls.js +17 -17
  39. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  40. package/dist/Chat/useTriggerMenu.js +2 -6
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +36 -125
  43. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  44. package/dist/ClickableCard/ClickableCard.js +3 -11
  45. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  46. package/dist/CodeBlock/CodeBlock.js +84 -67
  47. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  48. package/dist/Collapsible/Collapsible.js +35 -15
  49. package/dist/CommandPalette/CommandPaletteItem.d.ts +1 -1
  50. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  51. package/dist/CommandPalette/CommandPaletteItem.js +6 -3
  52. package/dist/CommandPalette/CommandPaletteList.js +1 -1
  53. package/dist/ComplexSelector/ComplexSelector.d.ts +1 -1
  54. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  55. package/dist/ComplexSelector/ComplexSelector.js +35 -33
  56. package/dist/ContextMenu/ContextMenu.d.ts +2 -2
  57. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  58. package/dist/ContextMenu/ContextMenu.js +1 -1
  59. package/dist/ContextMenu/index.d.ts +2 -1
  60. package/dist/ContextMenu/index.d.ts.map +1 -1
  61. package/dist/ContextMenu/index.js +1 -0
  62. package/dist/DateInput/DateInput.d.ts +9 -2
  63. package/dist/DateInput/DateInput.d.ts.map +1 -1
  64. package/dist/DateInput/DateInput.js +24 -23
  65. package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
  66. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  67. package/dist/DateRangeInput/DateRangeInput.js +11 -16
  68. package/dist/DateTimeInput/DateTimeInput.d.ts +9 -1
  69. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  70. package/dist/DateTimeInput/DateTimeInput.js +22 -17
  71. package/dist/Dialog/Dialog.d.ts.map +1 -1
  72. package/dist/Dialog/Dialog.js +4 -4
  73. package/dist/DropdownMenu/DropdownMenu.d.ts +33 -8
  74. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  75. package/dist/DropdownMenu/DropdownMenu.js +62 -20
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +8 -11
  77. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +32 -31
  79. package/dist/DropdownMenu/DropdownMenuDivider.d.ts +25 -0
  80. package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuDivider.js +66 -0
  82. package/dist/DropdownMenu/DropdownMenuItem.d.ts +15 -2
  83. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuItem.js +20 -8
  85. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +5 -5
  86. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  87. package/dist/DropdownMenu/DropdownMenuRadioItem.js +24 -67
  88. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  89. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -8
  90. package/dist/DropdownMenu/index.d.ts +2 -1
  91. package/dist/DropdownMenu/index.d.ts.map +1 -1
  92. package/dist/DropdownMenu/index.js +4 -0
  93. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  94. package/dist/DropdownMenu/renderDropdownItems.js +35 -27
  95. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  96. package/dist/EmptyState/EmptyState.js +9 -5
  97. package/dist/Field/InputClearButton.d.ts +16 -3
  98. package/dist/Field/InputClearButton.d.ts.map +1 -1
  99. package/dist/Field/InputClearButton.js +15 -4
  100. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  101. package/dist/HoverCard/useHoverCard.js +3 -19
  102. package/dist/Indicator/CheckIndicator.d.ts +25 -0
  103. package/dist/Indicator/CheckIndicator.d.ts.map +1 -0
  104. package/dist/Indicator/CheckIndicator.js +155 -0
  105. package/dist/Indicator/CheckboxIndicator.d.ts +22 -0
  106. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -0
  107. package/dist/Indicator/CheckboxIndicator.js +173 -0
  108. package/dist/Indicator/RadioIndicator.d.ts +23 -0
  109. package/dist/Indicator/RadioIndicator.d.ts.map +1 -0
  110. package/dist/Indicator/RadioIndicator.js +137 -0
  111. package/dist/Indicator/index.d.ts +24 -0
  112. package/dist/Indicator/index.d.ts.map +1 -0
  113. package/dist/Indicator/index.js +26 -0
  114. package/dist/Indicator/indicator.markers.stylex.d.ts +16 -0
  115. package/dist/Indicator/indicator.markers.stylex.d.ts.map +1 -0
  116. package/dist/Indicator/indicator.markers.stylex.js +21 -0
  117. package/dist/Indicator/indicatorRegistry.d.ts +71 -0
  118. package/dist/Indicator/indicatorRegistry.d.ts.map +1 -0
  119. package/dist/Indicator/indicatorRegistry.js +93 -0
  120. package/dist/Indicator/types.d.ts +167 -0
  121. package/dist/Indicator/types.d.ts.map +1 -0
  122. package/dist/Indicator/types.js +1 -0
  123. package/dist/Indicator/useIndicator.d.ts +18 -0
  124. package/dist/Indicator/useIndicator.d.ts.map +1 -0
  125. package/dist/Indicator/useIndicator.js +30 -0
  126. package/dist/Item/Item.d.ts.map +1 -1
  127. package/dist/Item/Item.js +4 -8
  128. package/dist/Layer/useLayer.d.ts +14 -0
  129. package/dist/Layer/useLayer.d.ts.map +1 -1
  130. package/dist/Layer/useLayer.js +25 -2
  131. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  132. package/dist/Lightbox/Lightbox.js +56 -53
  133. package/dist/Link/Link.d.ts.map +1 -1
  134. package/dist/Link/Link.js +4 -5
  135. package/dist/MobileNav/MobileNav.js +8 -8
  136. package/dist/MoreMenu/MoreMenu.d.ts +14 -1
  137. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  138. package/dist/MoreMenu/MoreMenu.js +4 -0
  139. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  140. package/dist/MultiSelector/MultiSelector.js +78 -75
  141. package/dist/NumberInput/NumberInput.d.ts +27 -1
  142. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  143. package/dist/NumberInput/NumberInput.js +230 -42
  144. package/dist/Outline/Outline.d.ts.map +1 -1
  145. package/dist/Outline/Outline.js +2 -3
  146. package/dist/Pagination/Pagination.d.ts.map +1 -1
  147. package/dist/Pagination/Pagination.js +61 -63
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +6 -8
  150. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  151. package/dist/PowerSearch/PowerSearch.js +1 -1
  152. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  153. package/dist/PowerSearch/PowerSearchEditPopover.js +7 -0
  154. package/dist/ProgressBar/ProgressBar.d.ts +11 -4
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +96 -6
  157. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  158. package/dist/RadioList/RadioListItem.js +29 -84
  159. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  160. package/dist/Resizable/ResizeHandle.js +2 -3
  161. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  162. package/dist/SegmentedControl/SegmentedControlItem.js +2 -3
  163. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  164. package/dist/SelectableCard/SelectableCard.js +3 -11
  165. package/dist/Selector/Selector.d.ts +2 -1
  166. package/dist/Selector/Selector.d.ts.map +1 -1
  167. package/dist/Selector/Selector.js +164 -94
  168. package/dist/Selector/hooks.d.ts +18 -6
  169. package/dist/Selector/hooks.d.ts.map +1 -1
  170. package/dist/Selector/hooks.js +57 -40
  171. package/dist/SideNav/SideNav.d.ts.map +1 -1
  172. package/dist/SideNav/SideNav.js +1 -1
  173. package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
  174. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  175. package/dist/SideNav/SideNavCollapseButton.js +29 -14
  176. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  177. package/dist/SideNav/SideNavHeading.js +56 -20
  178. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  179. package/dist/SideNav/SideNavItem.js +28 -22
  180. package/dist/Stack/stack.stylex.d.ts +3 -3
  181. package/dist/Switch/Switch.js +11 -10
  182. package/dist/TabList/Tab.d.ts.map +1 -1
  183. package/dist/TabList/Tab.js +2 -3
  184. package/dist/TabList/TabMenu.d.ts.map +1 -1
  185. package/dist/TabList/TabMenu.js +41 -19
  186. package/dist/Table/BaseTable.d.ts.map +1 -1
  187. package/dist/Table/BaseTable.js +8 -0
  188. package/dist/Table/index.d.ts +1 -1
  189. package/dist/Table/index.d.ts.map +1 -1
  190. package/dist/Table/index.js +1 -1
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  193. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +22 -18
  194. package/dist/Table/plugins/rowExpansion/index.d.ts +1 -1
  195. package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -1
  196. package/dist/Table/plugins/rowExpansion/index.js +1 -1
  197. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +41 -76
  198. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  199. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +94 -345
  200. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  201. package/dist/Table/plugins/sortable/useTableSortable.js +23 -4
  202. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  203. package/dist/Table/plugins/tree/useTableTreeData.js +29 -33
  204. package/dist/Table/tableContextMenu.d.ts.map +1 -1
  205. package/dist/Table/tableContextMenu.js +2 -1
  206. package/dist/Table/types.d.ts +11 -0
  207. package/dist/Table/types.d.ts.map +1 -1
  208. package/dist/TextArea/TextArea.d.ts +10 -1
  209. package/dist/TextArea/TextArea.d.ts.map +1 -1
  210. package/dist/TextArea/TextArea.js +16 -6
  211. package/dist/TextInput/TextInput.d.ts +10 -1
  212. package/dist/TextInput/TextInput.d.ts.map +1 -1
  213. package/dist/TextInput/TextInput.js +13 -19
  214. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  215. package/dist/Thumbnail/Thumbnail.js +32 -26
  216. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  217. package/dist/TimeInput/TimeInput.js +7 -15
  218. package/dist/Timestamp/TimestampHoverCard.d.ts.map +1 -1
  219. package/dist/Timestamp/TimestampHoverCard.js +16 -34
  220. package/dist/Token/Token.d.ts.map +1 -1
  221. package/dist/Token/Token.js +20 -11
  222. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  223. package/dist/Tokenizer/Tokenizer.js +4 -2
  224. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  225. package/dist/Tooltip/useTooltip.js +3 -19
  226. package/dist/TopNav/TopNav.d.ts.map +1 -1
  227. package/dist/TopNav/TopNav.js +6 -5
  228. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  229. package/dist/TopNav/TopNavHeading.js +54 -16
  230. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  231. package/dist/TopNav/TopNavItem.js +3 -9
  232. package/dist/TopNav/TopNavMegaMenu.d.ts +7 -0
  233. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  234. package/dist/TopNav/TopNavMegaMenu.js +146 -60
  235. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  236. package/dist/TopNav/TopNavMegaMenuItem.js +22 -3
  237. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  238. package/dist/TopNav/TopNavMenu.js +83 -38
  239. package/dist/TreeList/TreeList.d.ts.map +1 -1
  240. package/dist/TreeList/TreeList.js +13 -0
  241. package/dist/TreeList/TreeListBranches.d.ts +1 -1
  242. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  243. package/dist/TreeList/TreeListBranches.js +14 -4
  244. package/dist/TreeList/TreeListItem.d.ts +11 -1
  245. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  246. package/dist/TreeList/TreeListItem.js +57 -30
  247. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  248. package/dist/Typeahead/BaseTypeahead.js +16 -8
  249. package/dist/astryx.css +74 -85
  250. package/dist/astryx.umd.js +56 -56
  251. package/dist/astryx.umd.js.map +4 -4
  252. package/dist/hooks/containerReveal.stylex.d.ts +61 -0
  253. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -0
  254. package/dist/hooks/containerReveal.stylex.js +63 -0
  255. package/dist/hooks/index.d.ts +3 -0
  256. package/dist/hooks/index.d.ts.map +1 -1
  257. package/dist/hooks/index.js +2 -0
  258. package/dist/hooks/useClipboard.d.ts +59 -0
  259. package/dist/hooks/useClipboard.d.ts.map +1 -0
  260. package/dist/hooks/useClipboard.js +106 -0
  261. package/dist/hooks/useContainerReveal.d.ts +14 -8
  262. package/dist/hooks/useContainerReveal.d.ts.map +1 -1
  263. package/dist/hooks/useContainerReveal.js +28 -69
  264. package/dist/hooks/useIndicatorFocusRing.d.ts +28 -0
  265. package/dist/hooks/useIndicatorFocusRing.d.ts.map +1 -0
  266. package/dist/hooks/useIndicatorFocusRing.js +96 -0
  267. package/dist/hooks/useLongPress.d.ts.map +1 -1
  268. package/dist/hooks/useLongPress.js +12 -2
  269. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  270. package/dist/hooks/useScrollLock.js +33 -18
  271. package/dist/hooks/useStreamingText.d.ts +18 -3
  272. package/dist/hooks/useStreamingText.d.ts.map +1 -1
  273. package/dist/hooks/useStreamingText.js +47 -7
  274. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  275. package/dist/hooks/useTreeFocus.js +9 -3
  276. package/dist/hooks/useTypeahead.d.ts +3 -2
  277. package/dist/hooks/useTypeahead.d.ts.map +1 -1
  278. package/dist/hooks/useTypeahead.js +19 -7
  279. package/dist/index.d.ts +1 -0
  280. package/dist/index.d.ts.map +1 -1
  281. package/dist/index.js +1 -0
  282. package/dist/theme/defineTheme.d.ts +16 -0
  283. package/dist/theme/defineTheme.d.ts.map +1 -1
  284. package/dist/theme/defineTheme.js +8 -0
  285. package/dist/theme/derivedVarRegistry.d.ts +8 -0
  286. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  287. package/dist/theme/derivedVarRegistry.js +14 -13
  288. package/dist/theme/expandColorScale.d.ts +3 -1
  289. package/dist/theme/expandColorScale.d.ts.map +1 -1
  290. package/dist/theme/expandColorScale.js +17 -7
  291. package/dist/theme/expandTypeScale.d.ts.map +1 -1
  292. package/dist/theme/expandTypeScale.js +17 -0
  293. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  294. package/dist/theme/generateThemeRules.js +11 -0
  295. package/dist/theme/index.d.ts +1 -0
  296. package/dist/theme/index.d.ts.map +1 -1
  297. package/dist/utils/focusOutline.stylex.d.ts +107 -0
  298. package/dist/utils/focusOutline.stylex.d.ts.map +1 -0
  299. package/dist/utils/focusOutline.stylex.js +130 -0
  300. package/dist/utils/focusReturn.d.ts +24 -0
  301. package/dist/utils/focusReturn.d.ts.map +1 -0
  302. package/dist/utils/focusReturn.js +29 -0
  303. package/dist/utils/index.d.ts +1 -0
  304. package/dist/utils/index.d.ts.map +1 -1
  305. package/dist/utils/index.js +1 -0
  306. package/dist/utils/mergeRefs.d.ts.map +1 -1
  307. package/dist/utils/mergeRefs.js +7 -5
  308. package/dist/utils/rtlStyles.d.ts +2 -2
  309. package/dist/utils/rtlStyles.js +2 -2
  310. package/dist/utils/themeProps.d.ts +20 -1
  311. package/dist/utils/themeProps.d.ts.map +1 -1
  312. package/dist/utils/themeProps.js +8 -2
  313. package/locales/en.json +12 -0
  314. package/locales/pseudo.json +9 -0
  315. package/package.json +7 -2
  316. package/src/AlertDialog/AlertDialog.doc.mjs +16 -3
  317. package/src/AlertDialog/AlertDialog.test.tsx +208 -1
  318. package/src/AlertDialog/AlertDialog.tsx +21 -3
  319. package/src/AppShell/AppShell.doc.mjs +11 -0
  320. package/src/AppShell/AppShell.test.tsx +139 -0
  321. package/src/AppShell/AppShell.tsx +32 -22
  322. package/src/AspectRatio/AspectRatio.doc.mjs +27 -3
  323. package/src/AspectRatio/AspectRatio.test.tsx +30 -0
  324. package/src/AspectRatio/AspectRatio.tsx +13 -0
  325. package/src/Avatar/Avatar.doc.mjs +2 -13
  326. package/src/Avatar/Avatar.test.tsx +33 -8
  327. package/src/Avatar/Avatar.tsx +52 -50
  328. package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -9
  329. package/src/Banner/Banner.tsx +9 -6
  330. package/src/Breadcrumbs/BreadcrumbItem.tsx +16 -6
  331. package/src/Breadcrumbs/index.ts +5 -1
  332. package/src/Button/Button.tsx +21 -41
  333. package/src/Calendar/Calendar.tsx +2 -1
  334. package/src/Calendar/styles.ts +0 -8
  335. package/src/Carousel/Carousel.doc.mjs +3 -0
  336. package/src/Carousel/Carousel.tsx +10 -6
  337. package/src/Chat/Chat.doc.mjs +2 -0
  338. package/src/Chat/ChatDictationButton.tsx +7 -1
  339. package/src/Chat/ChatLayoutScrollButton.test.tsx +22 -0
  340. package/src/Chat/ChatLayoutScrollButton.tsx +8 -1
  341. package/src/Chat/ChatSendButton.tsx +2 -3
  342. package/src/Chat/ChatToolCalls.test.tsx +35 -0
  343. package/src/Chat/ChatToolCalls.tsx +36 -16
  344. package/src/Chat/useTriggerMenu.tsx +2 -5
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +4 -2
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +115 -0
  347. package/src/CheckboxInput/CheckboxInput.tsx +45 -202
  348. package/src/ClickableCard/ClickableCard.tsx +6 -11
  349. package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
  350. package/src/CodeBlock/CodeBlock.test.tsx +91 -0
  351. package/src/CodeBlock/CodeBlock.tsx +57 -80
  352. package/src/Collapsible/Collapsible.tsx +22 -18
  353. package/src/CommandPalette/CommandPalette.test.tsx +29 -0
  354. package/src/CommandPalette/CommandPaletteItem.tsx +7 -4
  355. package/src/CommandPalette/CommandPaletteList.tsx +2 -2
  356. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  357. package/src/ComplexSelector/ComplexSelector.test.tsx +64 -0
  358. package/src/ComplexSelector/ComplexSelector.tsx +46 -29
  359. package/src/ContextMenu/ContextMenu.doc.mjs +1 -1
  360. package/src/ContextMenu/ContextMenu.test.tsx +41 -0
  361. package/src/ContextMenu/ContextMenu.tsx +3 -3
  362. package/src/ContextMenu/index.ts +5 -1
  363. package/src/DateInput/DateInput.doc.mjs +18 -3
  364. package/src/DateInput/DateInput.test.tsx +93 -11
  365. package/src/DateInput/DateInput.tsx +41 -19
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +10 -2
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +110 -14
  368. package/src/DateRangeInput/DateRangeInput.tsx +24 -12
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -2
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +75 -0
  371. package/src/DateTimeInput/DateTimeInput.tsx +35 -8
  372. package/src/Dialog/Dialog.tsx +2 -9
  373. package/src/DropdownMenu/DropdownMenu.doc.mjs +5 -9
  374. package/src/DropdownMenu/DropdownMenu.test.tsx +534 -4
  375. package/src/DropdownMenu/DropdownMenu.tsx +113 -52
  376. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +27 -30
  377. package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +34 -0
  378. package/src/DropdownMenu/DropdownMenuDivider.tsx +81 -0
  379. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +39 -4
  380. package/src/DropdownMenu/DropdownMenuItem.tsx +48 -10
  381. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +30 -76
  382. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +25 -19
  383. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +44 -14
  384. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +1 -5
  385. package/src/DropdownMenu/index.ts +8 -1
  386. package/src/DropdownMenu/renderDropdownItems.tsx +33 -34
  387. package/src/EmptyState/EmptyState.doc.mjs +4 -0
  388. package/src/EmptyState/EmptyState.test.tsx +62 -0
  389. package/src/EmptyState/EmptyState.tsx +16 -6
  390. package/src/Field/Field.doc.mjs +1 -0
  391. package/src/Field/InputClearButton.test.tsx +117 -0
  392. package/src/Field/InputClearButton.tsx +30 -3
  393. package/src/HoverCard/useHoverCard.tsx +2 -23
  394. package/src/Icon/Icon.doc.mjs +2 -1
  395. package/src/Indicator/CheckIndicator.tsx +174 -0
  396. package/src/Indicator/CheckboxIndicator.tsx +257 -0
  397. package/src/Indicator/Indicator.doc.mjs +344 -0
  398. package/src/Indicator/Indicator.test.tsx +393 -0
  399. package/src/Indicator/RadioIndicator.tsx +206 -0
  400. package/src/Indicator/index.ts +45 -0
  401. package/src/Indicator/indicator.markers.stylex.ts +19 -0
  402. package/src/Indicator/indicatorRegistry.test.tsx +106 -0
  403. package/src/Indicator/indicatorRegistry.ts +120 -0
  404. package/src/Indicator/types.ts +186 -0
  405. package/src/Indicator/useIndicator.ts +39 -0
  406. package/src/Item/Item.doc.mjs +1 -1
  407. package/src/Item/Item.tsx +7 -14
  408. package/src/Layer/useLayer.doc.mjs +2 -2
  409. package/src/Layer/useLayer.test.tsx +85 -0
  410. package/src/Layer/useLayer.tsx +39 -1
  411. package/src/Lightbox/Lightbox.doc.mjs +1 -1
  412. package/src/Lightbox/Lightbox.tsx +43 -38
  413. package/src/Link/Link.tsx +4 -11
  414. package/src/MobileNav/MobileNav.tsx +3 -3
  415. package/src/MoreMenu/MoreMenu.doc.mjs +31 -0
  416. package/src/MoreMenu/MoreMenu.test.tsx +68 -1
  417. package/src/MoreMenu/MoreMenu.tsx +19 -0
  418. package/src/MultiSelector/MultiSelector.doc.mjs +9 -1
  419. package/src/MultiSelector/MultiSelector.test.tsx +255 -16
  420. package/src/MultiSelector/MultiSelector.tsx +103 -99
  421. package/src/NumberInput/NumberInput.doc.mjs +265 -50
  422. package/src/NumberInput/NumberInput.test.tsx +726 -21
  423. package/src/NumberInput/NumberInput.tsx +353 -59
  424. package/src/Outline/Outline.tsx +2 -5
  425. package/src/Pagination/Pagination.doc.mjs +1 -1
  426. package/src/Pagination/Pagination.test.tsx +22 -7
  427. package/src/Pagination/Pagination.tsx +38 -35
  428. package/src/Popover/Popover.tsx +6 -7
  429. package/src/PowerSearch/PowerSearch.tsx +1 -1
  430. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +42 -0
  431. package/src/PowerSearch/PowerSearchEditPopover.tsx +7 -0
  432. package/src/ProgressBar/ProgressBar.doc.mjs +27 -5
  433. package/src/ProgressBar/ProgressBar.test.tsx +224 -1
  434. package/src/ProgressBar/ProgressBar.tsx +124 -22
  435. package/src/RadioList/RadioList.doc.mjs +4 -2
  436. package/src/RadioList/RadioListItem.tsx +31 -130
  437. package/src/Resizable/ResizeHandle.tsx +2 -9
  438. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -9
  439. package/src/SelectableCard/SelectableCard.tsx +4 -9
  440. package/src/Selector/Selector.doc.mjs +7 -4
  441. package/src/Selector/Selector.test.tsx +935 -25
  442. package/src/Selector/Selector.tsx +189 -106
  443. package/src/Selector/hooks.ts +77 -50
  444. package/src/SideNav/SideNav.test.tsx +46 -0
  445. package/src/SideNav/SideNav.tsx +1 -0
  446. package/src/SideNav/SideNavCollapseButton.tsx +15 -10
  447. package/src/SideNav/SideNavHeading.tsx +48 -10
  448. package/src/SideNav/SideNavItem.tsx +20 -12
  449. package/src/Stack/Stack.doc.mjs +110 -0
  450. package/src/StatusDot/StatusDot.doc.mjs +12 -3
  451. package/src/Switch/Switch.test.tsx +32 -0
  452. package/src/Switch/Switch.tsx +8 -8
  453. package/src/TabList/Tab.tsx +2 -9
  454. package/src/TabList/TabMenu.tsx +14 -21
  455. package/src/Table/BaseTable.tsx +11 -0
  456. package/src/Table/index.ts +1 -4
  457. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +30 -14
  458. package/src/Table/plugins/rowExpansion/index.ts +1 -4
  459. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +125 -175
  460. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +115 -458
  461. package/src/Table/plugins/sortable/useTableSortable.tsx +2 -6
  462. package/src/Table/plugins/tree/useTableTreeData.tsx +43 -24
  463. package/src/Table/tableContextMenu.test.tsx +27 -0
  464. package/src/Table/tableContextMenu.tsx +1 -0
  465. package/src/Table/types.ts +11 -0
  466. package/src/Table/useTableGroupedRows.doc.mjs +1 -1
  467. package/src/Table/useTableRowExpansion.doc.mjs +43 -66
  468. package/src/TextArea/TextArea.doc.mjs +42 -2
  469. package/src/TextArea/TextArea.test.tsx +219 -0
  470. package/src/TextArea/TextArea.tsx +34 -7
  471. package/src/TextInput/TextInput.doc.mjs +18 -2
  472. package/src/TextInput/TextInput.test.tsx +189 -0
  473. package/src/TextInput/TextInput.tsx +25 -31
  474. package/src/Thumbnail/Thumbnail.tsx +2 -9
  475. package/src/TimeInput/TimeInput.doc.mjs +2 -2
  476. package/src/TimeInput/TimeInput.test.tsx +30 -3
  477. package/src/TimeInput/TimeInput.tsx +9 -26
  478. package/src/Timestamp/Timestamp.doc.mjs +4 -3
  479. package/src/Timestamp/TimestampHoverCard.tsx +13 -35
  480. package/src/Token/Token.tsx +5 -28
  481. package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
  482. package/src/Tokenizer/Tokenizer.test.tsx +45 -3
  483. package/src/Tokenizer/Tokenizer.tsx +10 -2
  484. package/src/Tooltip/useTooltip.tsx +2 -23
  485. package/src/TopNav/TopNav.tsx +1 -3
  486. package/src/TopNav/TopNavHeading.tsx +42 -13
  487. package/src/TopNav/TopNavItem.tsx +3 -21
  488. package/src/TopNav/TopNavMegaMenu.test.tsx +433 -33
  489. package/src/TopNav/TopNavMegaMenu.tsx +118 -53
  490. package/src/TopNav/TopNavMegaMenuItem.tsx +2 -9
  491. package/src/TopNav/TopNavMenu.tsx +34 -37
  492. package/src/TreeList/TreeList.doc.mjs +5 -0
  493. package/src/TreeList/TreeList.test.tsx +206 -7
  494. package/src/TreeList/TreeList.tsx +22 -0
  495. package/src/TreeList/TreeListBranches.tsx +25 -2
  496. package/src/TreeList/TreeListItem.tsx +89 -52
  497. package/src/Typeahead/BaseTypeahead.tsx +19 -6
  498. package/src/Typeahead/Typeahead.doc.mjs +1 -0
  499. package/src/Typeahead/Typeahead.test.tsx +76 -3
  500. package/src/hooks/containerReveal.stylex.ts +102 -0
  501. package/src/hooks/index.ts +4 -0
  502. package/src/hooks/useClipboard.doc.mjs +87 -0
  503. package/src/hooks/useClipboard.test.tsx +151 -0
  504. package/src/hooks/useClipboard.ts +135 -0
  505. package/src/hooks/useContainerReveal.doc.mjs +7 -7
  506. package/src/hooks/useContainerReveal.test.tsx +42 -62
  507. package/src/hooks/useContainerReveal.ts +36 -91
  508. package/src/hooks/useImageMode.test.ts +1 -1
  509. package/src/hooks/useIndicatorFocusRing.tsx +120 -0
  510. package/src/hooks/useLongPress.test.tsx +18 -0
  511. package/src/hooks/useLongPress.ts +12 -2
  512. package/src/hooks/useScrollLock.test.ts +72 -0
  513. package/src/hooks/useScrollLock.ts +51 -19
  514. package/src/hooks/useStreamingText.test.ts +46 -1
  515. package/src/hooks/useStreamingText.ts +49 -7
  516. package/src/hooks/useTreeFocus.test.tsx +50 -0
  517. package/src/hooks/useTreeFocus.ts +14 -3
  518. package/src/hooks/useTypeahead.test.tsx +150 -0
  519. package/src/hooks/useTypeahead.ts +24 -9
  520. package/src/i18n/InternationalizationProvider.doc.mjs +7 -2
  521. package/src/index.ts +1 -0
  522. package/src/theme/defineTheme.ts +24 -0
  523. package/src/theme/derivedVarRegistry.test.ts +21 -1
  524. package/src/theme/derivedVarRegistry.ts +19 -6
  525. package/src/theme/expandColorScale.test.ts +14 -0
  526. package/src/theme/expandColorScale.ts +24 -9
  527. package/src/theme/expandTypeScale.test.ts +43 -0
  528. package/src/theme/expandTypeScale.ts +17 -0
  529. package/src/theme/generateThemeRules.test.ts +61 -20
  530. package/src/theme/generateThemeRules.ts +11 -0
  531. package/src/theme/index.ts +13 -0
  532. package/src/utils/focusOutline.stylex.ts +163 -0
  533. package/src/utils/focusReturn.test.ts +42 -0
  534. package/src/utils/focusReturn.ts +31 -0
  535. package/src/utils/index.ts +1 -0
  536. package/src/utils/mergeRefs.test.ts +48 -0
  537. package/src/utils/mergeRefs.ts +12 -6
  538. package/src/utils/rtlStyles.ts +2 -2
  539. package/src/utils/themeProps.ts +26 -4
  540. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts +0 -9
  541. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts.map +0 -1
  542. package/dist/CheckboxInput/checkbox.markers.stylex.js +0 -14
  543. package/dist/RadioList/radio.markers.stylex.d.ts +0 -7
  544. package/dist/RadioList/radio.markers.stylex.d.ts.map +0 -1
  545. package/dist/RadioList/radio.markers.stylex.js +0 -12
  546. package/dist/hooks/containerReveal.pool.stylex.d.ts +0 -303
  547. package/dist/hooks/containerReveal.pool.stylex.d.ts.map +0 -1
  548. package/dist/hooks/containerReveal.pool.stylex.js +0 -292
  549. package/src/CheckboxInput/checkbox.markers.stylex.ts +0 -12
  550. package/src/RadioList/radio.markers.stylex.ts +0 -10
  551. package/src/hooks/containerReveal.pool.stylex.ts +0 -571
@@ -673,6 +673,132 @@ describe('TextArea', () => {
673
673
  });
674
674
  });
675
675
 
676
+ describe('form participation', () => {
677
+ it('submits the value under htmlName', () => {
678
+ const {container} = render(
679
+ <form>
680
+ <TextArea
681
+ label="Notes"
682
+ htmlName="notes"
683
+ value="hello"
684
+ onChange={() => {}}
685
+ />
686
+ </form>,
687
+ );
688
+ const data = new FormData(container.querySelector('form')!);
689
+ expect(data.get('notes')).toBe('hello');
690
+ });
691
+
692
+ it('is excluded from form data when disabled', () => {
693
+ const {container} = render(
694
+ <form>
695
+ <TextArea
696
+ label="Notes"
697
+ htmlName="notes"
698
+ value="hello"
699
+ onChange={() => {}}
700
+ isDisabled
701
+ />
702
+ </form>,
703
+ );
704
+ expect([
705
+ ...new FormData(container.querySelector('form')!).keys(),
706
+ ]).toEqual([]);
707
+ });
708
+
709
+ // Regression: a disabledMessage swaps the native `disabled` attribute for
710
+ // aria-disabled + readOnly so the reason stays focus-discoverable, but
711
+ // read-only fields still submit — the name has to be withheld too.
712
+ it('is excluded from form data when disabled, even with a disabledMessage', () => {
713
+ const {container} = render(
714
+ <form>
715
+ <TextArea
716
+ label="Notes"
717
+ htmlName="notes"
718
+ value="hello"
719
+ onChange={() => {}}
720
+ isDisabled
721
+ disabledMessage="Notes are locked while the review is open"
722
+ />
723
+ </form>,
724
+ );
725
+ expect([
726
+ ...new FormData(container.querySelector('form')!).keys(),
727
+ ]).toEqual([]);
728
+ });
729
+ });
730
+
731
+ describe('isReadOnly', () => {
732
+ it('marks the textarea read-only', () => {
733
+ render(
734
+ <TextArea label="Notes" value="hello" onChange={() => {}} isReadOnly />,
735
+ );
736
+ expect(screen.getByRole('textbox')).toHaveAttribute('readonly');
737
+ });
738
+
739
+ it('still submits its value with the form', () => {
740
+ const {container} = render(
741
+ <form>
742
+ <TextArea
743
+ label="Notes"
744
+ htmlName="notes"
745
+ value="hello"
746
+ onChange={() => {}}
747
+ isReadOnly
748
+ />
749
+ </form>,
750
+ );
751
+ expect(new FormData(container.querySelector('form')!).get('notes')).toBe(
752
+ 'hello',
753
+ );
754
+ });
755
+
756
+ it('does not call onChange when the user types', async () => {
757
+ const user = userEvent.setup();
758
+ const handleChange = vi.fn();
759
+ render(
760
+ <TextArea
761
+ label="Notes"
762
+ value="hello"
763
+ onChange={handleChange}
764
+ isReadOnly
765
+ />,
766
+ );
767
+ await user.type(screen.getByRole('textbox'), 'xyz');
768
+ expect(handleChange).not.toHaveBeenCalled();
769
+ });
770
+
771
+ it('stays focusable and is not disabled', async () => {
772
+ const user = userEvent.setup();
773
+ render(
774
+ <TextArea label="Notes" value="hello" onChange={() => {}} isReadOnly />,
775
+ );
776
+ const textarea = screen.getByRole('textbox');
777
+ expect(textarea).not.toBeDisabled();
778
+ await user.tab();
779
+ expect(textarea).toHaveFocus();
780
+ });
781
+
782
+ it('lets isDisabled win when both are set', () => {
783
+ const {container} = render(
784
+ <form>
785
+ <TextArea
786
+ label="Notes"
787
+ htmlName="notes"
788
+ value="hello"
789
+ onChange={() => {}}
790
+ isReadOnly
791
+ isDisabled
792
+ />
793
+ </form>,
794
+ );
795
+ expect(screen.getByRole('textbox')).toBeDisabled();
796
+ expect([
797
+ ...new FormData(container.querySelector('form')!).keys(),
798
+ ]).toEqual([]);
799
+ });
800
+ });
801
+
676
802
  describe('click-to-focus', () => {
677
803
  it('focuses textarea when clicking the start icon', () => {
678
804
  render(
@@ -863,4 +989,97 @@ describe('TextArea statusVariant forwarding', () => {
863
989
  'detached',
864
990
  );
865
991
  });
992
+
993
+ it('reserves trailing space for the on-field icon with the default (attached) status', () => {
994
+ // Attached renders the on-field status icon, so the textarea must inset its
995
+ // trailing edge to clear it.
996
+ render(
997
+ <TextArea
998
+ label="Bio"
999
+ value=""
1000
+ onChange={() => {}}
1001
+ status={{type: 'error', message: 'Required'}}
1002
+ />,
1003
+ );
1004
+ // The on-field status glyph renders (in the end slot).
1005
+ expect(document.querySelector('.astryx-input-status-icon')).not.toBeNull();
1006
+ });
1007
+
1008
+ it('does not render an on-field icon for statusVariant="detached", and does not reserve trailing space for it', () => {
1009
+ // The detached variant suppresses the on-field icon (its glyph lives in the
1010
+ // message box below), so the textarea must NOT inset its trailing edge —
1011
+ // otherwise the text is pushed in for an icon that never appears.
1012
+ const attached = render(
1013
+ <TextArea
1014
+ label="Bio"
1015
+ value=""
1016
+ onChange={() => {}}
1017
+ status={{type: 'error', message: 'Required'}}
1018
+ statusVariant="attached"
1019
+ />,
1020
+ );
1021
+ const attachedTextarea = attached.getByRole('textbox');
1022
+ const attachedClasses = new Set(attachedTextarea.className.split(/\s+/));
1023
+ attached.unmount();
1024
+
1025
+ const detached = render(
1026
+ <TextArea
1027
+ label="Bio"
1028
+ value=""
1029
+ onChange={() => {}}
1030
+ status={{type: 'error', message: 'Required'}}
1031
+ statusVariant="detached"
1032
+ />,
1033
+ );
1034
+ // No on-field icon is rendered for detached.
1035
+ expect(document.querySelector('.astryx-input-status-icon')).toBeNull();
1036
+
1037
+ const detachedTextarea = detached.getByRole('textbox');
1038
+ const detachedClasses = new Set(detachedTextarea.className.split(/\s+/));
1039
+
1040
+ // The attached textarea carries exactly one extra StyleX class over the
1041
+ // detached one: the trailing-reserve style. Detached must not carry it, so
1042
+ // its class set is a strict subset of attached's.
1043
+ for (const cls of detachedClasses) {
1044
+ expect(attachedClasses.has(cls)).toBe(true);
1045
+ }
1046
+ expect(detachedClasses.size).toBeLessThan(attachedClasses.size);
1047
+ });
1048
+ });
1049
+
1050
+ describe('TextArea disabled theme state', () => {
1051
+ it('reflects disabled on the root target so themes can gate paint on it', () => {
1052
+ const {container} = render(
1053
+ <TextArea label="Description" value="" onChange={() => {}} isDisabled />,
1054
+ );
1055
+ const root = container.querySelector('.astryx-textarea');
1056
+ expect(root).toHaveAttribute('data-disabled', 'disabled');
1057
+ expect(root).toHaveClass('disabled');
1058
+ });
1059
+
1060
+ it('omits data-disabled when enabled, like status does', () => {
1061
+ const {container} = render(
1062
+ <TextArea label="Description" value="" onChange={() => {}} />,
1063
+ );
1064
+ const root = container.querySelector('.astryx-textarea');
1065
+ expect(root).not.toHaveAttribute('data-disabled');
1066
+ });
1067
+ });
1068
+
1069
+ describe('TextArea readonly theme state', () => {
1070
+ it('reflects readonly on the root target so themes can gate paint on it', () => {
1071
+ const {container} = render(
1072
+ <TextArea label="Notes" value="" onChange={() => {}} isReadOnly />,
1073
+ );
1074
+ const root = container.querySelector('.astryx-textarea');
1075
+ expect(root).toHaveAttribute('data-readonly', 'readonly');
1076
+ });
1077
+
1078
+ it('omits data-readonly when editable', () => {
1079
+ const {container} = render(
1080
+ <TextArea label="Notes" value="" onChange={() => {}} />,
1081
+ );
1082
+ const root = container.querySelector('.astryx-textarea');
1083
+ expect(root).not.toHaveAttribute('data-readonly');
1084
+ });
866
1085
  });
@@ -66,7 +66,14 @@ const styles = stylex.create({
66
66
  wrapper: {
67
67
  zIndex: 1,
68
68
  display: 'block',
69
- padding: 0,
69
+ // Zero the shared wrapper inset with matching longhands, not the `padding`
70
+ // shorthand: StyleX ranks longhands above shorthands regardless of merge
71
+ // order, so a `padding: 0` shorthand loses to the base's
72
+ // paddingBlock/paddingInline. The wrapper would keep a duplicate inset that
73
+ // the <textarea>'s own padding then doubles — insetting the text and
74
+ // pushing the native resize grip in from the corner.
75
+ paddingBlock: 0,
76
+ paddingInline: 0,
70
77
  // Internal inline padding for the textarea's text. Defined on the wrapper
71
78
  // so the counter (a sibling overlay) inherits the same value and stays
72
79
  // aligned with the text edge. Kept as an internal var so it can later be
@@ -236,6 +243,15 @@ export interface TextAreaProps extends Omit<
236
243
  * @default false
237
244
  */
238
245
  isDisabled?: boolean;
246
+ /**
247
+ * Whether the textarea is read-only.
248
+ * The value is shown at full opacity and still submits with the form, but
249
+ * cannot be edited. Unlike `isDisabled`, a read-only textarea is not dimmed
250
+ * and stays in the tab order — use it for a value the user should see and
251
+ * send but not change. `isDisabled` takes precedence when both are set.
252
+ * @default false
253
+ */
254
+ isReadOnly?: boolean;
239
255
  /**
240
256
  * Explains why the textarea is disabled. When set together with
241
257
  * `isDisabled`, the textarea shows a tooltip with this text on hover and
@@ -350,6 +366,7 @@ export function TextArea({
350
366
  placeholder,
351
367
  rows = 3,
352
368
  isDisabled = false,
369
+ isReadOnly = false,
353
370
  disabledMessage,
354
371
  status,
355
372
  statusVariant = 'attached',
@@ -461,7 +478,7 @@ export function TextArea({
461
478
  // Value can't change while showing a disabled message (the field is
462
479
  // read-only and non-native-disabled), but guard the handler too so the
463
480
  // optimistic value and callbacks never fire.
464
- if (isDisabled) {
481
+ if (isDisabled || isReadOnly) {
465
482
  return;
466
483
  }
467
484
  const newValue = e.target.value;
@@ -518,7 +535,12 @@ export function TextArea({
518
535
  onClick={handleWrapperClick}
519
536
  onMouseUp={handleWrapperMouseUp}
520
537
  {...mergeProps(
521
- themeProps('textarea', {size, status: status?.type ?? null}),
538
+ themeProps('textarea', {
539
+ size,
540
+ status: status?.type ?? null,
541
+ disabled: isDisabled ? 'disabled' : null,
542
+ readonly: isReadOnly ? 'readonly' : null,
543
+ }),
522
544
  stylex.props(
523
545
  inputWrapperStyles.base,
524
546
  styles.wrapper,
@@ -540,7 +562,7 @@ export function TextArea({
540
562
  {...rest}
541
563
  ref={mergeRefs(ref, textareaRef)}
542
564
  id={id}
543
- name={htmlName}
565
+ name={isDisabled ? undefined : htmlName}
544
566
  value={optimisticValue}
545
567
  onChange={handleChange}
546
568
  onPaste={onPaste}
@@ -553,7 +575,7 @@ export function TextArea({
553
575
  // handleChange guard keep the value from changing.
554
576
  disabled={isDisabled && !showsDisabledMessage}
555
577
  aria-disabled={showsDisabledMessage ? 'true' : undefined}
556
- readOnly={showsDisabledMessage || undefined}
578
+ readOnly={isReadOnly || showsDisabledMessage || undefined}
557
579
  spellCheck={hasSpellCheck}
558
580
  autoFocus={hasAutoFocus}
559
581
  data-autofocus={hasAutoFocus || undefined}
@@ -571,8 +593,13 @@ export function TextArea({
571
593
  textareaSizeStyles[size],
572
594
  isDisabled && styles.textareaDisabled,
573
595
  Boolean(startIcon) && styles.textareaWithStartIcon,
574
- (status || isBusy) && styles.textareaWithStatus,
575
- isBusy && !!statusIcon && styles.textareaWithBusyStatus,
596
+ // Reserve trailing space only when the end slot actually renders
597
+ // something (spinner or on-field status icon). The `detached`
598
+ // status variant suppresses the on-field icon — its glyph lives in
599
+ // the message box below — so reserving here would inset the text
600
+ // for an icon that never appears.
601
+ (isBusy || statusIcon != null) && styles.textareaWithStatus,
602
+ isBusy && statusIcon != null && styles.textareaWithBusyStatus,
576
603
  maxLength != null && styles.textareaWithCounter,
577
604
  )}
578
605
  />
@@ -78,6 +78,13 @@ export const docs = {
78
78
  'Disables the input, preventing interaction and dimming the element.',
79
79
  default: 'false',
80
80
  },
81
+ {
82
+ name: 'isReadOnly',
83
+ type: 'boolean',
84
+ description:
85
+ 'Makes the input read-only: the value is shown at full opacity and still submits with the form, but cannot be edited. Unlike isDisabled, a read-only input is not dimmed and stays in the tab order. isDisabled takes precedence when both are set.',
86
+ default: 'false',
87
+ },
81
88
  {
82
89
  name: 'disabledMessage',
83
90
  type: 'string',
@@ -149,7 +156,7 @@ export const docs = {
149
156
  ],
150
157
  theming: {
151
158
  targets: [
152
- {className: 'astryx-text-input', visualProps: ['size', 'status']},
159
+ {className: 'astryx-text-input', visualProps: ['size', 'status'], states: ['disabled', 'readonly']},
153
160
  ],
154
161
  },
155
162
  usage: {
@@ -251,6 +258,13 @@ export const docsZh = {
251
258
  '禁用输入框,阻止交互并使元素变暗。',
252
259
  default: 'false',
253
260
  },
261
+ {
262
+ name: 'isReadOnly',
263
+ type: 'boolean',
264
+ description:
265
+ '将输入框设为只读:值以完整不透明度显示并仍随表单提交,但无法编辑。与 isDisabled 不同,只读输入框不会变暗,并保留在 Tab 顺序中。同时设置时 isDisabled 优先。',
266
+ default: 'false',
267
+ },
254
268
  {
255
269
  name: 'disabledMessage',
256
270
  type: 'string',
@@ -315,7 +329,7 @@ export const docsZh = {
315
329
  ],
316
330
  theming: {
317
331
  targets: [
318
- {className: 'astryx-text-input', visualProps: ['size', 'status']},
332
+ {className: 'astryx-text-input', visualProps: ['size', 'status'], states: ['disabled', 'readonly']},
319
333
  ],
320
334
  },
321
335
  usage: {
@@ -381,6 +395,8 @@ export const docsDense = {
381
395
  isOptional: 'Shows "Optional" indicator. Mutually exclusive w/ isRequired.',
382
396
  isRequired: 'Shows "Required" indicator+sets aria-required. Mutually exclusive w/ isOptional.',
383
397
  isDisabled: 'Disables input, prevents interaction, dims element.',
398
+ isReadOnly:
399
+ 'Read-only: value visible + still submits, but not editable. Unlike isDisabled: not dimmed, stays in tab order.',
384
400
  disabledMessage:
385
401
  'Explains why input is disabled. With isDisabled, shows tooltip on hover/focus + keeps input focusable via aria-disabled (field becomes read-only). Use instead of wrapping a disabled TextInput in Tooltip.',
386
402
  isLoading: 'Loading state w/ spinner+aria-busy.',
@@ -371,6 +371,155 @@ describe('TextInput', () => {
371
371
  });
372
372
  });
373
373
 
374
+ describe('form participation', () => {
375
+ it('submits the value under htmlName', () => {
376
+ const {container} = render(
377
+ <form>
378
+ <TextInput
379
+ label="Owner"
380
+ htmlName="owner"
381
+ value="alice"
382
+ onChange={() => {}}
383
+ />
384
+ </form>,
385
+ );
386
+ const data = new FormData(container.querySelector('form')!);
387
+ expect(data.get('owner')).toBe('alice');
388
+ });
389
+
390
+ it('is excluded from form data when disabled', () => {
391
+ const {container} = render(
392
+ <form>
393
+ <TextInput
394
+ label="Owner"
395
+ htmlName="owner"
396
+ value="alice"
397
+ onChange={() => {}}
398
+ isDisabled
399
+ />
400
+ </form>,
401
+ );
402
+ expect([
403
+ ...new FormData(container.querySelector('form')!).keys(),
404
+ ]).toEqual([]);
405
+ });
406
+
407
+ // Regression: a disabledMessage swaps the native `disabled` attribute for
408
+ // aria-disabled + readOnly so the reason stays focus-discoverable, but
409
+ // read-only fields still submit — the name has to be withheld too.
410
+ it('is excluded from form data when disabled, even with a disabledMessage', () => {
411
+ const {container} = render(
412
+ <form>
413
+ <TextInput
414
+ label="Owner"
415
+ htmlName="owner"
416
+ value="alice"
417
+ onChange={() => {}}
418
+ isDisabled
419
+ disabledMessage="You need the Editor role to change this"
420
+ />
421
+ </form>,
422
+ );
423
+ expect([
424
+ ...new FormData(container.querySelector('form')!).keys(),
425
+ ]).toEqual([]);
426
+ });
427
+ });
428
+
429
+ describe('isReadOnly', () => {
430
+ it('marks the input read-only', () => {
431
+ render(
432
+ <TextInput
433
+ label="Owner"
434
+ value="alice"
435
+ onChange={() => {}}
436
+ isReadOnly
437
+ />,
438
+ );
439
+ expect(screen.getByRole('textbox')).toHaveAttribute('readonly');
440
+ });
441
+
442
+ it('still submits its value with the form', () => {
443
+ const {container} = render(
444
+ <form>
445
+ <TextInput
446
+ label="Owner"
447
+ htmlName="owner"
448
+ value="alice"
449
+ onChange={() => {}}
450
+ isReadOnly
451
+ />
452
+ </form>,
453
+ );
454
+ expect(new FormData(container.querySelector('form')!).get('owner')).toBe(
455
+ 'alice',
456
+ );
457
+ });
458
+
459
+ it('does not call onChange when the user types', async () => {
460
+ const user = userEvent.setup();
461
+ const handleChange = vi.fn();
462
+ render(
463
+ <TextInput
464
+ label="Owner"
465
+ value="alice"
466
+ onChange={handleChange}
467
+ isReadOnly
468
+ />,
469
+ );
470
+ await user.type(screen.getByRole('textbox'), 'xyz');
471
+ expect(handleChange).not.toHaveBeenCalled();
472
+ });
473
+
474
+ it('stays focusable and is not disabled', async () => {
475
+ const user = userEvent.setup();
476
+ render(
477
+ <TextInput
478
+ label="Owner"
479
+ value="alice"
480
+ onChange={() => {}}
481
+ isReadOnly
482
+ />,
483
+ );
484
+ const input = screen.getByRole('textbox');
485
+ expect(input).not.toBeDisabled();
486
+ await user.tab();
487
+ expect(input).toHaveFocus();
488
+ });
489
+
490
+ it('hides the clear button', () => {
491
+ render(
492
+ <TextInput
493
+ label="Owner"
494
+ value="alice"
495
+ onChange={() => {}}
496
+ hasClear
497
+ isReadOnly
498
+ />,
499
+ );
500
+ expect(screen.queryByRole('button')).not.toBeInTheDocument();
501
+ });
502
+
503
+ it('lets isDisabled win when both are set', () => {
504
+ const {container} = render(
505
+ <form>
506
+ <TextInput
507
+ label="Owner"
508
+ htmlName="owner"
509
+ value="alice"
510
+ onChange={() => {}}
511
+ isReadOnly
512
+ isDisabled
513
+ />
514
+ </form>,
515
+ );
516
+ expect(screen.getByRole('textbox')).toBeDisabled();
517
+ expect([
518
+ ...new FormData(container.querySelector('form')!).keys(),
519
+ ]).toEqual([]);
520
+ });
521
+ });
522
+
374
523
  describe('onEnter', () => {
375
524
  it('calls onEnter when Enter key is pressed', async () => {
376
525
  const user = userEvent.setup();
@@ -809,3 +958,43 @@ describe('TextInput statusVariant forwarding', () => {
809
958
  expect(statusButton.getAttribute('aria-describedby')).toContain(tooltip.id);
810
959
  });
811
960
  });
961
+
962
+ describe('TextInput disabled theme state', () => {
963
+ // Reflecting isDisabled on the root theming target lets a theme gate its own
964
+ // hover/border treatment on disabled (data-disabled + a .disabled variant),
965
+ // mirroring how status is reflected — without structural :has() CSS.
966
+ it('reflects disabled on the root target so themes can gate paint on it', () => {
967
+ const {container} = render(
968
+ <TextInput label="Name" value="" onChange={() => {}} isDisabled />,
969
+ );
970
+ const root = container.querySelector('.astryx-text-input');
971
+ expect(root).toHaveAttribute('data-disabled', 'disabled');
972
+ expect(root).toHaveClass('disabled');
973
+ });
974
+
975
+ it('omits data-disabled when enabled, like status does', () => {
976
+ const {container} = render(
977
+ <TextInput label="Name" value="" onChange={() => {}} />,
978
+ );
979
+ const root = container.querySelector('.astryx-text-input');
980
+ expect(root).not.toHaveAttribute('data-disabled');
981
+ });
982
+ });
983
+
984
+ describe('TextInput readonly theme state', () => {
985
+ it('reflects readonly on the root target so themes can gate paint on it', () => {
986
+ const {container} = render(
987
+ <TextInput label="Name" value="" onChange={() => {}} isReadOnly />,
988
+ );
989
+ const root = container.querySelector('.astryx-text-input');
990
+ expect(root).toHaveAttribute('data-readonly', 'readonly');
991
+ });
992
+
993
+ it('omits data-readonly when editable', () => {
994
+ const {container} = render(
995
+ <TextInput label="Name" value="" onChange={() => {}} />,
996
+ );
997
+ const root = container.querySelector('.astryx-text-input');
998
+ expect(root).not.toHaveAttribute('data-readonly');
999
+ });
1000
+ });