@fluix-ui/vanilla 0.0.2

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/index.js ADDED
@@ -0,0 +1,743 @@
1
+ import { Toaster, TOAST_DEFAULTS, animateSpring, FLUIX_SPRING } from '@fluix-ui/core';
2
+ export { fluix } from '@fluix-ui/core';
3
+
4
+ // src/index.ts
5
+ var WIDTH = 350;
6
+ var HEIGHT = 40;
7
+ var PILL_PADDING = 10;
8
+ var MIN_EXPAND_RATIO = 2.25;
9
+ var HEADER_EXIT_MS = 600 * 0.7;
10
+ var BODY_MERGE_OVERLAP = 6;
11
+ var SVG_NS = "http://www.w3.org/2000/svg";
12
+ function createSvgIcon(state) {
13
+ const svg = document.createElementNS(SVG_NS, "svg");
14
+ svg.setAttribute("width", "14");
15
+ svg.setAttribute("height", "14");
16
+ svg.setAttribute("viewBox", "0 0 24 24");
17
+ svg.setAttribute("fill", "none");
18
+ svg.setAttribute("stroke", "currentColor");
19
+ svg.setAttribute("stroke-width", "2.5");
20
+ svg.setAttribute("stroke-linecap", "round");
21
+ svg.setAttribute("stroke-linejoin", "round");
22
+ svg.setAttribute("aria-hidden", "true");
23
+ switch (state) {
24
+ case "success": {
25
+ const p = document.createElementNS(SVG_NS, "polyline");
26
+ p.setAttribute("points", "20 6 9 17 4 12");
27
+ svg.appendChild(p);
28
+ break;
29
+ }
30
+ case "error": {
31
+ const l1 = document.createElementNS(SVG_NS, "line");
32
+ l1.setAttribute("x1", "18");
33
+ l1.setAttribute("y1", "6");
34
+ l1.setAttribute("x2", "6");
35
+ l1.setAttribute("y2", "18");
36
+ const l2 = document.createElementNS(SVG_NS, "line");
37
+ l2.setAttribute("x1", "6");
38
+ l2.setAttribute("y1", "6");
39
+ l2.setAttribute("x2", "18");
40
+ l2.setAttribute("y2", "18");
41
+ svg.appendChild(l1);
42
+ svg.appendChild(l2);
43
+ break;
44
+ }
45
+ case "warning": {
46
+ const path = document.createElementNS(SVG_NS, "path");
47
+ path.setAttribute(
48
+ "d",
49
+ "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
50
+ );
51
+ const l1 = document.createElementNS(SVG_NS, "line");
52
+ l1.setAttribute("x1", "12");
53
+ l1.setAttribute("y1", "9");
54
+ l1.setAttribute("x2", "12");
55
+ l1.setAttribute("y2", "13");
56
+ const l2 = document.createElementNS(SVG_NS, "line");
57
+ l2.setAttribute("x1", "12");
58
+ l2.setAttribute("y1", "17");
59
+ l2.setAttribute("x2", "12.01");
60
+ l2.setAttribute("y2", "17");
61
+ svg.appendChild(path);
62
+ svg.appendChild(l1);
63
+ svg.appendChild(l2);
64
+ break;
65
+ }
66
+ case "info": {
67
+ const c = document.createElementNS(SVG_NS, "circle");
68
+ c.setAttribute("cx", "12");
69
+ c.setAttribute("cy", "12");
70
+ c.setAttribute("r", "10");
71
+ const l1 = document.createElementNS(SVG_NS, "line");
72
+ l1.setAttribute("x1", "12");
73
+ l1.setAttribute("y1", "16");
74
+ l1.setAttribute("x2", "12");
75
+ l1.setAttribute("y2", "12");
76
+ const l2 = document.createElementNS(SVG_NS, "line");
77
+ l2.setAttribute("x1", "12");
78
+ l2.setAttribute("y1", "8");
79
+ l2.setAttribute("x2", "12.01");
80
+ l2.setAttribute("y2", "8");
81
+ svg.appendChild(c);
82
+ svg.appendChild(l1);
83
+ svg.appendChild(l2);
84
+ break;
85
+ }
86
+ case "loading": {
87
+ svg.setAttribute("data-fluix-icon", "spin");
88
+ const lines = [
89
+ ["12", "2", "12", "6"],
90
+ ["12", "18", "12", "22"],
91
+ ["4.93", "4.93", "7.76", "7.76"],
92
+ ["16.24", "16.24", "19.07", "19.07"],
93
+ ["2", "12", "6", "12"],
94
+ ["18", "12", "22", "12"],
95
+ ["4.93", "19.07", "7.76", "16.24"],
96
+ ["16.24", "7.76", "19.07", "4.93"]
97
+ ];
98
+ for (const [x1, y1, x2, y2] of lines) {
99
+ const l = document.createElementNS(SVG_NS, "line");
100
+ l.setAttribute("x1", x1);
101
+ l.setAttribute("y1", y1);
102
+ l.setAttribute("x2", x2);
103
+ l.setAttribute("y2", y2);
104
+ svg.appendChild(l);
105
+ }
106
+ break;
107
+ }
108
+ case "action": {
109
+ const c = document.createElementNS(SVG_NS, "circle");
110
+ c.setAttribute("cx", "12");
111
+ c.setAttribute("cy", "12");
112
+ c.setAttribute("r", "10");
113
+ const poly = document.createElementNS(SVG_NS, "polygon");
114
+ poly.setAttribute("points", "10 8 16 12 10 16 10 8");
115
+ poly.setAttribute("fill", "currentColor");
116
+ poly.setAttribute("stroke", "none");
117
+ svg.appendChild(c);
118
+ svg.appendChild(poly);
119
+ break;
120
+ }
121
+ default:
122
+ return null;
123
+ }
124
+ return svg;
125
+ }
126
+ function renderIconInto(container, icon, state) {
127
+ container.textContent = "";
128
+ if (icon != null) {
129
+ if (icon instanceof HTMLElement || icon instanceof SVGElement) {
130
+ container.appendChild(icon);
131
+ } else {
132
+ const span = document.createElement("span");
133
+ span.setAttribute("aria-hidden", "true");
134
+ span.textContent = String(icon);
135
+ container.appendChild(span);
136
+ }
137
+ return;
138
+ }
139
+ const svgIcon = createSvgIcon(state);
140
+ if (svgIcon) container.appendChild(svgIcon);
141
+ }
142
+ function getPillAlign(position) {
143
+ if (position.includes("right")) return "right";
144
+ if (position.includes("center")) return "center";
145
+ return "left";
146
+ }
147
+ function applyAttrs(el, attrs) {
148
+ for (const [key, value] of Object.entries(attrs)) {
149
+ el.setAttribute(key, value);
150
+ }
151
+ }
152
+ function resolveOffsetValue(v) {
153
+ return typeof v === "number" ? `${v}px` : v;
154
+ }
155
+ function applyViewportOffset(el, offset, position) {
156
+ el.style.top = "";
157
+ el.style.right = "";
158
+ el.style.bottom = "";
159
+ el.style.left = "";
160
+ el.style.paddingLeft = "";
161
+ el.style.paddingRight = "";
162
+ if (offset == null) return;
163
+ let top;
164
+ let right;
165
+ let bottom;
166
+ let left;
167
+ if (typeof offset === "number" || typeof offset === "string") {
168
+ const v = resolveOffsetValue(offset);
169
+ top = v;
170
+ right = v;
171
+ bottom = v;
172
+ left = v;
173
+ } else {
174
+ if (offset.top != null) top = resolveOffsetValue(offset.top);
175
+ if (offset.right != null) right = resolveOffsetValue(offset.right);
176
+ if (offset.bottom != null) bottom = resolveOffsetValue(offset.bottom);
177
+ if (offset.left != null) left = resolveOffsetValue(offset.left);
178
+ }
179
+ if (position.startsWith("top") && top) el.style.top = top;
180
+ if (position.startsWith("bottom") && bottom) el.style.bottom = bottom;
181
+ if (position.endsWith("right") && right) el.style.right = right;
182
+ if (position.endsWith("left") && left) el.style.left = left;
183
+ if (position.endsWith("center")) {
184
+ if (left) el.style.paddingLeft = left;
185
+ if (right) el.style.paddingRight = right;
186
+ }
187
+ }
188
+ function setTimer(inst, fn, ms) {
189
+ const id = setTimeout(() => {
190
+ inst.timers.delete(id);
191
+ fn();
192
+ }, ms);
193
+ inst.timers.add(id);
194
+ return id;
195
+ }
196
+ function computeLayout(item, inst) {
197
+ const { ready, expanded: isExpanded } = inst.localState;
198
+ const roundness = item.roundness ?? TOAST_DEFAULTS.roundness;
199
+ const blur = Math.min(10, Math.max(6, roundness * 0.45));
200
+ const hasDesc = Boolean(item.description) || Boolean(item.button);
201
+ const isLoading = item.state === "loading";
202
+ const open = hasDesc && isExpanded && !isLoading;
203
+ const position = getPillAlign(item.position);
204
+ const edge = item.position.startsWith("top") ? "bottom" : "top";
205
+ const resolvedPillWidth = Math.max(inst.pillWidth || HEIGHT, HEIGHT);
206
+ const pillHeight = HEIGHT + blur * 3;
207
+ const pillX = position === "right" ? WIDTH - resolvedPillWidth : position === "center" ? (WIDTH - resolvedPillWidth) / 2 : 0;
208
+ const minExpanded = HEIGHT * MIN_EXPAND_RATIO;
209
+ const rawExpanded = hasDesc ? Math.max(minExpanded, HEIGHT + inst.contentHeight) : minExpanded;
210
+ if (open) {
211
+ inst.frozenExpanded = rawExpanded;
212
+ }
213
+ const expanded = open ? rawExpanded : inst.frozenExpanded;
214
+ const expandedContent = Math.max(0, expanded - HEIGHT);
215
+ const svgHeight = hasDesc ? Math.max(expanded, minExpanded) : HEIGHT;
216
+ return {
217
+ ready,
218
+ isExpanded,
219
+ roundness,
220
+ blur,
221
+ hasDesc,
222
+ isLoading,
223
+ open,
224
+ position,
225
+ edge,
226
+ resolvedPillWidth,
227
+ pillHeight,
228
+ pillX,
229
+ minExpanded,
230
+ expanded,
231
+ expandedContent,
232
+ svgHeight
233
+ };
234
+ }
235
+ function createInstance(item, machine) {
236
+ const localState = { ready: false, expanded: false };
237
+ const roundness = item.roundness ?? TOAST_DEFAULTS.roundness;
238
+ const blur = Math.min(10, Math.max(6, roundness * 0.45));
239
+ const filterId = `fluix-gooey-${item.id.replace(/[^a-z0-9-]/gi, "-")}`;
240
+ const hasDesc = Boolean(item.description) || Boolean(item.button);
241
+ const el = document.createElement("button");
242
+ el.type = "button";
243
+ const canvasDiv = document.createElement("div");
244
+ const minExpanded = HEIGHT * MIN_EXPAND_RATIO;
245
+ const initialSvgHeight = hasDesc ? minExpanded : HEIGHT;
246
+ const svg = document.createElementNS(SVG_NS, "svg");
247
+ svg.setAttribute("data-fluix-svg", "");
248
+ svg.setAttribute("width", String(WIDTH));
249
+ svg.setAttribute("height", String(initialSvgHeight));
250
+ svg.setAttribute("viewBox", `0 0 ${WIDTH} ${initialSvgHeight}`);
251
+ svg.setAttribute("aria-hidden", "true");
252
+ const defs = document.createElementNS(SVG_NS, "defs");
253
+ const filter = document.createElementNS(SVG_NS, "filter");
254
+ filter.setAttribute("id", filterId);
255
+ filter.setAttribute("x", "-20%");
256
+ filter.setAttribute("y", "-20%");
257
+ filter.setAttribute("width", "140%");
258
+ filter.setAttribute("height", "140%");
259
+ filter.setAttribute("color-interpolation-filters", "sRGB");
260
+ const feBlur = document.createElementNS(SVG_NS, "feGaussianBlur");
261
+ feBlur.setAttribute("in", "SourceGraphic");
262
+ feBlur.setAttribute("stdDeviation", String(blur));
263
+ feBlur.setAttribute("result", "blur");
264
+ const feCM = document.createElementNS(SVG_NS, "feColorMatrix");
265
+ feCM.setAttribute("in", "blur");
266
+ feCM.setAttribute("type", "matrix");
267
+ feCM.setAttribute("values", "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10");
268
+ feCM.setAttribute("result", "goo");
269
+ const feComp = document.createElementNS(SVG_NS, "feComposite");
270
+ feComp.setAttribute("in", "SourceGraphic");
271
+ feComp.setAttribute("in2", "goo");
272
+ feComp.setAttribute("operator", "atop");
273
+ filter.appendChild(feBlur);
274
+ filter.appendChild(feCM);
275
+ filter.appendChild(feComp);
276
+ defs.appendChild(filter);
277
+ svg.appendChild(defs);
278
+ const g = document.createElementNS(SVG_NS, "g");
279
+ g.setAttribute("filter", `url(#${filterId})`);
280
+ const initialPillX = getPillAlign(item.position) === "right" ? WIDTH - HEIGHT : getPillAlign(item.position) === "center" ? (WIDTH - HEIGHT) / 2 : 0;
281
+ const pillEl = document.createElementNS(SVG_NS, "rect");
282
+ pillEl.setAttribute("data-fluix-pill", "");
283
+ pillEl.setAttribute("x", String(initialPillX));
284
+ pillEl.setAttribute("y", "0");
285
+ pillEl.setAttribute("width", String(HEIGHT));
286
+ pillEl.setAttribute("height", String(HEIGHT));
287
+ pillEl.setAttribute("rx", String(roundness));
288
+ pillEl.setAttribute("ry", String(roundness));
289
+ pillEl.setAttribute("fill", item.fill ?? "#FFFFFF");
290
+ const bodyEl = document.createElementNS(SVG_NS, "rect");
291
+ bodyEl.setAttribute("data-fluix-body", "");
292
+ bodyEl.setAttribute("x", "0");
293
+ bodyEl.setAttribute("y", String(HEIGHT));
294
+ bodyEl.setAttribute("width", String(WIDTH));
295
+ bodyEl.setAttribute("height", "0");
296
+ bodyEl.setAttribute("rx", String(roundness));
297
+ bodyEl.setAttribute("ry", String(roundness));
298
+ bodyEl.setAttribute("fill", item.fill ?? "#FFFFFF");
299
+ bodyEl.setAttribute("opacity", "0");
300
+ g.appendChild(pillEl);
301
+ g.appendChild(bodyEl);
302
+ svg.appendChild(g);
303
+ canvasDiv.appendChild(svg);
304
+ el.appendChild(canvasDiv);
305
+ const headerEl = document.createElement("div");
306
+ const headerStackEl = document.createElement("div");
307
+ headerStackEl.setAttribute("data-fluix-header-stack", "");
308
+ const innerEl = document.createElement("div");
309
+ innerEl.setAttribute("data-fluix-header-inner", "");
310
+ innerEl.setAttribute("data-layer", "current");
311
+ const badgeEl = document.createElement("div");
312
+ renderIconInto(badgeEl, item.icon, item.state);
313
+ const titleEl = document.createElement("span");
314
+ titleEl.textContent = item.title ?? item.state;
315
+ innerEl.appendChild(badgeEl);
316
+ innerEl.appendChild(titleEl);
317
+ headerStackEl.appendChild(innerEl);
318
+ headerEl.appendChild(headerStackEl);
319
+ el.appendChild(headerEl);
320
+ let contentEl = null;
321
+ let descriptionEl = null;
322
+ if (hasDesc) {
323
+ contentEl = document.createElement("div");
324
+ descriptionEl = document.createElement("div");
325
+ if (item.styles?.description) {
326
+ descriptionEl.className = item.styles.description;
327
+ }
328
+ if (item.description != null) {
329
+ if (typeof item.description === "string") {
330
+ descriptionEl.textContent = item.description;
331
+ } else if (item.description instanceof HTMLElement) {
332
+ descriptionEl.appendChild(item.description);
333
+ }
334
+ }
335
+ if (item.button) {
336
+ const btn = document.createElement("button");
337
+ btn.type = "button";
338
+ btn.textContent = item.button.title;
339
+ if (item.styles?.button) {
340
+ btn.className = item.styles.button;
341
+ }
342
+ btn.addEventListener("click", (e) => {
343
+ e.stopPropagation();
344
+ item.button?.onClick();
345
+ });
346
+ descriptionEl.appendChild(btn);
347
+ }
348
+ contentEl.appendChild(descriptionEl);
349
+ el.appendChild(contentEl);
350
+ }
351
+ const attrs = Toaster.getAttrs(item, localState);
352
+ applyAttrs(el, attrs.root);
353
+ applyAttrs(canvasDiv, attrs.canvas);
354
+ applyAttrs(headerEl, attrs.header);
355
+ applyAttrs(badgeEl, attrs.badge);
356
+ if (item.styles?.badge) badgeEl.className = item.styles.badge;
357
+ applyAttrs(titleEl, attrs.title);
358
+ if (item.styles?.title) titleEl.className = item.styles.title;
359
+ if (contentEl) applyAttrs(contentEl, attrs.content);
360
+ if (descriptionEl) applyAttrs(descriptionEl, attrs.description);
361
+ if (item.button && descriptionEl) {
362
+ const btnEl = descriptionEl.querySelector("button");
363
+ if (btnEl) applyAttrs(btnEl, attrs.button);
364
+ }
365
+ const headerKey = `${item.state}-${item.title ?? item.state}`;
366
+ const inst = {
367
+ el,
368
+ pillEl,
369
+ bodyEl,
370
+ svgEl: svg,
371
+ headerEl,
372
+ innerEl,
373
+ headerStackEl,
374
+ contentEl,
375
+ descriptionEl,
376
+ localState,
377
+ pillWidth: 0,
378
+ contentHeight: 0,
379
+ frozenExpanded: HEIGHT * MIN_EXPAND_RATIO,
380
+ hovering: false,
381
+ pendingDismiss: false,
382
+ dismissRequested: false,
383
+ pillRo: null,
384
+ contentRo: null,
385
+ pillAnim: null,
386
+ prevPill: { x: initialPillX, width: HEIGHT, height: HEIGHT },
387
+ pillFirst: true,
388
+ headerKey,
389
+ headerPad: null,
390
+ connectHandle: null,
391
+ timers: /* @__PURE__ */ new Set(),
392
+ item
393
+ };
394
+ inst.pillRo = new ResizeObserver(() => {
395
+ requestAnimationFrame(() => {
396
+ measurePillWidth(inst);
397
+ applyVars(inst, inst.item);
398
+ animatePill(inst, inst.item);
399
+ });
400
+ });
401
+ inst.pillRo.observe(innerEl);
402
+ if (descriptionEl) {
403
+ inst.contentRo = new ResizeObserver(() => {
404
+ requestAnimationFrame(() => {
405
+ const h = descriptionEl.scrollHeight;
406
+ if (h !== inst.contentHeight) {
407
+ inst.contentHeight = h;
408
+ applyVars(inst, inst.item);
409
+ }
410
+ });
411
+ });
412
+ inst.contentRo.observe(descriptionEl);
413
+ }
414
+ inst.connectHandle = Toaster.connect(
415
+ el,
416
+ {
417
+ onExpand: () => {
418
+ if (inst.item.exiting || inst.dismissRequested) return;
419
+ inst.localState.expanded = true;
420
+ applyUpdate(inst, inst.item);
421
+ },
422
+ onCollapse: () => {
423
+ if (inst.item.exiting || inst.dismissRequested) return;
424
+ if (inst.item.autopilot !== false) return;
425
+ inst.localState.expanded = false;
426
+ applyUpdate(inst, inst.item);
427
+ },
428
+ onDismiss: () => {
429
+ if (inst.dismissRequested) return;
430
+ inst.dismissRequested = true;
431
+ machine.dismiss(item.id);
432
+ },
433
+ onHoverStart: () => {
434
+ inst.hovering = true;
435
+ },
436
+ onHoverEnd: () => {
437
+ inst.hovering = false;
438
+ if (inst.pendingDismiss) {
439
+ inst.pendingDismiss = false;
440
+ if (inst.dismissRequested) return;
441
+ inst.dismissRequested = true;
442
+ machine.dismiss(inst.item.id);
443
+ }
444
+ }
445
+ },
446
+ item
447
+ );
448
+ applyVars(inst, item);
449
+ setTimer(
450
+ inst,
451
+ () => {
452
+ inst.localState.ready = true;
453
+ applyUpdate(inst, inst.item);
454
+ },
455
+ 32
456
+ );
457
+ setupAutoDismiss(inst, item, machine);
458
+ setupAutopilot(inst, item);
459
+ measurePillWidth(inst);
460
+ return inst;
461
+ }
462
+ function measurePillWidth(inst) {
463
+ if (!inst.el.isConnected) return;
464
+ if (inst.headerPad === null) {
465
+ const cs = getComputedStyle(inst.headerEl);
466
+ inst.headerPad = Number.parseFloat(cs.paddingLeft) + Number.parseFloat(cs.paddingRight);
467
+ }
468
+ const intrinsicWidth = inst.innerEl.getBoundingClientRect().width;
469
+ const w = intrinsicWidth + inst.headerPad + PILL_PADDING;
470
+ if (w > PILL_PADDING && w !== inst.pillWidth) {
471
+ inst.pillWidth = w;
472
+ }
473
+ }
474
+ function applyVars(inst, item) {
475
+ const layout = computeLayout(item, inst);
476
+ const { open, expanded, resolvedPillWidth, pillX, edge, expandedContent, svgHeight } = layout;
477
+ const vars = {
478
+ "--_h": `${open ? expanded : HEIGHT}px`,
479
+ "--_pw": `${resolvedPillWidth}px`,
480
+ "--_px": `${pillX}px`,
481
+ "--_ht": `translateY(${open ? edge === "bottom" ? 3 : -3 : 0}px) scale(${open ? 0.9 : 1})`,
482
+ "--_co": `${open ? 1 : 0}`,
483
+ "--_cy": `${open ? 0 : -14}px`,
484
+ "--_cm": `${open ? expandedContent : 0}px`,
485
+ "--_by": `${open ? HEIGHT - BODY_MERGE_OVERLAP : HEIGHT}px`,
486
+ "--_bh": `${open ? expandedContent : 0}px`,
487
+ "--_bo": `${open ? 1 : 0}`
488
+ };
489
+ for (const [key, value] of Object.entries(vars)) {
490
+ inst.el.style.setProperty(key, value);
491
+ }
492
+ inst.svgEl.setAttribute("height", String(svgHeight));
493
+ inst.svgEl.setAttribute("viewBox", `0 0 ${WIDTH} ${svgHeight}`);
494
+ }
495
+ function animatePill(inst, item) {
496
+ const layout = computeLayout(item, inst);
497
+ const { open, resolvedPillWidth, pillX, pillHeight } = layout;
498
+ const prev = inst.prevPill;
499
+ const next = { x: pillX, width: resolvedPillWidth, height: open ? pillHeight : HEIGHT };
500
+ if (prev.x === next.x && prev.width === next.width && prev.height === next.height) return;
501
+ inst.pillAnim?.cancel();
502
+ if (!inst.localState.ready || inst.pillFirst) {
503
+ inst.pillFirst = false;
504
+ inst.pillEl.setAttribute("x", String(next.x));
505
+ inst.pillEl.setAttribute("width", String(next.width));
506
+ inst.pillEl.setAttribute("height", String(next.height));
507
+ inst.prevPill = next;
508
+ return;
509
+ }
510
+ const anim = animateSpring(
511
+ inst.pillEl,
512
+ {
513
+ x: { from: prev.x, to: next.x, unit: "px" },
514
+ width: { from: prev.width, to: next.width, unit: "px" },
515
+ height: { from: prev.height, to: next.height, unit: "px" }
516
+ },
517
+ FLUIX_SPRING
518
+ );
519
+ inst.pillAnim = anim;
520
+ inst.prevPill = next;
521
+ }
522
+ function setupAutoDismiss(inst, item, machine) {
523
+ const duration = item.duration;
524
+ if (duration == null || duration <= 0) return;
525
+ setTimer(
526
+ inst,
527
+ () => {
528
+ if (inst.hovering) {
529
+ inst.pendingDismiss = true;
530
+ setTimer(
531
+ inst,
532
+ () => {
533
+ if (inst.dismissRequested) return;
534
+ inst.dismissRequested = true;
535
+ inst.pendingDismiss = false;
536
+ machine.dismiss(item.id);
537
+ },
538
+ 1200
539
+ );
540
+ return;
541
+ }
542
+ inst.pendingDismiss = false;
543
+ inst.dismissRequested = true;
544
+ machine.dismiss(item.id);
545
+ },
546
+ duration
547
+ );
548
+ }
549
+ function setupAutopilot(inst, item, machine) {
550
+ if (!inst.localState.ready) return;
551
+ if (item.autoExpandDelayMs != null && item.autoExpandDelayMs > 0) {
552
+ setTimer(
553
+ inst,
554
+ () => {
555
+ if (!inst.hovering) {
556
+ inst.localState.expanded = true;
557
+ applyUpdate(inst, inst.item);
558
+ }
559
+ },
560
+ item.autoExpandDelayMs
561
+ );
562
+ }
563
+ if (item.autoCollapseDelayMs != null && item.autoCollapseDelayMs > 0) {
564
+ setTimer(
565
+ inst,
566
+ () => {
567
+ if (!inst.hovering) {
568
+ inst.localState.expanded = false;
569
+ applyUpdate(inst, inst.item);
570
+ }
571
+ },
572
+ item.autoCollapseDelayMs
573
+ );
574
+ }
575
+ }
576
+ function applyUpdate(inst, item, _machine) {
577
+ inst.item = item;
578
+ const attrs = Toaster.getAttrs(item, inst.localState);
579
+ applyAttrs(inst.el, attrs.root);
580
+ const canvasEl = inst.el.querySelector("[data-fluix-canvas]");
581
+ if (canvasEl) applyAttrs(canvasEl, attrs.canvas);
582
+ applyAttrs(inst.headerEl, attrs.header);
583
+ const badgeEl = inst.innerEl.querySelector("[data-fluix-badge]");
584
+ if (badgeEl) {
585
+ applyAttrs(badgeEl, attrs.badge);
586
+ if (item.styles?.badge) badgeEl.className = item.styles.badge;
587
+ }
588
+ const titleEl = inst.innerEl.querySelector("[data-fluix-title]");
589
+ if (titleEl) {
590
+ applyAttrs(titleEl, attrs.title);
591
+ if (item.styles?.title) titleEl.className = item.styles.title;
592
+ }
593
+ if (inst.contentEl) applyAttrs(inst.contentEl, attrs.content);
594
+ if (inst.descriptionEl) applyAttrs(inst.descriptionEl, attrs.description);
595
+ if (item.button && inst.descriptionEl) {
596
+ const btnEl = inst.descriptionEl.querySelector("button");
597
+ if (btnEl) applyAttrs(btnEl, attrs.button);
598
+ }
599
+ inst.pillEl.setAttribute("fill", item.fill ?? "#FFFFFF");
600
+ inst.bodyEl.setAttribute("fill", item.fill ?? "#FFFFFF");
601
+ const newHeaderKey = `${item.state}-${item.title ?? item.state}`;
602
+ if (newHeaderKey !== inst.headerKey) {
603
+ crossfadeHeader(inst, item, attrs);
604
+ inst.headerKey = newHeaderKey;
605
+ }
606
+ applyVars(inst, item);
607
+ animatePill(inst, item);
608
+ }
609
+ function crossfadeHeader(inst, item, attrs) {
610
+ const oldInner = inst.innerEl;
611
+ oldInner.setAttribute("data-layer", "prev");
612
+ oldInner.setAttribute("data-exiting", "true");
613
+ const newInner = document.createElement("div");
614
+ newInner.setAttribute("data-fluix-header-inner", "");
615
+ newInner.setAttribute("data-layer", "current");
616
+ const badgeEl = document.createElement("div");
617
+ applyAttrs(badgeEl, attrs.badge);
618
+ if (item.styles?.badge) badgeEl.className = item.styles.badge;
619
+ renderIconInto(badgeEl, item.icon, item.state);
620
+ const titleEl = document.createElement("span");
621
+ applyAttrs(titleEl, attrs.title);
622
+ if (item.styles?.title) titleEl.className = item.styles.title;
623
+ titleEl.textContent = item.title ?? item.state;
624
+ newInner.appendChild(badgeEl);
625
+ newInner.appendChild(titleEl);
626
+ inst.headerStackEl.insertBefore(newInner, oldInner);
627
+ inst.innerEl = newInner;
628
+ inst.pillRo.unobserve(oldInner);
629
+ inst.pillRo.observe(newInner);
630
+ setTimer(
631
+ inst,
632
+ () => {
633
+ oldInner.remove();
634
+ },
635
+ HEADER_EXIT_MS
636
+ );
637
+ requestAnimationFrame(() => {
638
+ measurePillWidth(inst);
639
+ applyVars(inst, inst.item);
640
+ animatePill(inst, inst.item);
641
+ });
642
+ }
643
+ function destroyInstance(inst) {
644
+ for (const t of inst.timers) clearTimeout(t);
645
+ inst.timers.clear();
646
+ inst.pillAnim?.cancel();
647
+ inst.pillRo.disconnect();
648
+ inst.contentRo?.disconnect();
649
+ inst.connectHandle.destroy();
650
+ inst.el.remove();
651
+ }
652
+ function createToaster(config) {
653
+ const machine = Toaster.getMachine();
654
+ let currentConfig = config;
655
+ if (currentConfig) machine.configure(currentConfig);
656
+ const instances = /* @__PURE__ */ new Map();
657
+ const viewports = /* @__PURE__ */ new Map();
658
+ function ensureViewport(position, layout, offset) {
659
+ let vp = viewports.get(position);
660
+ if (!vp) {
661
+ vp = document.createElement("section");
662
+ const vpAttrs = Toaster.getViewportAttrs(position, layout);
663
+ applyAttrs(vp, vpAttrs);
664
+ applyViewportOffset(vp, offset, position);
665
+ document.body.appendChild(vp);
666
+ viewports.set(position, vp);
667
+ }
668
+ return vp;
669
+ }
670
+ function removeViewport(position) {
671
+ const vp = viewports.get(position);
672
+ if (vp) {
673
+ vp.remove();
674
+ viewports.delete(position);
675
+ }
676
+ }
677
+ function sync() {
678
+ const next = machine.store.getSnapshot();
679
+ const resolvedLayout = next.config?.layout ?? currentConfig?.layout ?? "stack";
680
+ const resolvedOffset = next.config?.offset ?? currentConfig?.offset;
681
+ const activePositions = /* @__PURE__ */ new Set();
682
+ const nextIds = new Set(next.toasts.map((t) => t.id));
683
+ for (const [id, inst] of instances) {
684
+ if (!nextIds.has(id)) {
685
+ destroyInstance(inst);
686
+ instances.delete(id);
687
+ }
688
+ }
689
+ for (const item of next.toasts) {
690
+ activePositions.add(item.position);
691
+ const vp = ensureViewport(item.position, resolvedLayout, resolvedOffset);
692
+ const existing = instances.get(item.id);
693
+ if (!existing) {
694
+ const inst = createInstance(item, machine);
695
+ instances.set(item.id, inst);
696
+ vp.appendChild(inst.el);
697
+ } else if (existing.item.instanceId !== item.instanceId) {
698
+ destroyInstance(existing);
699
+ const inst = createInstance(item, machine);
700
+ instances.set(item.id, inst);
701
+ vp.appendChild(inst.el);
702
+ } else {
703
+ applyUpdate(existing, item);
704
+ if (existing.el.parentElement !== vp) {
705
+ vp.appendChild(existing.el);
706
+ }
707
+ }
708
+ }
709
+ for (const [position, vp] of viewports) {
710
+ const vpAttrs = Toaster.getViewportAttrs(position, resolvedLayout);
711
+ applyAttrs(vp, vpAttrs);
712
+ applyViewportOffset(vp, resolvedOffset, position);
713
+ }
714
+ for (const [position] of viewports) {
715
+ if (!activePositions.has(position)) {
716
+ removeViewport(position);
717
+ }
718
+ }
719
+ }
720
+ sync();
721
+ const unsubscribe = machine.store.subscribe(sync);
722
+ return {
723
+ destroy() {
724
+ unsubscribe();
725
+ for (const inst of instances.values()) {
726
+ destroyInstance(inst);
727
+ }
728
+ instances.clear();
729
+ for (const vp of viewports.values()) {
730
+ vp.remove();
731
+ }
732
+ viewports.clear();
733
+ },
734
+ update(newConfig) {
735
+ currentConfig = newConfig;
736
+ machine.configure(newConfig);
737
+ }
738
+ };
739
+ }
740
+
741
+ export { createToaster };
742
+ //# sourceMappingURL=index.js.map
743
+ //# sourceMappingURL=index.js.map