@deepfuture/dui-components 1.2.1 → 1.3.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/all.d.ts CHANGED
@@ -44,6 +44,7 @@ import "./textarea/index.js";
44
44
  import "./toggle/index.js";
45
45
  import "./toolbar/index.js";
46
46
  import "./tooltip/index.js";
47
+ import "./tree/index.js";
47
48
  import "./trunc/index.js";
48
49
  export { DuiAccordion } from "./accordion/index.js";
49
50
  export { DuiAccordionItem } from "./accordion/index.js";
@@ -133,6 +134,8 @@ export { DuiToolbar } from "./toolbar/index.js";
133
134
  export { DuiTooltip } from "./tooltip/index.js";
134
135
  export { DuiTooltipPopup } from "./tooltip/index.js";
135
136
  export { DuiTooltipTrigger } from "./tooltip/index.js";
137
+ export { DuiTree } from "./tree/index.js";
138
+ export { DuiTreeItem } from "./tree/index.js";
136
139
  export { DuiTrunc } from "./trunc/index.js";
137
140
  export type { AccordionContext } from "@deepfuture/dui-primitives/accordion";
138
141
  export type { AlertDialogOpenChangeDetail } from "@deepfuture/dui-primitives/alert-dialog";
@@ -159,3 +162,4 @@ export type { TextareaResize } from "@deepfuture/dui-primitives/textarea";
159
162
  export type { ToggleGroupContext } from "@deepfuture/dui-primitives/toggle";
160
163
  export type { TooltipOpenChangeDetail } from "@deepfuture/dui-primitives/tooltip";
161
164
  export type { TooltipContext, TooltipSide } from "@deepfuture/dui-primitives/tooltip";
165
+ export type { SelectionMode, TreeContext } from "@deepfuture/dui-primitives/tree";
package/all.js CHANGED
@@ -44,6 +44,7 @@ import "./textarea/index.js";
44
44
  import "./toggle/index.js";
45
45
  import "./toolbar/index.js";
46
46
  import "./tooltip/index.js";
47
+ import "./tree/index.js";
47
48
  import "./trunc/index.js";
48
49
  export { DuiAccordion } from "./accordion/index.js";
49
50
  export { DuiAccordionItem } from "./accordion/index.js";
@@ -133,4 +134,6 @@ export { DuiToolbar } from "./toolbar/index.js";
133
134
  export { DuiTooltip } from "./tooltip/index.js";
134
135
  export { DuiTooltipPopup } from "./tooltip/index.js";
135
136
  export { DuiTooltipTrigger } from "./tooltip/index.js";
137
+ export { DuiTree } from "./tree/index.js";
138
+ export { DuiTreeItem } from "./tree/index.js";
136
139
  export { DuiTrunc } from "./trunc/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepfuture/dui-components",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "DUI styled web components — extends dui-primitives with design tokens and variant CSS",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -186,6 +186,10 @@
186
186
  "import": "./tooltip/index.js",
187
187
  "types": "./tooltip/index.d.ts"
188
188
  },
189
+ "./tree": {
190
+ "import": "./tree/index.js",
191
+ "types": "./tree/index.d.ts"
192
+ },
189
193
  "./tokens": {
190
194
  "import": "./tokens/tokens.js",
191
195
  "types": "./tokens/tokens.d.ts"
@@ -210,7 +214,7 @@
210
214
  "README.md"
211
215
  ],
212
216
  "dependencies": {
213
- "@deepfuture/dui-primitives": "1.1.0",
217
+ "@deepfuture/dui-primitives": "1.3.0",
214
218
  "lit": "^3.3.2",
215
219
  "@lit/context": "^1.1.3"
216
220
  },
@@ -0,0 +1,6 @@
1
+ import "./tree.js";
2
+ import "./tree-item.js";
3
+ export { DuiTree } from "./tree.js";
4
+ export { DuiTreeItem } from "./tree-item.js";
5
+ export type { SelectionMode, TreeContext } from "@deepfuture/dui-primitives/tree";
6
+ export { actionEvent, expandedChangeEvent, loadChildrenEvent, selectionChangeEvent, } from "@deepfuture/dui-primitives/tree";
package/tree/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import "./tree.js";
2
+ import "./tree-item.js";
3
+ export { DuiTree } from "./tree.js";
4
+ export { DuiTreeItem } from "./tree-item.js";
5
+ export { actionEvent, expandedChangeEvent, loadChildrenEvent, selectionChangeEvent, } from "@deepfuture/dui-primitives/tree";
@@ -0,0 +1,5 @@
1
+ import { DuiTreeItemPrimitive } from "@deepfuture/dui-primitives/tree";
2
+ import "../_install.js";
3
+ export declare class DuiTreeItem extends DuiTreeItemPrimitive {
4
+ static styles: import("lit").CSSResult[];
5
+ }
@@ -0,0 +1,144 @@
1
+ import { css, unsafeCSS } from "lit";
2
+ import { DuiTreeItemPrimitive } from "@deepfuture/dui-primitives/tree";
3
+ import "../_install.js";
4
+ // Lucide chevron-right, encoded for use as a CSS mask
5
+ const chevronMask = unsafeCSS(`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E")`);
6
+ // Lucide loader-2 (rotating arc), used as a spinner mask while loading
7
+ const spinnerMask = unsafeCSS(`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-6.219-8.56'/%3E%3C/svg%3E")`);
8
+ const styles = css `
9
+ /* ── Row container ─────────────────────────────────────────────────── */
10
+
11
+ /*
12
+ * IMPORTANT: All visual styling lives on [part="content"], NOT [part="root"].
13
+ * [part="root"] wraps both the row and the children group, so :hover on it
14
+ * would bleed onto descendant rows. See the hover-bleed regression demo.
15
+ */
16
+ [part="content"] {
17
+ height: var(--dui-tree-row-height);
18
+ padding-inline-start: calc(
19
+ var(--_tree-row-px) +
20
+ (var(--dui-tree-level, 1) - 1) * var(--dui-tree-indent)
21
+ );
22
+ padding-inline-end: var(--_tree-row-px);
23
+ margin-block: calc(var(--dui-tree-row-spacing) / 2);
24
+ margin-inline: var(--space-1);
25
+ border-radius: var(--dui-tree-row-radius);
26
+ gap: var(--_tree-inline-gap);
27
+ color: var(--text-1);
28
+ font-family: var(--font-sans);
29
+ font-size: var(--_tree-label-font-size);
30
+ line-height: var(--_tree-label-line-height);
31
+ cursor: pointer;
32
+ transition-property: background, box-shadow, color;
33
+ transition-duration: var(--duration-faster);
34
+ transition-timing-function: var(--ease-out-3);
35
+ /* Allow children to truncate */
36
+ min-width: 0;
37
+ }
38
+
39
+ @media (hover: hover) {
40
+ [part="content"]:hover {
41
+ background: var(--dui-tree-hover-bg);
42
+ }
43
+ }
44
+
45
+ :host([data-selected]) [part="content"] {
46
+ background: var(--dui-tree-selected-bg);
47
+ }
48
+
49
+ :host([data-disabled]) [part="content"] {
50
+ opacity: 0.4;
51
+ cursor: not-allowed;
52
+ }
53
+
54
+ /* ── Focus ring ─────────────────────────────────────────────────────── */
55
+
56
+ [part="root"]:focus-visible {
57
+ outline: none;
58
+ }
59
+
60
+ [part="root"]:focus-visible [part="content"] {
61
+ box-shadow:
62
+ 0 0 0 var(--focus-ring-offset) var(--background),
63
+ 0 0 0 calc(var(--focus-ring-offset) + var(--focus-ring-width))
64
+ var(--focus-ring-color);
65
+ /* Keep the highlighted row above siblings so the ring isn't clipped. */
66
+ position: relative;
67
+ z-index: 1;
68
+ }
69
+
70
+ /* ── Indicator (chevron / spinner / leaf placeholder) ─────────────── */
71
+
72
+ [part="indicator"] {
73
+ width: var(--_tree-indicator-size);
74
+ height: var(--_tree-indicator-size);
75
+ color: var(--text-2);
76
+ }
77
+
78
+ /* Branch chevron */
79
+ :host([data-branch]) [part="indicator"]::before {
80
+ content: "";
81
+ display: block;
82
+ width: 100%;
83
+ height: 100%;
84
+ background: currentColor;
85
+ -webkit-mask: ${chevronMask} center / contain no-repeat;
86
+ mask: ${chevronMask} center / contain no-repeat;
87
+ transition-property: transform;
88
+ transition-duration: var(--duration-fast);
89
+ transition-timing-function: var(--ease-out-3);
90
+ }
91
+
92
+ :host([data-expanded]) [part="indicator"]::before {
93
+ transform: rotate(90deg);
94
+ }
95
+
96
+ /* Loading spinner replaces the chevron in-place */
97
+ :host([data-loading]) [part="indicator"]::before {
98
+ -webkit-mask: ${spinnerMask} center / contain no-repeat;
99
+ mask: ${spinnerMask} center / contain no-repeat;
100
+ transform: none;
101
+ animation: dui-tree-spin 0.9s linear infinite;
102
+ }
103
+
104
+ @keyframes dui-tree-spin {
105
+ from {
106
+ transform: rotate(0deg);
107
+ }
108
+ to {
109
+ transform: rotate(360deg);
110
+ }
111
+ }
112
+
113
+ /* ── Slotted content ────────────────────────────────────────────────── */
114
+
115
+ ::slotted([slot="label"]) {
116
+ min-width: 0;
117
+ overflow: hidden;
118
+ text-overflow: ellipsis;
119
+ white-space: nowrap;
120
+ }
121
+
122
+ ::slotted([slot="end"]) {
123
+ flex-shrink: 0;
124
+ color: var(--text-2);
125
+ font-size: var(--_tree-end-font-size);
126
+ line-height: var(--_tree-end-line-height);
127
+ }
128
+
129
+ /* ── Reduced motion ────────────────────────────────────────────────── */
130
+
131
+ @media (prefers-reduced-motion: reduce) {
132
+ [part="content"],
133
+ :host([data-branch]) [part="indicator"]::before {
134
+ transition-duration: 0s;
135
+ }
136
+ :host([data-loading]) [part="indicator"]::before {
137
+ animation: none;
138
+ }
139
+ }
140
+ `;
141
+ export class DuiTreeItem extends DuiTreeItemPrimitive {
142
+ static styles = [...DuiTreeItemPrimitive.styles, styles];
143
+ }
144
+ customElements.define(DuiTreeItem.tagName, DuiTreeItem);
package/tree/tree.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { DuiTreePrimitive } from "@deepfuture/dui-primitives/tree";
2
+ import "../_install.js";
3
+ export declare class DuiTree extends DuiTreePrimitive {
4
+ static styles: import("lit").CSSResult[];
5
+ }
package/tree/tree.js ADDED
@@ -0,0 +1,51 @@
1
+ import { css } from "lit";
2
+ import { DuiTreePrimitive } from "@deepfuture/dui-primitives/tree";
3
+ import "../_install.js";
4
+ const styles = css `
5
+ /* ── Sizes — write to public tokens on :host so consumer overrides win ── */
6
+
7
+ :host {
8
+ /* Default = sm */
9
+ --dui-tree-row-height: var(--component-height-sm);
10
+ --dui-tree-indent: var(--space-4);
11
+ --dui-tree-row-spacing: 0;
12
+ --dui-tree-row-radius: var(--radius-sm);
13
+ --dui-tree-hover-bg: oklch(from var(--foreground) l c h / 0.05);
14
+ --dui-tree-selected-bg: oklch(from var(--foreground) l c h / 0.10);
15
+
16
+ /* Internal-only (not part of public token surface) */
17
+ --_tree-label-font-size: var(--text-xs);
18
+ --_tree-label-line-height: var(--text-xs--line-height);
19
+ --_tree-end-font-size: var(--text-2xs);
20
+ --_tree-end-line-height: var(--text-2xs--line-height);
21
+ --_tree-indicator-size: 14px;
22
+ --_tree-row-px: var(--space-2);
23
+ --_tree-inline-gap: var(--space-2);
24
+ }
25
+
26
+ :host([size="md"]) {
27
+ --dui-tree-row-height: var(--component-height-md);
28
+ --dui-tree-indent: var(--space-5);
29
+ --_tree-label-font-size: var(--text-sm);
30
+ --_tree-label-line-height: var(--text-sm--line-height);
31
+ --_tree-end-font-size: var(--text-xs);
32
+ --_tree-end-line-height: var(--text-xs--line-height);
33
+ --_tree-indicator-size: var(--space-4);
34
+ --_tree-inline-gap: var(--space-2);
35
+ }
36
+
37
+ :host([size="lg"]) {
38
+ --dui-tree-row-height: var(--component-height-lg);
39
+ --dui-tree-indent: var(--space-6);
40
+ --_tree-label-font-size: var(--text-sm);
41
+ --_tree-label-line-height: var(--text-sm--line-height);
42
+ --_tree-end-font-size: var(--text-xs);
43
+ --_tree-end-line-height: var(--text-xs--line-height);
44
+ --_tree-indicator-size: var(--space-4);
45
+ --_tree-inline-gap: var(--space-2_5);
46
+ }
47
+ `;
48
+ export class DuiTree extends DuiTreePrimitive {
49
+ static styles = [...DuiTreePrimitive.styles, styles];
50
+ }
51
+ customElements.define(DuiTree.tagName, DuiTree);