@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
@@ -1,12 +1,44 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ /** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
4
+ const anatomy = [
5
+ {
6
+ name: 'Text',
7
+ required: true,
8
+ description:
9
+ 'Polymorphic text element that renders the supplied content with themed typography.',
10
+ },
11
+ {
12
+ name: 'Heading',
13
+ required: false,
14
+ description:
15
+ 'Referenced Heading member that renders the supplied content as a semantic h1–h6 element.',
16
+ },
17
+ {
18
+ name: 'Truncation tooltip',
19
+ required: false,
20
+ description:
21
+ 'Tooltip-owned surface available only when Text or Heading is measured as truncated and truncation tooltips are enabled.',
22
+ },
23
+ ];
24
+
3
25
  /** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
4
26
 
5
27
  export const docs = {
6
28
  name: 'Text',
7
29
  displayName: 'Text',
8
30
  category: 'Content',
9
- keywords: ["text","typography","label","paragraph","heading","caption","font","body","subtitle"],
31
+ keywords: [
32
+ 'text',
33
+ 'typography',
34
+ 'label',
35
+ 'paragraph',
36
+ 'heading',
37
+ 'caption',
38
+ 'font',
39
+ 'body',
40
+ 'subtitle',
41
+ ],
10
42
  playground: {
11
43
  defaults: {
12
44
  children: 'The quick brown fox jumps over the lazy dog.',
@@ -19,12 +51,14 @@ export const docs = {
19
51
  {className: 'astryx-text', visualProps: ['type', 'size', 'color']},
20
52
  ],
21
53
  },
22
- description: 'Semantic body text component that renders text with type-based styling from the theme, with optional truncation, decoration, and layout props.',
54
+ description:
55
+ 'Semantic body text component that renders text with type-based styling from the theme, with optional truncation, decoration, and layout props.',
23
56
  props: [
24
57
  {
25
58
  name: 'type',
26
59
  type: "'body' | 'large' | 'label' | 'supporting' | 'code' | 'display-1' | 'display-2' | 'display-3' | 'inherit'",
27
- description: "Semantic text type. Determines size, weight, and line-height from the theme. 'inherit' takes all three from the surrounding text instead. Themes may add custom types. Note: this prop is called `type`, not `variant`.",
60
+ description:
61
+ "Semantic text type. Determines size, weight, and line-height from the theme. 'inherit' takes all three from the surrounding text instead. Themes may add custom types. Note: this prop is called `type`, not `variant`.",
28
62
  default: "'body'",
29
63
  },
30
64
  {
@@ -36,12 +70,14 @@ export const docs = {
36
70
  {
37
71
  name: 'size',
38
72
  type: "'4xs' | '3xs' | '2xs' | 'xsm' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'",
39
- description: 'Explicit font size override. Overrides the size from `type` but preserves other type properties. Prefer using `type` alone.',
73
+ description:
74
+ 'Explicit font size override. Overrides the size from `type` but preserves other type properties. Prefer using `type` alone.',
40
75
  },
41
76
  {
42
77
  name: 'color',
43
78
  type: "'primary' | 'secondary' | 'disabled' | 'placeholder' | 'accent' | 'inherit'",
44
- description: "Text color. Defaults to 'secondary' for the 'supporting' type, 'primary' for all others. Themes may add custom colors.",
79
+ description:
80
+ "Text color. Defaults to 'secondary' for the 'supporting' type, 'primary' for all others. Themes may add custom colors.",
45
81
  },
46
82
  {
47
83
  name: 'weight',
@@ -51,7 +87,8 @@ export const docs = {
51
87
  {
52
88
  name: 'display',
53
89
  type: "'inline' | 'block'",
54
- description: "Display type. Silently overridden to 'block' when maxLines > 0 or hasCapsize is true.",
90
+ description:
91
+ "Display type. Silently overridden to 'block' when maxLines > 0 or hasCapsize is true.",
55
92
  default: "'inline'",
56
93
  },
57
94
  {
@@ -63,19 +100,22 @@ export const docs = {
63
100
  {
64
101
  name: 'maxLines',
65
102
  type: 'number',
66
- description: 'Maximum lines before truncation. 0 means no truncation. When set, shows a tooltip on hover if content is truncated.',
103
+ description:
104
+ 'Maximum lines before truncation. 0 means no truncation. When set, shows a tooltip on hover if content is truncated.',
67
105
  default: '0',
68
106
  },
69
107
  {
70
108
  name: 'hasTruncateTooltip',
71
109
  type: "boolean | 'above' | 'below' | 'start' | 'end'",
72
- description: "Controls tooltip behavior for truncated text. true shows the tooltip at the default position, false disables it, or a placement string ('above' | 'below' | 'start' | 'end') sets a specific position.",
110
+ description:
111
+ "Controls tooltip behavior for truncated text. true shows the tooltip at the default position, false disables it, or a placement string ('above' | 'below' | 'start' | 'end') sets a specific position.",
73
112
  default: 'true',
74
113
  },
75
114
  {
76
115
  name: 'wordBreak',
77
116
  type: "'break-word' | 'break-all'",
78
- description: "Word break behavior when truncating. Defaults to 'break-all' for single-line truncation, 'break-word' otherwise.",
117
+ description:
118
+ "Word break behavior when truncating. Defaults to 'break-all' for single-line truncation, 'break-word' otherwise.",
79
119
  },
80
120
  {
81
121
  name: 'textWrap',
@@ -85,13 +125,15 @@ export const docs = {
85
125
  {
86
126
  name: 'justify',
87
127
  type: "'start' | 'center' | 'end'",
88
- description: 'Text alignment (justification). Uses logical values (start/end) for i18n/RTL compatibility.',
128
+ description:
129
+ 'Text alignment (justification). Uses logical values (start/end) for i18n/RTL compatibility.',
89
130
  default: "'start'",
90
131
  },
91
132
  {
92
133
  name: 'hasCapsize',
93
134
  type: 'boolean',
94
- description: 'Enable optical alignment using text-box-trim. Forces block display.',
135
+ description:
136
+ 'Enable optical alignment using text-box-trim. Forces block display.',
95
137
  default: 'false',
96
138
  },
97
139
  {
@@ -114,25 +156,61 @@ export const docs = {
114
156
  {
115
157
  name: 'xstyle',
116
158
  type: 'StyleXStyles',
117
- description: 'StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value: not an inline style object like style={{}}.',
159
+ description:
160
+ 'StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value: not an inline style object like style={{}}.',
118
161
  },
119
162
  ],
120
- components: [
121
- {name: 'Heading'},
122
- ],
163
+ components: [{name: 'Heading'}],
123
164
  usage: {
165
+ anatomy,
124
166
  description:
125
167
  'Text renders styled body text and headings from the theme. Use Text with a semantic type for body copy, labels, and captions, and Heading for section titles that output the correct h1\u2013h6 element.',
126
168
  bestPractices: [
127
- { guidance: true, description: 'Pick a semantic type (body, label, supporting, large, code) instead of manually setting size and weight; the theme handles the details.' },
128
- { guidance: true, description: 'Set accessibilityLevel on Heading when the visual level differs from the document outline so screen readers announce the correct hierarchy.' },
129
- { guidance: true, description: 'Use maxLines with a number to truncate long content; a tooltip appears automatically on hover so no text is lost.' },
130
- { guidance: true, description: 'Enable hasTabularNumbers for columns of numeric data so digits align vertically across rows.' },
131
- { guidance: false, description: 'Override size and weight when a semantic type already matches; extra overrides fight the theme and break when themes change.' },
132
- { guidance: false, description: 'Skip heading levels in the document outline; go h1 then h2 then h3, never h1 then h3.' },
133
- { guidance: false, description: 'Use raw HTML tags like <p>, <h1>\u2013<h6>, or <span> for text; Text and Heading apply the correct theme tokens automatically.' },
134
- { guidance: false, description: 'Pass a `variant` prop; Text does not have a `variant` prop. Use `type` for semantic styling (body, label, large, supporting, code) or use Heading for headings.' },
135
- { guidance: false, description: 'Use Text for headings; use Heading with a `level` prop (1\u20136) for section titles and headings.' },
169
+ {
170
+ guidance: true,
171
+ description:
172
+ 'Pick a semantic type (body, label, supporting, large, code) instead of manually setting size and weight; the theme handles the details.',
173
+ },
174
+ {
175
+ guidance: true,
176
+ description:
177
+ 'Set accessibilityLevel on Heading when the visual level differs from the document outline so screen readers announce the correct hierarchy.',
178
+ },
179
+ {
180
+ guidance: true,
181
+ description:
182
+ 'Use maxLines with a number to truncate long content; a tooltip appears automatically on hover so no text is lost.',
183
+ },
184
+ {
185
+ guidance: true,
186
+ description:
187
+ 'Enable hasTabularNumbers for columns of numeric data so digits align vertically across rows.',
188
+ },
189
+ {
190
+ guidance: false,
191
+ description:
192
+ 'Override size and weight when a semantic type already matches; extra overrides fight the theme and break when themes change.',
193
+ },
194
+ {
195
+ guidance: false,
196
+ description:
197
+ 'Skip heading levels in the document outline; go h1 then h2 then h3, never h1 then h3.',
198
+ },
199
+ {
200
+ guidance: false,
201
+ description:
202
+ 'Use raw HTML tags like <p>, <h1>\u2013<h6>, or <span> for text; Text and Heading apply the correct theme tokens automatically.',
203
+ },
204
+ {
205
+ guidance: false,
206
+ description:
207
+ 'Pass a `variant` prop; Text does not have a `variant` prop. Use `type` for semantic styling (body, label, large, supporting, code) or use Heading for headings.',
208
+ },
209
+ {
210
+ guidance: false,
211
+ description:
212
+ 'Use Text for headings; use Heading with a `level` prop (1\u20136) for section titles and headings.',
213
+ },
136
214
  ],
137
215
  },
138
216
  };
@@ -143,35 +221,104 @@ export const docsZh = {
143
221
  description:
144
222
  'Text renders styled body text and headings from the theme. Use Text with a semantic type for body copy, labels, and captions, and Heading for section titles that output the correct h1\u2013h6 element.',
145
223
  bestPractices: [
146
- { guidance: true, description: 'Pick a semantic type (body, label, supporting, large, code) instead of manually setting size and weight; the theme handles the details.' },
147
- { guidance: true, description: 'Set accessibilityLevel on Heading when the visual level differs from the document outline so screen readers announce the correct hierarchy.' },
148
- { guidance: true, description: 'Use maxLines with a number to truncate long content; a tooltip appears automatically on hover so no text is lost.' },
149
- { guidance: true, description: 'Enable hasTabularNumbers for columns of numeric data so digits align vertically across rows.' },
150
- { guidance: false, description: 'Override size and weight when a semantic type already matches; extra overrides fight the theme and break when themes change.' },
151
- { guidance: false, description: 'Skip heading levels in the document outline; go h1 then h2 then h3, never h1 then h3.' },
152
- { guidance: false, description: 'Use raw HTML tags like <p>, <h1>\u2013<h6>, or <span> for text; Text and Heading apply the correct theme tokens automatically.' },
153
- { guidance: false, description: 'Pass a `variant` prop; Text does not have a `variant` prop. Use `type` for semantic styling (body, label, large, supporting, code) or use Heading for headings.' },
154
- { guidance: false, description: 'Use Text for headings; use Heading with a `level` prop (1\u20136) for section titles and headings.' },
224
+ {
225
+ guidance: true,
226
+ description:
227
+ 'Pick a semantic type (body, label, supporting, large, code) instead of manually setting size and weight; the theme handles the details.',
228
+ },
229
+ {
230
+ guidance: true,
231
+ description:
232
+ 'Set accessibilityLevel on Heading when the visual level differs from the document outline so screen readers announce the correct hierarchy.',
233
+ },
234
+ {
235
+ guidance: true,
236
+ description:
237
+ 'Use maxLines with a number to truncate long content; a tooltip appears automatically on hover so no text is lost.',
238
+ },
239
+ {
240
+ guidance: true,
241
+ description:
242
+ 'Enable hasTabularNumbers for columns of numeric data so digits align vertically across rows.',
243
+ },
244
+ {
245
+ guidance: false,
246
+ description:
247
+ 'Override size and weight when a semantic type already matches; extra overrides fight the theme and break when themes change.',
248
+ },
249
+ {
250
+ guidance: false,
251
+ description:
252
+ 'Skip heading levels in the document outline; go h1 then h2 then h3, never h1 then h3.',
253
+ },
254
+ {
255
+ guidance: false,
256
+ description:
257
+ 'Use raw HTML tags like <p>, <h1>\u2013<h6>, or <span> for text; Text and Heading apply the correct theme tokens automatically.',
258
+ },
259
+ {
260
+ guidance: false,
261
+ description:
262
+ 'Pass a `variant` prop; Text does not have a `variant` prop. Use `type` for semantic styling (body, label, large, supporting, code) or use Heading for headings.',
263
+ },
264
+ {
265
+ guidance: false,
266
+ description:
267
+ 'Use Text for headings; use Heading with a `level` prop (1\u20136) for section titles and headings.',
268
+ },
155
269
  ],
156
270
  },
157
271
  };
158
272
 
159
273
  /** @type {import('@astryxdesign/cli/authoring').ComponentTranslationDoc} */
160
274
  export const docsDense = {
161
- description: 'semantic body text + headings w/ theme-driven type scale, truncation, tabular numbers',
275
+ description:
276
+ 'semantic body text + headings w/ theme-driven type scale, truncation, tabular numbers',
162
277
  usage: {
163
278
  description:
164
279
  'Text renders styled body text and headings. Text for body copy with semantic types, Heading for h1\u2013h6 with theme tokens.',
165
280
  bestPractices: [
166
- { guidance: true, description: 'Semantic type (body, label, supporting, large, code) instead of manual size/weight.' },
167
- { guidance: true, description: 'accessibilityLevel on Heading when visual level differs from document outline.' },
168
- { guidance: true, description: 'maxLines for truncation; tooltip shows full text on hover.' },
169
- { guidance: true, description: 'hasTabularNumbers for aligned numeric columns.' },
170
- { guidance: false, description: 'Override size/weight when a semantic type already matches.' },
171
- { guidance: false, description: 'Skip heading levels; sequential h1 \u2192 h2 \u2192 h3.' },
172
- { guidance: false, description: 'Raw <p>/<h1>/<span>; use Text/Heading for theme tokens.' },
173
- { guidance: false, description: '`variant` prop, which does not exist. Use `type` for text styling or Heading for headings.' },
174
- { guidance: false, description: 'Text for headings: use Heading with level (1\u20136).' },
281
+ {
282
+ guidance: true,
283
+ description:
284
+ 'Semantic type (body, label, supporting, large, code) instead of manual size/weight.',
285
+ },
286
+ {
287
+ guidance: true,
288
+ description:
289
+ 'accessibilityLevel on Heading when visual level differs from document outline.',
290
+ },
291
+ {
292
+ guidance: true,
293
+ description:
294
+ 'maxLines for truncation; tooltip shows full text on hover.',
295
+ },
296
+ {
297
+ guidance: true,
298
+ description: 'hasTabularNumbers for aligned numeric columns.',
299
+ },
300
+ {
301
+ guidance: false,
302
+ description:
303
+ 'Override size/weight when a semantic type already matches.',
304
+ },
305
+ {
306
+ guidance: false,
307
+ description: 'Skip heading levels; sequential h1 \u2192 h2 \u2192 h3.',
308
+ },
309
+ {
310
+ guidance: false,
311
+ description: 'Raw <p>/<h1>/<span>; use Text/Heading for theme tokens.',
312
+ },
313
+ {
314
+ guidance: false,
315
+ description:
316
+ '`variant` prop, which does not exist. Use `type` for text styling or Heading for headings.',
317
+ },
318
+ {
319
+ guidance: false,
320
+ description: 'Text for headings: use Heading with level (1\u20136).',
321
+ },
175
322
  ],
176
323
  },
177
324
  };
@@ -0,0 +1,206 @@
1
+ ---
2
+ schema_version: 3
3
+ template_version: 3
4
+ kind: component
5
+ id: component:Text
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: [theming]
13
+ verified_by:
14
+ [
15
+ packages/core/src/Text/Text.test.tsx,
16
+ packages/core/src/Heading/Heading.test.tsx,
17
+ packages/core/src/theme/themingTargets.test.ts,
18
+ scripts/check-knowledge.mjs,
19
+ ]
20
+ modules: []
21
+ families: []
22
+ design_specs: []
23
+ architecture: [architecture:component-theming-surface]
24
+ contributing: []
25
+ system_specs: []
26
+ ---
27
+
28
+ # Text component contract
29
+
30
+ ## Intent
31
+
32
+ Text renders themed body text through the Text component and semantic headings
33
+ through its referenced Heading member. This draft records their current consumer
34
+ anatomy, the two live root targets owned by the canonical Text documentation, and
35
+ the conditional Tooltip delegation used for truncated content. It does not
36
+ change runtime behavior, styling, targets, or public API.
37
+
38
+ ## Compatibility and migration
39
+
40
+ - Released default preserved: `yes`
41
+ - Compatibility class: additive documentation only; runtime, DOM, styling,
42
+ targets, and public API remain unchanged
43
+ - Controlled/uncontrolled behavior: not applicable
44
+ - Migration decision: none
45
+
46
+ Consumer migration instructions belong in consumer docs and release notes.
47
+
48
+ ## Ownership boundary
49
+
50
+ **Owns**
51
+
52
+ - The polymorphic Text element and its current `text` theming target.
53
+ - The referenced Heading member's semantic heading element and its current
54
+ `heading` theming target.
55
+ - Deciding from the current truncation state whether to compose Tooltip against
56
+ the Text or Heading element.
57
+
58
+ **Does not own / non-goals**
59
+
60
+ - The truncation tooltip surface, layer behavior, or `tooltip` target — owned by
61
+ Tooltip when that composed surface renders.
62
+ - Content supplied as Text or Heading children — owned by the caller.
63
+ - A separate Heading component contract; Heading remains a referenced member of
64
+ the canonical Text documentation and target inventory.
65
+ - New typography behavior, truncation behavior, semantics, styling, targets,
66
+ public API, or target aliases.
67
+
68
+ ## Public concepts
69
+
70
+ No new public concept is introduced. Text and Heading props, defaults, and usage
71
+ remain documented in `Text.doc.mjs` and `Heading.doc.mjs`.
72
+
73
+ ## Behavioral and layout contract
74
+
75
+ | ID | Candidate invariant | Basis | Draft review state |
76
+ | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------- |
77
+ | FR1 | Text renders one polymorphic text element carrying the current `text` target. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
78
+ | FR2 | Heading renders one semantic `h1`–`h6` element carrying the current `heading` target. Heading is a referenced member of Text rather than a nested Text element. | Current source, docs, tests, and target history | Verified current behavior and canonical ownership |
79
+ | FR3 | Text or Heading composes Tooltip only when `maxLines` is positive, truncation tooltips are enabled, and measurement reports that the rendered content is truncated. Otherwise the truncation Tooltip path does not render. | Current source, tests, and truncation history | Verified current conditional behavior |
80
+ | FR4 | When the conditional truncation path renders, Tooltip owns the layer surface and applies the `tooltip` target. Text and Heading retain their own root targets and do not apply or claim Tooltip's target. | Current source and owner target metadata | Verified current ownership; no target change |
81
+ | FR5 | Text reflects `type`, `size`, and resolved `color` on `text`; Heading reflects `level`, `color`, and an explicitly supplied `type` on `heading`. Display mode, truncation state, semantic element choice, and Tooltip open state do not become separate anatomy parts or Text-owned targets. | Current source, docs, tests, and target inventory | Verified current capabilities; no new anatomy decided |
82
+
83
+ ### Observed current behavior
84
+
85
+ These observations describe the implementation; they do not establish new
86
+ intent:
87
+
88
+ - Text defaults to a `span` and may render another supported text element through
89
+ `as`. Heading maps `level` directly to `h1` through `h6`.
90
+ - Both members render caller content directly inside their targeted root element;
91
+ neither creates a separately targeted content wrapper.
92
+ - Both members measure truncation only when `maxLines` is positive. When the
93
+ measurement reports overflow and `hasTruncateTooltip` is not `false`, they
94
+ lazy-load Tooltip in sibling-anchor mode.
95
+ - Tooltip owns its layer lifecycle and applies `tooltip` only to its own rendered
96
+ layer. The truncated Text or Heading element remains the anchor and keeps only
97
+ its own target.
98
+ - Text and Heading no longer add a native `title` for this path; the composed
99
+ Tooltip is the single tooltip presentation.
100
+
101
+ ### Allowed variation
102
+
103
+ - **AV1 — Text element and content.** Text may use any currently supported `as`
104
+ element and either member may contain caller-supplied content without changing
105
+ target ownership.
106
+ - **AV2 — Typography.** Level, type, size, color, weight, wrapping, decoration,
107
+ and theme or consumer styling may vary through the existing APIs and targets.
108
+ - **AV3 — Conditional tooltip.** Fitting content, disabled truncation tooltips,
109
+ and non-truncating configurations omit the delegated Tooltip path.
110
+
111
+ ### Representative states
112
+
113
+ | State | Required invariant | Allowed variation |
114
+ | ----------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------- |
115
+ | Text without truncation | One Text element carries `text`; no truncation Tooltip path renders. | Supported element, content, and typography props. |
116
+ | Heading without truncation | One semantic heading carries `heading`; no Text target or truncation Tooltip path renders. | Heading level, accessibility level, and type. |
117
+ | Truncation configured but content fits | The owning Text or Heading root remains; no truncation Tooltip path renders. | Line limit and available width. |
118
+ | Truncated with tooltip disabled | The owning root remains clipped; no truncation Tooltip path renders. | Text or Heading owner. |
119
+ | Truncated with tooltip enabled and opened | The owning root anchors one Tooltip-owned surface carrying `tooltip`. | Placement and full text content. |
120
+
121
+ ### Transformation and precedence order
122
+
123
+ - No new typography, truncation, element-selection, or styling precedence rule is
124
+ introduced.
125
+
126
+ ### Performance and resources
127
+
128
+ - This draft records the current shared ResizeObserver-based truncation path and
129
+ lazy Tooltip composition but introduces no new measurement, observer, render,
130
+ or loading requirement.
131
+
132
+ ## Accessibility contract
133
+
134
+ This draft does not change or extend Text's polymorphic semantics, Heading's
135
+ native heading level and optional `aria-level`, or Tooltip's existing
136
+ `aria-describedby` ownership for truncated content.
137
+
138
+ ## Design relationships
139
+
140
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
141
+ | ------------------ | --------------------------------------------------------------------------------- | ------------------------------ | -------------- | ------------------ |
142
+ | Text | Presents caller content in the current polymorphic text element. | Current source and public docs | Prominent | FR1, FR5 |
143
+ | Heading | Presents caller content in the referenced semantic Heading member. | Current source and public docs | Prominent | FR2, FR5 |
144
+ | Truncation tooltip | Presents the full content only on the current measured-overflow and enabled path. | `component:Tooltip` | Supporting | FR3, FR4 |
145
+
146
+ Text and Heading are sibling public components represented by the canonical Text
147
+ documentation; Heading is not nested inside Text. The Tooltip row is conditional
148
+ composition rather than a Text- or Heading-owned surface.
149
+
150
+ ### Theming anatomy
151
+
152
+ <!-- anatomy-theming:v1 -->
153
+
154
+ ```json
155
+ {
156
+ "Text": {"target": "text"},
157
+ "Heading": {"target": "heading"},
158
+ "Truncation tooltip": {
159
+ "delegatesTo": {"owner": "component:Tooltip", "target": "tooltip"}
160
+ }
161
+ }
162
+ ```
163
+
164
+ The two local targets are the exact current targets declared by the canonical
165
+ Text doc. The delegated `tooltip` target applies only when the conditional
166
+ truncation path reaches Tooltip and Tooltip renders its layer; it is not a
167
+ Text- or Heading-owned target. No anatomy part requires a `none` disposition.
168
+
169
+ ## Family and system relationships
170
+
171
+ - `architecture:component-theming-surface` owns anatomy qualification, aggregate
172
+ parent/member target ownership, exact target mapping, and delegation.
173
+ - Text is the canonical documentation and contract owner for both `text` and
174
+ `heading`; Heading remains a referenced member and does not receive a separate
175
+ component spec.
176
+ - Tooltip retains ownership of its delegated surface, interaction behavior, and
177
+ `tooltip` target.
178
+
179
+ ## Verification map
180
+
181
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
182
+ | ------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------- |
183
+ | FR1, FR5 | `Text.test.tsx` and `themingTargets.test.ts` | Default, polymorphic, custom type/color, explicit size | Removing, renaming, or changing the current Text target capabilities fails focused or global target assertions. | `audit:Text/theming` |
184
+ | FR2, FR5 | `Heading.test.tsx`, `themingTargets.test.ts`, and target-introduction history | Levels 1–6, display type, custom color | Removing, renaming, or changing the current Heading target capabilities fails focused or global assertions. | `audit:Heading/theming` |
185
+ | FR3, FR4 | Text and Heading source inspection, truncation tests, Tooltip target metadata, and fix history | Fitting, overflowing, disabled, enabled | Moving the surface or target into Text/Heading, or restoring a native duplicate tooltip, violates current evidence. | `audit:Text/anatomy` |
186
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy, both local targets, Tooltip delegate | Missing, extra, prefixed, stale, multiply assigned, or invalid delegated mappings fail repository validation. | `audit:Text/theming` |
187
+
188
+ Current focused tests pin both local target names and Text's lack of a duplicate
189
+ native `title`. The conditional Tooltip path and Heading's matching no-`title`
190
+ behavior are established by shared source shape and history rather than a
191
+ separate focused assertion in each suite.
192
+
193
+ ## Decision log
194
+
195
+ None. This draft records current facts and introduces no component-local design,
196
+ API, behavior, accessibility, or theming decision.
197
+
198
+ ## Open questions
199
+
200
+ None.
201
+
202
+ ## Content boundary
203
+
204
+ This file does not duplicate consumer prop tables/examples, typography token
205
+ values, truncation measurement mechanics, Tooltip internals, implementation
206
+ steps, current audit results, or system theming rules. It links to their owners.
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import {useCallback, useRef, useState, type RefCallback} from 'react';
16
- import {observeResize, unobserveResize} from '../utils/sharedResizeObserver';
16
+ import {observeResize} from '../utils/sharedResizeObserver';
17
17
 
18
18
  export interface UseTruncationOptions {
19
19
  /**
@@ -43,7 +43,7 @@ export interface UseTruncationReturn {
43
43
  /**
44
44
  * Hook for detecting text overflow/truncation.
45
45
  *
46
- * Uses a shared ResizeObserver singleton (via observeResize/unobserveResize)
46
+ * Uses a shared ResizeObserver singleton (via observeResize)
47
47
  * for efficient detection when content or container changes. A single
48
48
  * ResizeObserver instance is shared across all mounted useTruncation hooks,
49
49
  * so even hundreds of table cells only create one observer.
@@ -70,6 +70,7 @@ export function useTruncation(
70
70
  const [isTruncated, setIsTruncated] = useState(false);
71
71
  const [fullText, setFullText] = useState('');
72
72
  const elementRef = useRef<HTMLElement | null>(null);
73
+ const unobserveRef = useRef<(() => void) | null>(null);
73
74
 
74
75
  const checkTruncation = useCallback(
75
76
  (element: HTMLElement) => {
@@ -108,10 +109,10 @@ export function useTruncation(
108
109
 
109
110
  const ref: RefCallback<HTMLElement> = useCallback(
110
111
  (element: HTMLElement | null) => {
111
- // Cleanup previous observation
112
- if (elementRef.current) {
113
- unobserveResize(elementRef.current);
114
- }
112
+ // Cleanup previous observation. Unsubscribing by callback rather than
113
+ // by element leaves any other observer of the same node untouched.
114
+ unobserveRef.current?.();
115
+ unobserveRef.current = null;
115
116
 
116
117
  elementRef.current = element;
117
118
 
@@ -120,7 +121,7 @@ export function useTruncation(
120
121
  // observeResize fires the callback once on registration,
121
122
  // so no separate initial check is needed.
122
123
  if (typeof ResizeObserver !== 'undefined') {
123
- observeResize(element, () => {
124
+ unobserveRef.current = observeResize(element, () => {
124
125
  checkTruncation(element);
125
126
  });
126
127
  } else {