@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
@@ -25,6 +25,12 @@ import React, {
25
25
  type ReactNode,
26
26
  } from 'react';
27
27
  import * as stylex from '@stylexjs/stylex';
28
+ import {
29
+ BusyIndicatorLaneProvider,
30
+ createBusyIndicatorLane,
31
+ useIsBusy,
32
+ type BusyIndicatorLane,
33
+ } from '../Typeahead/busyIndicatorLane';
28
34
  import type {BaseProps} from '../BaseProps';
29
35
  import type {SizeValue} from '../utils/types';
30
36
  import {BaseTypeahead} from '../Typeahead/BaseTypeahead';
@@ -40,6 +46,8 @@ import {
40
46
  type FieldStatusVariant,
41
47
  } from '../Field';
42
48
  import {Token} from '../Token';
49
+ import {Spinner} from '../Spinner';
50
+ import {useEndLaneReserve} from './useEndLaneReserve';
43
51
  import {renderIconSlot, type IconType} from '../Icon';
44
52
  import {OverflowList} from '../OverflowList';
45
53
  import {useLayer} from '../Layer/useLayer';
@@ -232,6 +240,10 @@ export interface TokenizerProps<T extends SearchableItem> extends Omit<
232
240
  // Styles
233
241
  // =============================================================================
234
242
 
243
+ // How far the end lane sits from the field's inline-end border, named once
244
+ // because the input's reserve is derived from the same value.
245
+ const END_LANE_INSET = spacingVars['--spacing-2'];
246
+
235
247
  const styles = stylex.create({
236
248
  wrapper: {
237
249
  position: 'relative',
@@ -267,7 +279,7 @@ const styles = stylex.create({
267
279
  // Match the field's inline padding (inputWrapperStyles.base uses
268
280
  // spacing-2) so end content (clear button, resultCount) lines up with
269
281
  // the text/start-icon inset instead of hugging the border at ~3px.
270
- insetInlineEnd: spacingVars['--spacing-2'],
282
+ insetInlineEnd: END_LANE_INSET,
271
283
  display: 'flex',
272
284
  alignItems: 'center',
273
285
  gap: spacingVars['--spacing-2'],
@@ -387,6 +399,45 @@ const CREATABLE_ID_PREFIX = '__xds_create__';
387
399
  * />
388
400
  * ```
389
401
  */
402
+ /**
403
+ * The field's inline-end lane: the busy Spinner, then `endContent`, then the
404
+ * clear button — pinned to the field's first row while tokens wrap below it.
405
+ *
406
+ * A separate component so that subscribing to the busy state re-renders THIS
407
+ * and nothing else. Subscribed from `Tokenizer`, a search transition
408
+ * re-rendered every selected token twice — twenty tokens meant forty renders
409
+ * per search for one glyph none of them contain.
410
+ *
411
+ * Renders nothing when the lane would be empty, so `useEndLaneReserve` sees no
412
+ * element, publishes no width, and the input keeps its full content box.
413
+ */
414
+ function EndLane({
415
+ lane,
416
+ laneRef,
417
+ laneXStyle,
418
+ loadingLabel,
419
+ hasStaticContent,
420
+ children,
421
+ }: {
422
+ lane: BusyIndicatorLane;
423
+ laneRef: (node: HTMLElement | null) => void;
424
+ laneXStyle: stylex.StyleXStyles[];
425
+ loadingLabel: string;
426
+ hasStaticContent: boolean;
427
+ children: ReactNode;
428
+ }) {
429
+ const isBusy = useIsBusy(lane);
430
+ if (!isBusy && !hasStaticContent) {
431
+ return null;
432
+ }
433
+ return (
434
+ <div ref={laneRef} {...stylex.props(laneXStyle)}>
435
+ {isBusy && <Spinner size="sm" aria-label={loadingLabel} />}
436
+ {children}
437
+ </div>
438
+ );
439
+ }
440
+
390
441
  export function Tokenizer<T extends SearchableItem>({
391
442
  label,
392
443
  isLabelHidden = false,
@@ -461,6 +512,24 @@ export function Tokenizer<T extends SearchableItem>({
461
512
  }));
462
513
 
463
514
  // Focus-within state for overflow truncation
515
+ // Reported by BaseTypeahead so the indicator can live in this field's own
516
+ // end lane, beside endContent and the clear button.
517
+ // The base owns the busy state; this field only paints it. Held in a store
518
+ // rather than this component's state — held here, a search transition
519
+ // re-rendered every selected token twice, for a glyph no token contains.
520
+ // See busyIndicatorLane.tsx.
521
+ const busyLane = useMemo(() => createBusyIndicatorLane(), []);
522
+ // What sits in the end lane varies most here — a spinner, arbitrary
523
+ // `endContent`, a clear button, or all three — so its width is measured
524
+ // rather than assumed, and the input reserves it.
525
+ const [laneRef, laneReserve] = useEndLaneReserve(END_LANE_INSET);
526
+ // The half of the lane's contents this component knows about. The busy half
527
+ // is the leaf's own business — folding it in here would mean reading the
528
+ // busy state during this render, which is exactly the re-render the store
529
+ // exists to avoid.
530
+ const hasStaticEndLane = Boolean(
531
+ endContent || (hasClear && value.length > 0 && !isDisabled),
532
+ );
464
533
  const [isFocusedWithin, setIsFocusedWithin] = useState(false);
465
534
  const isTruncated =
466
535
  !isFocusedWithin && tokenOverflowBehavior !== 'none' && value.length > 0;
@@ -795,37 +864,48 @@ export function Tokenizer<T extends SearchableItem>({
795
864
  ) : (
796
865
  tokens
797
866
  )}
798
- <BaseTypeahead
799
- ref={inputRef}
800
- searchSource={isAtMax ? emptySource : filteredSource}
801
- value={null}
802
- onChange={handleAdd}
803
- renderItem={renderItem}
804
- placeholder={value.length === 0 ? placeholder : ''}
805
- hasEntriesOnFocus={isAtMax ? false : hasEntriesOnFocus}
806
- maxMenuItems={maxMenuItems}
807
- menuWidth={menuWidth}
808
- minQueryLength={minQueryLength}
809
- emptySearchResultsText={emptySearchResultsText}
810
- isDisabled={isDisabled}
811
- isFocusableDisabled={showsDisabledMessage}
812
- hasAutoFocus={hasAutoFocus}
813
- inputId={inputId}
814
- ariaDescribedBy={ariaDescribedBy}
815
- onChangeQuery={onChangeQuery}
816
- __queryEntries={createEntries}
817
- debounceMs={debounceMs}
818
- onKeyDown={handleKeyDown}
819
- anchorRef={wrapperRef}
820
- size={size}
821
- inputXStyle={
822
- isAtMax || isTruncated
823
- ? styles.inputAtMax
824
- : value.length > 0
825
- ? styles.inputCompact
826
- : undefined
827
- }
828
- />
867
+ {/* The base reports its busy state through this lane, so the
868
+ indicator lands in the end controls below beside the clear
869
+ button rather than as a second one inside the base. */}
870
+ <BusyIndicatorLaneProvider value={busyLane}>
871
+ <BaseTypeahead
872
+ ref={inputRef}
873
+ searchSource={isAtMax ? emptySource : filteredSource}
874
+ value={null}
875
+ onChange={handleAdd}
876
+ renderItem={renderItem}
877
+ placeholder={value.length === 0 ? placeholder : ''}
878
+ hasEntriesOnFocus={isAtMax ? false : hasEntriesOnFocus}
879
+ maxMenuItems={maxMenuItems}
880
+ menuWidth={menuWidth}
881
+ minQueryLength={minQueryLength}
882
+ emptySearchResultsText={emptySearchResultsText}
883
+ isDisabled={isDisabled}
884
+ isFocusableDisabled={showsDisabledMessage}
885
+ hasAutoFocus={hasAutoFocus}
886
+ inputId={inputId}
887
+ ariaDescribedBy={ariaDescribedBy}
888
+ onChangeQuery={onChangeQuery}
889
+ __queryEntries={createEntries}
890
+ debounceMs={debounceMs}
891
+ onKeyDown={handleKeyDown}
892
+ anchorRef={wrapperRef}
893
+ size={size}
894
+ inputXStyle={[
895
+ isAtMax || isTruncated
896
+ ? styles.inputAtMax
897
+ : value.length > 0
898
+ ? styles.inputCompact
899
+ : undefined,
900
+ // Not for the collapsed states above: those give the input no width
901
+ // to pad, and `inputAtMax` zeroes its padding outright.
902
+ // Applied whether or not a lane is up: the reserve resolves to
903
+ // zero until the lane publishes a width, so this does not need to
904
+ // know about the busy state.
905
+ !(isAtMax || isTruncated) && laneReserve,
906
+ ]}
907
+ />
908
+ </BusyIndicatorLaneProvider>
829
909
  {htmlName != null &&
830
910
  value.map(item => (
831
911
  <input
@@ -838,20 +918,23 @@ export function Tokenizer<T extends SearchableItem>({
838
918
  disabled={isDisabled}
839
919
  />
840
920
  ))}
841
- {(endContent || (hasClear && value.length > 0 && !isDisabled)) && (
842
- <div {...stylex.props(styles.endSection, endSectionSizeStyles[size])}>
843
- {endContent}
844
- {hasClear && value.length > 0 && !isDisabled && (
845
- <InputClearButton
846
- label={t('@astryx.tokenizer.clearAll')}
847
- onClick={e => {
848
- e.stopPropagation();
849
- handleClearAll();
850
- }}
851
- />
852
- )}
853
- </div>
854
- )}
921
+ <EndLane
922
+ lane={busyLane}
923
+ laneRef={laneRef}
924
+ laneXStyle={[styles.endSection, endSectionSizeStyles[size]]}
925
+ loadingLabel={t('@astryx.typeahead.loading')}
926
+ hasStaticContent={hasStaticEndLane}>
927
+ {endContent}
928
+ {hasClear && value.length > 0 && !isDisabled && (
929
+ <InputClearButton
930
+ label={t('@astryx.tokenizer.clearAll')}
931
+ onClick={e => {
932
+ e.stopPropagation();
933
+ handleClearAll();
934
+ }}
935
+ />
936
+ )}
937
+ </EndLane>
855
938
  </div>
856
939
  );
857
940
 
@@ -0,0 +1,146 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useEndLaneReserve.ts
7
+ * @input Uses React and the shared ResizeObserver
8
+ * @output Exports useEndLaneReserve, which keeps a field's input clear of the
9
+ * absolutely-positioned lane at its inline end
10
+ * @position Tokenizer internal. Tokenizer parks its clear button, end content
11
+ * and busy indicator in one absolutely-positioned lane at the field's
12
+ * inline end, and this keeps the input's text and caret clear of it.
13
+ *
14
+ * It lives here, beside its one caller, rather than in `Field/` with the
15
+ * shared field internals — deliberately. A measured reserve is Tokenizer's
16
+ * workaround for a constraint only Tokenizer has: its lane must stay pinned
17
+ * to the field's first row while tokens wrap below it, so it cannot be in
18
+ * flow, and an out-of-flow box reserves nothing. Every other field, this
19
+ * one's own Typeahead included, puts its end controls in flow as flex
20
+ * siblings the way TextInput does, which needs no measuring at all. Filed
21
+ * under `Field/` this read as shared infrastructure and invited the next
22
+ * field to reach for it; that is the wrong default.
23
+ *
24
+ * SYNC: When modified, update this header and the caller:
25
+ * - /packages/core/src/Tokenizer/Tokenizer.tsx
26
+ */
27
+
28
+ import {useCallback} from 'react';
29
+ import * as stylex from '@stylexjs/stylex';
30
+ import {observeResize} from '../utils/sharedResizeObserver';
31
+
32
+ /**
33
+ * The measured lane width, published on the field wrapper and read by the
34
+ * input through inheritance. A custom property is the point of the design: it
35
+ * carries a measurement into CSS without carrying it through React, so a lane
36
+ * that grows or shrinks repaints without re-rendering the field.
37
+ */
38
+ const LANE_RESERVE_VAR = '--_tokenizer-end-lane-reserve';
39
+
40
+ // Keep the input's text and caret out from under the lane.
41
+ //
42
+ // The input's content box already stops one wrapper padding short of the
43
+ // border, and the lane is inset from that same border — so what is left for
44
+ // the input to clear is the lane's inset plus its width, less the padding it
45
+ // already has, plus a padding's worth of gap so the text does not touch the
46
+ // glyph. The two paddings cancel, which is why this reads as inset + width.
47
+ //
48
+ // The variable carries the WHOLE reserve, not just the width, so its absence
49
+ // means "no lane" rather than "a lane of zero width": with no lane up, the
50
+ // fallback resolves the padding to 0 and the input keeps its full content box.
51
+ // That is what lets the caller apply this style unconditionally, which in turn
52
+ // is what keeps the field from having to know whether a search is running.
53
+ // The rule stays static either way — one class, generated once, never
54
+ // regenerated as the lane changes.
55
+ const reserveStyles = stylex.create({
56
+ reserve: {
57
+ paddingInlineEnd: `var(${LANE_RESERVE_VAR}, 0px)`,
58
+ },
59
+ });
60
+
61
+ /**
62
+ * Keep a field's input clear of the lane at its inline end.
63
+ *
64
+ * The lane is absolutely positioned — it has to be, because these wrappers
65
+ * wrap, and an in-flow sibling gets pushed onto a second row by a token — and
66
+ * an out-of-flow box reserves no space by definition. There is no CSS that
67
+ * makes one do so: the input cannot see a sibling's width. So the lane is
68
+ * measured, and the input spends the measurement as padding.
69
+ *
70
+ * What the lane holds is not a fixed set: a clear button that comes and goes
71
+ * with the value, a busy indicator that comes and goes with the search, and
72
+ * arbitrary `endContent`. Measuring covers all of it, including the
73
+ * combinations, and needs no constant kept in step with what renders.
74
+ *
75
+ * **The measurement never enters React state.** It is written to a custom
76
+ * property on the field wrapper and inherited by the input. Held in state, it
77
+ * cost the field a second commit every time the lane changed size — once when
78
+ * the spinner arrived and again when it left — doubling the field's renders
79
+ * across a search for a value no JavaScript ever reads. The observation is
80
+ * shared as well: `observeResize` batches every field on the page into one
81
+ * callback per frame instead of one observer each.
82
+ *
83
+ * Takes the lane's inset from the field's inline-end border, as the CSS
84
+ * expression that positions it. Returns a ref callback for the lane, and the
85
+ * style for the input — which the caller applies unconditionally: with no lane
86
+ * mounted the variable is absent and the padding resolves to zero, so the
87
+ * caller never has to know whether a search is running.
88
+ */
89
+ export function useEndLaneReserve(
90
+ laneInset: string,
91
+ ): [(node: HTMLElement | null) => void, stylex.StyleXStyles] {
92
+ const laneRef = useCallback(
93
+ (node: HTMLElement | null) => {
94
+ if (node == null) {
95
+ return;
96
+ }
97
+ // The lane's parent: the field wrapper in both callers, and an ancestor
98
+ // of the input either way, which is what inheritance needs.
99
+ const host = node.parentElement;
100
+
101
+ const unobserve = observeResize(node, () => {
102
+ // `offsetWidth`, not `getBoundingClientRect().width`. The rect is in
103
+ // VIEWPORT space — it carries every CSS transform between this element
104
+ // and the root — while the padding it feeds is in the element's own
105
+ // LOCAL space. Under `scale(.5)` the rect reads half the lane's real
106
+ // width and the input reserves half of what it needs, so the query runs
107
+ // under the controls again; under `scale(2)` it reads double and the
108
+ // caret sits in a gap twice the lane's width. Measured in Chromium on a
109
+ // 123px lane in a 280px field: at `scale(.5)` the rect published 61.48px
110
+ // and the spinner covered 14px of the input's content box; at `scale(2)`
111
+ // it published 245.91px, leaving a 125.95px gap and growing the field
112
+ // from 32px to 55px tall. `offsetWidth` is the untransformed border-box
113
+ // width, so it reports 123 at every scale — the number this padding is
114
+ // actually denominated in.
115
+ //
116
+ // It is already an integer, which is the rounding the old `Math.ceil`
117
+ // was there for: a fractional width left as-is reserves a hair too
118
+ // little and the glyph's last subpixel column lands on the caret.
119
+ const width = node.offsetWidth;
120
+ if (width > 0) {
121
+ host?.style.setProperty(
122
+ LANE_RESERVE_VAR,
123
+ `calc(${laneInset} + ${width}px)`,
124
+ );
125
+ } else {
126
+ // A mounted but empty lane (no end content, no clear, not searching)
127
+ // claims nothing.
128
+ host?.style.removeProperty(LANE_RESERVE_VAR);
129
+ }
130
+ });
131
+
132
+ // React 19 runs a ref callback's return value as its cleanup, so the
133
+ // observer is released exactly when the lane unmounts.
134
+ return () => {
135
+ unobserve();
136
+ // The room the lane claimed goes back to the input. Removing beats
137
+ // setting 0px: the rule's fallback is already that, and this leaves no
138
+ // stale property behind on the DOM.
139
+ host?.style.removeProperty(LANE_RESERVE_VAR);
140
+ };
141
+ },
142
+ [laneInset],
143
+ );
144
+
145
+ return [laneRef, reserveStyles.reserve];
146
+ }
@@ -1,5 +1,21 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ /** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
4
+ const anatomy = [
5
+ {
6
+ name: 'Outer chrome',
7
+ required: true,
8
+ description:
9
+ 'Section-owned container that supplies the toolbar surface, padding, and selected divider edges.',
10
+ },
11
+ {
12
+ name: 'Toolbar',
13
+ required: true,
14
+ description:
15
+ 'Named toolbar row that owns toolbar semantics and the start, optional center, and end layout.',
16
+ },
17
+ ];
18
+
3
19
  /** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
4
20
 
5
21
  export const docs = {
@@ -147,6 +163,7 @@ export const docs = {
147
163
  ], },
148
164
  ],
149
165
  usage: {
166
+ anatomy,
150
167
  description:
151
168
  'Toolbar is a horizontal bar with left, center, and right areas. Use it for contextual actions within a content area (above a table, inside a card, or in a panel), not as a page-level header. Set the size once on the toolbar and all buttons, inputs, and tabs inside it match automatically.',
152
169
  bestPractices: [
@@ -182,6 +199,7 @@ export const docsZh = {
182
199
  },
183
200
  ],
184
201
  usage: {
202
+ anatomy,
185
203
  description:
186
204
  'Toolbar is a horizontal bar with left, center, and right areas. Use it for contextual actions within a content area (above a table, inside a card, or in a panel), not as a page-level header. Set the size once on the toolbar and all buttons, inputs, and tabs inside it match automatically.',
187
205
  bestPractices: [
@@ -200,6 +218,7 @@ export const docsZh = {
200
218
  export const docsDense = {
201
219
  description: 'Horizontal bar w/ left, center, right areas. For contextual actions in content, not page headers. Size cascades to children.',
202
220
  usage: {
221
+ anatomy,
203
222
  description:
204
223
  'Horizontal bar w/ left, optional center, right. For contextual actions within content (tables, cards, panels), not page-level headers. Size cascades to children.',
205
224
  bestPractices: [
@@ -0,0 +1,174 @@
1
+ ---
2
+ schema_version: 3
3
+ template_version: 3
4
+ kind: component
5
+ id: component:Toolbar
6
+ authority: draft
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: null
10
+ approved_at: null
11
+ owners: [cixzhang]
12
+ review_triggers: [layout, theming, accessibility]
13
+ verified_by:
14
+ [
15
+ packages/core/src/Toolbar/Toolbar.test.tsx,
16
+ packages/core/src/Section/Section.test.tsx,
17
+ packages/core/src/theme/themingTargets.test.ts,
18
+ scripts/check-knowledge.mjs,
19
+ ]
20
+ modules: []
21
+ families: [family:layout-regions]
22
+ design_specs: []
23
+ architecture:
24
+ [
25
+ architecture:component-theming-surface,
26
+ architecture:container-padding,
27
+ architecture:public-component-api,
28
+ ]
29
+ contributing: []
30
+ system_specs: []
31
+ ---
32
+
33
+ # Toolbar component contract
34
+
35
+ ## Intent
36
+
37
+ Toolbar presents a named contextual action region with one semantic toolbar row
38
+ inside Section-owned outer chrome. This draft records the current consumer
39
+ anatomy and theming ownership without changing layout, keyboard behavior,
40
+ targets, or public API.
41
+
42
+ ## Compatibility and migration
43
+
44
+ - Released default preserved: `yes`
45
+ - Compatibility class: additive documentation only; runtime, DOM, styling,
46
+ targets, and public API remain unchanged
47
+ - Controlled/uncontrolled behavior: not applicable
48
+ - Migration decision: none
49
+
50
+ Consumer migration instructions belong in consumer docs and release notes.
51
+
52
+ ## Ownership boundary
53
+
54
+ **Owns**
55
+
56
+ - The named toolbar row, toolbar semantics, start/optional-center/end layout,
57
+ keyboard movement, size cascade, and current `toolbar` target.
58
+
59
+ **Does not own / non-goals**
60
+
61
+ - The painted outer chrome, variant, block padding, and selected divider edges —
62
+ delegated to `component:Section`.
63
+ - Content supplied through the three layout slots — owned by the caller or its
64
+ rendered components.
65
+ - A general arbitrary-child layout primitive contract.
66
+ - New runtime behavior, targets, props, or target placement.
67
+
68
+ ## Public concepts
69
+
70
+ No new public concept is introduced. Consumer props, slots, and usage remain
71
+ documented in `Toolbar.doc.mjs`; `family:layout-regions` owns the shared
72
+ structural-region boundary.
73
+
74
+ ## Behavioral and layout contract
75
+
76
+ | ID | Candidate invariant | Basis | Draft review state |
77
+ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -------------------------------------------------- |
78
+ | FR1 | The current render contains Section-owned Outer chrome around one named Toolbar carrying `role="toolbar"` and the current `toolbar` target. | Current source, docs, tests, and family | Verified current behavior; no new behavior decided |
79
+ | FR2 | The Toolbar uses its current two-lane layout without center content and its current three-lane layout when center content is supplied. | Current source and focused structure tests | Verified current behavior; no layout change |
80
+ | FR3 | Variant, selected divider edges, and outer padding remain delegated to Section, while Toolbar retains toolbar semantics, keyboard behavior, and internal layout. | Current source, docs, and family contract | Verified current ownership; no target change |
81
+
82
+ ### Allowed variation
83
+
84
+ - Caller content may be absent or supplied in the start, center, and end slots
85
+ without changing Outer chrome or Toolbar ownership.
86
+ - Size and orientation may change the current row geometry and keyboard axis
87
+ without creating separate anatomy parts or targets.
88
+
89
+ ### Representative states
90
+
91
+ | State | Required invariant | Allowed variation |
92
+ | ----------------- | ------------------------------------------------------- | --------------------------------------------------- |
93
+ | Empty toolbar | Outer chrome and named Toolbar remain rendered. | No caller slot content. |
94
+ | Two-lane layout | Start and/or end content use the current flex layout. | Either lane may be absent. |
95
+ | Three-lane layout | Center content selects the current three-column layout. | Start or end content may be absent. |
96
+ | Vertical toolbar | Toolbar semantics and target remain unchanged. | Layout and keyboard movement use the vertical axis. |
97
+
98
+ ### Transformation and precedence order
99
+
100
+ - No new slot, layout, edge-compensation, padding, or event precedence rule is
101
+ introduced.
102
+
103
+ ### Performance and resources
104
+
105
+ - No new performance or resource rule is introduced.
106
+
107
+ ## Accessibility contract
108
+
109
+ This draft does not change or extend Toolbar's current accessible name,
110
+ orientation, roving-tabindex, keyboard-hint, caret-guard, or event-composition
111
+ behavior.
112
+
113
+ ## Design relationships
114
+
115
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
116
+ | ---------------- | -------------------------------------------------------------------------------- | ----------------------------------------- | -------------- | ------------------ |
117
+ | Outer chrome | Supplies the painted surface, padding, and selected boundaries around the row. | `component:Section` | Supporting | FR1, FR3 |
118
+ | Toolbar | Presents the named toolbar semantics and current two- or three-lane arrangement. | Current source, docs, and family contract | Prominent | FR1, FR2, FR3 |
119
+
120
+ The slot contents are caller-owned content inside Toolbar's layout. They do not
121
+ become additional Toolbar-owned anatomy or targets.
122
+
123
+ ### Theming anatomy
124
+
125
+ <!-- anatomy-theming:v1 -->
126
+
127
+ ```json
128
+ {
129
+ "Outer chrome": {
130
+ "delegatesTo": {"owner": "component:Section", "target": "section"}
131
+ },
132
+ "Toolbar": {"target": "toolbar"}
133
+ }
134
+ ```
135
+
136
+ ## Family and system relationships
137
+
138
+ - `family:layout-regions` owns the structural-region vocabulary and records
139
+ Toolbar as a Section-backed contextual action region.
140
+ - `architecture:container-padding` owns the inherited inset and edge-compensation
141
+ geometry used by the composed Section and Toolbar.
142
+ - `architecture:component-theming-surface` owns anatomy qualification, local
143
+ target mapping, and component delegation.
144
+ - `architecture:public-component-api` owns the stable prop and slot surface; this
145
+ documentation adds no API.
146
+
147
+ ## Verification map
148
+
149
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
150
+ | ------------------- | --------------------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------- |
151
+ | FR1 | `Toolbar.test.tsx` role, name, pass-through, and Section suites | Empty and populated Toolbar | Removing the role/name, Toolbar target, Section composition, or delegated Section target fails current evidence. | `audit:Toolbar/anatomy` |
152
+ | FR2 | `Toolbar.test.tsx` slot and child-count suites | Start-only, end-only, two-lane, and three-lane | Changing current slot presence or two-/three-lane structure fails focused assertions. | `audit:Toolbar/layout` |
153
+ | FR3 | Toolbar and Section source plus their focused tests | Variant, divider, size, and orientation states | Moving outer chrome ownership into Toolbar or moving toolbar semantics onto Section contradicts source and coverage. | `audit:Toolbar/theming` |
154
+ | Target inventory | `packages/core/src/theme/themingTargets.test.ts` | Toolbar and delegated Section targets | Runtime and documented target metadata drift fails target validation. | `audit:Toolbar/theming` |
155
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current local target | Missing, extra, prefixed, or stale mappings fail repository validation. | `audit:Toolbar/theming` |
156
+
157
+ Existing tests assert the semantic row, slot structure, and Section composition.
158
+ They do not prove computed inset or edge-compensation geometry for every parent
159
+ padding state; this documentation does not claim that coverage.
160
+
161
+ ## Decision log
162
+
163
+ None. This draft records current facts and introduces no component-local design,
164
+ layout, accessibility, or API decision.
165
+
166
+ ## Open questions
167
+
168
+ None.
169
+
170
+ ## Content boundary
171
+
172
+ This file does not duplicate consumer prop tables, examples, family layout
173
+ rules, container-padding mechanics, keyboard algorithms, implementation steps,
174
+ or system rules. It links to their owners.
@@ -1,5 +1,19 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ /** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
4
+ const anatomy = [
5
+ {
6
+ name: 'Tooltip surface',
7
+ required: true,
8
+ description: 'Painted overlay surface that presents the tooltip.',
9
+ },
10
+ {
11
+ name: 'Tooltip text',
12
+ required: true,
13
+ description: 'Tooltip content rendered within the surface.',
14
+ },
15
+ ];
16
+
3
17
  /** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
4
18
 
5
19
  export const docs = {
@@ -110,6 +124,7 @@ export const docs = {
110
124
  ],
111
125
  },
112
126
  usage: {
127
+ anatomy,
113
128
  description:
114
129
  'A short text hint that appears on hover or focus, anchored to a trigger element. Use it to describe icon-only buttons, show the full text of truncated labels, or provide supplementary context without cluttering the UI.',
115
130
  bestPractices: [
@@ -217,6 +232,7 @@ export const docsZh = {
217
232
  ],
218
233
  },
219
234
  usage: {
235
+ anatomy,
220
236
  description:
221
237
  'A short text hint that appears on hover or focus, anchored to a trigger element. Use it to describe icon-only buttons, show the full text of truncated labels, or provide supplementary context without cluttering the UI.',
222
238
  bestPractices: [
@@ -233,6 +249,7 @@ export const docsZh = {
233
249
  export const docsDense = {
234
250
  description: 'Hover/focus triggered tooltip for displaying short, non-interactive text anchored to trigger element.',
235
251
  usage: {
252
+ anatomy,
236
253
  description:
237
254
  'A short text hint that appears on hover or focus, anchored to a trigger element. Use it to describe icon-only buttons, show the full text of truncated labels, or provide supplementary context without cluttering the UI.',
238
255
  bestPractices: [