@sproutsocial/seeds-react-tree 0.4.1 → 0.4.4

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.
package/dist/tree.css ADDED
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Seeds Tree component classes
3
+ * CSS-class mirror of the styled-components that previously styled the tree
4
+ * package (TreeStyles.tsx, TreeCombobox.tsx, Common/TreeSearchableSelectBase.tsx).
5
+ *
6
+ * Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
7
+ * CSS variable definitions and dark mode support.
8
+ *
9
+ * Rules live in @layer components so consumer Tailwind utilities (which sit in
10
+ * the later `utilities` layer) can override component styles on equal
11
+ * specificity. Without the layer, these unlayered rules would out-rank any
12
+ * utility class.
13
+ *
14
+ * Usage:
15
+ * <ul class="seeds-tree" role="tree">
16
+ * <li class="seeds-tree-item" role="treeitem">
17
+ * <div class="seeds-tree-item-row" style="--tree-level: 1">…</div>
18
+ * </li>
19
+ * </ul>
20
+ */
21
+
22
+ @layer components {
23
+ /* ---------------------------------------------------------------------- */
24
+ /* Tree / TreeItem (mirror of TreeStyles.tsx) */
25
+ /* ---------------------------------------------------------------------- */
26
+
27
+ .seeds-tree {
28
+ list-style: none;
29
+ margin: 0;
30
+ padding: 0;
31
+ font-family: var(--font-family);
32
+ font-size: var(--font-size-200);
33
+ line-height: var(--line-height-200);
34
+ color: var(--color-text-body);
35
+ }
36
+
37
+ .seeds-tree-item {
38
+ list-style: none;
39
+ outline: none;
40
+ }
41
+
42
+ /*
43
+ * Tree rows stack tightly, so the standard outset Seeds focusRing bleeds
44
+ * into adjacent rows. Use an inset outline so the ring is drawn just inside
45
+ * the row's edge and never overlaps siblings or children.
46
+ *
47
+ * The same ring is drawn when [data-treeitem-active] is set so combobox
48
+ * hosts that drive the tree via aria-activedescendant (no real DOM focus)
49
+ * still get a visible "active" indicator on the row.
50
+ */
51
+ .seeds-tree-item:focus-visible > .seeds-tree-item-row,
52
+ .seeds-tree-item[data-treeitem-active] > .seeds-tree-item-row {
53
+ outline: 2px solid var(--color-button-primary-bg-base);
54
+ outline-offset: -2px;
55
+ }
56
+
57
+ .seeds-tree-item-row {
58
+ display: flex;
59
+ align-items: center;
60
+ gap: var(--space-300);
61
+ padding: var(--space-300);
62
+ /* Indentation per depth: --tree-level is set inline on each row. */
63
+ padding-left: calc(
64
+ var(--space-300) + (var(--tree-level) - 1) * var(--space-400)
65
+ );
66
+ border-radius: var(--radius-400);
67
+ cursor: pointer;
68
+ user-select: none;
69
+ background: transparent;
70
+ transition: background-color var(--duration-fast) var(--ease-ease_in);
71
+ }
72
+
73
+ .seeds-tree-item-row:hover {
74
+ background: var(--color-listItem-bg-hover);
75
+ }
76
+
77
+ .seeds-tree-item-row.selected {
78
+ background: var(--color-listItem-bg-hover);
79
+ font-weight: var(--font-weight-semibold);
80
+ }
81
+
82
+ .seeds-tree-item-row.disabled {
83
+ opacity: 0.4;
84
+ cursor: not-allowed;
85
+ pointer-events: none;
86
+ }
87
+
88
+ .seeds-tree-item-group {
89
+ list-style: none;
90
+ margin: 0;
91
+ padding: 0;
92
+ }
93
+
94
+ .seeds-tree-item-label {
95
+ flex: 1;
96
+ min-width: 0;
97
+ overflow: hidden;
98
+ text-overflow: ellipsis;
99
+ white-space: nowrap;
100
+ }
101
+
102
+ .seeds-tree-item-icon {
103
+ display: inline-flex;
104
+ align-items: center;
105
+ flex-shrink: 0;
106
+ }
107
+
108
+ .seeds-tree-item-chevron {
109
+ display: inline-flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ flex-shrink: 0;
113
+ width: var(--space-500);
114
+ height: var(--space-500);
115
+ padding: 0;
116
+ border: none;
117
+ background: transparent;
118
+ color: var(--color-icon-base);
119
+ cursor: pointer;
120
+ border-radius: var(--radius-300);
121
+ transition: transform var(--duration-fast) var(--ease-ease_in);
122
+ transform: rotate(-90deg);
123
+ }
124
+
125
+ .seeds-tree-item-chevron.expanded {
126
+ transform: rotate(0deg);
127
+ }
128
+
129
+ .seeds-tree-item-chevron:hover {
130
+ background: var(--color-listItem-bg-hover);
131
+ }
132
+
133
+ /* ---------------------------------------------------------------------- */
134
+ /* TreeCombobox (mirror of TreeCombobox.tsx) */
135
+ /* ---------------------------------------------------------------------- */
136
+
137
+ .seeds-tree-combobox {
138
+ display: flex;
139
+ flex-direction: column;
140
+ flex: 1;
141
+ min-height: 0;
142
+ }
143
+
144
+ .seeds-tree-combobox-search {
145
+ padding: var(--space-300);
146
+ border-bottom: 1px solid var(--color-container-border-base);
147
+ flex-shrink: 0;
148
+ }
149
+
150
+ .seeds-tree-combobox-input-group {
151
+ display: flex;
152
+ align-items: center;
153
+ gap: var(--space-200);
154
+ padding: var(--space-200) var(--space-300);
155
+ border-radius: var(--radius-500);
156
+ border: 1px solid var(--color-container-border-base);
157
+ background: var(--color-container-bg-base);
158
+ color: var(--color-icon-base);
159
+ }
160
+
161
+ .seeds-tree-combobox-input-group:focus-within {
162
+ box-shadow:
163
+ 0 0 0 1px var(--color-button-primary-bg-base),
164
+ 0 0px 0px 4px
165
+ color-mix(
166
+ in srgb,
167
+ var(--color-button-primary-bg-base),
168
+ transparent 70%
169
+ );
170
+ outline: none;
171
+ }
172
+
173
+ .seeds-tree-combobox-input {
174
+ flex: 1;
175
+ border: none;
176
+ background: transparent;
177
+ outline: none;
178
+ font-family: var(--font-family);
179
+ font-size: var(--font-size-200);
180
+ line-height: var(--line-height-200);
181
+ color: var(--color-text-body);
182
+ }
183
+
184
+ .seeds-tree-combobox-input::placeholder {
185
+ color: var(--color-text-subtext);
186
+ }
187
+
188
+ .seeds-tree-combobox-list {
189
+ padding: var(--space-200);
190
+ overflow: auto;
191
+ flex: 1;
192
+ min-height: 0;
193
+ }
194
+
195
+ .seeds-tree-combobox-empty {
196
+ padding: var(--space-300) var(--space-350);
197
+ text-align: center;
198
+ color: var(--color-text-subtext);
199
+ font-size: var(--font-size-200);
200
+ line-height: var(--line-height-200);
201
+ }
202
+
203
+ /* ---------------------------------------------------------------------- */
204
+ /* TreeSearchableSelect (mirror of Common/TreeSearchableSelectBase.tsx) */
205
+ /* ---------------------------------------------------------------------- */
206
+
207
+ .seeds-tree-select-trigger {
208
+ display: flex;
209
+ align-items: center;
210
+ justify-content: space-between;
211
+ gap: var(--space-300);
212
+ padding: var(--space-200) var(--space-350);
213
+ min-width: 160px;
214
+ width: auto;
215
+ border-radius: var(--radius-500);
216
+ border: 1px solid var(--color-form-border-base);
217
+ background: var(--color-form-bg-base);
218
+ font-family: var(--font-family);
219
+ font-size: var(--font-size-200);
220
+ line-height: var(--line-height-200);
221
+ color: var(--color-text-body);
222
+ text-align: left;
223
+ cursor: default;
224
+ outline: none;
225
+ user-select: none;
226
+ transition:
227
+ border-color var(--duration-fast) var(--ease-ease_in),
228
+ box-shadow var(--duration-fast) var(--ease-ease_in);
229
+ }
230
+
231
+ .seeds-tree-select-trigger.full-width {
232
+ width: 100%;
233
+ }
234
+
235
+ .seeds-tree-select-trigger:focus-visible {
236
+ box-shadow:
237
+ 0 0 0 1px var(--color-button-primary-bg-base),
238
+ 0 0px 0px 4px
239
+ color-mix(
240
+ in srgb,
241
+ var(--color-button-primary-bg-base),
242
+ transparent 70%
243
+ );
244
+ outline: none;
245
+ }
246
+
247
+ .seeds-tree-select-trigger::-moz-focus-inner {
248
+ border: 0;
249
+ }
250
+
251
+ .seeds-tree-select-trigger[aria-disabled="true"],
252
+ .seeds-tree-select-trigger:disabled {
253
+ opacity: 0.4;
254
+ cursor: not-allowed;
255
+ }
256
+
257
+ .seeds-tree-select-trigger.invalid {
258
+ border-color: var(--color-form-border-error);
259
+ }
260
+
261
+ .seeds-tree-select-trigger-label {
262
+ flex: 1;
263
+ min-width: 0;
264
+ overflow: hidden;
265
+ text-overflow: ellipsis;
266
+ white-space: nowrap;
267
+ }
268
+
269
+ .seeds-tree-select-placeholder {
270
+ color: var(--color-text-subtext);
271
+ }
272
+
273
+ .seeds-tree-select-chevron {
274
+ display: inline-flex;
275
+ align-items: center;
276
+ color: var(--color-icon-base);
277
+ transition: transform var(--duration-fast) var(--ease-ease_in);
278
+ }
279
+
280
+ .seeds-tree-select-chevron.open {
281
+ transform: rotate(-180deg);
282
+ }
283
+
284
+ /*
285
+ * Visually-hidden input that mirrors the combobox's selected value into a
286
+ * form-submittable element. Matches Base UI's pattern (used by v3
287
+ * SearchableSelect): the input stays in the DOM so HTML forms pick up the
288
+ * value, but it's removed from view and from the accessibility tree.
289
+ */
290
+ .seeds-tree-select-hidden-input {
291
+ clip-path: inset(50%);
292
+ overflow: hidden;
293
+ white-space: nowrap;
294
+ border: 0;
295
+ padding: 0;
296
+ width: 1px;
297
+ height: 1px;
298
+ margin: -1px;
299
+ position: fixed;
300
+ top: 0;
301
+ left: 0;
302
+ }
303
+
304
+ .seeds-tree-select-popout {
305
+ display: flex;
306
+ flex-direction: column;
307
+ /* Width sizes to content within bounds: at least the trigger width, never */
308
+ /* wider than the available viewport space. Shallow popups stay flush with */
309
+ /* the trigger; deeply nested trees expand horizontally as needed. */
310
+ min-width: var(--radix-popover-trigger-width);
311
+ max-width: var(--radix-popper-available-width);
312
+ /* Height caps at 400px (or available viewport) so a popup with many */
313
+ /* expanded branches scrolls vertically instead of growing off-screen. */
314
+ max-height: min(var(--radix-popper-available-height), 400px);
315
+ /* Width is set inline via --tree-select-width (defaults to max-content). */
316
+ width: var(--tree-select-width, max-content);
317
+ }
318
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-tree",
3
- "version": "0.4.1",
3
+ "version": "0.4.4",
4
4
  "description": "Seeds React Tree (WAI-ARIA treeview)",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -17,11 +17,12 @@
17
17
  "types": "./dist/index.d.ts",
18
18
  "import": "./dist/esm/index.js",
19
19
  "require": "./dist/index.js"
20
- }
20
+ },
21
+ "./dist/tree.css": "./dist/tree.css"
21
22
  },
22
23
  "scripts": {
23
- "build": "tsup --dts",
24
- "build:debug": "tsup --dts --metafile",
24
+ "build": "tsup --dts && cp src/tree.css dist/tree.css",
25
+ "build:debug": "tsup --dts --metafile && cp src/tree.css dist/tree.css",
25
26
  "dev": "tsup --watch --dts",
26
27
  "clean": "rm -rf .turbo dist",
27
28
  "clean:modules": "rm -rf node_modules",
@@ -30,17 +31,17 @@
30
31
  "test:watch": "jest --watch --coverage=false"
31
32
  },
32
33
  "dependencies": {
33
- "@sproutsocial/seeds-react-icon": "^2.4.1",
34
- "@sproutsocial/seeds-react-mixins": "^4.3.8",
35
- "@sproutsocial/seeds-react-popout": "^2.5.10",
36
- "@sproutsocial/seeds-react-system-props": "^3.1.1",
37
- "@sproutsocial/seeds-react-theme": "^4.1.1",
34
+ "@sproutsocial/seeds-react-icon": "^2.4.3",
35
+ "@sproutsocial/seeds-react-mixins": "^4.3.9",
36
+ "@sproutsocial/seeds-react-popout": "^2.5.12",
37
+ "@sproutsocial/seeds-react-system-props": "^3.1.2",
38
+ "@sproutsocial/seeds-react-theme": "^4.2.0",
38
39
  "@sproutsocial/seeds-react-utilities": "^4.3.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@sproutsocial/eslint-config-seeds": "*",
42
- "@sproutsocial/seeds-react-button": "^2.2.2",
43
- "@sproutsocial/seeds-react-popout": "^2.5.10",
43
+ "@sproutsocial/seeds-react-button": "^2.2.4",
44
+ "@sproutsocial/seeds-react-popout": "^2.5.12",
44
45
  "@sproutsocial/seeds-react-testing-library": "*",
45
46
  "@sproutsocial/seeds-testing": "*",
46
47
  "@sproutsocial/seeds-tsconfig": "*",
@@ -1,9 +1,8 @@
1
1
  import * as React from "react";
2
- import styled, { css } from "styled-components";
3
2
  import { Icon } from "@sproutsocial/seeds-react-icon";
4
- import { focusRing } from "@sproutsocial/seeds-react-mixins";
5
3
  import { Popout } from "@sproutsocial/seeds-react-popout/v2";
6
4
  import { TreeCombobox } from "../TreeCombobox";
5
+ import { cn } from "../cn";
7
6
  import { flattenTreeItems } from "./flattenTree";
8
7
  import type {
9
8
  TreeItemData,
@@ -12,113 +11,14 @@ import type {
12
11
  TreeSelectionMode,
13
12
  } from "./types";
14
13
 
15
- const invalidStyles = css`
16
- border-color: ${({ theme }) => theme.colors.form.border.error};
17
- `;
18
-
19
- const Trigger = styled.button<{ $isInvalid?: boolean; $fullWidth: boolean }>`
20
- display: flex;
21
- align-items: center;
22
- justify-content: space-between;
23
- gap: ${({ theme }) => theme.space[300]};
24
- padding: ${({ theme }) => theme.space[200]} ${({ theme }) => theme.space[350]};
25
- min-width: 160px;
26
- width: ${({ $fullWidth }) => ($fullWidth ? "100%" : "auto")};
27
- border-radius: ${({ theme }) => theme.radii[500]};
28
- border: 1px solid ${({ theme }) => theme.colors.form.border.base};
29
- background: ${({ theme }) => theme.colors.form.background.base};
30
- font-family: ${({ theme }) => theme.fontFamily};
31
- font-size: ${({ theme }) => theme.typography[200].fontSize};
32
- line-height: ${({ theme }) => theme.typography[200].lineHeight};
33
- color: ${({ theme }) => theme.colors.text.body};
34
- text-align: left;
35
- cursor: default;
36
- outline: none;
37
- user-select: none;
38
- transition: border-color ${({ theme }) => theme.duration.fast}
39
- ${({ theme }) => theme.easing.ease_in},
40
- box-shadow ${({ theme }) => theme.duration.fast}
41
- ${({ theme }) => theme.easing.ease_in};
42
-
43
- &:focus-visible {
44
- ${focusRing}
45
- }
46
-
47
- &[aria-disabled="true"],
48
- &:disabled {
49
- opacity: 0.4;
50
- cursor: not-allowed;
51
- }
52
-
53
- ${({ $isInvalid }) => $isInvalid && invalidStyles}
54
- `;
55
-
56
- const TriggerLabel = styled.span`
57
- flex: 1;
58
- min-width: 0;
59
- overflow: hidden;
60
- text-overflow: ellipsis;
61
- white-space: nowrap;
62
- `;
63
-
64
- const PlaceholderText = styled.span`
65
- color: ${({ theme }) => theme.colors.text.subtext};
66
- `;
67
-
68
- const Chevron = styled.span<{ $open: boolean }>`
69
- display: inline-flex;
70
- align-items: center;
71
- color: ${({ theme }) => theme.colors.icon.base};
72
- transition: transform ${({ theme }) => theme.duration.fast}
73
- ${({ theme }) => theme.easing.ease_in};
74
- ${({ $open }) =>
75
- $open &&
76
- css`
77
- transform: rotate(-180deg);
78
- `}
79
- `;
80
-
81
- /*
82
- * Visually-hidden input that mirrors the combobox's selected value into a
83
- * form-submittable element. Matches Base UI's pattern (used by v3
84
- * SearchableSelect): the input stays in the DOM so HTML forms pick up the
85
- * value, but it's removed from view and from the accessibility tree.
86
- */
87
- const HiddenInput = styled.input`
88
- clip-path: inset(50%);
89
- overflow: hidden;
90
- white-space: nowrap;
91
- border: 0;
92
- padding: 0;
93
- width: 1px;
94
- height: 1px;
95
- margin: -1px;
96
- position: fixed;
97
- top: 0;
98
- left: 0;
99
- `;
100
-
101
- const PopoutContent = styled.div<{ $width?: number | string }>`
102
- display: flex;
103
- flex-direction: column;
104
- /* Width sizes to content within bounds: at least the trigger width, never */
105
- /* wider than the available viewport space. Shallow popups stay flush with */
106
- /* the trigger; deeply nested trees expand horizontally as needed. */
107
- min-width: var(--radix-popover-trigger-width);
108
- max-width: var(--radix-popper-available-width);
109
- /* Height caps at 400px (or available viewport) so a popup with many */
110
- /* expanded branches scrolls vertically instead of growing off-screen. */
111
- max-height: min(var(--radix-popper-available-height), 400px);
112
- width: ${({ $width }) =>
113
- $width === undefined
114
- ? "max-content"
115
- : typeof $width === "number"
116
- ? `${$width}px`
117
- : $width};
118
- `;
119
-
120
14
  const MAX_INLINE_LABELS = 3;
121
15
 
16
+ /** Resolve the popout width prop to a CSS length for `--tree-select-width`. */
17
+ function popoutWidth(width?: number | string): string | undefined {
18
+ if (width === undefined) return undefined;
19
+ return typeof width === "number" ? `${width}px` : width;
20
+ }
21
+
122
22
  export type RenderTrigger = (selected: TreeItemData[]) => React.ReactNode;
123
23
 
124
24
  type TreeSearchableSelectBaseProps = {
@@ -189,7 +89,9 @@ function defaultRenderTrigger(
189
89
  placeholder: string
190
90
  ): React.ReactNode {
191
91
  if (selected.length === 0) {
192
- return <PlaceholderText>{placeholder}</PlaceholderText>;
92
+ return (
93
+ <span className="seeds-tree-select-placeholder">{placeholder}</span>
94
+ );
193
95
  }
194
96
  if (selected.length <= MAX_INLINE_LABELS) {
195
97
  return selected.map((s) => s.label).join(", ");
@@ -284,7 +186,8 @@ export function TreeSearchableSelectBase(props: TreeSearchableSelectBaseProps) {
284
186
 
285
187
  return (
286
188
  <>
287
- <HiddenInput
189
+ <input
190
+ className="seeds-tree-select-hidden-input"
288
191
  type="text"
289
192
  id={primaryInputId}
290
193
  name={primaryInputName}
@@ -327,7 +230,15 @@ export function TreeSearchableSelectBase(props: TreeSearchableSelectBaseProps) {
327
230
  }
328
231
  }}
329
232
  content={
330
- <PopoutContent id={popupId} $width={contentWidth}>
233
+ <div
234
+ className="seeds-tree-select-popout"
235
+ id={popupId}
236
+ style={
237
+ {
238
+ "--tree-select-width": popoutWidth(contentWidth),
239
+ } as React.CSSProperties
240
+ }
241
+ >
331
242
  <TreeCombobox
332
243
  /*
333
244
  * The wrapper's `aria-label` / `aria-labelledby` already names the
@@ -352,15 +263,16 @@ export function TreeSearchableSelectBase(props: TreeSearchableSelectBaseProps) {
352
263
  expanded={expanded}
353
264
  onExpandedChange={onExpandedChange}
354
265
  />
355
- </PopoutContent>
266
+ </div>
356
267
  }
357
268
  >
358
- <Trigger
269
+ <button
270
+ className={cn("seeds-tree-select-trigger", className, {
271
+ "full-width": fullWidth,
272
+ invalid: !!isInvalid,
273
+ })}
359
274
  type="button"
360
275
  id={id}
361
- className={className}
362
- $isInvalid={isInvalid}
363
- $fullWidth={fullWidth}
364
276
  disabled={disabled}
365
277
  role="combobox"
366
278
  aria-haspopup="dialog"
@@ -372,11 +284,16 @@ export function TreeSearchableSelectBase(props: TreeSearchableSelectBaseProps) {
372
284
  aria-invalid={isInvalid || undefined}
373
285
  aria-required={required || undefined}
374
286
  >
375
- <TriggerLabel>{triggerContent}</TriggerLabel>
376
- <Chevron $open={open} aria-hidden>
287
+ <span className="seeds-tree-select-trigger-label">
288
+ {triggerContent}
289
+ </span>
290
+ <span
291
+ className={cn("seeds-tree-select-chevron", { open })}
292
+ aria-hidden
293
+ >
377
294
  <Icon name="chevron-down-outline" size="small" />
378
- </Chevron>
379
- </Trigger>
295
+ </span>
296
+ </button>
380
297
  </Popout>
381
298
  </>
382
299
  );
@@ -229,3 +229,29 @@ export const DisabledItems: Story = {
229
229
  </Wrapper>
230
230
  ),
231
231
  };
232
+
233
+ export const TailwindOverrides: Story = {
234
+ name: "Tailwind utility overrides",
235
+ render: () => (
236
+ <Wrapper>
237
+ {/*
238
+ The CSS-class path lands a bare `className` directly on the
239
+ `.seeds-tree` <ul>. `mx-200`, `p-300`, and `inline-flex` come from the
240
+ Tailwind `utilities` layer and only win over tree.css's `margin: 0`,
241
+ `padding: 0`, and default `display` because tree.css's rules live in
242
+ `@layer components`. The border makes the applied margin/padding
243
+ visible, proving the utilities take effect.
244
+ */}
245
+ <Tree
246
+ aria-label="Tree with Tailwind utility overrides"
247
+ defaultExpanded={["src"]}
248
+ className="mx-200 p-300 inline-flex border border-container-border-base rounded-400"
249
+ >
250
+ <TreeItem id="src" label="src">
251
+ <TreeItem id="Button.tsx" label="Button.tsx" />
252
+ <TreeItem id="Tree.tsx" label="Tree.tsx" />
253
+ </TreeItem>
254
+ </Tree>
255
+ </Wrapper>
256
+ ),
257
+ };
package/src/Tree.tsx CHANGED
@@ -10,7 +10,7 @@ import type {
10
10
  TreeSelectionIndicator,
11
11
  TreeSelectionMode,
12
12
  } from "./Common/types";
13
- import { TreeRoot } from "./TreeStyles";
13
+ import { cn } from "./cn";
14
14
 
15
15
  export type TreeProps = {
16
16
  children: React.ReactNode;
@@ -164,17 +164,17 @@ export const Tree = React.forwardRef<HTMLUListElement, TreeProps>(function Tree(
164
164
  return (
165
165
  <TreeContext.Provider value={ctxValue}>
166
166
  <TreeItemContext.Provider value={{ level: 1 }}>
167
- <TreeRoot
167
+ <ul
168
168
  ref={innerRef}
169
169
  role="tree"
170
170
  id={id}
171
- className={className}
171
+ className={cn("seeds-tree", className)}
172
172
  aria-label={ariaLabel}
173
173
  aria-labelledby={ariaLabelledBy}
174
174
  aria-multiselectable={selectionMode === "multiple" ? true : undefined}
175
175
  >
176
176
  {children}
177
- </TreeRoot>
177
+ </ul>
178
178
  </TreeItemContext.Provider>
179
179
  </TreeContext.Provider>
180
180
  );