@astryxdesign/core 0.1.7 → 0.1.8-canary.08a0957

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 (262) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +30 -19
  3. package/dist/AppShell/AppShell.d.ts.map +1 -1
  4. package/dist/AppShell/AppShell.js +6 -2
  5. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  6. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  7. package/dist/Avatar/Avatar.d.ts +10 -5
  8. package/dist/Avatar/Avatar.d.ts.map +1 -1
  9. package/dist/Avatar/Avatar.js +12 -8
  10. package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
  11. package/dist/Avatar/AvatarSizeContext.js +1 -1
  12. package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
  13. package/dist/Avatar/AvatarStatusDot.js +2 -2
  14. package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
  15. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  16. package/dist/AvatarGroup/AvatarGroup.js +2 -2
  17. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  19. package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
  20. package/dist/Banner/Banner.d.ts.map +1 -1
  21. package/dist/Button/Button.d.ts.map +1 -1
  22. package/dist/Button/Button.js +1 -0
  23. package/dist/Carousel/Carousel.d.ts +3 -2
  24. package/dist/Carousel/Carousel.d.ts.map +1 -1
  25. package/dist/Carousel/Carousel.js +32 -2
  26. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  27. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  28. package/dist/Chat/ChatMessage.d.ts +1 -1
  29. package/dist/Chat/ChatMessage.d.ts.map +1 -1
  30. package/dist/Chat/ChatMessage.js +1 -1
  31. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  32. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  33. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  34. package/dist/Citation/Citation.d.ts.map +1 -1
  35. package/dist/Citation/Citation.js +10 -4
  36. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  37. package/dist/Collapsible/Collapsible.d.ts +11 -1
  38. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  39. package/dist/Collapsible/Collapsible.js +36 -4
  40. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  41. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  42. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  43. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  44. package/dist/ContextMenu/ContextMenu.js +4 -3
  45. package/dist/ContextMenu/index.d.ts +3 -0
  46. package/dist/ContextMenu/index.d.ts.map +1 -1
  47. package/dist/ContextMenu/index.js +7 -1
  48. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  49. package/dist/Dialog/Dialog.d.ts.map +1 -1
  50. package/dist/Dialog/Dialog.js +2 -1
  51. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  52. package/dist/DropdownMenu/DropdownMenu.js +8 -4
  53. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +88 -0
  54. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
  55. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +169 -0
  56. package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
  57. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  58. package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
  59. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +68 -0
  60. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
  61. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +69 -0
  62. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
  63. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
  64. package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
  65. package/dist/DropdownMenu/index.d.ts +5 -1
  66. package/dist/DropdownMenu/index.d.ts.map +1 -1
  67. package/dist/DropdownMenu/index.js +10 -1
  68. package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
  69. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
  70. package/dist/DropdownMenu/menuItemRoles.js +15 -0
  71. package/dist/FileInput/FileInput.d.ts.map +1 -1
  72. package/dist/Icon/Icon.d.ts +43 -13
  73. package/dist/Icon/Icon.d.ts.map +1 -1
  74. package/dist/Icon/Icon.js +62 -27
  75. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  76. package/dist/MobileNav/MobileNav.js +6 -1
  77. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  78. package/dist/MobileNav/MobileNavToggle.js +4 -0
  79. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  80. package/dist/MultiSelector/MultiSelector.js +36 -10
  81. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  82. package/dist/Outline/Outline.d.ts +68 -3
  83. package/dist/Outline/Outline.d.ts.map +1 -1
  84. package/dist/Outline/Outline.js +128 -26
  85. package/dist/Outline/useScrollSpy.d.ts +27 -7
  86. package/dist/Outline/useScrollSpy.d.ts.map +1 -1
  87. package/dist/Outline/useScrollSpy.js +202 -70
  88. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  89. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  90. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  91. package/dist/TabList/Tab.d.ts.map +1 -1
  92. package/dist/TabList/Tab.js +2 -2
  93. package/dist/TabList/TabList.d.ts.map +1 -1
  94. package/dist/TabList/TabList.js +2 -0
  95. package/dist/TabList/TabMenu.d.ts.map +1 -1
  96. package/dist/TabList/TabMenu.js +1 -1
  97. package/dist/Table/Table.d.ts +1 -1
  98. package/dist/Table/Table.js +1 -1
  99. package/dist/Table/index.d.ts +4 -0
  100. package/dist/Table/index.d.ts.map +1 -1
  101. package/dist/Table/index.js +2 -0
  102. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  103. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  104. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
  105. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  106. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  107. package/dist/Table/plugins/rowStatus/index.js +3 -0
  108. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  109. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  110. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  111. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  112. package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
  113. package/dist/Table/plugins/tree/index.d.ts +5 -0
  114. package/dist/Table/plugins/tree/index.d.ts.map +1 -0
  115. package/dist/Table/plugins/tree/index.js +4 -0
  116. package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
  117. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
  118. package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
  119. package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
  120. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
  121. package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
  122. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  123. package/dist/Table/useBaseTablePlugins.js +6 -3
  124. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  125. package/dist/Toolbar/Toolbar.js +8 -1
  126. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  127. package/dist/TreeList/TreeListItem.js +5 -14
  128. package/dist/astryx.css +26 -4
  129. package/dist/astryx.umd.js +54 -54
  130. package/dist/astryx.umd.js.map +4 -4
  131. package/dist/authoring/doc.d.ts.map +1 -1
  132. package/dist/theme/Theme.js +1 -1
  133. package/dist/theme/defineTheme.d.ts +1 -1
  134. package/dist/theme/defineTheme.js +1 -1
  135. package/dist/theme/hct.d.ts +0 -1
  136. package/dist/theme/hct.d.ts.map +1 -1
  137. package/dist/theme/hct.js +0 -3
  138. package/docs.mjs +32 -461
  139. package/locales/en.json +200 -172
  140. package/locales/pseudo.json +21 -0
  141. package/package.json +3 -1
  142. package/scripts/postinstall.mjs +93 -0
  143. package/src/AppShell/AppShell.tsx +6 -0
  144. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  145. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  146. package/src/Avatar/Avatar.doc.mjs +6 -6
  147. package/src/Avatar/Avatar.tsx +16 -11
  148. package/src/Avatar/AvatarSizeContext.ts +1 -1
  149. package/src/Avatar/AvatarStatusDot.tsx +2 -2
  150. package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
  151. package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
  152. package/src/AvatarGroup/AvatarGroup.tsx +3 -3
  153. package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
  154. package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
  155. package/src/Banner/Banner.tsx +10 -2
  156. package/src/Button/Button.test.tsx +21 -0
  157. package/src/Button/Button.tsx +1 -0
  158. package/src/Carousel/Carousel.doc.mjs +1 -0
  159. package/src/Carousel/Carousel.test.tsx +96 -1
  160. package/src/Carousel/Carousel.tsx +29 -2
  161. package/src/Chat/Chat.doc.mjs +1 -1
  162. package/src/Chat/ChatLayout.tsx +3 -1
  163. package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
  164. package/src/Chat/ChatMessage.tsx +4 -2
  165. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  166. package/src/Chat/ChatSendButton.tsx +5 -1
  167. package/src/Chat/useTriggerMenu.tsx +3 -1
  168. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  169. package/src/Citation/Citation.test.tsx +51 -0
  170. package/src/Citation/Citation.tsx +8 -2
  171. package/src/CodeBlock/CodeBlock.tsx +3 -1
  172. package/src/Collapsible/Collapsible.doc.mjs +7 -0
  173. package/src/Collapsible/Collapsible.test.tsx +80 -0
  174. package/src/Collapsible/Collapsible.tsx +58 -5
  175. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  176. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  177. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  178. package/src/ContextMenu/ContextMenu.test.tsx +75 -1
  179. package/src/ContextMenu/ContextMenu.tsx +10 -5
  180. package/src/ContextMenu/index.ts +15 -0
  181. package/src/DateTimeInput/DateTimeInput.tsx +3 -1
  182. package/src/Dialog/Dialog.tsx +12 -2
  183. package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
  184. package/src/DropdownMenu/DropdownMenu.tsx +11 -6
  185. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
  186. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +236 -0
  187. package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
  188. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
  189. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
  190. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +118 -0
  191. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
  192. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
  193. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
  194. package/src/DropdownMenu/index.ts +23 -3
  195. package/src/DropdownMenu/menuItemRoles.ts +22 -0
  196. package/src/Field/Field.doc.mjs +1 -1
  197. package/src/Field/FieldLabel.doc.mjs +1 -1
  198. package/src/FileInput/FileInput.tsx +4 -1
  199. package/src/Icon/Icon.doc.mjs +12 -0
  200. package/src/Icon/Icon.test.tsx +142 -0
  201. package/src/Icon/Icon.tsx +98 -30
  202. package/src/Item/Item.doc.mjs +1 -1
  203. package/src/MobileNav/MobileNav.tsx +12 -1
  204. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  205. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  206. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  207. package/src/MultiSelector/MultiSelector.test.tsx +79 -1
  208. package/src/MultiSelector/MultiSelector.tsx +51 -11
  209. package/src/NumberInput/NumberInput.doc.mjs +2 -2
  210. package/src/NumberInput/NumberInput.tsx +1 -2
  211. package/src/Outline/Outline.doc.mjs +123 -3
  212. package/src/Outline/Outline.test.tsx +735 -0
  213. package/src/Outline/Outline.tsx +183 -33
  214. package/src/Outline/useScrollSpy.ts +248 -72
  215. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  216. package/src/Selector/SelectorOption.doc.mjs +1 -1
  217. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  218. package/src/SideNav/SideNavItem.doc.mjs +2 -2
  219. package/src/SideNav/SideNavItem.tsx +5 -1
  220. package/src/Switch/Switch.doc.mjs +1 -1
  221. package/src/TabList/Tab.tsx +5 -1
  222. package/src/TabList/TabList.doc.mjs +3 -3
  223. package/src/TabList/TabList.test.tsx +66 -0
  224. package/src/TabList/TabList.tsx +8 -0
  225. package/src/TabList/TabMenu.tsx +4 -1
  226. package/src/Table/Table.doc.mjs +2 -0
  227. package/src/Table/Table.tsx +1 -1
  228. package/src/Table/index.ts +12 -0
  229. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  230. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
  231. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
  232. package/src/Table/plugins/rowStatus/index.ts +7 -0
  233. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  234. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  235. package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
  236. package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
  237. package/src/Table/plugins/tree/index.ts +12 -0
  238. package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
  239. package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
  240. package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
  241. package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
  242. package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
  243. package/src/Table/useBaseTablePlugins.ts +6 -2
  244. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  245. package/src/Table/useTableTreeData.doc.mjs +75 -0
  246. package/src/Table/useTableTreeState.doc.mjs +98 -0
  247. package/src/TextArea/TextArea.doc.mjs +1 -1
  248. package/src/TextInput/TextInput.doc.mjs +1 -1
  249. package/src/Toolbar/Toolbar.tsx +10 -3
  250. package/src/TreeList/TreeList.test.tsx +32 -1
  251. package/src/TreeList/TreeListItem.tsx +19 -10
  252. package/src/authoring/doc.ts +1 -3
  253. package/src/docs-script.test.mjs +18 -13
  254. package/src/postinstall.test.mjs +71 -0
  255. package/src/theme/Theme.doc.mjs +4 -4
  256. package/src/theme/Theme.tsx +1 -1
  257. package/src/theme/defineTheme.ts +1 -1
  258. package/src/theme/hct.ts +0 -4
  259. package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
  260. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
  261. package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
  262. package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
@@ -0,0 +1,253 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useTableTreeState.tsx
7
+ * @input React
8
+ * @output Exports useTableTreeState hook and config/result types
9
+ * @position Tree state helper; owns the expanded set + flattens nested data.
10
+ * Pairs with useTableTreeData (the headless tree plugin).
11
+ *
12
+ * Modeled after useTableSortableState — a convenience layer that owns state
13
+ * (controlled or uncontrolled) and produces both the visible flattened rows
14
+ * and a ready-to-use config for the headless plugin. Data shaping stays
15
+ * outside the render pipeline: Table always receives exactly the rows it
16
+ * renders, so collapsed subtrees are unmounted, not hidden.
17
+ */
18
+
19
+ import {useCallback, useMemo, useRef, useState} from 'react';
20
+ import type {
21
+ TableTreeRowMeta,
22
+ UseTableTreeDataConfig,
23
+ } from './useTableTreeData';
24
+
25
+ // =============================================================================
26
+ // Config
27
+ // =============================================================================
28
+
29
+ export interface UseTableTreeStateConfig<T extends Record<string, unknown>> {
30
+ /** Nested data: rows may carry child rows under `childrenKey`. */
31
+ data: T[];
32
+ /** Row ID accessor: property name, or a function returning a unique id. */
33
+ idKey: (keyof T & string) | ((item: T) => string | number);
34
+ /**
35
+ * Property holding each row's children array.
36
+ * @default 'children'
37
+ */
38
+ childrenKey?: string;
39
+ /** Initial expanded row ids for uncontrolled mode. Ignored when `expandedIds` is provided. */
40
+ defaultExpandedIds?: Iterable<string>;
41
+ /**
42
+ * Controlled set of expanded row ids. When provided, the hook uses this
43
+ * instead of internal state. Pair with `onExpandedIdsChange`.
44
+ */
45
+ expandedIds?: ReadonlySet<string>;
46
+ /** Called with the next expanded set whenever expansion changes. */
47
+ onExpandedIdsChange?: (ids: ReadonlySet<string>) => void;
48
+ /**
49
+ * Should this row show an expander? Overrides the default
50
+ * "has a non-empty children array" check — use for lazy loading, where a
51
+ * row is expandable before its children have been fetched.
52
+ */
53
+ isItemExpandable?: (item: T) => boolean;
54
+ /**
55
+ * Sort each sibling group independently during flattening. Children always
56
+ * stay directly under their parent — sorting never crosses levels. Pass
57
+ * `applySort` from `useTableSortableState` to compose with column sorting.
58
+ */
59
+ sortSiblings?: (siblings: T[]) => T[];
60
+ /** Indent step per level, forwarded to useTableTreeData. */
61
+ indent?: 'sm' | 'md' | 'lg';
62
+ /** Column that carries the tree affordance, forwarded to useTableTreeData. */
63
+ treeColumnKey?: string;
64
+ }
65
+
66
+ // =============================================================================
67
+ // Result
68
+ // =============================================================================
69
+
70
+ export interface UseTableTreeStateResult<T extends Record<string, unknown>> {
71
+ /** The flattened, currently-visible rows. Pass to `<Table data>`. */
72
+ visibleData: T[];
73
+ /** Ready-to-use config for useTableTreeData. */
74
+ treeConfig: UseTableTreeDataConfig<T>;
75
+ /** The current expanded row ids. */
76
+ expandedIds: ReadonlySet<string>;
77
+ /** Expand every expandable row in the tree. */
78
+ expandAll: () => void;
79
+ /** Collapse every row. */
80
+ collapseAll: () => void;
81
+ }
82
+
83
+ // =============================================================================
84
+ // Hook
85
+ // =============================================================================
86
+
87
+ export function useTableTreeState<T extends Record<string, unknown>>(
88
+ config: UseTableTreeStateConfig<T>,
89
+ ): UseTableTreeStateResult<T> {
90
+ const {
91
+ data,
92
+ idKey,
93
+ childrenKey = 'children',
94
+ defaultExpandedIds,
95
+ expandedIds: controlledExpandedIds,
96
+ onExpandedIdsChange,
97
+ isItemExpandable,
98
+ sortSiblings,
99
+ indent,
100
+ treeColumnKey,
101
+ } = config;
102
+
103
+ const getId = useCallback(
104
+ (item: T): string =>
105
+ typeof idKey === 'function' ? String(idKey(item)) : String(item[idKey]),
106
+ [idKey],
107
+ );
108
+
109
+ const getChildren = useCallback(
110
+ (item: T): T[] => {
111
+ const children = item[childrenKey];
112
+ return Array.isArray(children) ? (children as T[]) : [];
113
+ },
114
+ [childrenKey],
115
+ );
116
+
117
+ const getIsExpandable = useCallback(
118
+ (item: T): boolean =>
119
+ isItemExpandable ? isItemExpandable(item) : getChildren(item).length > 0,
120
+ [isItemExpandable, getChildren],
121
+ );
122
+
123
+ // Internal state (used in uncontrolled mode)
124
+ const [internalExpandedIds, setInternalExpandedIds] = useState<
125
+ ReadonlySet<string>
126
+ >(() => new Set(defaultExpandedIds));
127
+
128
+ // Resolve controlled vs uncontrolled
129
+ const isControlled = controlledExpandedIds !== undefined;
130
+ const expandedIds = isControlled
131
+ ? controlledExpandedIds
132
+ : internalExpandedIds;
133
+
134
+ // Stable refs so callbacks don't churn on inline-prop identity changes
135
+ const expandedIdsRef = useRef(expandedIds);
136
+ expandedIdsRef.current = expandedIds;
137
+ const onExpandedIdsChangeRef = useRef(onExpandedIdsChange);
138
+ onExpandedIdsChangeRef.current = onExpandedIdsChange;
139
+
140
+ const commitExpandedIds = useCallback(
141
+ (next: ReadonlySet<string>) => {
142
+ // Advance the ref immediately so a second commit in the same React
143
+ // batch (e.g. two onToggleItem calls in one event handler) builds
144
+ // on this one instead of the pre-batch set. The ref re-syncs from
145
+ // the resolved state on the next render either way.
146
+ expandedIdsRef.current = next;
147
+ if (!isControlled) {
148
+ setInternalExpandedIds(next);
149
+ }
150
+ onExpandedIdsChangeRef.current?.(next);
151
+ },
152
+ [isControlled],
153
+ );
154
+
155
+ // Flatten: depth-first walk emitting only visible rows, collecting per-row
156
+ // meta ({level, hasChildren, isExpanded}) in the same pass. Children mount
157
+ // only when their parent is expanded, so collapsed subtrees stay unmounted.
158
+ // `path` holds the ids on the current ancestor chain: an edge pointing back
159
+ // at an ancestor is skipped instead of recursing forever. Sibling arrays
160
+ // are copied before sortSiblings so an in-place sorter can't reorder the
161
+ // consumer's data.
162
+ const {visibleData, metaMap} = useMemo(() => {
163
+ const rows: T[] = [];
164
+ const meta = new Map<string, TableTreeRowMeta>();
165
+ const path = new Set<string>();
166
+ const walk = (items: T[], level: number) => {
167
+ const siblings = sortSiblings ? sortSiblings([...items]) : items;
168
+ for (const item of siblings) {
169
+ const id = getId(item);
170
+ if (path.has(id)) {
171
+ continue; // cyclic edge — this row is its own ancestor
172
+ }
173
+ const hasChildren = getIsExpandable(item);
174
+ const isExpanded = hasChildren && expandedIds.has(id);
175
+ rows.push(item);
176
+ meta.set(id, {id, level, hasChildren, isExpanded});
177
+ if (isExpanded) {
178
+ path.add(id);
179
+ walk(getChildren(item), level + 1);
180
+ path.delete(id);
181
+ }
182
+ }
183
+ };
184
+ walk(data, 0);
185
+ return {visibleData: rows, metaMap: meta};
186
+ }, [data, expandedIds, getId, getChildren, getIsExpandable, sortSiblings]);
187
+
188
+ // Every expandable row id across the whole tree, including collapsed
189
+ // subtrees (drives expandAll and the migration no-op flag). Same
190
+ // ancestor-chain guard as the flatten walk.
191
+ const allExpandableIds = useMemo(() => {
192
+ const idsAcc: string[] = [];
193
+ const path = new Set<string>();
194
+ const walk = (items: T[]) => {
195
+ for (const item of items) {
196
+ const id = getId(item);
197
+ if (path.has(id)) {
198
+ continue;
199
+ }
200
+ if (getIsExpandable(item)) {
201
+ idsAcc.push(id);
202
+ }
203
+ path.add(id);
204
+ walk(getChildren(item));
205
+ path.delete(id);
206
+ }
207
+ };
208
+ walk(data);
209
+ return idsAcc;
210
+ }, [data, getId, getChildren, getIsExpandable]);
211
+
212
+ const hasExpandableRows = allExpandableIds.length > 0;
213
+
214
+ const onToggleItem = useCallback(
215
+ (item: T) => {
216
+ const id = getId(item);
217
+ const next = new Set(expandedIdsRef.current);
218
+ if (next.has(id)) {
219
+ next.delete(id);
220
+ } else {
221
+ next.add(id);
222
+ }
223
+ commitExpandedIds(next);
224
+ },
225
+ [getId, commitExpandedIds],
226
+ );
227
+
228
+ const expandAll = useCallback(() => {
229
+ commitExpandedIds(new Set(allExpandableIds));
230
+ }, [commitExpandedIds, allExpandableIds]);
231
+
232
+ const collapseAll = useCallback(() => {
233
+ commitExpandedIds(new Set());
234
+ }, [commitExpandedIds]);
235
+
236
+ const getRowMeta = useCallback(
237
+ (item: T): TableTreeRowMeta | undefined => metaMap.get(getId(item)),
238
+ [metaMap, getId],
239
+ );
240
+
241
+ const treeConfig = useMemo(
242
+ (): UseTableTreeDataConfig<T> => ({
243
+ getRowMeta,
244
+ onToggleItem,
245
+ hasExpandableRows,
246
+ indent,
247
+ treeColumnKey,
248
+ }),
249
+ [getRowMeta, onToggleItem, hasExpandableRows, indent, treeColumnKey],
250
+ );
251
+
252
+ return {visibleData, treeConfig, expandedIds, expandAll, collapseAll};
253
+ }
@@ -18,12 +18,15 @@
18
18
  * Canonical order:
19
19
  * 1. columnSettings — column filtering (future: transformColumns)
20
20
  * 2. sort — header cell sort controls
21
- * 3. selection — checkbox column + row selection
22
- * 4. pagination — pagination controls around the table
21
+ * 3. tree — indent + expander on the tree column
22
+ * 4. selection — checkbox column + row selection
23
+ * 5. pagination — pagination controls around the table
23
24
  *
24
25
  * Rationale:
25
26
  * - columnSettings filters columns before sort/selection see them
26
27
  * - sort adds header cell UI before selection adds its header column
28
+ * - tree wraps the first *user* column before selection prepends its
29
+ * checkbox column, so the expander never lands in the checkbox column
27
30
  * - selection adds its column after sort so the checkbox header
28
31
  * doesn't get a sort button
29
32
  * - pagination wraps the table in context last (outermost provider)
@@ -46,6 +49,7 @@ import type {TablePlugin} from './types';
46
49
  const PLUGIN_ORDER: ReadonlyArray<string> = [
47
50
  'columnSettings',
48
51
  'sort',
52
+ 'tree',
49
53
  'selection',
50
54
  'pagination',
51
55
  ];
@@ -0,0 +1,30 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../docs-types').ComponentDoc} */
4
+
5
+ export const docs = {
6
+ name: 'useTableRowStatus',
7
+ subComponentOf: 'Table',
8
+ displayName: 'useTableRowStatus',
9
+ description:
10
+ 'Hook that returns a TablePlugin which prepends a narrow column signaling per-row status: a colored status dot by default, or an icon when provided (shape + color is more accessible than color alone). getStatus maps a row to a semantic color (mapped to a theme token) or raw CSS color, an optional icon, and a required accessible label (shown in a tooltip on hover and announced to assistive technology, so status is never color-only); return null for no indicator. Memoize getStatus with useCallback for a stable plugin identity.',
11
+ props: [
12
+ {
13
+ name: 'getStatus',
14
+ type: "(item: T) => { color: 'accent' | 'success' | 'error' | 'warning' | 'red' | 'orange' | 'green' | 'yellow' | 'blue' | 'gray' | string; icon?: IconName; label: string } | null",
15
+ description:
16
+ 'Derive the status indicator for a row: a semantic color (accent, success, error, warning, red, orange, green, yellow, blue, gray — mapped to a theme token) or a raw CSS color as an escape hatch; an optional icon to signal status by shape instead of the dot (recommended when multiple statuses coexist; valid names: close, chevronDown, chevronLeft, chevronRight, check, success, error, warning, info, calendar, clock, externalLink, menu, moreHorizontal, search, arrowUp, arrowDown, arrowsUpDown, funnel, eyeSlash, viewColumns, copy, checkDouble, wrench, stop, microphone); and a required accessible label (announced via role="img" and shown in a tooltip on hover, so a status is never conveyed by color alone). Return null for rows with no status. Memoize with useCallback for a stable plugin identity.',
17
+ required: true,
18
+ },
19
+ ],
20
+ };
21
+
22
+ /** @type {import('../docs-types').TranslationDoc} */
23
+ export const docsDense = {
24
+ description:
25
+ 'Returns a TablePlugin that prepends a narrow per-row status column: a colored dot, or an icon when provided (shape + color beats color alone for a11y). getStatus maps a row to {color, icon?, label} or null. color is a semantic name (success/error/warning/etc.) mapped to a theme token, or a raw CSS color. label is required (tooltip + accessible name). Memoize getStatus with useCallback.',
26
+ propDescriptions: {
27
+ getStatus:
28
+ 'Map a row to {color, icon?, label} or null. color = semantic status name (mapped to a token) or raw CSS; icon = shape signifier (a11y); label = required accessible name (tooltip + role="img"). Memoize with useCallback.',
29
+ },
30
+ };
@@ -0,0 +1,75 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../docs-types').ComponentDoc} */
4
+
5
+ export const docs = {
6
+ name: 'useTableTreeData',
7
+ subComponentOf: 'Table',
8
+ displayName: 'useTableTreeData',
9
+ description:
10
+ 'Headless tree plugin for Table: renders nested rows with per-level indentation and expand/collapse chevrons in the tree column (the first column by default), and reflects hierarchy on body rows via aria-level and aria-expanded. Composable with the other Table plugins: the canonical plugin order places tree before selection, so the checkbox column lands left of the indented tree column. Feed it the treeConfig from useTableTreeState, or construct the config directly for server-driven or pre-flattened trees. When no row is expandable (flat data), every transform is a pass-through and the table renders identically to one without the plugin. Known limitation: the tree column wraps its cell content, so textOverflow="truncate" tooltips do not apply within the tree column.',
11
+ props: [
12
+ {
13
+ name: 'getRowMeta',
14
+ type: '(item: T) => TableTreeRowMeta | undefined',
15
+ description:
16
+ 'Structural meta for a visible row: {id, level (0-based), hasChildren, isExpanded}.',
17
+ required: true,
18
+ },
19
+ {
20
+ name: 'onToggleItem',
21
+ type: '(item: T) => void',
22
+ description: 'Toggle a row\'s expansion.',
23
+ required: true,
24
+ },
25
+ {
26
+ name: 'hasExpandableRows',
27
+ type: 'boolean',
28
+ description:
29
+ 'Whether any row in the dataset is expandable. When false the plugin is a no-op: no expanders, no indent, no tree ARIA.',
30
+ required: true,
31
+ },
32
+ {
33
+ name: 'indent',
34
+ type: "'sm' | 'md' | 'lg'",
35
+ description:
36
+ 'Indent step per level, mapped to the spacing-3 / spacing-4 / spacing-6 tokens.',
37
+ default: "'md'",
38
+ },
39
+ {
40
+ name: 'treeColumnKey',
41
+ type: 'string',
42
+ description:
43
+ 'Column that carries the indent + expander. Defaults to the first column.',
44
+ },
45
+ ],
46
+ examples: [
47
+ {
48
+ label: 'File tree with expandable folders',
49
+ code: `const {visibleData, treeConfig} = useTableTreeState({
50
+ data: files, // rows may nest under 'children'
51
+ idKey: 'id',
52
+ defaultExpandedIds: ['src'],
53
+ });
54
+ const tree = useTableTreeData(treeConfig);
55
+
56
+ <Table data={visibleData} columns={columns} idKey="id" plugins={{tree}} />;`,
57
+ },
58
+ ],
59
+ };
60
+
61
+ export const docsDense = {
62
+ name: 'useTableTreeData',
63
+ displayName: 'useTableTreeData',
64
+ description:
65
+ 'Headless tree plugin: indent + expander chevron on the tree column (first column by default), aria-level/aria-expanded on body rows. Canonical plugin order puts tree before selection (checkbox column lands left of tree column). Consume treeConfig from useTableTreeState, or construct directly for server-driven trees. hasExpandableRows=false => full no-op (flat-data migration).',
66
+ propDescriptions: {
67
+ getRowMeta:
68
+ 'structural meta per visible row: {id, level (0-based), hasChildren, isExpanded}',
69
+ onToggleItem: 'toggle row expansion',
70
+ hasExpandableRows: 'false => plugin is a no-op (no expanders/indent/ARIA)',
71
+ indent:
72
+ "indent step per level: 'sm' | 'md' | 'lg' (spacing-3/4/6). Defaults to 'md'.",
73
+ treeColumnKey: 'column carrying indent + expander. Defaults to first column.',
74
+ },
75
+ };
@@ -0,0 +1,98 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../docs-types').ComponentDoc} */
4
+
5
+ export const docs = {
6
+ name: 'useTableTreeState',
7
+ subComponentOf: 'Table',
8
+ displayName: 'useTableTreeState',
9
+ description:
10
+ 'State management companion for useTableTreeData. Owns the expanded set (controlled or uncontrolled) and flattens nested data into the visible row array; collapsed subtrees are unmounted, not hidden, so the table body contains exactly the visible rows. Returns expandAll/collapseAll helpers and a ready-to-use config for the tree plugin. Note: because collapsed rows unmount, cell-local React state inside collapsed subtrees is lost on collapse; lift state that must survive.',
11
+ props: [
12
+ {
13
+ name: 'data',
14
+ type: 'T[]',
15
+ description:
16
+ 'Nested data: rows may carry child rows under childrenKey. Flat data (no children anywhere) makes the plugin a no-op, so it can be adopted before the data becomes hierarchical.',
17
+ required: true,
18
+ },
19
+ {
20
+ name: 'idKey',
21
+ type: '(keyof T & string) | ((item: T) => string | number)',
22
+ description:
23
+ 'Row ID accessor: property name or function returning a unique id.',
24
+ required: true,
25
+ },
26
+ {
27
+ name: 'childrenKey',
28
+ type: 'string',
29
+ description: 'Property holding each row\'s children array.',
30
+ default: "'children'",
31
+ },
32
+ {
33
+ name: 'defaultExpandedIds',
34
+ type: 'Iterable<string>',
35
+ description:
36
+ 'Initial expanded row ids for uncontrolled mode. Ignored when expandedIds is provided.',
37
+ },
38
+ {
39
+ name: 'expandedIds',
40
+ type: 'ReadonlySet<string>',
41
+ description:
42
+ 'Controlled set of expanded row ids. Pair with onExpandedIdsChange.',
43
+ },
44
+ {
45
+ name: 'onExpandedIdsChange',
46
+ type: '(ids: ReadonlySet<string>) => void',
47
+ description:
48
+ 'Called with the next expanded set whenever expansion changes (both modes). In lazy-loading setups, trigger the children fetch here.',
49
+ },
50
+ {
51
+ name: 'isItemExpandable',
52
+ type: '(item: T) => boolean',
53
+ description:
54
+ 'Should this row show an expander? Overrides the default non-empty-children check; use for lazy loading, where a row is expandable before its children have been fetched.',
55
+ },
56
+ {
57
+ name: 'sortSiblings',
58
+ type: '(siblings: T[]) => T[]',
59
+ description:
60
+ 'Sort each sibling group independently during flattening; children always stay directly under their parent. Pass applySort from useTableSortableState to compose with column sorting.',
61
+ },
62
+ {
63
+ name: 'indent',
64
+ type: "'sm' | 'md' | 'lg'",
65
+ description:
66
+ 'Indent step per level (spacing-3 / spacing-4 / spacing-6), forwarded to useTableTreeData.',
67
+ default: "'md'",
68
+ },
69
+ {
70
+ name: 'treeColumnKey',
71
+ type: 'string',
72
+ description:
73
+ 'Column that carries the indent + expander, forwarded to useTableTreeData. Defaults to the first column.',
74
+ },
75
+ ],
76
+ };
77
+
78
+ export const docsDense = {
79
+ name: 'useTableTreeState',
80
+ displayName: 'useTableTreeState',
81
+ description:
82
+ 'State companion for useTableTreeData. Owns expanded set (controlled or uncontrolled), flattens nested data to visible rows (collapsed subtrees unmounted, not hidden). Returns {visibleData, treeConfig, expandedIds, expandAll, collapseAll}. Cell-local state in collapsed subtrees is lost on collapse; lift state that must survive.',
83
+ propDescriptions: {
84
+ data: 'nested data; rows carry child rows under childrenKey. Flat data => plugin is a no-op.',
85
+ idKey: 'row ID accessor: property name or fn returning unique id',
86
+ childrenKey: "property holding children array. Defaults to 'children'.",
87
+ defaultExpandedIds: 'initial expanded ids (uncontrolled)',
88
+ expandedIds: 'controlled expanded set; pair with onExpandedIdsChange',
89
+ onExpandedIdsChange:
90
+ 'called with next expanded set on change (both modes); trigger lazy children fetch here',
91
+ isItemExpandable:
92
+ 'overrides non-empty-children expandability check (lazy loading)',
93
+ sortSiblings:
94
+ 'sorts each sibling group during flattening; pass applySort from useTableSortableState',
95
+ indent: "indent step per level: 'sm' | 'md' | 'lg'. Defaults to 'md'.",
96
+ treeColumnKey: 'column carrying indent + expander. Defaults to first column.',
97
+ },
98
+ };
@@ -116,7 +116,7 @@ export const docs = {
116
116
  name: 'startIcon',
117
117
  type: 'IconType',
118
118
  description:
119
- 'Icon component rendered inside the leading edge of the textarea wrapper. See `npx astryx docs icons` for valid semantic names.',
119
+ 'Icon component rendered inside the leading edge of the textarea wrapper. See `astryx docs icons` for valid semantic names.',
120
120
  },
121
121
  {
122
122
  name: 'hasSpellCheck',
@@ -106,7 +106,7 @@ export const docs = {
106
106
  name: 'startIcon',
107
107
  type: 'IconType',
108
108
  description:
109
- 'SVG icon component displayed at the start of the input. See `npx astryx docs icons` for valid semantic names.',
109
+ 'SVG icon component displayed at the start of the input. See `astryx docs icons` for valid semantic names.',
110
110
  },
111
111
  {
112
112
  name: 'status',
@@ -239,6 +239,7 @@ export function Toolbar({
239
239
  const hasCenterContent = centerContent != null;
240
240
  const hasStartContent = startContent != null;
241
241
  const hasEndContent = endContent != null;
242
+ const hasBottomDivider = dividers?.includes('bottom') ?? false;
242
243
 
243
244
  const gapVar = spacingVars[spacingStepToVar[gap]] as string;
244
245
 
@@ -316,9 +317,15 @@ export function Toolbar({
316
317
  orientation === 'vertical' && styles.vertical,
317
318
  sizeStyles.base,
318
319
  dynamicStyles.gap(gapVar),
319
- dynamicStyles.tabIndicatorBottom(
320
- `calc(-1 * (${blockPaddingVarForSize[size]}${dividers?.includes('bottom') ? ' + 1px' : ''}))`,
321
- ),
320
+ // Only drop the tab indicator through the toolbar's block padding
321
+ // onto the rail when a bottom divider is actually present.
322
+ // Without a divider, leave `--_tab-indicator-bottom` unset so a
323
+ // nested TabList's indicator keeps its own default (hugging the
324
+ // tab) instead of floating down into the toolbar's padding.
325
+ hasBottomDivider &&
326
+ dynamicStyles.tabIndicatorBottom(
327
+ `calc(-1 * (${blockPaddingVarForSize[size]} + 1px))`,
328
+ ),
322
329
  ),
323
330
  )}
324
331
  {...props}>
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi} from 'vitest';
13
- import {render, screen} from '@testing-library/react';
13
+ import {render, screen, fireEvent} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {TreeList} from './TreeList';
16
16
  import type {TreeListItemData} from './TreeListTypes';
@@ -251,6 +251,37 @@ describe('TreeList', () => {
251
251
  expect(screen.getByText('Leaf')).toBeInTheDocument();
252
252
  });
253
253
 
254
+ // ===========================================================================
255
+ // Focus-visible outline scoping (regression: focusing a parent row must not
256
+ // leak the ring onto descendant rows — see #4130)
257
+ // ===========================================================================
258
+
259
+ it('scopes the focus-visible outline to the focused row, not its descendants', () => {
260
+ render(<TreeList items={deepItems} />);
261
+ const root = screen.getByText('Root').closest('li')!;
262
+ const mid = screen.getByText('Mid').closest('li')!;
263
+ const leaf = screen.getByText('Leaf').closest('li')!;
264
+
265
+ // A keydown before .focus() establishes keyboard modality so jsdom's
266
+ // :focus-visible heuristic applies deterministically, regardless of
267
+ // pointer events left over from other tests in this file.
268
+ fireEvent.keyDown(document.body, {key: 'Tab'});
269
+ root.focus();
270
+ expect(root).toHaveFocus();
271
+
272
+ expect(
273
+ getComputedStyle(root).getPropertyValue('--_tree-focus-outline'),
274
+ ).toContain('solid');
275
+ // Mid and Leaf are DOM descendants of Root's <li> (nested <ul role="group">
276
+ // subtrees) — their own outline var must stay unset, not inherit Root's.
277
+ expect(
278
+ getComputedStyle(mid).getPropertyValue('--_tree-focus-outline'),
279
+ ).toBe('none');
280
+ expect(
281
+ getComputedStyle(leaf).getPropertyValue('--_tree-focus-outline'),
282
+ ).toBe('none');
283
+ });
284
+
254
285
  // ===========================================================================
255
286
  // Interactive items
256
287
  // ===========================================================================
@@ -29,7 +29,6 @@ import {mergeProps} from '../utils';
29
29
  import {useLinkComponent} from '../Link/useLinkComponent';
30
30
  import {TreeListBranches} from './TreeListBranches';
31
31
  import type {TreeListDensity} from './TreeListTypes';
32
- import {treeItemScope} from './treeListItem.markers.stylex';
33
32
  import {themeProps} from '../utils/themeProps';
34
33
  import {useTranslator} from '../i18n';
35
34
 
@@ -47,6 +46,19 @@ const styles = stylex.create({
47
46
  // The treeitem row is the roving-tabindex focus owner; suppress the
48
47
  // native focus ring in favor of the row's :focus-visible outline below.
49
48
  outline: 'none',
49
+ // Publish this row's own focus state as an inheritable CSS variable
50
+ // instead of matching it via an ancestor selector. Every nested <li>
51
+ // redeclares these vars (default: 'none' / '0'), so a descendant row's
52
+ // default shadows an ancestor's active value — the ring can never leak
53
+ // past the nearest containing treeitem, however deep the tree nests.
54
+ '--_tree-focus-outline': {
55
+ default: 'none',
56
+ ':focus-visible': `2px solid ${colorVars['--color-accent']}`,
57
+ },
58
+ '--_tree-focus-outline-offset': {
59
+ default: '0',
60
+ ':focus-visible': '2px',
61
+ },
50
62
  },
51
63
  childGroup: {
52
64
  margin: 0,
@@ -86,17 +98,14 @@ const styles = stylex.create({
86
98
  },
87
99
  focusVisibleOutline: {
88
100
  outline: {
89
- default: 'none',
90
- // Focus lives on the treeitem row (the <li>) via roving tabindex.
91
- // Scoped to the row's OWN treeitem so focusing a parent does not leak
92
- // the ring onto descendant rows. Also support inner focusable actions.
93
- [stylex.when.ancestor(':focus-visible', treeItemScope)]:
94
- `2px solid ${colorVars['--color-accent']}`,
101
+ // Reads the row's own --_tree-focus-outline (published on the <li> in
102
+ // `wrapper`), which resolves to the nearest containing treeitem only.
103
+ default: 'var(--_tree-focus-outline, none)',
104
+ // Also support inner focusable actions.
95
105
  ':has(:focus-visible)': `2px solid ${colorVars['--color-accent']}`,
96
106
  },
97
107
  outlineOffset: {
98
- default: '0',
99
- [stylex.when.ancestor(':focus-visible', treeItemScope)]: '2px',
108
+ default: 'var(--_tree-focus-outline-offset, 0)',
100
109
  ':has(:focus-visible)': '2px',
101
110
  },
102
111
  },
@@ -451,7 +460,7 @@ export function TreeListItem({
451
460
  data-tree-id={id}
452
461
  data-tree-level={nestedLevel + 1}
453
462
  data-tree-disabled={isDisabled || undefined}
454
- {...stylex.props(styles.wrapper, treeItemScope)}>
463
+ {...stylex.props(styles.wrapper)}>
455
464
  <div {...stylex.props(styles.treeBranches)}>
456
465
  <TreeListBranches
457
466
  ancestorsIsLast={ancestorsIsLast}
@@ -130,9 +130,7 @@ export type AstryxGenericDocInput = AstryxBaseDocInput;
130
130
  * any code referring to the old name.
131
131
  */
132
132
  export type AstryxComponentDoc =
133
- | AstryxComponentDocInput
134
- | AstryxFunctionDocInput
135
- | AstryxGenericDocInput;
133
+ AstryxComponentDocInput | AstryxFunctionDocInput | AstryxGenericDocInput;
136
134
 
137
135
  /**
138
136
  * Author a component doc. Stamp-only: returns the def with `type: 'component'`