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