luxen-ui 0.1.0
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/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/css/elements/avatar.css +20 -0
- package/dist/css/elements/badge.css +159 -0
- package/dist/css/elements/button.css +171 -0
- package/dist/css/elements/close-button/circle.css +66 -0
- package/dist/css/elements/close-button/ring.css +71 -0
- package/dist/css/elements/close-button/square.css +70 -0
- package/dist/css/elements/disclosure.css +137 -0
- package/dist/css/elements/divider.css +75 -0
- package/dist/css/elements/input-otp.css +164 -0
- package/dist/css/elements/input-stepper/default.css +245 -0
- package/dist/css/elements/input-stepper/rounded.css +238 -0
- package/dist/css/elements/kbd.css +21 -0
- package/dist/css/elements/progress.css +114 -0
- package/dist/css/elements/select.css +71 -0
- package/dist/css/elements/skeleton.css +89 -0
- package/dist/css/elements/tabs/enclosed.css +148 -0
- package/dist/css/elements/tabs/line.css +138 -0
- package/dist/css/elements/toast.css +260 -0
- package/dist/css/index.css +885 -0
- package/dist/custom-elements.json +14424 -0
- package/dist/define.d.ts +9 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +16 -0
- package/dist/elements/avatar/avatar.css +128 -0
- package/dist/elements/avatar/avatar.d.ts +21 -0
- package/dist/elements/avatar/avatar.d.ts.map +1 -0
- package/dist/elements/avatar/avatar.js +106 -0
- package/dist/elements/avatar/index.d.ts +8 -0
- package/dist/elements/avatar/index.d.ts.map +1 -0
- package/dist/elements/avatar/index.js +4 -0
- package/dist/elements/badge/badge.d.ts +17 -0
- package/dist/elements/badge/badge.d.ts.map +1 -0
- package/dist/elements/badge/badge.js +34 -0
- package/dist/elements/badge/index.d.ts +8 -0
- package/dist/elements/badge/index.d.ts.map +1 -0
- package/dist/elements/badge/index.js +4 -0
- package/dist/elements/carousel/carousel.css +205 -0
- package/dist/elements/carousel/carousel.d.ts +148 -0
- package/dist/elements/carousel/carousel.d.ts.map +1 -0
- package/dist/elements/carousel/carousel.js +473 -0
- package/dist/elements/carousel/index.d.ts +8 -0
- package/dist/elements/carousel/index.d.ts.map +1 -0
- package/dist/elements/carousel/index.js +4 -0
- package/dist/elements/carousel-item/carousel-item.css +11 -0
- package/dist/elements/carousel-item/carousel-item.d.ts +13 -0
- package/dist/elements/carousel-item/carousel-item.d.ts.map +1 -0
- package/dist/elements/carousel-item/carousel-item.js +20 -0
- package/dist/elements/carousel-item/index.d.ts +8 -0
- package/dist/elements/carousel-item/index.d.ts.map +1 -0
- package/dist/elements/carousel-item/index.js +4 -0
- package/dist/elements/dialog/dialog.css +92 -0
- package/dist/elements/dialog/dialog.d.ts +56 -0
- package/dist/elements/dialog/dialog.d.ts.map +1 -0
- package/dist/elements/dialog/dialog.js +204 -0
- package/dist/elements/dialog/dialog.styles.d.ts +8 -0
- package/dist/elements/dialog/dialog.styles.d.ts.map +1 -0
- package/dist/elements/dialog/dialog.styles.js +8 -0
- package/dist/elements/dialog/index.d.ts +8 -0
- package/dist/elements/dialog/index.d.ts.map +1 -0
- package/dist/elements/dialog/index.js +4 -0
- package/dist/elements/divider/divider.d.ts +23 -0
- package/dist/elements/divider/divider.d.ts.map +1 -0
- package/dist/elements/divider/divider.js +49 -0
- package/dist/elements/divider/index.d.ts +8 -0
- package/dist/elements/divider/index.d.ts.map +1 -0
- package/dist/elements/divider/index.js +4 -0
- package/dist/elements/drawer/drawer.css +66 -0
- package/dist/elements/drawer/drawer.d.ts +34 -0
- package/dist/elements/drawer/drawer.d.ts.map +1 -0
- package/dist/elements/drawer/drawer.js +46 -0
- package/dist/elements/drawer/index.d.ts +8 -0
- package/dist/elements/drawer/index.d.ts.map +1 -0
- package/dist/elements/drawer/index.js +4 -0
- package/dist/elements/dropdown/dropdown.css +31 -0
- package/dist/elements/dropdown/dropdown.d.ts +64 -0
- package/dist/elements/dropdown/dropdown.d.ts.map +1 -0
- package/dist/elements/dropdown/dropdown.js +322 -0
- package/dist/elements/dropdown/index.d.ts +8 -0
- package/dist/elements/dropdown/index.d.ts.map +1 -0
- package/dist/elements/dropdown/index.js +4 -0
- package/dist/elements/dropdown-item/dropdown-item.css +51 -0
- package/dist/elements/dropdown-item/dropdown-item.d.ts +25 -0
- package/dist/elements/dropdown-item/dropdown-item.d.ts.map +1 -0
- package/dist/elements/dropdown-item/dropdown-item.js +110 -0
- package/dist/elements/dropdown-item/index.d.ts +8 -0
- package/dist/elements/dropdown-item/index.d.ts.map +1 -0
- package/dist/elements/dropdown-item/index.js +4 -0
- package/dist/elements/icon/icon.css +10 -0
- package/dist/elements/icon/icon.d.ts +19 -0
- package/dist/elements/icon/icon.d.ts.map +1 -0
- package/dist/elements/icon/icon.js +53 -0
- package/dist/elements/icon/index.d.ts +8 -0
- package/dist/elements/icon/index.d.ts.map +1 -0
- package/dist/elements/icon/index.js +4 -0
- package/dist/elements/input-otp/index.d.ts +8 -0
- package/dist/elements/input-otp/index.d.ts.map +1 -0
- package/dist/elements/input-otp/index.js +4 -0
- package/dist/elements/input-otp/input-otp.d.ts +31 -0
- package/dist/elements/input-otp/input-otp.d.ts.map +1 -0
- package/dist/elements/input-otp/input-otp.js +139 -0
- package/dist/elements/input-stepper/index.d.ts +8 -0
- package/dist/elements/input-stepper/index.d.ts.map +1 -0
- package/dist/elements/input-stepper/index.js +4 -0
- package/dist/elements/input-stepper/input-stepper.d.ts +63 -0
- package/dist/elements/input-stepper/input-stepper.d.ts.map +1 -0
- package/dist/elements/input-stepper/input-stepper.js +249 -0
- package/dist/elements/popover/index.d.ts +8 -0
- package/dist/elements/popover/index.d.ts.map +1 -0
- package/dist/elements/popover/index.js +4 -0
- package/dist/elements/popover/popover.css +61 -0
- package/dist/elements/popover/popover.d.ts +62 -0
- package/dist/elements/popover/popover.d.ts.map +1 -0
- package/dist/elements/popover/popover.js +244 -0
- package/dist/elements/rating/index.d.ts +8 -0
- package/dist/elements/rating/index.d.ts.map +1 -0
- package/dist/elements/rating/index.js +4 -0
- package/dist/elements/rating/rating.css +102 -0
- package/dist/elements/rating/rating.d.ts +38 -0
- package/dist/elements/rating/rating.d.ts.map +1 -0
- package/dist/elements/rating/rating.js +193 -0
- package/dist/elements/skeleton/index.d.ts +8 -0
- package/dist/elements/skeleton/index.d.ts.map +1 -0
- package/dist/elements/skeleton/index.js +4 -0
- package/dist/elements/skeleton/skeleton.d.ts +12 -0
- package/dist/elements/skeleton/skeleton.d.ts.map +1 -0
- package/dist/elements/skeleton/skeleton.js +13 -0
- package/dist/elements/spinner/index.d.ts +8 -0
- package/dist/elements/spinner/index.d.ts.map +1 -0
- package/dist/elements/spinner/index.js +4 -0
- package/dist/elements/spinner/spinner.css +28 -0
- package/dist/elements/spinner/spinner.d.ts +16 -0
- package/dist/elements/spinner/spinner.d.ts.map +1 -0
- package/dist/elements/spinner/spinner.js +37 -0
- package/dist/elements/tabs/index.d.ts +8 -0
- package/dist/elements/tabs/index.d.ts.map +1 -0
- package/dist/elements/tabs/index.js +4 -0
- package/dist/elements/tabs/tabs.d.ts +48 -0
- package/dist/elements/tabs/tabs.d.ts.map +1 -0
- package/dist/elements/tabs/tabs.js +210 -0
- package/dist/elements/toast/index.d.ts +9 -0
- package/dist/elements/toast/index.d.ts.map +1 -0
- package/dist/elements/toast/index.js +5 -0
- package/dist/elements/toast/toast.d.ts +72 -0
- package/dist/elements/toast/toast.d.ts.map +1 -0
- package/dist/elements/toast/toast.js +375 -0
- package/dist/elements/tooltip/index.d.ts +8 -0
- package/dist/elements/tooltip/index.d.ts.map +1 -0
- package/dist/elements/tooltip/index.js +4 -0
- package/dist/elements/tooltip/tooltip.css +37 -0
- package/dist/elements/tooltip/tooltip.d.ts +59 -0
- package/dist/elements/tooltip/tooltip.d.ts.map +1 -0
- package/dist/elements/tooltip/tooltip.js +231 -0
- package/dist/elements/tree/index.d.ts +8 -0
- package/dist/elements/tree/index.d.ts.map +1 -0
- package/dist/elements/tree/index.js +4 -0
- package/dist/elements/tree/tree.css +26 -0
- package/dist/elements/tree/tree.d.ts +76 -0
- package/dist/elements/tree/tree.d.ts.map +1 -0
- package/dist/elements/tree/tree.js +432 -0
- package/dist/elements/tree-item/index.d.ts +8 -0
- package/dist/elements/tree-item/index.d.ts.map +1 -0
- package/dist/elements/tree-item/index.js +4 -0
- package/dist/elements/tree-item/tree-item.css +172 -0
- package/dist/elements/tree-item/tree-item.d.ts +74 -0
- package/dist/elements/tree-item/tree-item.d.ts.map +1 -0
- package/dist/elements/tree-item/tree-item.js +301 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/registry.d.ts +22 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +33 -0
- package/dist/shared/controllers/popover.d.ts +44 -0
- package/dist/shared/controllers/popover.d.ts.map +1 -0
- package/dist/shared/controllers/popover.js +359 -0
- package/dist/shared/luxen-element.d.ts +20 -0
- package/dist/shared/luxen-element.d.ts.map +1 -0
- package/dist/shared/luxen-element.js +23 -0
- package/dist/shared/luxen-form-associated-element.d.ts +49 -0
- package/dist/shared/luxen-form-associated-element.d.ts.map +1 -0
- package/dist/shared/luxen-form-associated-element.js +123 -0
- package/dist/shared/styles/host.css +13 -0
- package/dist/shared/styles/host.styles.d.ts +9 -0
- package/dist/shared/styles/host.styles.d.ts.map +1 -0
- package/dist/shared/styles/host.styles.js +9 -0
- package/dist/skills/luxen-ui/SKILL.md +82 -0
- package/dist/skills/luxen-ui/references/avatar.md +259 -0
- package/dist/skills/luxen-ui/references/badge.md +289 -0
- package/dist/skills/luxen-ui/references/button.md +309 -0
- package/dist/skills/luxen-ui/references/close-button.md +104 -0
- package/dist/skills/luxen-ui/references/dialog.md +435 -0
- package/dist/skills/luxen-ui/references/drawer.md +400 -0
- package/dist/skills/luxen-ui/references/progress.md +133 -0
- package/dist/skills/luxen-ui/references/select.md +100 -0
- package/dist/skills/luxen-ui/references/toast.md +396 -0
- package/dist/skills/luxen-ui/references/tree.md +359 -0
- package/package.json +116 -0
- package/postcss-plugin-prefix.js +63 -0
- package/vite-plugin.ts +29 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Tree
|
|
6
|
+
|
|
7
|
+
Tree views present hierarchical data like file explorers, navigation menus, and taxonomies. Each node expands/collapses to reveal nested items and can be selected in several ways.
|
|
8
|
+
|
|
9
|
+
<ElementSpec
|
|
10
|
+
tag="l-tree"
|
|
11
|
+
type="shadow"
|
|
12
|
+
/>
|
|
13
|
+
|
|
14
|
+
## Options
|
|
15
|
+
|
|
16
|
+
### Basic
|
|
17
|
+
|
|
18
|
+
Wrap `<l-tree-item>` nodes inside `<l-tree>`. Nested `<l-tree-item>` children become sub-nodes automatically.
|
|
19
|
+
|
|
20
|
+
<ComponentWrapper vertical :html="treeBasic" />
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<l-tree>
|
|
24
|
+
<l-tree-item>
|
|
25
|
+
Documents
|
|
26
|
+
<l-tree-item>
|
|
27
|
+
Photos
|
|
28
|
+
<l-tree-item>beach.jpg</l-tree-item>
|
|
29
|
+
<l-tree-item>mountain.jpg</l-tree-item>
|
|
30
|
+
</l-tree-item>
|
|
31
|
+
<l-tree-item>
|
|
32
|
+
Invoices
|
|
33
|
+
<l-tree-item>january.pdf</l-tree-item>
|
|
34
|
+
<l-tree-item>february.pdf</l-tree-item>
|
|
35
|
+
</l-tree-item>
|
|
36
|
+
</l-tree-item>
|
|
37
|
+
<l-tree-item>
|
|
38
|
+
Projects
|
|
39
|
+
<l-tree-item>luxen-ui</l-tree-item>
|
|
40
|
+
<l-tree-item>website</l-tree-item>
|
|
41
|
+
</l-tree-item>
|
|
42
|
+
<l-tree-item>Downloads</l-tree-item>
|
|
43
|
+
</l-tree>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Custom icons
|
|
47
|
+
|
|
48
|
+
Place any element in the `prefix` slot to render a leading icon before the label. Icons inherit the current text color.
|
|
49
|
+
|
|
50
|
+
<ComponentWrapper vertical :html="treeIcons" />
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<l-tree>
|
|
54
|
+
<l-tree-item expanded>
|
|
55
|
+
<l-icon
|
|
56
|
+
slot="prefix"
|
|
57
|
+
name="lucide:folder-open"
|
|
58
|
+
></l-icon>
|
|
59
|
+
src
|
|
60
|
+
<l-tree-item>
|
|
61
|
+
<l-icon
|
|
62
|
+
slot="prefix"
|
|
63
|
+
name="lucide:folder"
|
|
64
|
+
></l-icon>
|
|
65
|
+
components
|
|
66
|
+
<l-tree-item>
|
|
67
|
+
<l-icon
|
|
68
|
+
slot="prefix"
|
|
69
|
+
name="lucide:file-code"
|
|
70
|
+
></l-icon>
|
|
71
|
+
Button.ts
|
|
72
|
+
</l-tree-item>
|
|
73
|
+
<l-tree-item>
|
|
74
|
+
<l-icon
|
|
75
|
+
slot="prefix"
|
|
76
|
+
name="lucide:file-code"
|
|
77
|
+
></l-icon>
|
|
78
|
+
Tree.ts
|
|
79
|
+
</l-tree-item>
|
|
80
|
+
</l-tree-item>
|
|
81
|
+
<l-tree-item>
|
|
82
|
+
<l-icon
|
|
83
|
+
slot="prefix"
|
|
84
|
+
name="lucide:file-json"
|
|
85
|
+
></l-icon>
|
|
86
|
+
package.json
|
|
87
|
+
</l-tree-item>
|
|
88
|
+
<l-tree-item>
|
|
89
|
+
<l-icon
|
|
90
|
+
slot="prefix"
|
|
91
|
+
name="lucide:file-text"
|
|
92
|
+
></l-icon>
|
|
93
|
+
README.md
|
|
94
|
+
</l-tree-item>
|
|
95
|
+
</l-tree-item>
|
|
96
|
+
<l-tree-item>
|
|
97
|
+
<l-icon
|
|
98
|
+
slot="prefix"
|
|
99
|
+
name="lucide:folder"
|
|
100
|
+
></l-icon>
|
|
101
|
+
public
|
|
102
|
+
<l-tree-item>
|
|
103
|
+
<l-icon
|
|
104
|
+
slot="prefix"
|
|
105
|
+
name="lucide:image"
|
|
106
|
+
></l-icon>
|
|
107
|
+
logo.svg
|
|
108
|
+
</l-tree-item>
|
|
109
|
+
</l-tree-item>
|
|
110
|
+
</l-tree>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Custom expand icons
|
|
114
|
+
|
|
115
|
+
Override the `expand-icon` and `collapse-icon` slots to show a different icon per state — e.g. a closed folder when the branch is collapsed and an open folder when expanded. Leaves keep the `prefix` slot for their own icon.
|
|
116
|
+
|
|
117
|
+
<ComponentWrapper vertical :html="treeExpandIcons" />
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<l-tree>
|
|
121
|
+
<l-tree-item expanded>
|
|
122
|
+
<l-icon
|
|
123
|
+
slot="expand-icon"
|
|
124
|
+
name="lucide:folder"
|
|
125
|
+
></l-icon>
|
|
126
|
+
<l-icon
|
|
127
|
+
slot="collapse-icon"
|
|
128
|
+
name="lucide:folder-open"
|
|
129
|
+
></l-icon>
|
|
130
|
+
src
|
|
131
|
+
<l-tree-item>
|
|
132
|
+
<l-icon
|
|
133
|
+
slot="expand-icon"
|
|
134
|
+
name="lucide:folder"
|
|
135
|
+
></l-icon>
|
|
136
|
+
<l-icon
|
|
137
|
+
slot="collapse-icon"
|
|
138
|
+
name="lucide:folder-open"
|
|
139
|
+
></l-icon>
|
|
140
|
+
components
|
|
141
|
+
<l-tree-item>
|
|
142
|
+
<l-icon
|
|
143
|
+
slot="prefix"
|
|
144
|
+
name="lucide:file-code"
|
|
145
|
+
></l-icon>
|
|
146
|
+
Button.ts
|
|
147
|
+
</l-tree-item>
|
|
148
|
+
<l-tree-item>
|
|
149
|
+
<l-icon
|
|
150
|
+
slot="prefix"
|
|
151
|
+
name="lucide:file-code"
|
|
152
|
+
></l-icon>
|
|
153
|
+
Tree.ts
|
|
154
|
+
</l-tree-item>
|
|
155
|
+
</l-tree-item>
|
|
156
|
+
<l-tree-item>
|
|
157
|
+
<l-icon
|
|
158
|
+
slot="prefix"
|
|
159
|
+
name="lucide:file-json"
|
|
160
|
+
></l-icon>
|
|
161
|
+
package.json
|
|
162
|
+
</l-tree-item>
|
|
163
|
+
<l-tree-item>
|
|
164
|
+
<l-icon
|
|
165
|
+
slot="prefix"
|
|
166
|
+
name="lucide:file-text"
|
|
167
|
+
></l-icon>
|
|
168
|
+
README.md
|
|
169
|
+
</l-tree-item>
|
|
170
|
+
</l-tree-item>
|
|
171
|
+
</l-tree>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Row actions
|
|
175
|
+
|
|
176
|
+
Place any interactive element inside a `<l-tree-item>` to expose per-row actions (e.g. an `<l-dropdown>` menu, a button, a link). Clicks on `<button>`, `<a>`, `<input>` and elements with `role="button"` or `role="menuitem"` never toggle the row's selection or expansion.
|
|
177
|
+
|
|
178
|
+
This demo is controlled from Vue state: the yellow `…` trigger is rendered only on the selected row via `v-if`, so clicking another row moves the button there without duplicating it in the DOM.
|
|
179
|
+
|
|
180
|
+
<TreeActionsDemo />
|
|
181
|
+
|
|
182
|
+
### Multiple selection
|
|
183
|
+
|
|
184
|
+
Set `selection="multiple"` to render a native checkbox on every item. Toggling a parent cascades the selection to its descendants and sets the indeterminate state when only some children are selected.
|
|
185
|
+
|
|
186
|
+
<ComponentWrapper vertical :html="treeMultiple" />
|
|
187
|
+
|
|
188
|
+
```html
|
|
189
|
+
<l-tree selection="multiple">
|
|
190
|
+
<l-tree-item expanded>
|
|
191
|
+
Fruits
|
|
192
|
+
<l-tree-item>Apple</l-tree-item>
|
|
193
|
+
<l-tree-item selected>Banana</l-tree-item>
|
|
194
|
+
<l-tree-item>Cherry</l-tree-item>
|
|
195
|
+
</l-tree-item>
|
|
196
|
+
<l-tree-item>
|
|
197
|
+
Vegetables
|
|
198
|
+
<l-tree-item>Carrot</l-tree-item>
|
|
199
|
+
<l-tree-item>Lettuce</l-tree-item>
|
|
200
|
+
</l-tree-item>
|
|
201
|
+
</l-tree>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Independent selection
|
|
205
|
+
|
|
206
|
+
Add `independent` to decouple parents and children: a parent can be selected without ticking any of its descendants and vice-versa. Useful when nodes represent independent concepts (categories, tags, permissions) rather than aggregations.
|
|
207
|
+
|
|
208
|
+
<ComponentWrapper vertical :html="treeIndependent" />
|
|
209
|
+
|
|
210
|
+
```html
|
|
211
|
+
<l-tree
|
|
212
|
+
selection="multiple"
|
|
213
|
+
independent
|
|
214
|
+
>
|
|
215
|
+
<l-tree-item expanded>
|
|
216
|
+
Region: Europe
|
|
217
|
+
<l-tree-item>France</l-tree-item>
|
|
218
|
+
<l-tree-item>Germany</l-tree-item>
|
|
219
|
+
<l-tree-item>Italy</l-tree-item>
|
|
220
|
+
</l-tree-item>
|
|
221
|
+
<l-tree-item expanded>
|
|
222
|
+
Region: Americas
|
|
223
|
+
<l-tree-item>Brazil</l-tree-item>
|
|
224
|
+
<l-tree-item>United States</l-tree-item>
|
|
225
|
+
</l-tree-item>
|
|
226
|
+
</l-tree>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Leaf-only selection
|
|
230
|
+
|
|
231
|
+
Set `selection="leaf"` when only terminal nodes represent selectable values. Clicking a branch only toggles its expansion.
|
|
232
|
+
|
|
233
|
+
<ComponentWrapper vertical :html="treeLeaf" />
|
|
234
|
+
|
|
235
|
+
```html
|
|
236
|
+
<l-tree selection="leaf">
|
|
237
|
+
<l-tree-item expanded>
|
|
238
|
+
Components
|
|
239
|
+
<l-tree-item>Button</l-tree-item>
|
|
240
|
+
<l-tree-item>Dropdown</l-tree-item>
|
|
241
|
+
<l-tree-item>Tree</l-tree-item>
|
|
242
|
+
</l-tree-item>
|
|
243
|
+
<l-tree-item>
|
|
244
|
+
Tokens
|
|
245
|
+
<l-tree-item>Colors</l-tree-item>
|
|
246
|
+
<l-tree-item>Spacing</l-tree-item>
|
|
247
|
+
</l-tree-item>
|
|
248
|
+
</l-tree>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Disabled items
|
|
252
|
+
|
|
253
|
+
Add `disabled` to any item to prevent selection and interaction. The item remains visible and part of the structure.
|
|
254
|
+
|
|
255
|
+
<ComponentWrapper vertical :html="treeDisabled" />
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<l-tree selection="multiple">
|
|
259
|
+
<l-tree-item expanded>
|
|
260
|
+
Settings
|
|
261
|
+
<l-tree-item>Profile</l-tree-item>
|
|
262
|
+
<l-tree-item disabled>Billing (locked)</l-tree-item>
|
|
263
|
+
<l-tree-item>Notifications</l-tree-item>
|
|
264
|
+
</l-tree-item>
|
|
265
|
+
</l-tree>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Lazy loading
|
|
269
|
+
|
|
270
|
+
Add `lazy` to an item whose children will be fetched on first expand. The component emits `lazy-load`; set `loading` to render a spinner in place of the chevron, then append children and remove `lazy`.
|
|
271
|
+
|
|
272
|
+
<TreeLazyDemo />
|
|
273
|
+
|
|
274
|
+
## Examples
|
|
275
|
+
|
|
276
|
+
### Comment thread
|
|
277
|
+
|
|
278
|
+
A discussion tree styled like a Reddit comment thread. Demonstrates stacking `prefix` (avatar), the default slot (multi-line content + action buttons), and swapped `expand-icon` / `collapse-icon` slots (`lucide:circle-plus` / `lucide:circle-minus`). The `::part(base)` and `::part(label)` are overridden to align content to the top and allow wrapping; `--indent-size` and `--indent-guide-width` are tuned for the denser look.
|
|
279
|
+
|
|
280
|
+
<TreeRedditDemo />
|
|
281
|
+
|
|
282
|
+
## Accessibility
|
|
283
|
+
|
|
284
|
+
### Criteria
|
|
285
|
+
|
|
286
|
+
| Check | Description |
|
|
287
|
+
|-------|-------------|
|
|
288
|
+
| Role | Container has `role="tree"`, items have `role="treeitem"`, groups have `role="group"` |
|
|
289
|
+
| Expanded state | Branches expose `aria-expanded` reflecting open state. Leaf nodes omit the attribute. |
|
|
290
|
+
| Selected state | Selected items expose `aria-selected="true"` |
|
|
291
|
+
| Multi-selectable | Container sets `aria-multiselectable="true"` when `selection="multiple"` |
|
|
292
|
+
| Disabled state | Disabled items expose `aria-disabled="true"` and stay in the DOM for discoverability |
|
|
293
|
+
| Focus management | Roving tabindex: only one item is in the tab order at a time; arrow keys move focus within the tree |
|
|
294
|
+
| Motion | Chevron rotation and spinner respect `prefers-reduced-motion` |
|
|
295
|
+
|
|
296
|
+
### Rules
|
|
297
|
+
- Every item needs visible text content for its accessible name
|
|
298
|
+
- Use `selection="leaf"` when branches are never valid values — this prevents keyboard users from accidentally selecting aggregations
|
|
299
|
+
- Prefer `selection="multiple" independent` for forms where parent selection is a distinct choice from the children
|
|
300
|
+
|
|
301
|
+
### Keyboard interactions
|
|
302
|
+
|
|
303
|
+
| Key | Description |
|
|
304
|
+
|-----|-------------|
|
|
305
|
+
| ArrowDown | Moves focus to the next visible item |
|
|
306
|
+
| ArrowUp | Moves focus to the previous visible item |
|
|
307
|
+
| ArrowRight | Expands a collapsed branch, or moves to the first child when already expanded |
|
|
308
|
+
| ArrowLeft | Collapses an expanded branch, or moves to the parent item |
|
|
309
|
+
| Home | Moves focus to the first visible item |
|
|
310
|
+
| End | Moves focus to the last visible item |
|
|
311
|
+
| Enter | Activates the item (selects or toggles expansion depending on mode) |
|
|
312
|
+
| Space | Activates the item (selects or toggles expansion depending on mode) |
|
|
313
|
+
| * | Expands all sibling branches of the focused item |
|
|
314
|
+
|
|
315
|
+
## API reference
|
|
316
|
+
|
|
317
|
+
### Importing
|
|
318
|
+
|
|
319
|
+
```js
|
|
320
|
+
import 'luxen-ui/tree';
|
|
321
|
+
import 'luxen-ui/tree-item';
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Attributes & Properties
|
|
325
|
+
|
|
326
|
+
<ApiTable :data="[
|
|
327
|
+
{ Attribute: 'selection', Description: 'Selection mode: `single` (default), `multiple`, `leaf`, or `none`' },
|
|
328
|
+
{ Attribute: 'independent', Description: 'When set with `selection="multiple"`, parent and descendants selection are decoupled (no cascade, no indeterminate)' },
|
|
329
|
+
]" />
|
|
330
|
+
|
|
331
|
+
### Methods
|
|
332
|
+
|
|
333
|
+
<ApiTable :data="[
|
|
334
|
+
{ Method: 'getAllItems()', Description: 'Returns every `<l-tree-item>` in document order (including nested)' },
|
|
335
|
+
{ Method: 'getSelection()', Description: 'Returns the currently selected items' },
|
|
336
|
+
{ Method: 'expandAll()', Description: 'Expands every branch' },
|
|
337
|
+
{ Method: 'collapseAll()', Description: 'Collapses every item' },
|
|
338
|
+
]" />
|
|
339
|
+
|
|
340
|
+
### Events
|
|
341
|
+
|
|
342
|
+
<ApiTable :data="[
|
|
343
|
+
{ Event: 'selection-change', Description: 'Fired when the selection changes. Detail: `{ selection }`' },
|
|
344
|
+
]" />
|
|
345
|
+
|
|
346
|
+
### CSS custom properties
|
|
347
|
+
|
|
348
|
+
<ApiTable :data="[
|
|
349
|
+
{ Name: '--indent-size', Description: 'Horizontal indent per depth level. Default `1rem`' },
|
|
350
|
+
{ Name: '--indent-guide-width', Description: 'Thickness of the vertical guide line between a parent and its children. Default `1px`. Set to `0` to hide guides' },
|
|
351
|
+
{ Name: '--indent-guide-style', Description: 'Line style of the guide: `solid` (default), `dashed`, `dotted`, `double`' },
|
|
352
|
+
{ Name: '--indent-guide-color', Description: 'Color of the guide line' },
|
|
353
|
+
{ Name: '--row-height', Description: 'Minimum row height. Default `1.75rem`' },
|
|
354
|
+
{ Name: '--row-padding-inline', Description: 'Inner inline padding of the row; also drives the content slot left indent and the guide column. Default `0.25rem`' },
|
|
355
|
+
{ Name: '--chevron-size', Description: 'Size of the expand/collapse chevron/avatar box. Default `1.125rem`' },
|
|
356
|
+
{ Name: '--item-gap', Description: 'Horizontal gap between chevron, prefix, label and suffix on the row; also drives the content slot left indent. Default `0.375rem`' },
|
|
357
|
+
]" />
|
|
358
|
+
|
|
359
|
+
See [`<l-tree-item>`](/elements/tree-item) for the per-item API.
|
package/package.json
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "luxen-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Modern web components and CSS-first UI library built with Lit. Framework-agnostic, customizable prefix, design tokens.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"custom-elements",
|
|
7
|
+
"design-system",
|
|
8
|
+
"lit",
|
|
9
|
+
"ui-library",
|
|
10
|
+
"web-components"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://luxen-ui.com",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/luxen-ui/luxen-ui/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Luxen UI",
|
|
19
|
+
"url": "https://luxen-ui.com"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/luxen-ui/luxen-ui.git",
|
|
24
|
+
"directory": "packages/ui"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist/",
|
|
28
|
+
"vite-plugin.ts",
|
|
29
|
+
"postcss-plugin-prefix.js",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"type": "module",
|
|
34
|
+
"sideEffects": [
|
|
35
|
+
"*.css",
|
|
36
|
+
"**/define.js",
|
|
37
|
+
"**/registry.js"
|
|
38
|
+
],
|
|
39
|
+
"main": "./dist/index.js",
|
|
40
|
+
"module": "./dist/index.js",
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"exports": {
|
|
43
|
+
".": {
|
|
44
|
+
"types": "./dist/index.d.ts",
|
|
45
|
+
"import": "./dist/index.js"
|
|
46
|
+
},
|
|
47
|
+
"./css": {
|
|
48
|
+
"style": "./dist/css/index.css",
|
|
49
|
+
"import": "./dist/css/index.css"
|
|
50
|
+
},
|
|
51
|
+
"./css/base": {
|
|
52
|
+
"style": "./dist/css/index.css",
|
|
53
|
+
"import": "./dist/css/index.css"
|
|
54
|
+
},
|
|
55
|
+
"./css/*": {
|
|
56
|
+
"style": "./dist/css/elements/*.css",
|
|
57
|
+
"import": "./dist/css/elements/*.css"
|
|
58
|
+
},
|
|
59
|
+
"./dist/custom-elements.json": "./dist/custom-elements.json",
|
|
60
|
+
"./dist/elements/*": "./dist/elements/*",
|
|
61
|
+
"./dist/css/*": "./dist/css/*",
|
|
62
|
+
"./vite-plugin": "./vite-plugin.ts",
|
|
63
|
+
"./postcss-plugin-prefix": "./postcss-plugin-prefix.js",
|
|
64
|
+
"./*/element": {
|
|
65
|
+
"types": "./dist/elements/*/*.d.ts",
|
|
66
|
+
"import": "./dist/elements/*/*.js"
|
|
67
|
+
},
|
|
68
|
+
"./luxen-element": {
|
|
69
|
+
"types": "./dist/shared/luxen-element.d.ts",
|
|
70
|
+
"import": "./dist/shared/luxen-element.js"
|
|
71
|
+
},
|
|
72
|
+
"./luxen-form-associated-element": {
|
|
73
|
+
"types": "./dist/shared/luxen-form-associated-element.d.ts",
|
|
74
|
+
"import": "./dist/shared/luxen-form-associated-element.js"
|
|
75
|
+
},
|
|
76
|
+
"./*": {
|
|
77
|
+
"types": "./dist/elements/*/index.d.ts",
|
|
78
|
+
"import": "./dist/elements/*/index.js"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"publishConfig": {
|
|
82
|
+
"access": "public",
|
|
83
|
+
"provenance": false
|
|
84
|
+
},
|
|
85
|
+
"scripts": {
|
|
86
|
+
"dev": "WATCH=true DEV=true vp build --config vite.config.css.ts",
|
|
87
|
+
"dev:elements": "vp dev",
|
|
88
|
+
"build": "vp build --config vite.config.css.ts && tsc && node scripts/copy-styles.mjs && vp build && pnpm run manifest && node scripts/generate-skill.mjs",
|
|
89
|
+
"build:css": "vp build --config vite.config.css.ts",
|
|
90
|
+
"build:skill": "node scripts/generate-skill.mjs",
|
|
91
|
+
"manifest": "cem analyze --litelement --outdir dist",
|
|
92
|
+
"preview": "vp preview"
|
|
93
|
+
},
|
|
94
|
+
"dependencies": {
|
|
95
|
+
"@floating-ui/dom": "catalog:",
|
|
96
|
+
"embla-carousel": "catalog:",
|
|
97
|
+
"embla-carousel-autoplay": "catalog:",
|
|
98
|
+
"iconify-icon": "catalog:",
|
|
99
|
+
"lit": "catalog:"
|
|
100
|
+
},
|
|
101
|
+
"devDependencies": {
|
|
102
|
+
"@custom-elements-manifest/analyzer": "catalog:",
|
|
103
|
+
"@custom-elements-manifest/to-markdown": "catalog:",
|
|
104
|
+
"@types/node": "catalog:",
|
|
105
|
+
"autoprefixer": "catalog:",
|
|
106
|
+
"css-extras": "catalog:",
|
|
107
|
+
"postcss": "catalog:",
|
|
108
|
+
"postcss-import": "catalog:",
|
|
109
|
+
"tinyglobby": "catalog:",
|
|
110
|
+
"typescript": "catalog:",
|
|
111
|
+
"vite": "catalog:",
|
|
112
|
+
"vite-plugin-dts": "catalog:",
|
|
113
|
+
"vite-plus": "catalog:"
|
|
114
|
+
},
|
|
115
|
+
"customElements": "dist/custom-elements.json"
|
|
116
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostCSS plugin that rewrites all `l-` prefixed identifiers in CSS.
|
|
3
|
+
*
|
|
4
|
+
* Runs after `postcss-import` inlines all `@import`s, so imported files
|
|
5
|
+
* like `_tokens.css` are also rewritten.
|
|
6
|
+
*
|
|
7
|
+
* Supports separate element and CSS prefixes:
|
|
8
|
+
* - Element prefix: type selectors (`l-toast` → `{elementPrefix}-toast`)
|
|
9
|
+
* - CSS prefix: classes, custom properties, keyframes, animation references
|
|
10
|
+
*
|
|
11
|
+
* @param {{ elementPrefix?: string, cssPrefix?: string }} opts
|
|
12
|
+
*/
|
|
13
|
+
const plugin = (opts = {}) => {
|
|
14
|
+
const elementPrefix = opts.elementPrefix || 'l';
|
|
15
|
+
const cssPrefix = opts.cssPrefix || 'l';
|
|
16
|
+
if (elementPrefix === 'l' && cssPrefix === 'l') return { postcssPlugin: 'luxen-prefix' };
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
postcssPlugin: 'luxen-prefix',
|
|
20
|
+
|
|
21
|
+
Once(root) {
|
|
22
|
+
root.walk((node) => {
|
|
23
|
+
if (node.type === 'atrule') {
|
|
24
|
+
if (node.name === 'keyframes' && node.params.startsWith('l-')) {
|
|
25
|
+
node.params = node.params.replace(/^l-/, `${cssPrefix}-`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (node.type === 'rule') {
|
|
30
|
+
node.selector = rewriteSelector(node.selector, elementPrefix, cssPrefix);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (node.type === 'decl') {
|
|
34
|
+
if (node.prop.startsWith('--l-')) {
|
|
35
|
+
node.prop = node.prop.replace(/^--l-/, `--${cssPrefix}-`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (node.value.includes('l-')) {
|
|
39
|
+
node.value = rewriteValue(node.value, cssPrefix);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
plugin.postcss = true;
|
|
48
|
+
|
|
49
|
+
function rewriteSelector(selector, elementPrefix, cssPrefix) {
|
|
50
|
+
let result = selector;
|
|
51
|
+
result = result.replace(/\.l-/g, `.${cssPrefix}-`);
|
|
52
|
+
result = result.replace(/(^|[\s,>+~(])l-/gm, `$1${elementPrefix}-`);
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function rewriteValue(value, cssPrefix) {
|
|
57
|
+
let result = value;
|
|
58
|
+
result = result.replaceAll('--l-', `--${cssPrefix}-`);
|
|
59
|
+
result = result.replace(/(?<![-\w])l-/g, `${cssPrefix}-`);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default plugin;
|
package/vite-plugin.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Plugin } from 'vite-plus';
|
|
2
|
+
import postcssPrefix from './postcss-plugin-prefix.js';
|
|
3
|
+
|
|
4
|
+
export interface LuxenOptions {
|
|
5
|
+
/** Custom element tag name prefix. Default `'l'`. */
|
|
6
|
+
elementPrefix?: string;
|
|
7
|
+
/** CSS class/token/keyframe prefix. Default `'l'`. */
|
|
8
|
+
cssPrefix?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function luxen(options: LuxenOptions = {}): Plugin {
|
|
12
|
+
const elementPrefix = options.elementPrefix || 'l';
|
|
13
|
+
const cssPrefix = options.cssPrefix || 'l';
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
name: 'luxen',
|
|
17
|
+
|
|
18
|
+
config() {
|
|
19
|
+
if (elementPrefix === 'l' && cssPrefix === 'l') return;
|
|
20
|
+
return {
|
|
21
|
+
css: {
|
|
22
|
+
postcss: {
|
|
23
|
+
plugins: [postcssPrefix({ elementPrefix, cssPrefix })],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|