@astryxdesign/core 0.5.2-canary.2da7a39 → 0.5.2-canary.2e51213

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 (375) hide show
  1. package/dist/AppShell/AppShell.d.ts +2 -2
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +10 -7
  4. package/dist/Banner/Banner.d.ts +5 -0
  5. package/dist/Banner/Banner.d.ts.map +1 -1
  6. package/dist/Banner/Banner.js +7 -2
  7. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +3 -2
  8. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  9. package/dist/Breadcrumbs/BreadcrumbItem.js +22 -15
  10. package/dist/Carousel/Carousel.d.ts.map +1 -1
  11. package/dist/Carousel/Carousel.js +2 -2
  12. package/dist/Chat/ChatComposer.d.ts +2 -2
  13. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  14. package/dist/Chat/ChatComposer.js +21 -2
  15. package/dist/Chat/ChatToolCalls.d.ts +1 -1
  16. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  17. package/dist/Chat/ChatToolCalls.js +12 -34
  18. package/dist/Chat/useChatNewMessages.js +3 -3
  19. package/dist/CheckboxList/CheckboxListItem.d.ts +5 -3
  20. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  21. package/dist/DateTimeInput/DateTimeInput.d.ts +47 -13
  22. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  23. package/dist/DateTimeInput/DateTimeInput.js +269 -162
  24. package/dist/DateTimeInput/NativeDateSegment.d.ts +24 -0
  25. package/dist/DateTimeInput/NativeDateSegment.d.ts.map +1 -0
  26. package/dist/DateTimeInput/NativeDateSegment.js +235 -0
  27. package/dist/DateTimeInput/NativeTimeSegment.d.ts +30 -0
  28. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -0
  29. package/dist/DateTimeInput/NativeTimeSegment.js +248 -0
  30. package/dist/DateTimeInput/index.d.ts +1 -1
  31. package/dist/DateTimeInput/index.d.ts.map +1 -1
  32. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +95 -0
  33. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -0
  34. package/dist/DateTimeInput/nativePickerSegmentStyles.js +103 -0
  35. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  36. package/dist/DropdownMenu/DropdownMenu.js +2 -4
  37. package/dist/Field/Field.d.ts.map +1 -1
  38. package/dist/Field/Field.js +10 -4
  39. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  40. package/dist/Field/PanelSearchInput.js +3 -5
  41. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  42. package/dist/FieldStatus/FieldStatus.js +4 -2
  43. package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
  44. package/dist/Layer/useTouchTrigger.js +2 -4
  45. package/dist/Layout/Layout.d.ts +7 -8
  46. package/dist/Layout/Layout.d.ts.map +1 -1
  47. package/dist/Layout/Layout.js +7 -8
  48. package/dist/MultiSelector/MultiSelector.d.ts +11 -1
  49. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  50. package/dist/MultiSelector/MultiSelector.js +89 -45
  51. package/dist/RadioList/RadioListItem.d.ts +30 -5
  52. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  53. package/dist/RadioList/RadioListItem.js +17 -4
  54. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  55. package/dist/Resizable/ResizeHandle.js +102 -44
  56. package/dist/Resizable/index.d.ts +2 -0
  57. package/dist/Resizable/index.d.ts.map +1 -1
  58. package/dist/Resizable/index.js +1 -0
  59. package/dist/Resizable/useResizable.d.ts +89 -8
  60. package/dist/Resizable/useResizable.d.ts.map +1 -1
  61. package/dist/Resizable/useResizable.js +521 -38
  62. package/dist/Resizable/utils.d.ts +35 -0
  63. package/dist/Resizable/utils.d.ts.map +1 -0
  64. package/dist/Resizable/utils.js +27 -0
  65. package/dist/Selector/Selector.d.ts +10 -0
  66. package/dist/Selector/Selector.d.ts.map +1 -1
  67. package/dist/Selector/Selector.js +105 -57
  68. package/dist/Selector/useSelectorPresentation.d.ts +1 -0
  69. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  70. package/dist/Selector/useSelectorPresentation.js +17 -2
  71. package/dist/Slider/Slider.d.ts.map +1 -1
  72. package/dist/Slider/Slider.js +3 -5
  73. package/dist/Spinner/Spinner.d.ts.map +1 -1
  74. package/dist/Spinner/Spinner.js +9 -10
  75. package/dist/Stepper/Step.d.ts +2 -1
  76. package/dist/Stepper/Step.d.ts.map +1 -1
  77. package/dist/Stepper/Step.js +75 -11
  78. package/dist/Stepper/Stepper.d.ts.map +1 -1
  79. package/dist/Stepper/Stepper.js +1 -0
  80. package/dist/TabList/TabList.d.ts.map +1 -1
  81. package/dist/TabList/TabList.js +2 -3
  82. package/dist/Table/index.d.ts +16 -1
  83. package/dist/Table/index.d.ts.map +1 -1
  84. package/dist/Table/index.js +4 -0
  85. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  86. package/dist/Table/plugins/columnResize/useTableColumnResize.js +8 -9
  87. package/dist/Table/plugins/rowStatus/index.d.ts +1 -1
  88. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -1
  89. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +11 -30
  90. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  91. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +108 -62
  92. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  93. package/dist/Table/plugins/sortable/useTableSortable.js +9 -1
  94. package/dist/Text/useTruncation.d.ts +1 -1
  95. package/dist/Text/useTruncation.d.ts.map +1 -1
  96. package/dist/Text/useTruncation.js +8 -7
  97. package/dist/TimeInput/TimeInput.d.ts +16 -1
  98. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  99. package/dist/TimeInput/TimeInput.js +98 -60
  100. package/dist/TimeInput/index.d.ts +1 -1
  101. package/dist/TimeInput/index.d.ts.map +1 -1
  102. package/dist/Timestamp/Timestamp.d.ts +2 -2
  103. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  104. package/dist/Timestamp/Timestamp.js +4 -145
  105. package/dist/Timestamp/formatRelativeTime.d.ts +20 -0
  106. package/dist/Timestamp/formatRelativeTime.d.ts.map +1 -0
  107. package/dist/Timestamp/formatRelativeTime.js +81 -0
  108. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  109. package/dist/Toast/ToastViewport.js +125 -77
  110. package/dist/Tokenizer/Tokenizer.d.ts +0 -38
  111. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  112. package/dist/Tokenizer/Tokenizer.js +94 -26
  113. package/dist/Tokenizer/useEndLaneReserve.d.ts +31 -0
  114. package/dist/Tokenizer/useEndLaneReserve.d.ts.map +1 -0
  115. package/dist/Tokenizer/useEndLaneReserve.js +136 -0
  116. package/dist/Tooltip/useTooltip.d.ts +1 -1
  117. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  118. package/dist/Tooltip/useTooltip.js +53 -42
  119. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  120. package/dist/Typeahead/BaseTypeahead.js +85 -30
  121. package/dist/Typeahead/Typeahead.d.ts +0 -21
  122. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  123. package/dist/Typeahead/Typeahead.js +108 -67
  124. package/dist/Typeahead/busyIndicatorLane.d.ts +62 -0
  125. package/dist/Typeahead/busyIndicatorLane.d.ts.map +1 -0
  126. package/dist/Typeahead/busyIndicatorLane.js +98 -0
  127. package/dist/astryx.css +25 -7
  128. package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -1
  129. package/dist/hooks/useFocusReturnVisibility.js +3 -5
  130. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
  131. package/dist/hooks/useIsomorphicLayoutEffect.d.ts.map +1 -1
  132. package/dist/hooks/useOverflow.js +11 -11
  133. package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
  134. package/dist/hooks/useScrollOverflow.js +7 -4
  135. package/dist/theme/defineTheme.d.ts +13 -0
  136. package/dist/theme/defineTheme.d.ts.map +1 -1
  137. package/dist/theme/defineTheme.js +7 -0
  138. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  139. package/dist/theme/generateThemeRules.js +1 -1
  140. package/dist/theme/localTokens.d.ts +18 -0
  141. package/dist/theme/localTokens.d.ts.map +1 -0
  142. package/dist/theme/localTokens.js +162 -0
  143. package/dist/utils/interactionModality.d.ts +6 -28
  144. package/dist/utils/interactionModality.d.ts.map +1 -1
  145. package/dist/utils/interactionModality.js +72 -37
  146. package/dist/utils/sharedResizeObserver.d.ts +17 -9
  147. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  148. package/dist/utils/sharedResizeObserver.js +54 -12
  149. package/locales/en.json +8 -0
  150. package/locales/pseudo.json +6 -0
  151. package/package.json +7 -2
  152. package/src/AlertDialog/useImperativeAlertDialog.test.tsx +163 -0
  153. package/src/AppShell/AppShell.doc.mjs +4 -3
  154. package/src/AppShell/AppShell.test.tsx +21 -0
  155. package/src/AppShell/AppShell.tsx +12 -7
  156. package/src/Banner/Banner.doc.mjs +5 -1
  157. package/src/Banner/Banner.tsx +12 -1
  158. package/src/BottomSheet/BottomSheet.doc.mjs +31 -0
  159. package/src/BottomSheet/BottomSheet.spec.md +178 -0
  160. package/src/Breadcrumbs/BreadcrumbItem.tsx +18 -9
  161. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +4 -1
  162. package/src/Breadcrumbs/Breadcrumbs.test.tsx +12 -0
  163. package/src/Button/Button.doc.mjs +56 -0
  164. package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
  165. package/src/Calendar/hooks/useCalendarConstraints.test.ts +153 -0
  166. package/src/Calendar/hooks/useCalendarDays.test.ts +265 -0
  167. package/src/Calendar/hooks/useCalendarNavigation.test.ts +252 -0
  168. package/src/Carousel/Carousel.test.tsx +83 -0
  169. package/src/Carousel/Carousel.tsx +8 -2
  170. package/src/Chat/ChatComposer.doc.mjs +7 -4
  171. package/src/Chat/ChatComposer.test.tsx +84 -2
  172. package/src/Chat/ChatComposer.tsx +45 -3
  173. package/src/Chat/ChatSendButton.test.tsx +189 -0
  174. package/src/Chat/ChatToolCalls.doc.mjs +1 -1
  175. package/src/Chat/ChatToolCalls.test.tsx +33 -1
  176. package/src/Chat/ChatToolCalls.tsx +13 -34
  177. package/src/Chat/useChatNewMessages.ts +3 -3
  178. package/src/Chat/useChatPasteAsToken.test.ts +191 -0
  179. package/src/CheckboxList/CheckboxList.doc.mjs +172 -31
  180. package/src/CheckboxList/CheckboxList.spec.md +215 -0
  181. package/src/CheckboxList/CheckboxList.test.tsx +45 -0
  182. package/src/CheckboxList/CheckboxListItem.doc.mjs +11 -9
  183. package/src/CheckboxList/CheckboxListItem.tsx +5 -3
  184. package/src/Collapsible/useCollapsible.test.tsx +236 -0
  185. package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
  186. package/src/CommandPalette/CommandPalette.spec.md +224 -0
  187. package/src/CommandPalette/CommandPaletteEmpty.test.tsx +101 -0
  188. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  189. package/src/ComplexSelector/ComplexSelector.spec.md +190 -0
  190. package/src/ContextMenu/ContextMenu.doc.mjs +48 -0
  191. package/src/ContextMenu/ContextMenu.spec.md +207 -0
  192. package/src/DateTimeInput/DateTimeInput.doc.mjs +43 -23
  193. package/src/DateTimeInput/DateTimeInput.tsx +388 -181
  194. package/src/DateTimeInput/DateTimeInputTouch.test.tsx +10 -2
  195. package/src/DateTimeInput/NativeDateSegment.tsx +308 -0
  196. package/src/DateTimeInput/NativePickerSegments.test.tsx +626 -0
  197. package/src/DateTimeInput/NativeTimeSegment.tsx +331 -0
  198. package/src/DateTimeInput/index.ts +1 -0
  199. package/src/DateTimeInput/nativePickerSegmentStyles.ts +120 -0
  200. package/src/Divider/Divider.doc.mjs +24 -0
  201. package/src/Divider/Divider.spec.md +157 -0
  202. package/src/DropdownMenu/DropdownMenu.doc.mjs +104 -0
  203. package/src/DropdownMenu/DropdownMenu.spec.md +246 -0
  204. package/src/DropdownMenu/DropdownMenu.tsx +2 -4
  205. package/src/Field/Field.doc.mjs +1 -0
  206. package/src/Field/Field.test.tsx +39 -0
  207. package/src/Field/Field.tsx +18 -1
  208. package/src/Field/InputClearButton.test.tsx +68 -6
  209. package/src/Field/PanelSearchInput.tsx +3 -5
  210. package/src/FieldStatus/FieldStatus.doc.mjs +21 -0
  211. package/src/FieldStatus/FieldStatus.spec.md +213 -0
  212. package/src/FieldStatus/FieldStatus.test.tsx +17 -0
  213. package/src/FieldStatus/FieldStatus.tsx +7 -2
  214. package/src/Grid/Grid.doc.mjs +19 -0
  215. package/src/Grid/Grid.spec.md +162 -0
  216. package/src/Icon/Icon.doc.mjs +196 -48
  217. package/src/Icon/Icon.spec.md +192 -0
  218. package/src/IconButton/IconButton.doc.mjs +38 -0
  219. package/src/Kbd/Kbd.doc.mjs +18 -0
  220. package/src/Kbd/Kbd.spec.md +142 -0
  221. package/src/Layer/useTouchTrigger.ts +2 -4
  222. package/src/Layout/Layout.doc.mjs +57 -17
  223. package/src/Layout/Layout.spec.md +235 -0
  224. package/src/Layout/Layout.test.tsx +1 -1
  225. package/src/Layout/Layout.tsx +7 -8
  226. package/src/Layout/LayoutHeader.doc.mjs +9 -0
  227. package/src/Layout/LayoutPanel.doc.mjs +4 -3
  228. package/src/Lightbox/Lightbox.doc.mjs +149 -33
  229. package/src/Lightbox/Lightbox.spec.md +198 -0
  230. package/src/Lightbox/useLightbox.test.tsx +263 -0
  231. package/src/Markdown/Markdown.doc.mjs +60 -0
  232. package/src/Markdown/Markdown.spec.md +207 -0
  233. package/src/MobileNav/MobileNav.doc.mjs +140 -32
  234. package/src/MobileNav/MobileNav.spec.md +209 -0
  235. package/src/MoreMenu/MoreMenu.doc.mjs +42 -0
  236. package/src/MoreMenu/MoreMenu.spec.md +222 -0
  237. package/src/MultiSelector/MultiSelector.doc.mjs +124 -1
  238. package/src/MultiSelector/MultiSelector.spec.md +293 -0
  239. package/src/MultiSelector/MultiSelector.test.tsx +305 -2
  240. package/src/MultiSelector/MultiSelector.tsx +124 -55
  241. package/src/NavIcon/NavIcon.doc.mjs +17 -0
  242. package/src/NavIcon/NavIcon.spec.md +134 -0
  243. package/src/NavMenu/NavHeadingMenu.spec.md +210 -0
  244. package/src/NavMenu/NavMenu.doc.mjs +37 -0
  245. package/src/Outline/Outline.doc.mjs +35 -0
  246. package/src/Outline/Outline.spec.md +162 -0
  247. package/src/OverflowList/OverflowList.doc.mjs +24 -0
  248. package/src/OverflowList/OverflowList.spec.md +157 -0
  249. package/src/Pagination/Pagination.doc.mjs +74 -0
  250. package/src/Pagination/Pagination.spec.md +226 -0
  251. package/src/Popover/Popover.doc.mjs +264 -65
  252. package/src/Popover/Popover.spec.md +421 -0
  253. package/src/Popover/usePopover.doc.mjs +208 -33
  254. package/src/PowerSearch/PowerSearch.spec.md +291 -0
  255. package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +253 -0
  256. package/src/PowerSearch/PowerSearchToken.test.tsx +228 -0
  257. package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
  258. package/src/ProgressBar/ProgressBar.spec.md +261 -0
  259. package/src/RadioList/RadioList.test.tsx +136 -2
  260. package/src/RadioList/RadioListItem.doc.mjs +30 -11
  261. package/src/RadioList/RadioListItem.tsx +44 -8
  262. package/src/Resizable/Resizable.doc.mjs +130 -24
  263. package/src/Resizable/Resizable.spec.md +246 -0
  264. package/src/Resizable/ResizeHandle.test.tsx +177 -56
  265. package/src/Resizable/ResizeHandle.tsx +128 -45
  266. package/src/Resizable/index.ts +2 -0
  267. package/src/Resizable/shippedDirections.test.ts +462 -0
  268. package/src/Resizable/useResizable.doc.mjs +124 -29
  269. package/src/Resizable/useResizable.test.ts +1277 -3
  270. package/src/Resizable/useResizable.ts +760 -48
  271. package/src/Resizable/utils.test.ts +74 -0
  272. package/src/Resizable/utils.ts +35 -0
  273. package/src/Section/Section.doc.mjs +17 -0
  274. package/src/Section/Section.spec.md +139 -0
  275. package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
  276. package/src/SegmentedControl/SegmentedControl.spec.md +152 -0
  277. package/src/Selector/Selector.doc.mjs +133 -97
  278. package/src/Selector/Selector.spec.md +300 -0
  279. package/src/Selector/Selector.test.tsx +318 -1
  280. package/src/Selector/Selector.tsx +127 -60
  281. package/src/Selector/useSelectorPresentation.ts +24 -4
  282. package/src/SideNav/SideNav.test.tsx +3 -3
  283. package/src/SideNav/SideNavCollapseButton.doc.mjs +8 -0
  284. package/src/SideNav/SideNavItem.doc.mjs +1 -1
  285. package/src/Skeleton/Skeleton.doc.mjs +12 -0
  286. package/src/Skeleton/Skeleton.spec.md +127 -0
  287. package/src/Slider/Slider.doc.mjs +184 -30
  288. package/src/Slider/Slider.spec.md +224 -0
  289. package/src/Slider/Slider.tsx +2 -5
  290. package/src/Spinner/Spinner.doc.mjs +5 -5
  291. package/src/Spinner/Spinner.test.tsx +32 -7
  292. package/src/Spinner/Spinner.tsx +46 -14
  293. package/src/Stack/Stack.doc.mjs +22 -0
  294. package/src/Stack/Stack.spec.md +155 -0
  295. package/src/StatusDot/StatusDot.doc.mjs +17 -0
  296. package/src/StatusDot/StatusDot.spec.md +138 -0
  297. package/src/Stepper/Step.doc.mjs +1 -1
  298. package/src/Stepper/Step.tsx +118 -5
  299. package/src/Stepper/Stepper.doc.mjs +51 -1
  300. package/src/Stepper/Stepper.spec.md +245 -0
  301. package/src/Stepper/Stepper.test.tsx +315 -0
  302. package/src/Stepper/Stepper.tsx +8 -0
  303. package/src/Switch/Switch.doc.mjs +40 -0
  304. package/src/Switch/Switch.spec.md +175 -0
  305. package/src/TabList/TabList.tsx +2 -3
  306. package/src/Table/Table.doc.mjs +102 -24
  307. package/src/Table/Table.perf.test.tsx +51 -1
  308. package/src/Table/Table.spec.md +325 -0
  309. package/src/Table/TableHeaderCell.test.tsx +291 -0
  310. package/src/Table/index.ts +19 -4
  311. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -12
  312. package/src/Table/plugins/filtering/useTableFilterState.test.tsx +143 -0
  313. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +2 -1
  314. package/src/Table/plugins/rowStatus/index.ts +1 -4
  315. package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +92 -0
  316. package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +354 -0
  317. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +431 -56
  318. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +155 -84
  319. package/src/Table/plugins/sortable/useTableSortable.test.tsx +72 -0
  320. package/src/Table/plugins/sortable/useTableSortable.tsx +18 -1
  321. package/src/Table/useTableGroupedRows.doc.mjs +2 -2
  322. package/src/Table/useTableRowStatus.doc.mjs +5 -5
  323. package/src/Text/Text.doc.mjs +190 -43
  324. package/src/Text/Text.spec.md +206 -0
  325. package/src/Text/useTruncation.ts +8 -7
  326. package/src/TextArea/TextArea.doc.mjs +255 -45
  327. package/src/TextArea/TextArea.spec.md +205 -0
  328. package/src/TimeInput/NativeTimeInput.test.tsx +269 -0
  329. package/src/TimeInput/TimeInput.doc.mjs +47 -23
  330. package/src/TimeInput/TimeInput.tsx +109 -50
  331. package/src/TimeInput/index.ts +1 -0
  332. package/src/Timestamp/Timestamp.doc.mjs +2 -2
  333. package/src/Timestamp/Timestamp.test.tsx +75 -3
  334. package/src/Timestamp/Timestamp.tsx +7 -153
  335. package/src/Timestamp/formatRelativeTime.ts +96 -0
  336. package/src/Toast/ToastViewport.test.tsx +194 -0
  337. package/src/Toast/ToastViewport.tsx +136 -58
  338. package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
  339. package/src/Tokenizer/Tokenizer.doc.mjs +2 -0
  340. package/src/Tokenizer/Tokenizer.spec.md +293 -0
  341. package/src/Tokenizer/Tokenizer.test.tsx +335 -1
  342. package/src/Tokenizer/Tokenizer.tsx +129 -46
  343. package/src/Tokenizer/useEndLaneReserve.ts +146 -0
  344. package/src/Toolbar/Toolbar.doc.mjs +19 -0
  345. package/src/Toolbar/Toolbar.spec.md +174 -0
  346. package/src/Tooltip/Tooltip.doc.mjs +17 -0
  347. package/src/Tooltip/Tooltip.spec.md +142 -0
  348. package/src/Tooltip/Tooltip.test.tsx +21 -1
  349. package/src/Tooltip/useTooltip.test.tsx +318 -0
  350. package/src/Tooltip/useTooltip.tsx +60 -39
  351. package/src/TreeList/TreeList.doc.mjs +58 -0
  352. package/src/TreeList/TreeList.spec.md +224 -0
  353. package/src/Typeahead/BaseTypeahead.tsx +96 -25
  354. package/src/Typeahead/Typeahead.doc.mjs +89 -1
  355. package/src/Typeahead/Typeahead.spec.md +257 -0
  356. package/src/Typeahead/Typeahead.test.tsx +502 -1
  357. package/src/Typeahead/Typeahead.tsx +187 -69
  358. package/src/Typeahead/TypeaheadItem.test.tsx +93 -2
  359. package/src/Typeahead/busyIndicatorLane.tsx +109 -0
  360. package/src/__tests__/stylexDeclarations.ts +51 -0
  361. package/src/hooks/useFocusReturnVisibility.ts +3 -5
  362. package/src/hooks/useOverflow.ts +11 -11
  363. package/src/hooks/useScrollLock.test.ts +103 -0
  364. package/src/hooks/useScrollOverflow.ts +7 -4
  365. package/src/i18n/useTranslator.test.tsx +197 -0
  366. package/src/theme/defineTheme.test.ts +227 -0
  367. package/src/theme/defineTheme.ts +30 -0
  368. package/src/theme/derivedVarRegistry.test.ts +6 -0
  369. package/src/theme/generateThemeRules.ts +4 -1
  370. package/src/theme/localTokens.ts +272 -0
  371. package/src/theme/themingTargets.test.ts +126 -28
  372. package/src/utils/interactionModality.test.ts +83 -0
  373. package/src/utils/interactionModality.ts +88 -36
  374. package/src/utils/sharedResizeObserver.test.ts +124 -5
  375. package/src/utils/sharedResizeObserver.ts +60 -14
@@ -101,6 +101,15 @@ const styles = {
101
101
  k3aq6I: "x1c071of x1pdlv7q",
102
102
  $$css: true
103
103
  },
104
+ triggerReadOnly: {
105
+ kkrTdU: "xt0e3qv",
106
+ $$css: true
107
+ },
108
+ triggerGhostReadOnly: {
109
+ kKwaWg: "x18o3ruo",
110
+ k3aq6I: "x1c071of x1pdlv7q",
111
+ $$css: true
112
+ },
104
113
  statusButton: {
105
114
  k1xSpc: "x78zum5",
106
115
  kGNEyG: "x6s0dn4",
@@ -261,6 +270,7 @@ export function MultiSelector({
261
270
  isOptional = false,
262
271
  isRequired = false,
263
272
  isDisabled = false,
273
+ isReadOnly = false,
264
274
  disabledMessage,
265
275
  options,
266
276
  value,
@@ -308,11 +318,13 @@ export function MultiSelector({
308
318
  const emptySearchText = emptySearchTextFromProps ?? t('@astryx.multiSelector.emptySearchResults');
309
319
  const size = useSize(sizeProp, 'md');
310
320
  const effectiveStatusVariant = variant === 'ghost' && statusVariant === 'attached' ? 'detached' : statusVariant;
321
+ const isEffectivelyReadOnly = isReadOnly && !isDisabled;
311
322
  const triggerId = useId();
312
323
  const listboxId = useId();
313
324
  const descriptionId = useId();
314
325
  const statusMessageId = useId();
315
326
  const inputLabelId = useId();
327
+ const readOnlyDescriptionId = useId();
316
328
  const searchId = useId();
317
329
  const triggerRef = useRef(null);
318
330
  const searchRef = useRef(null);
@@ -351,7 +363,7 @@ export function MultiSelector({
351
363
  const {
352
364
  ariaLabelledBy,
353
365
  ariaDescribedBy
354
- } = getInputARIA(inputLabelId, [description ? descriptionId : null, !inputGroup && effectiveStatusVariant !== 'tooltip' && status?.message ? statusMessageId : null, effectiveStatusVariant === 'tooltip' && status?.message ? statusTooltip.describedBy : null, showsDisabledMessage ? disabledMessageTooltip.describedBy : null], inputGroup);
366
+ } = getInputARIA(inputLabelId, [description ? descriptionId : null, !inputGroup && effectiveStatusVariant !== 'tooltip' && status?.message ? statusMessageId : null, effectiveStatusVariant === 'tooltip' && status?.message ? statusTooltip.describedBy : null, showsDisabledMessage ? disabledMessageTooltip.describedBy : null, isEffectivelyReadOnly ? readOnlyDescriptionId : null], inputGroup);
355
367
 
356
368
  // Flatten options for keyboard navigation
357
369
  const selectableItems = useMemo(() => getSelectableOptions(options), [options]);
@@ -457,16 +469,27 @@ export function MultiSelector({
457
469
  const {
458
470
  popover
459
471
  } = surface;
472
+ const hideSurface = surface.hide;
473
+ const isSurfaceOpen = surface.isOpen;
460
474
  const keepOpenProps = useKeepLayerOpenProps(popover.id, popover.isOpen);
461
475
 
462
- // Open dropdown on mount when isDefaultOpen is true
476
+ // Open dropdown on mount when isDefaultOpen is true and interaction is allowed.
463
477
  useEffect(() => {
464
- if (isDefaultOpen) {
478
+ if (isDefaultOpen && !isEffectivelyReadOnly) {
465
479
  surface.show();
466
480
  }
467
481
  // eslint-disable-next-line @eslint-react/exhaustive-deps -- mount-only: isDefaultOpen is not reactive
468
482
  }, []);
469
483
 
484
+ // Read-only is controlled by caller policy, so an already-open surface must
485
+ // close when that policy changes. The presentation controller keeps a sheet
486
+ // mounted through its exit and lets BottomSheet return final focus.
487
+ useEffect(() => {
488
+ if (isEffectivelyReadOnly && isSurfaceOpen) {
489
+ hideSurface();
490
+ }
491
+ }, [isEffectivelyReadOnly, isSurfaceOpen, hideSurface]);
492
+
470
493
  // Announce the filtered result count from the query-change handler (matching
471
494
  // BaseTypeahead) rather than a reactive effect: computing the count for the
472
495
  // next query here fires the announcement exactly once per keystroke and does
@@ -595,7 +618,7 @@ export function MultiSelector({
595
618
  } = useMultiCombobox({
596
619
  wasJustDismissed: surface.wasJustDismissed,
597
620
  selectableItems: sortedItems,
598
- isDisabled,
621
+ isDisabled: isDisabled || isEffectivelyReadOnly,
599
622
  isOpen: surface.isOpen,
600
623
  hasSearch,
601
624
  onOpen: useCallback(() => {
@@ -975,19 +998,48 @@ export function MultiSelector({
975
998
  children: renderOptions()
976
999
  })
977
1000
  });
978
- const selectionSurface = surface.activePresentation === 'bottom-sheet' ? /*#__PURE__*/_jsx(SelectorBottomSheet, {
979
- isOpen: surface.isSheetOpen,
980
- onOpenChange: surface.onSheetOpenChange,
981
- finalFocusRef: triggerRef,
982
- initialFocusRef: hasSearch ? searchRef : listboxRef,
983
- label: label,
984
- children: panelContent
985
- }) : popover.render(panelContent, {
986
- placement: 'below',
987
- alignment: 'start',
988
- offset: spacingVars['--spacing-1'],
989
- xstyle: styles.popover
990
- });
1001
+ let selectionSurface = null;
1002
+ if (surface.activePresentation === 'bottom-sheet') {
1003
+ if (!isEffectivelyReadOnly || surface.isSheetPresented) {
1004
+ selectionSurface = /*#__PURE__*/_jsx(SelectorBottomSheet, {
1005
+ isOpen: surface.isSheetOpen,
1006
+ onOpenChange: surface.onSheetOpenChange,
1007
+ finalFocusRef: triggerRef,
1008
+ initialFocusRef: hasSearch ? searchRef : listboxRef,
1009
+ label: label,
1010
+ children: panelContent
1011
+ });
1012
+ }
1013
+ } else if (!isEffectivelyReadOnly) {
1014
+ selectionSurface = popover.render(panelContent, {
1015
+ placement: 'below',
1016
+ alignment: 'start',
1017
+ offset: spacingVars['--spacing-1'],
1018
+ xstyle: styles.popover
1019
+ });
1020
+ }
1021
+ const triggerSharedProps = {
1022
+ id: triggerId,
1023
+ 'aria-describedby': ariaDescribedBy,
1024
+ 'aria-labelledby': ariaLabelledBy,
1025
+ 'aria-required': isEffectivelyRequired ? 'true' : undefined,
1026
+ 'aria-invalid': status?.type === 'error' ? 'true' : undefined,
1027
+ 'aria-busy': isBusy || undefined,
1028
+ onKeyDown,
1029
+ onFocus: event => {
1030
+ onFocus?.(event);
1031
+ surface.onTriggerFocus(event);
1032
+ },
1033
+ tabIndex: isDisabled && !showsDisabledMessage ? -1 : 0,
1034
+ ...{
1035
+ 0: {
1036
+ className: "x78zum5 x6s0dn4 x1qughib x1txdalj x1iyjqo2 xs83m0k x1r8uery xeuugli x1717udv x1ghz6dp xc342km xng3xce xjbqb8w xjb2p0i x1qlqyl8 x15bjb6t x1heor9g x1ypdohk x16khyan x1a2a7pz xh6dtrn"
1037
+ },
1038
+ 1: {
1039
+ className: "x78zum5 x6s0dn4 x1qughib x1txdalj x1iyjqo2 xs83m0k x1r8uery xeuugli x1717udv x1ghz6dp xc342km xng3xce xjbqb8w xjb2p0i x1qlqyl8 x15bjb6t x1heor9g x1a2a7pz xh6dtrn xt0e3qv"
1040
+ }
1041
+ }[!!isEffectivelyReadOnly << 0]
1042
+ };
991
1043
  const multiSelectorContent = /*#__PURE__*/_jsxs(_Fragment, {
992
1044
  children: [/*#__PURE__*/_jsxs("div", {
993
1045
  ref: el => {
@@ -1003,47 +1055,39 @@ export function MultiSelector({
1003
1055
  variant,
1004
1056
  size,
1005
1057
  status: status?.type ?? null,
1006
- disabled: isDisabled ? 'disabled' : null
1007
- }), stylex.props(inputWrapperStyles.base, styles.triggerContainer, sizeStyles[size], variant === 'ghost' && styles.triggerGhost, variant === 'ghost' && interactionOverlayStyles.backgroundImage, variant === 'ghost' && focusOutlineStyles.focusWithin, surface.isTriggerFocusRingSuppressed && selectorPresentationStyles.pointerRestoredFocus, isDisabled && inputWrapperStyles.disabled, variant === 'ghost' && isDisabled && styles.triggerGhostDisabled, optimisticValue.length === 0 && styles.triggerPlaceholder, variant !== 'ghost' && status && inputStatusBorderStyles[status.type], variant !== 'ghost' && status && !isDisabled && inputStatusHoverShadowStyles[status.type], variant !== 'ghost' && inputGroup && groupStyles.inGroup, xstyle), className, style),
1058
+ disabled: isDisabled ? 'disabled' : null,
1059
+ readonly: isEffectivelyReadOnly ? 'readonly' : null
1060
+ }), stylex.props(inputWrapperStyles.base, styles.triggerContainer, sizeStyles[size], variant === 'ghost' && styles.triggerGhost, variant === 'ghost' && interactionOverlayStyles.backgroundImage, variant === 'ghost' && focusOutlineStyles.focusWithin, surface.isTriggerFocusRingSuppressed && selectorPresentationStyles.pointerRestoredFocus, isDisabled && inputWrapperStyles.disabled, isEffectivelyReadOnly && styles.triggerReadOnly, variant === 'ghost' && isDisabled && styles.triggerGhostDisabled, variant === 'ghost' && isEffectivelyReadOnly && styles.triggerGhostReadOnly, optimisticValue.length === 0 && styles.triggerPlaceholder, variant !== 'ghost' && status && inputStatusBorderStyles[status.type], variant !== 'ghost' && status && !isDisabled && inputStatusHoverShadowStyles[status.type], variant !== 'ghost' && inputGroup && groupStyles.inGroup, xstyle), className, style),
1008
1061
  children: [startIcon && renderIconSlot(startIcon, {
1009
1062
  size: 'sm',
1010
1063
  color: 'secondary'
1011
1064
  }), inputGroup && /*#__PURE__*/_jsx(VisuallyHidden, {
1012
1065
  id: inputLabelId,
1013
1066
  children: label
1067
+ }), isEffectivelyReadOnly && /*#__PURE__*/_jsx(VisuallyHidden, {
1068
+ id: readOnlyDescriptionId,
1069
+ children: t('@astryx.input.readOnly')
1014
1070
  }), /*#__PURE__*/_jsx("button", {
1071
+ ...triggerSharedProps,
1015
1072
  ref: triggerRef,
1016
- id: triggerId,
1017
1073
  type: "button"
1018
- // In hasSearch mode the popup's search input is the combobox (it owns
1019
- // focus + aria-activedescendant, comboboxes-4), so the trigger is a
1020
- // plain button that opens the listbox — not a second combobox.
1074
+ // The read-only trigger stays a combobox even when hasSearch is set:
1075
+ // no search input is rendered in that state, and preserving the role
1076
+ // keeps the control's programmatic identity stable. Editable search
1077
+ // mode still moves combobox semantics to the popup input.
1021
1078
  ,
1022
- role: hasSearch ? undefined : 'combobox',
1023
- "aria-haspopup": surface.activePresentation === 'bottom-sheet' ? 'dialog' : 'listbox',
1024
- "aria-expanded": surface.isOpen,
1025
- "aria-controls": listboxId,
1026
- "aria-activedescendant": !hasSearch && surface.isOpen && highlightedIndex >= 0 ? getItemId(highlightedIndex) : undefined,
1027
- "aria-describedby": ariaDescribedBy,
1028
- "aria-labelledby": ariaLabelledBy,
1029
- "aria-required": isEffectivelyRequired ? 'true' : undefined,
1030
- "aria-invalid": status?.type === 'error' ? 'true' : undefined,
1031
- "aria-busy": isBusy || undefined
1079
+ role: isEffectivelyReadOnly || !hasSearch ? 'combobox' : undefined,
1080
+ "aria-haspopup": isEffectivelyReadOnly ? undefined : surface.activePresentation === 'bottom-sheet' ? 'dialog' : 'listbox',
1081
+ "aria-expanded": isEffectivelyReadOnly ? false : surface.isOpen,
1082
+ "aria-controls": isEffectivelyReadOnly ? undefined : listboxId,
1083
+ "aria-readonly": isEffectivelyReadOnly || undefined,
1084
+ "aria-activedescendant": !isEffectivelyReadOnly && !hasSearch && surface.isOpen && highlightedIndex >= 0 ? getItemId(highlightedIndex) : undefined
1032
1085
  // With a disabledMessage the trigger keeps focusability via
1033
1086
  // aria-disabled so the reason is focus-discoverable; activation is
1034
1087
  // still blocked by the isDisabled guards in useMultiCombobox.
1035
1088
  ,
1036
1089
  disabled: isDisabled && !showsDisabledMessage,
1037
1090
  "aria-disabled": showsDisabledMessage ? 'true' : undefined,
1038
- onKeyDown: onKeyDown,
1039
- onFocus: event => {
1040
- onFocus?.(event);
1041
- surface.onTriggerFocus(event);
1042
- },
1043
- tabIndex: isDisabled && !showsDisabledMessage ? -1 : 0,
1044
- ...{
1045
- className: "x78zum5 x6s0dn4 x1qughib x1txdalj x1iyjqo2 xs83m0k x1r8uery xeuugli x1717udv x1ghz6dp xc342km xng3xce xjbqb8w xjb2p0i x1qlqyl8 x15bjb6t x1heor9g x1ypdohk x16khyan x1a2a7pz xh6dtrn"
1046
- },
1047
1091
  children: /*#__PURE__*/_jsx("span", {
1048
1092
  ...{
1049
1093
  className: "x78zum5 x6s0dn4 xzye2dw x1iyjqo2 xs83m0k x1r8uery xeuugli xb3r6kr"
@@ -1060,7 +1104,7 @@ export function MultiSelector({
1060
1104
  disabled: isDisabled
1061
1105
  }, v)), isBusy && /*#__PURE__*/_jsx(Spinner, {
1062
1106
  size: "sm"
1063
- }), hasClear && value.length > 0 && !isDisabled && /*#__PURE__*/_jsx(InternalInputClearButton, {
1107
+ }), hasClear && value.length > 0 && !isDisabled && !isEffectivelyReadOnly && /*#__PURE__*/_jsx(InternalInputClearButton, {
1064
1108
  ...keepOpenProps,
1065
1109
  label: t('@astryx.multiSelector.clearAll', {
1066
1110
  label
@@ -1086,7 +1130,7 @@ export function MultiSelector({
1086
1130
  size: "sm",
1087
1131
  color: STATUS_ICON_COLOR_MAP[status.type],
1088
1132
  xstyle: styles.triggerIcon
1089
- }) : /*#__PURE__*/_jsx(Icon, {
1133
+ }) : !isEffectivelyReadOnly ? /*#__PURE__*/_jsx(Icon, {
1090
1134
  icon: "chevronDown",
1091
1135
  size: "sm",
1092
1136
  color: "secondary"
@@ -1104,7 +1148,7 @@ export function MultiSelector({
1104
1148
  ...themeProps('multi-selector-indicator-icon', {
1105
1149
  state: surface.isOpen ? 'expanded' : 'collapsed'
1106
1150
  })
1107
- })]
1151
+ }) : null]
1108
1152
  }), selectionSurface, showStatusTooltip && statusTooltip.renderTooltip(status?.message ?? ''), showsDisabledMessage && disabledMessageTooltip.renderTooltip(disabledMessage)]
1109
1153
  });
1110
1154
  if (inputGroup) {
@@ -18,17 +18,42 @@ import type { BaseProps } from '../BaseProps';
18
18
  export interface RadioListItemProps extends BaseProps<HTMLDivElement> {
19
19
  ref?: React.Ref<HTMLDivElement>;
20
20
  /**
21
- * Label text for the radio item.
21
+ * Primary label for the radio item.
22
+ *
23
+ * Accepts a plain string or a ReactNode for rich content. Links and buttons
24
+ * in the label keep their own behavior; only non-interactive row clicks
25
+ * delegate to the radio. The radio points at the rendered label for its
26
+ * accessible name, so a rich label still names it from its own text — pass
27
+ * `aria-label` when that text is absent or too noisy to announce.
22
28
  */
23
- label: string;
29
+ label: ReactNode;
30
+ /**
31
+ * Plain-text accessible name for the radio, applied to the control rather
32
+ * than the row. It replaces the name the label would otherwise supply — a
33
+ * plain string label included — so reach for it when a rich label's own
34
+ * text is absent or reads badly, not as a routine addition.
35
+ *
36
+ * @example
37
+ * ```
38
+ * <RadioListItem
39
+ * label={<span>Pro plan <Badge label="Recommended" /></span>}
40
+ * aria-label="Pro plan"
41
+ * value="pro"
42
+ * />
43
+ * ```
44
+ */
45
+ 'aria-label'?: string;
24
46
  /**
25
47
  * Value of this radio item.
26
48
  */
27
49
  value: string;
28
50
  /**
29
- * Description text displayed below the label.
51
+ * Secondary content displayed below the label. Accepts a plain string or a
52
+ * ReactNode. Links and buttons anywhere in the row keep their own click
53
+ * behaviour — the row only delegates clicks from its non-interactive
54
+ * surface to the radio.
30
55
  */
31
- description?: string;
56
+ description?: ReactNode;
32
57
  /**
33
58
  * Whether this individual radio item is disabled.
34
59
  * @default false
@@ -56,7 +81,7 @@ export interface RadioListItemProps extends BaseProps<HTMLDivElement> {
56
81
  * />
57
82
  * ```
58
83
  */
59
- export declare function RadioListItem({ ref, label, value, description, isDisabled: isItemDisabled, startContent, endContent, xstyle, className, style, onClick, ...rest }: RadioListItemProps): React.JSX.Element;
84
+ export declare function RadioListItem({ ref, label, 'aria-label': ariaLabel, value, description, isDisabled: isItemDisabled, startContent, endContent, xstyle, className, style, onClick, ...rest }: RadioListItemProps): React.JSX.Element;
60
85
  export declare namespace RadioListItem {
61
86
  var displayName: string;
62
87
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RadioListItem.d.ts","sourceRoot":"","sources":["../../src/RadioList/RadioListItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,EAAqB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAGhE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAyF5C,MAAM,WAAW,kBAAmB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACnE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,KAAK,EACL,KAAK,EACL,WAAW,EACX,UAAU,EAAE,cAAsB,EAClC,YAAY,EACZ,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EACP,GAAG,IAAI,EACR,EAAE,kBAAkB,qBA4IpB;yBAzJe,aAAa"}
1
+ {"version":3,"file":"RadioListItem.d.ts","sourceRoot":"","sources":["../../src/RadioList/RadioListItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,EAAqB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAGhE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAyF5C,MAAM,WAAW,kBAAmB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACnE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;;;;OAQG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;;;;;;;;;;;;;OAcG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,KAAK,EACL,YAAY,EAAE,SAAS,EACvB,KAAK,EACL,WAAW,EACX,UAAU,EAAE,cAAsB,EAClC,YAAY,EACZ,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EACP,GAAG,IAAI,EACR,EAAE,kBAAkB,qBAsJpB;yBApKe,aAAa"}
@@ -94,6 +94,7 @@ const rowStyles = {
94
94
  export function RadioListItem({
95
95
  ref,
96
96
  label,
97
+ 'aria-label': ariaLabel,
97
98
  value,
98
99
  description,
99
100
  isDisabled: isItemDisabled = false,
@@ -110,6 +111,7 @@ export function RadioListItem({
110
111
  throw new Error('RadioListItem must be used within an RadioList');
111
112
  }
112
113
  const id = useId();
114
+ const labelID = useId();
113
115
  const descriptionID = useId();
114
116
  const isDisabled = context.isDisabled || isItemDisabled;
115
117
  // When the whole group is disabled with a disabledMessage, radios stay
@@ -118,6 +120,10 @@ export function RadioListItem({
118
120
  // and always uses the native disabled attribute.
119
121
  const keepsFocusableForMessage = context.hasDisabledMessage && !isItemDisabled;
120
122
  const isChecked = context.value === value;
123
+ // One predicate for both the rendered element and the aria-describedby that
124
+ // points at it. A ReactNode description can be `false` or `''`, which render
125
+ // nothing — a `!= null` check would leave the link dangling.
126
+ const hasDescription = isRenderable(description);
121
127
  const size = context.size;
122
128
  // The radio visual is an indicator: a theme can restyle it through the
123
129
  // `radio` / `radio-dot` targets or replace the component outright.
@@ -148,7 +154,6 @@ export function RadioListItem({
148
154
  name: context.name,
149
155
  value: value,
150
156
  checked: isChecked,
151
- "aria-label": label,
152
157
  disabled: isDisabled && !keepsFocusableForMessage,
153
158
  "aria-disabled": keepsFocusableForMessage ? 'true' : undefined
154
159
  // A focusable-disabled radio is not natively disabled, so detach it
@@ -167,8 +172,15 @@ export function RadioListItem({
167
172
  // both direct control clicks and row-surface clicks the row delegates
168
173
  // to the input — the same routing CheckboxListItem uses.
169
174
  ,
170
- onClick: onClick,
171
- "aria-describedby": description ? descriptionID : undefined,
175
+ onClick: onClick
176
+ // The radio names itself from the visible label element, which works
177
+ // for a rich label as well as a plain string. `aria-label` replaces
178
+ // that name rather than adding to it, so the two are mutually
179
+ // exclusive — aria-labelledby would otherwise win over it.
180
+ ,
181
+ "aria-label": ariaLabel,
182
+ "aria-labelledby": ariaLabel == null ? labelID : undefined,
183
+ "aria-describedby": hasDescription ? descriptionID : undefined,
172
184
  ...stylex.props(styles.input, wrapperSizeStyles[size], isDisabled && styles.inputDisabled)
173
185
  }), /*#__PURE__*/_jsx("span", {
174
186
  ref: indicatorRef,
@@ -195,9 +207,10 @@ export function RadioListItem({
195
207
  interactiveRef: radioRef,
196
208
  isDisabled: isDisabled,
197
209
  label: /*#__PURE__*/_jsx("span", {
210
+ id: labelID,
198
211
  children: label
199
212
  }),
200
- description: isRenderable(description) ? /*#__PURE__*/_jsx("span", {
213
+ description: hasDescription ? /*#__PURE__*/_jsx("span", {
201
214
  id: descriptionID,
202
215
  children: description
203
216
  }) : undefined,
@@ -1 +1 @@
1
- {"version":3,"file":"ResizeHandle.d.ts","sourceRoot":"","sources":["../../src/Resizable/ResizeHandle.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAErC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAU5C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,gBAAgB,CAAC;AAQnD,KAAK,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AA0NzD,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,SAAS,CAAC,cAAc,CAAC,EACzB,OAAO,CACR;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IAEtC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAEhC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,6CAA6C;IAC7C,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,EAC3B,SAAwB,EACxB,QAAQ,EAAE,YAAuB,EACjC,UAAkB,EAClB,UAAkB,EAClB,UAAkB,EAClB,eAAsB,EACtB,aAAsB,EACtB,KAAK,EAAE,cAAc,EACrB,SAAS,EACT,QAAQ,EACR,MAAM,EACN,SAAS,EACT,GAAG,EACH,GAAG,KAAK,EACT,EAAE,iBAAiB,+BA6RnB;yBA5Se,YAAY"}
1
+ {"version":3,"file":"ResizeHandle.d.ts","sourceRoot":"","sources":["../../src/Resizable/ResizeHandle.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAErC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAW5C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,gBAAgB,CAAC;AAQnD,KAAK,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAyOzD,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,SAAS,CAAC,cAAc,CAAC,EACzB,OAAO,CACR;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IAEtC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAEhC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,6CAA6C;IAC7C,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,EAC3B,SAAwB,EACxB,QAAQ,EAAE,YAAuB,EACjC,UAAkB,EAClB,UAAkB,EAClB,UAAkB,EAClB,eAAsB,EACtB,aAAsB,EACtB,KAAK,EAAE,cAAc,EACrB,SAAS,EACT,QAAQ,EACR,MAAM,EACN,SAAS,EACT,GAAG,EACH,GAAG,KAAK,EACT,EAAE,iBAAiB,+BAuVnB;yBAtWe,YAAY"}
@@ -21,9 +21,19 @@
21
21
  * While the panel is collapsed, aria-valuenow is clamped to aria-valuemin
22
22
  * (a value below the minimum is invalid per WCAG 4.1.2) and a localized
23
23
  * "Collapsed" aria-valuetext announces the real state.
24
+ *
25
+ * Dragging takes pointer capture on the grab zone, so the rest of the gesture
26
+ * is delivered to that element instead of being hit-tested against whatever
27
+ * the cursor happens to be over. A resizable region that contains an embedded
28
+ * frame otherwise loses the drag the moment the cursor crosses the frame: the
29
+ * events go to the guest document, and a listener on the host window never
30
+ * hears them again. Captured events are delivered to the capturing element,
31
+ * so the move/up/cancel handlers live on the grab zone (as in Slider), not on
32
+ * window.
24
33
  */
25
34
  import { useCallback, useEffect, useRef, useState } from 'react';
26
35
  import * as stylex from '@stylexjs/stylex';
36
+ import { devWarn } from "../utils/devWarning.js";
27
37
  import "../theme/tokens.stylex.js";
28
38
  import { colorVars, durationVars, easeVars, radiusVars, spacingVars } from "../theme/tokens.stylex.js";
29
39
  import { focusOutlineProps } from "../utils/focusOutline.stylex.js";
@@ -66,6 +76,20 @@ function hitAreaBiasDir(effectiveSide) {
66
76
  }
67
77
  return effectiveSide === 'start' ? -1 : 1;
68
78
  }
79
+
80
+ /**
81
+ * Drag feedback lives on `<body>`, not on the handle: the pointer spends the
82
+ * drag off the handle, over whatever the panels contain, and the resize cursor
83
+ * has to hold there. Module scope so the writes stay outside the component.
84
+ */
85
+ function applyBodyDragStyles(isHorizontal) {
86
+ document.body.style.cursor = isHorizontal ? 'col-resize' : 'row-resize';
87
+ document.body.style.userSelect = 'none';
88
+ }
89
+ function clearBodyDragStyles() {
90
+ document.body.style.cursor = '';
91
+ document.body.style.userSelect = '';
92
+ }
69
93
  const styles = {
70
94
  handle: {
71
95
  kVAEAm: "x1n2onr6",
@@ -273,9 +297,10 @@ export function ResizeHandle({
273
297
  const t = useTranslator();
274
298
  const label = labelFromProps ?? t('@astryx.resizable.handle.label');
275
299
  const handleRef = useRef(null);
276
- // Removes the in-flight drag's window listeners (and resets body styles).
277
- // Held in a ref so unmount can tear down a drag that never got a pointerup.
278
- const dragCleanupRef = useRef(null);
300
+ // The pointer that owns the in-flight drag, plus the values its deltas are
301
+ // measured against. A ref so moves don't re-render, and so unmount can tell
302
+ // a live drag from a finished one.
303
+ const dragRef = useRef(null);
279
304
  const [isDragging, setIsDragging] = useState(false);
280
305
  const [isHovered, setIsHovered] = useState(false);
281
306
  const [isFocused, setIsFocused] = useState(false);
@@ -295,48 +320,73 @@ export function ResizeHandle({
295
320
  }, []);
296
321
  const isInteracting = isHovered || isFocused;
297
322
 
323
+ // A handle whose axis disagrees with its hook drags along one axis while the
324
+ // hook measures percentage bounds on the other. `ResizableProps` is exported,
325
+ // so `_direction` is optional for old hand-built objects; absent means there
326
+ // is no hook axis to compare, not that the hook is horizontal.
327
+ const didWarnAxisRef = useRef(false);
328
+ useEffect(() => {
329
+ const hookDirection = resizable?._direction;
330
+ if (hookDirection != null && hookDirection !== direction && !didWarnAxisRef.current) {
331
+ didWarnAxisRef.current = true;
332
+ devWarn('ResizeHandle', `direction="${direction}" but its useResizable region is ` + `"${hookDirection}". They must match: the hook measures one axis ` + 'and the handle drags the other.');
333
+ }
334
+ }, [direction, resizable]);
335
+
298
336
  // --- Pointer drag ---
337
+ const clearDrag = useCallback(() => {
338
+ dragRef.current = null;
339
+ setIsDragging(false);
340
+ clearBodyDragStyles();
341
+ }, []);
299
342
  const handlePointerDown = useCallback(e => {
300
- if (isDisabled || !resizable) {
343
+ if (isDisabled || !resizable || dragRef.current) {
301
344
  return;
302
345
  }
303
346
  e.preventDefault();
304
347
  e.stopPropagation();
348
+ // Take the pointer for the whole gesture. Without capture the browser
349
+ // re-hit-tests every move, so a frame anywhere under the drag path
350
+ // swallows the rest of it into the guest document.
351
+ e.currentTarget.setPointerCapture?.(e.pointerId);
352
+ dragRef.current = {
353
+ pointerId: e.pointerId,
354
+ startPos: isHorizontal ? e.clientX : e.clientY,
355
+ rtl: isHorizontal ? getRTLMultiplier() : 1
356
+ };
305
357
  setIsDragging(true);
306
358
  resizable._onResizeStart();
307
- const startPos = isHorizontal ? e.clientX : e.clientY;
308
- const rtl = isHorizontal ? getRTLMultiplier() : 1;
309
- document.body.style.cursor = isHorizontal ? 'col-resize' : 'row-resize';
310
- document.body.style.userSelect = 'none';
311
- const onMove = ev => {
312
- const currentPos = isHorizontal ? ev.clientX : ev.clientY;
313
- const delta = (currentPos - startPos) * rtl * sign;
314
- resizable._onResizeMove(delta);
315
- };
316
- const onUp = () => {
317
- cleanup();
318
- setIsDragging(false);
319
- resizable._onResizeEnd();
320
- document.body.style.cursor = '';
321
- document.body.style.userSelect = '';
322
- };
323
- const onCancel = () => {
324
- cleanup();
325
- setIsDragging(false);
326
- document.body.style.cursor = '';
327
- document.body.style.userSelect = '';
328
- };
329
- function cleanup() {
330
- window.removeEventListener('pointermove', onMove);
331
- window.removeEventListener('pointerup', onUp);
332
- window.removeEventListener('pointercancel', onCancel);
333
- dragCleanupRef.current = null;
359
+ applyBodyDragStyles(isHorizontal);
360
+ }, [isDisabled, resizable, isHorizontal, getRTLMultiplier]);
361
+ const handlePointerMove = useCallback(e => {
362
+ const drag = dragRef.current;
363
+ if (!drag || drag.pointerId !== e.pointerId || !resizable) {
364
+ return;
334
365
  }
335
- window.addEventListener('pointermove', onMove);
336
- window.addEventListener('pointerup', onUp);
337
- window.addEventListener('pointercancel', onCancel);
338
- dragCleanupRef.current = cleanup;
339
- }, [isDisabled, resizable, isHorizontal, getRTLMultiplier, sign]);
366
+ const currentPos = isHorizontal ? e.clientX : e.clientY;
367
+ resizable._onResizeMove((currentPos - drag.startPos) * drag.rtl * sign);
368
+ }, [resizable, isHorizontal, sign]);
369
+ const handlePointerUp = useCallback(e => {
370
+ if (dragRef.current?.pointerId !== e.pointerId) {
371
+ return;
372
+ }
373
+ clearDrag();
374
+ resizable?._onResizeEnd();
375
+ }, [clearDrag, resizable]);
376
+
377
+ // Serves pointercancel and lostpointercapture alike: an interrupted drag
378
+ // ends without signalling a resize end. lostpointercapture also fires on the
379
+ // implicit release after every pointerup/pointercancel, by which point the
380
+ // drag is already cleared and this is a no-op.
381
+ const cancelDrag = useCallback(e => {
382
+ if (dragRef.current?.pointerId !== e.pointerId) {
383
+ return;
384
+ }
385
+ clearDrag();
386
+ // Not a resize end, but still the end of the gesture: the region froze
387
+ // state for the duration of the drag and has to be told it is over.
388
+ resizable?._onResizeCancel?.();
389
+ }, [clearDrag, resizable]);
340
390
 
341
391
  // --- Keyboard ---
342
392
  const handleKeyDown = useCallback(e => {
@@ -406,16 +456,20 @@ export function ResizeHandle({
406
456
  }, [isDisabled, resizable]);
407
457
 
408
458
  // --- Cleanup on unmount ---
409
- // A drag in flight when the handle unmounts never gets its pointerup, so
410
- // tear down the window listeners here too — otherwise every pointermove
411
- // keeps driving the (still-mounted) region's resize state after the handle
412
- // is gone, and the body cursor/user-select overrides stick.
459
+ // Removing the grab zone implicitly releases its captured pointer and takes
460
+ // its listeners with it, so an in-flight drag stops driving the region on
461
+ // its own. The body cursor/user-select overrides are not on the element,
462
+ // though, so they have to be released here or they stick after the handle
463
+ // is gone — and the region, which outlives this handle, still has to hear
464
+ // that its gesture is over.
465
+ const cancelOnUnmountRef = useRef(undefined);
466
+ cancelOnUnmountRef.current = resizable?._onResizeCancel;
413
467
  useEffect(() => {
414
468
  return () => {
415
- if (dragCleanupRef.current) {
416
- dragCleanupRef.current();
417
- document.body.style.cursor = '';
418
- document.body.style.userSelect = '';
469
+ if (dragRef.current) {
470
+ dragRef.current = null;
471
+ clearBodyDragStyles();
472
+ cancelOnUnmountRef.current?.();
419
473
  }
420
474
  };
421
475
  }, []);
@@ -464,6 +518,10 @@ export function ResizeHandle({
464
518
  // construction so the two stay aligned in LTR and RTL alike.
465
519
  hitBiasDir == null ? isHorizontal ? rtlStyles.centerInline('0px') : styles.hitAreaCenteredY : isHorizontal ? dynamicStyles.hitAreaOffsetX(hitBiasDir) : dynamicStyles.hitAreaOffsetY(hitBiasDir), isDisabled && styles.disabled),
466
520
  onPointerDown: handlePointerDown,
521
+ onPointerMove: handlePointerMove,
522
+ onPointerUp: handlePointerUp,
523
+ onPointerCancel: cancelDrag,
524
+ onLostPointerCapture: cancelDrag,
467
525
  onPointerEnter: () => setIsHovered(true),
468
526
  onPointerLeave: () => {
469
527
  if (!isDragging) {
@@ -4,7 +4,9 @@
4
4
  * @output Re-exports all public Resizable API
5
5
  * @position Package entry point for Resizable
6
6
  */
7
+ export { percent } from './utils';
7
8
  export { useResizable } from './useResizable';
9
+ export type { ResizablePercentSize, ResizableSize } from './utils';
8
10
  export type { ResizableRegion, ResizableRegionConfig, ResizableProps, ResizableConfig, UseResizableSingleConfig, UseResizableMultiConfig, } from './useResizable';
9
11
  export { ResizeHandle } from './ResizeHandle';
10
12
  export type { ResizeHandleProps } from './ResizeHandle';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Resizable/index.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,YAAY,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Resizable/index.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAChC,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,YAAY,EAAC,oBAAoB,EAAE,aAAa,EAAC,MAAM,SAAS,CAAC;AACjE,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,YAAY,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAC"}
@@ -8,5 +8,6 @@
8
8
  * @output Re-exports all public Resizable API
9
9
  * @position Package entry point for Resizable
10
10
  */
11
+ export { percent } from "./utils.js";
11
12
  export { useResizable } from "./useResizable.js";
12
13
  export { ResizeHandle } from "./ResizeHandle.js";