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