@deepfuture/dui-components 1.3.0 → 1.4.1

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.
@@ -0,0 +1,307 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { css } from "lit";
36
+ import { property } from "lit/decorators.js";
37
+ import { DuiToastRegionPrimitive } from "@deepfuture/dui-primitives/toast";
38
+ import "../_install.js";
39
+ const styles = css `
40
+ /* ── Region-scoped tokens ──
41
+ *
42
+ * Bump the default region width (primitive ships 22rem; 24rem reads
43
+ * better with action + close affordances).
44
+ *
45
+ * Stack-visual knobs (peek, scale falloff, expanded gap) are exposed for
46
+ * consumer tuning without rewriting the cascade transforms.
47
+ */
48
+ :host {
49
+ --toast-region-width: 24rem;
50
+ --toast-stack-peek: var(--space-3_5);
51
+ --toast-stack-scale-step: 0.05;
52
+ --toast-expanded-gap: var(--space-2);
53
+ }
54
+
55
+ /* ── theme="light|dark" overrides ──
56
+ *
57
+ * Re-declare the four Layer-1 primitives AND every Layer-2 derivation on
58
+ * the host. Custom-property var() references resolve at the *declaring*
59
+ * element's scope, so the recipes in tokens.css (declared on :root) bake
60
+ * in :root's --background/--foreground at definition time. Descendants
61
+ * inherit the already-resolved values, not the recipes themselves. To
62
+ * make scoped theme overrides actually work we re-declare the recipes
63
+ * here so they resolve against the host's overridden Layer-1 values.
64
+ *
65
+ * If other components want scoped theme overrides later, we promote this
66
+ * to tokens.css (selectors that match :root[data-theme] AND nested
67
+ * [data-theme]).
68
+ */
69
+ :host([data-theme="light"]) {
70
+ --background: oklch(0.97 0 0);
71
+ --foreground: oklch(0.15 0 0);
72
+ --accent: oklch(0.55 0.25 260);
73
+ --destructive: oklch(0.55 0.22 25);
74
+ --success: oklch(0.55 0.18 145);
75
+ --warning: oklch(0.65 0.18 70);
76
+ --info: oklch(0.55 0.18 230);
77
+ }
78
+
79
+ :host([data-theme="dark"]) {
80
+ --background: oklch(0.15 0.015 260);
81
+ --foreground: oklch(0.93 0 0);
82
+ --accent: oklch(0.75 0.18 260);
83
+ --destructive: oklch(0.70 0.18 25);
84
+ --success: oklch(0.72 0.16 145);
85
+ --warning: oklch(0.78 0.15 70);
86
+ --info: oklch(0.72 0.15 230);
87
+ }
88
+
89
+ /* Layer-2 derivations re-declared at the host so they pick up the
90
+ overridden Layer-1 values above. */
91
+ :host([data-theme="light"]),
92
+ :host([data-theme="dark"]) {
93
+ --sunken-2: oklch(from var(--background) calc(l - 0.03) c h);
94
+ --sunken-1: oklch(from var(--background) calc(l - 0.01) c h);
95
+ --surface-1: oklch(from var(--background) calc(l + 0.02) c h);
96
+ --surface-2: oklch(from var(--background) calc(l + 0.05) c h);
97
+ --surface-3: oklch(from var(--background) calc(l + 0.09) c h);
98
+
99
+ --border: oklch(from var(--foreground) l c h / 0.15);
100
+ --border-strong: oklch(from var(--foreground) l c h / 0.25);
101
+
102
+ --text-1: oklch(from var(--foreground) l c h / 0.90);
103
+ --text-2: oklch(from var(--foreground) l c h / 0.63);
104
+ --text-3: oklch(from var(--foreground) l c h / 0.45);
105
+
106
+ --accent-subtle: oklch(from var(--accent) l c h / 0.10);
107
+ --accent-text: oklch(from var(--accent) calc(l * 1.1) calc(c * 0.8) h);
108
+
109
+ --destructive-subtle: oklch(from var(--destructive) l c h / 0.10);
110
+ --destructive-text: var(--destructive);
111
+
112
+ --success-subtle: oklch(from var(--success) l c h / 0.10);
113
+ --success-text: oklch(from var(--success) calc(l * 1.05) calc(c * 0.9) h);
114
+
115
+ --warning-subtle: oklch(from var(--warning) l c h / 0.10);
116
+ --warning-text: oklch(from var(--warning) calc(l * 1.05) calc(c * 0.9) h);
117
+
118
+ --info-subtle: oklch(from var(--info) l c h / 0.10);
119
+ --info-text: oklch(from var(--info) calc(l * 1.05) calc(c * 0.9) h);
120
+
121
+ --scrim: oklch(from var(--foreground) l c h / 0.35);
122
+ }
123
+
124
+ /* ── List layout (cascade stacking on by default) ──
125
+ *
126
+ * Toasts overlap via position: absolute and use the HOST as their
127
+ * containing block, so they pin to the region's anchored edge regardless
128
+ * of stack size. The list itself stays in normal flow with a min-height
129
+ * so the region is a viable hover target when collapsed.
130
+ *
131
+ * IMPORTANT: do NOT make the list position: relative. Doing so would make
132
+ * IT the containing block; since the absolute toasts don't contribute to
133
+ * its height, the list collapses to min-height (60px) and toasts pin to
134
+ * a 60px box instead of the (potentially taller) region. The host's
135
+ * position: fixed (auto-region) or position: relative (declarative,
136
+ * scoped use) is the right containing block.
137
+ */
138
+ [part="list"] {
139
+ min-height: 60px;
140
+ /* Override primitive's default flex gap — we position absolutely. */
141
+ gap: 0;
142
+ }
143
+
144
+ /* ── Toast stacking transforms (collapsed) ──
145
+ *
146
+ * Bottom-anchored: each older toast lifts up + scales down a step.
147
+ * Top-anchored: mirror image — older toasts drop down + scale down.
148
+ */
149
+ ::slotted(dui-toast) {
150
+ position: absolute;
151
+ left: 0;
152
+ right: 0;
153
+ bottom: 0;
154
+ pointer-events: none;
155
+ transform-origin: bottom center;
156
+ transform: translateY(
157
+ calc(var(--toast-index, 0) * -1 * var(--toast-stack-peek))
158
+ ) scale(calc(1 - var(--toast-index, 0) * var(--toast-stack-scale-step)));
159
+ transition:
160
+ transform var(--duration-normal) var(--ease-out-3),
161
+ opacity var(--duration-normal) var(--ease-out-3);
162
+ }
163
+
164
+ :host([data-position^="top-"]) ::slotted(dui-toast) {
165
+ top: 0;
166
+ bottom: auto;
167
+ transform-origin: top center;
168
+ transform: translateY(calc(var(--toast-index, 0) * var(--toast-stack-peek)))
169
+ scale(calc(1 - var(--toast-index, 0) * var(--toast-stack-scale-step)));
170
+ }
171
+
172
+ /* Front toast and any toast inside an expanded region are interactive. */
173
+ ::slotted(dui-toast[data-front]),
174
+ ::slotted(dui-toast[data-region-expanded]) {
175
+ pointer-events: auto;
176
+ }
177
+
178
+ /* ── Expanded layout: real cumulative-height layout ── */
179
+ ::slotted(dui-toast[data-region-expanded]) {
180
+ transform: translateY(
181
+ calc(
182
+ -1 * var(--toasts-before-height, 0px) -
183
+ var(--toast-index, 0) * var(--toast-expanded-gap)
184
+ )
185
+ );
186
+ }
187
+
188
+ :host([data-position^="top-"]) ::slotted(dui-toast[data-region-expanded]) {
189
+ transform: translateY(
190
+ calc(
191
+ var(--toasts-before-height, 0px) +
192
+ var(--toast-index, 0) * var(--toast-expanded-gap)
193
+ )
194
+ );
195
+ }
196
+
197
+ /* ── Overflow: hide toasts past max-visible when collapsed ── */
198
+ ::slotted(dui-toast[data-overflow]) {
199
+ opacity: 0;
200
+ pointer-events: none;
201
+ }
202
+
203
+ ::slotted(dui-toast[data-overflow][data-region-expanded]) {
204
+ opacity: 1;
205
+ pointer-events: auto;
206
+ }
207
+
208
+ /* ── Reduced motion ── */
209
+ @media (prefers-reduced-motion: reduce) {
210
+ ::slotted(dui-toast) {
211
+ transition-duration: 0s;
212
+ }
213
+ }
214
+ `;
215
+ /**
216
+ * `<dui-toast-region>` — Styled toast viewport.
217
+ *
218
+ * Extends the primitive with:
219
+ * - Sonner-style cascade-and-expand stacking (default-on)
220
+ * - Wider 24rem default width
221
+ * - Consumer-tunable stack visuals (`--toast-stack-peek`,
222
+ * `--toast-stack-scale-step`, `--toast-expanded-gap`)
223
+ * - Optional `theme="light|dark|inverted"` palette override
224
+ *
225
+ * Default position is the primitive's `bottom-right`; flip via the inherited
226
+ * `position` attribute. Imperative `toast()` calls land here automatically.
227
+ *
228
+ * @attr theme - `"light" | "dark" | "inverted"`. Unset = inherit page theme.
229
+ * `inverted` resolves at runtime to the opposite of the document's
230
+ * `<html data-theme>` attribute.
231
+ */
232
+ let DuiToastRegion = (() => {
233
+ let _classSuper = DuiToastRegionPrimitive;
234
+ let _theme_decorators;
235
+ let _theme_initializers = [];
236
+ let _theme_extraInitializers = [];
237
+ return class DuiToastRegion extends _classSuper {
238
+ static {
239
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
240
+ _theme_decorators = [property({ type: String, reflect: true })];
241
+ __esDecorate(this, null, _theme_decorators, { kind: "accessor", name: "theme", static: false, private: false, access: { has: obj => "theme" in obj, get: obj => obj.theme, set: (obj, value) => { obj.theme = value; } }, metadata: _metadata }, _theme_initializers, _theme_extraInitializers);
242
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
243
+ }
244
+ static styles = [...DuiToastRegionPrimitive.styles, styles];
245
+ #theme_accessor_storage = __runInitializers(this, _theme_initializers, undefined);
246
+ /**
247
+ * Optional palette override. Unset = inherit page theme. `inverted`
248
+ * resolves at runtime to the opposite of `<html>`'s `data-theme`.
249
+ */
250
+ get theme() { return this.#theme_accessor_storage; }
251
+ set theme(value) { this.#theme_accessor_storage = value; }
252
+ /** Watcher for the document-level theme so `inverted` stays correct. */
253
+ #docThemeObserver = __runInitializers(this, _theme_extraInitializers);
254
+ connectedCallback() {
255
+ super.connectedCallback();
256
+ this.#applyTheme();
257
+ if (this.theme === "inverted")
258
+ this.#observeDocTheme();
259
+ }
260
+ disconnectedCallback() {
261
+ super.disconnectedCallback();
262
+ this.#stopObservingDocTheme();
263
+ }
264
+ willUpdate(changed) {
265
+ super.willUpdate(changed);
266
+ if (changed.has("theme")) {
267
+ this.#applyTheme();
268
+ if (this.theme === "inverted")
269
+ this.#observeDocTheme();
270
+ else
271
+ this.#stopObservingDocTheme();
272
+ }
273
+ }
274
+ /**
275
+ * Resolve the `theme` property into a `data-theme` attribute on the host.
276
+ * For `inverted`, read `<html>`'s `data-theme` and flip.
277
+ */
278
+ #applyTheme() {
279
+ if (!this.theme) {
280
+ this.removeAttribute("data-theme");
281
+ return;
282
+ }
283
+ if (this.theme === "inverted") {
284
+ const docTheme = document.documentElement.getAttribute("data-theme");
285
+ const inverted = docTheme === "dark" ? "light" : "dark";
286
+ this.setAttribute("data-theme", inverted);
287
+ return;
288
+ }
289
+ this.setAttribute("data-theme", this.theme);
290
+ }
291
+ #observeDocTheme() {
292
+ if (this.#docThemeObserver)
293
+ return;
294
+ this.#docThemeObserver = new MutationObserver(() => this.#applyTheme());
295
+ this.#docThemeObserver.observe(document.documentElement, {
296
+ attributes: true,
297
+ attributeFilter: ["data-theme"],
298
+ });
299
+ }
300
+ #stopObservingDocTheme() {
301
+ this.#docThemeObserver?.disconnect();
302
+ this.#docThemeObserver = undefined;
303
+ }
304
+ };
305
+ })();
306
+ export { DuiToastRegion };
307
+ customElements.define(DuiToastRegion.tagName, DuiToastRegion);
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `<dui-toast>` — Styled toast item.
3
+ *
4
+ * NOTE: This is the only DUI component that overrides `render()`. The styled
5
+ * layer's contract is normally "extend with CSS only" — toasts are an
6
+ * exception because we ship built-in default icons keyed off `data-type`,
7
+ * which require a new shadow-DOM slot (`icon`) the primitive doesn't have.
8
+ * The override carefully preserves the primitive's full markup (root, title,
9
+ * description, default slot, action slot, close slot, ARIA wiring,
10
+ * `data-*` state attributes) so consumer-facing parts/slots stay stable.
11
+ */
12
+ import { type TemplateResult } from "lit";
13
+ import { DuiToastPrimitive } from "@deepfuture/dui-primitives/toast";
14
+ import "../_install.js";
15
+ import "../spinner/index.js";
16
+ /**
17
+ * `<dui-toast>` — Styled toast item.
18
+ *
19
+ * Extends the primitive with:
20
+ * - Sonner-default look (neutral surface, colored icon, title + description)
21
+ * - Built-in default icons per `type` (success/info/warning/error/loading);
22
+ * slot `icon` to override
23
+ * - `appearance="rich"` for tinted-surface mode
24
+ * - CSS Grid layout with full slot/part preservation
25
+ * - Hover-visible close button (always visible on touch)
26
+ *
27
+ * @slot title - Title text (auto-wires aria-labelledby).
28
+ * @slot description - Description text (auto-wires aria-describedby).
29
+ * @slot - Default custom content (Tier-2 escape hatch).
30
+ * @slot icon - Custom icon to replace the type-default. Empty = built-in.
31
+ * @slot action - Action button(s); typically a `<dui-toast-action>` wrapper.
32
+ * @slot close - Close affordance; typically a `<dui-toast-close>` wrapper.
33
+ *
34
+ * @csspart root - The toast container.
35
+ * @csspart icon - The icon container.
36
+ * @csspart title - The title wrapper.
37
+ * @csspart description - The description wrapper.
38
+ * @csspart action-wrapper - The action slot's grid cell.
39
+ * @csspart close-wrapper - The close slot's absolute-positioned container.
40
+ *
41
+ * @attr appearance - `"default" | "rich"`. Rich tints surface + border
42
+ * by type color. Default keeps the surface neutral.
43
+ */
44
+ export declare class DuiToast extends DuiToastPrimitive {
45
+ #private;
46
+ static styles: import("lit").CSSResult[];
47
+ /**
48
+ * Override the primitive's render to inject an `icon` slot with built-in
49
+ * defaults. Preserves all other parts/slots/ARIA wiring exactly.
50
+ */
51
+ render(): TemplateResult;
52
+ }