goey-toast 0.1.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/dist/index.js ADDED
@@ -0,0 +1,980 @@
1
+ import { useState, useEffect, Component, useRef, useCallback, useLayoutEffect } from 'react';
2
+ import { toast, Toaster } from 'sonner';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { animate, AnimatePresence, motion } from 'framer-motion';
5
+
6
+ // src/components/GoeyToaster.tsx
7
+
8
+ // src/context.ts
9
+ var _position = "bottom-right";
10
+ function setGoeyPosition(position) {
11
+ _position = position;
12
+ }
13
+ function getGoeyPosition() {
14
+ return _position;
15
+ }
16
+ function GoeyToaster({
17
+ position = "bottom-right",
18
+ duration,
19
+ gap = 14,
20
+ offset = "24px",
21
+ theme = "light",
22
+ toastOptions,
23
+ expand,
24
+ closeButton,
25
+ richColors,
26
+ visibleToasts,
27
+ dir
28
+ }) {
29
+ useEffect(() => {
30
+ setGoeyPosition(position);
31
+ }, [position]);
32
+ useEffect(() => {
33
+ if (process.env.NODE_ENV !== "development") return;
34
+ const el = document.createElement("div");
35
+ el.setAttribute("data-goey-toast-css", "");
36
+ el.style.position = "absolute";
37
+ el.style.width = "0";
38
+ el.style.height = "0";
39
+ el.style.overflow = "hidden";
40
+ el.style.pointerEvents = "none";
41
+ document.body.appendChild(el);
42
+ const value = getComputedStyle(el).getPropertyValue("--goey-toast");
43
+ document.body.removeChild(el);
44
+ if (!value) {
45
+ console.warn(
46
+ '[goey-toast] Styles not found. Make sure to import the CSS:\n\n import "goey-toast/styles.css";\n'
47
+ );
48
+ }
49
+ }, []);
50
+ return /* @__PURE__ */ jsx(
51
+ Toaster,
52
+ {
53
+ position,
54
+ duration,
55
+ gap,
56
+ offset,
57
+ theme,
58
+ toastOptions: { unstyled: true, ...toastOptions },
59
+ expand,
60
+ closeButton,
61
+ richColors,
62
+ visibleToasts,
63
+ dir
64
+ }
65
+ );
66
+ }
67
+ var DefaultIcon = ({ size = 20, className }) => /* @__PURE__ */ jsxs(
68
+ "svg",
69
+ {
70
+ xmlns: "http://www.w3.org/2000/svg",
71
+ width: size,
72
+ height: size,
73
+ viewBox: "0 0 24 24",
74
+ fill: "none",
75
+ stroke: "currentColor",
76
+ strokeWidth: 2,
77
+ strokeLinecap: "round",
78
+ strokeLinejoin: "round",
79
+ className,
80
+ children: [
81
+ /* @__PURE__ */ jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
82
+ /* @__PURE__ */ jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
83
+ ]
84
+ }
85
+ );
86
+ var SuccessIcon = ({ size = 20, className }) => /* @__PURE__ */ jsxs(
87
+ "svg",
88
+ {
89
+ xmlns: "http://www.w3.org/2000/svg",
90
+ width: size,
91
+ height: size,
92
+ viewBox: "0 0 24 24",
93
+ fill: "none",
94
+ stroke: "currentColor",
95
+ strokeWidth: 2,
96
+ strokeLinecap: "round",
97
+ strokeLinejoin: "round",
98
+ className,
99
+ children: [
100
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
101
+ /* @__PURE__ */ jsx("path", { d: "M9 12l2 2 4-4" })
102
+ ]
103
+ }
104
+ );
105
+ var ErrorIcon = ({ size = 20, className }) => /* @__PURE__ */ jsxs(
106
+ "svg",
107
+ {
108
+ xmlns: "http://www.w3.org/2000/svg",
109
+ width: size,
110
+ height: size,
111
+ viewBox: "0 0 24 24",
112
+ fill: "none",
113
+ stroke: "currentColor",
114
+ strokeWidth: 2,
115
+ strokeLinecap: "round",
116
+ strokeLinejoin: "round",
117
+ className,
118
+ children: [
119
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
120
+ /* @__PURE__ */ jsx("path", { d: "M15 9l-6 6" }),
121
+ /* @__PURE__ */ jsx("path", { d: "M9 9l6 6" })
122
+ ]
123
+ }
124
+ );
125
+ var WarningIcon = ({ size = 20, className }) => /* @__PURE__ */ jsxs(
126
+ "svg",
127
+ {
128
+ xmlns: "http://www.w3.org/2000/svg",
129
+ width: size,
130
+ height: size,
131
+ viewBox: "0 0 24 24",
132
+ fill: "none",
133
+ stroke: "currentColor",
134
+ strokeWidth: 2,
135
+ strokeLinecap: "round",
136
+ strokeLinejoin: "round",
137
+ className,
138
+ children: [
139
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
140
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
141
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
142
+ ]
143
+ }
144
+ );
145
+ var InfoIcon = ({ size = 20, className }) => /* @__PURE__ */ jsxs(
146
+ "svg",
147
+ {
148
+ xmlns: "http://www.w3.org/2000/svg",
149
+ width: size,
150
+ height: size,
151
+ viewBox: "0 0 24 24",
152
+ fill: "none",
153
+ stroke: "currentColor",
154
+ strokeWidth: 2,
155
+ strokeLinecap: "round",
156
+ strokeLinejoin: "round",
157
+ className,
158
+ children: [
159
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
160
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
161
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
162
+ ]
163
+ }
164
+ );
165
+ var SpinnerIcon = ({ size = 20, className }) => /* @__PURE__ */ jsx(
166
+ "svg",
167
+ {
168
+ xmlns: "http://www.w3.org/2000/svg",
169
+ width: size,
170
+ height: size,
171
+ viewBox: "0 0 24 24",
172
+ fill: "none",
173
+ stroke: "currentColor",
174
+ strokeWidth: 2,
175
+ strokeLinecap: "round",
176
+ strokeLinejoin: "round",
177
+ className,
178
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
179
+ }
180
+ );
181
+ var QUERY = "(prefers-reduced-motion: reduce)";
182
+ function getInitialState() {
183
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
184
+ return false;
185
+ }
186
+ return window.matchMedia(QUERY).matches;
187
+ }
188
+ function usePrefersReducedMotion() {
189
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState(getInitialState);
190
+ useEffect(() => {
191
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
192
+ return;
193
+ }
194
+ const mql = window.matchMedia(QUERY);
195
+ const handler = (event) => {
196
+ setPrefersReducedMotion(event.matches);
197
+ };
198
+ mql.addEventListener("change", handler);
199
+ return () => mql.removeEventListener("change", handler);
200
+ }, []);
201
+ return prefersReducedMotion;
202
+ }
203
+
204
+ // src/components/GoeyToast.module.css
205
+ var GoeyToast_default = {};
206
+ var phaseIconMap = {
207
+ default: DefaultIcon,
208
+ success: SuccessIcon,
209
+ error: ErrorIcon,
210
+ warning: WarningIcon,
211
+ info: InfoIcon
212
+ };
213
+ var titleColorMap = {
214
+ loading: GoeyToast_default.titleLoading,
215
+ default: GoeyToast_default.titleDefault,
216
+ success: GoeyToast_default.titleSuccess,
217
+ error: GoeyToast_default.titleError,
218
+ warning: GoeyToast_default.titleWarning,
219
+ info: GoeyToast_default.titleInfo
220
+ };
221
+ var actionColorMap = {
222
+ loading: GoeyToast_default.actionInfo,
223
+ default: GoeyToast_default.actionDefault,
224
+ success: GoeyToast_default.actionSuccess,
225
+ error: GoeyToast_default.actionError,
226
+ warning: GoeyToast_default.actionWarning,
227
+ info: GoeyToast_default.actionInfo
228
+ };
229
+ var PH = 34;
230
+ var DEFAULT_DISPLAY_DURATION = 4e3;
231
+ var BASE_SQUISH = { type: "spring", stiffness: 380, damping: 16, mass: 0.7 };
232
+ var DEFAULT_EXPAND_DUR = 0.6;
233
+ var DEFAULT_COLLAPSE_DUR = 0.9;
234
+ function squishSpring(durationSec, defaultDur) {
235
+ const scale = durationSec / defaultDur;
236
+ return { ...BASE_SQUISH, mass: BASE_SQUISH.mass * scale };
237
+ }
238
+ var observerRegistry = /* @__PURE__ */ new Map();
239
+ function registerSonnerObserver(ol, callback) {
240
+ let entry = observerRegistry.get(ol);
241
+ if (!entry) {
242
+ const callbacks = /* @__PURE__ */ new Set();
243
+ let applying = false;
244
+ const observer = new MutationObserver(() => {
245
+ if (applying) return;
246
+ applying = true;
247
+ requestAnimationFrame(() => {
248
+ callbacks.forEach((cb) => cb());
249
+ requestAnimationFrame(() => {
250
+ applying = false;
251
+ });
252
+ });
253
+ });
254
+ observer.observe(ol, {
255
+ attributes: true,
256
+ attributeFilter: ["style"],
257
+ subtree: true,
258
+ childList: true
259
+ });
260
+ entry = { observer, callbacks };
261
+ observerRegistry.set(ol, entry);
262
+ }
263
+ entry.callbacks.add(callback);
264
+ return () => {
265
+ entry.callbacks.delete(callback);
266
+ if (entry.callbacks.size === 0) {
267
+ entry.observer.disconnect();
268
+ observerRegistry.delete(ol);
269
+ }
270
+ };
271
+ }
272
+ function syncSonnerHeights(wrapperEl) {
273
+ if (!wrapperEl) return;
274
+ const li = wrapperEl.closest("[data-sonner-toast]");
275
+ if (!li?.parentElement) return;
276
+ const ol = li.parentElement;
277
+ const toasts = Array.from(
278
+ ol.querySelectorAll(":scope > [data-sonner-toast]")
279
+ );
280
+ for (const t of toasts) {
281
+ const content = t.firstElementChild;
282
+ const height = content ? content.getBoundingClientRect().height : 0;
283
+ if (height > 0) {
284
+ t.style.setProperty("--initial-height", `${height}px`);
285
+ }
286
+ }
287
+ }
288
+ function morphPath(pw, bw, th, t) {
289
+ const pr = PH / 2;
290
+ const pillW = Math.min(pw, bw);
291
+ const bodyH = PH + (th - PH) * t;
292
+ if (t <= 0 || bodyH - PH < 8) {
293
+ return [
294
+ `M 0,${pr}`,
295
+ `A ${pr},${pr} 0 0 1 ${pr},0`,
296
+ `H ${pillW - pr}`,
297
+ `A ${pr},${pr} 0 0 1 ${pillW},${pr}`,
298
+ `A ${pr},${pr} 0 0 1 ${pillW - pr},${PH}`,
299
+ `H ${pr}`,
300
+ `A ${pr},${pr} 0 0 1 0,${pr}`,
301
+ `Z`
302
+ ].join(" ");
303
+ }
304
+ const curve = 14 * t;
305
+ const cr = Math.min(16, (bodyH - PH) * 0.45);
306
+ const bodyW = pillW + (bw - pillW) * t;
307
+ const bodyTop = PH - curve;
308
+ const qEndX = Math.min(pillW + curve, bodyW - cr);
309
+ return [
310
+ `M 0,${pr}`,
311
+ `A ${pr},${pr} 0 0 1 ${pr},0`,
312
+ `H ${pillW - pr}`,
313
+ `A ${pr},${pr} 0 0 1 ${pillW},${pr}`,
314
+ `L ${pillW},${bodyTop}`,
315
+ `Q ${pillW},${bodyTop + curve} ${qEndX},${bodyTop + curve}`,
316
+ `H ${bodyW - cr}`,
317
+ `A ${cr},${cr} 0 0 1 ${bodyW},${bodyTop + curve + cr}`,
318
+ `L ${bodyW},${bodyH - cr}`,
319
+ `A ${cr},${cr} 0 0 1 ${bodyW - cr},${bodyH}`,
320
+ `H ${cr}`,
321
+ `A ${cr},${cr} 0 0 1 0,${bodyH - cr}`,
322
+ `Z`
323
+ ].join(" ");
324
+ }
325
+ var GoeyToast = ({
326
+ title,
327
+ description,
328
+ action,
329
+ icon,
330
+ phase,
331
+ classNames,
332
+ fillColor = "#ffffff",
333
+ borderColor,
334
+ borderWidth,
335
+ timing
336
+ }) => {
337
+ const position = getGoeyPosition();
338
+ const isRight = position?.includes("right") ?? false;
339
+ const prefersReducedMotion = usePrefersReducedMotion();
340
+ const [actionSuccess, setActionSuccess] = useState(null);
341
+ const [dismissing, setDismissing] = useState(false);
342
+ const collapsingRef = useRef(false);
343
+ const preDismissRef = useRef(false);
344
+ const collapseEndTime = useRef(0);
345
+ const expandedDimsRef = useRef({ pw: 0, bw: 0, th: 0 });
346
+ const effectiveTitle = actionSuccess ?? title;
347
+ const effectivePhase = actionSuccess ? "success" : phase;
348
+ const effectiveDescription = actionSuccess ? void 0 : description;
349
+ const effectiveAction = actionSuccess ? void 0 : action;
350
+ const isLoading = effectivePhase === "loading";
351
+ const hasDescription = Boolean(effectiveDescription);
352
+ const hasAction = Boolean(effectiveAction);
353
+ const isExpanded = (hasDescription || hasAction) && !dismissing;
354
+ const [showBody, setShowBody] = useState(false);
355
+ const wrapperRef = useRef(null);
356
+ const pathRef = useRef(null);
357
+ const headerRef = useRef(null);
358
+ const contentRef = useRef(null);
359
+ const morphCtrl = useRef(null);
360
+ const pillResizeCtrl = useRef(null);
361
+ const headerSquishCtrl = useRef(null);
362
+ const morphTRef = useRef(0);
363
+ const aDims = useRef({ pw: 0, bw: 0, th: 0 });
364
+ const dimsRef = useRef({ pw: 0, bw: 0, th: 0 });
365
+ const [dims, setDims] = useState({ pw: 0, bw: 0, th: 0 });
366
+ useEffect(() => {
367
+ dimsRef.current = dims;
368
+ }, [dims]);
369
+ const flush = useCallback(() => {
370
+ const { pw: p, bw: b, th: h } = aDims.current;
371
+ if (p <= 0 || b <= 0 || h <= 0) return;
372
+ const t = Math.max(0, Math.min(1, morphTRef.current));
373
+ const pos = getGoeyPosition();
374
+ const rightSide = pos?.includes("right") ?? false;
375
+ pathRef.current?.setAttribute("d", morphPath(p, b, h, t));
376
+ if (t >= 1) {
377
+ if (wrapperRef.current) {
378
+ wrapperRef.current.style.width = "";
379
+ }
380
+ if (contentRef.current) {
381
+ contentRef.current.style.width = "";
382
+ contentRef.current.style.overflow = "";
383
+ contentRef.current.style.maxHeight = "";
384
+ contentRef.current.style.clipPath = "";
385
+ }
386
+ } else if (t > 0) {
387
+ const targetBw = dimsRef.current.bw;
388
+ const targetTh = dimsRef.current.th;
389
+ const pillW = Math.min(p, b);
390
+ const currentW = pillW + (b - pillW) * t;
391
+ const currentH = PH + (targetTh - PH) * t;
392
+ if (wrapperRef.current) {
393
+ wrapperRef.current.style.width = currentW + "px";
394
+ }
395
+ if (contentRef.current) {
396
+ contentRef.current.style.width = targetBw + "px";
397
+ contentRef.current.style.overflow = "hidden";
398
+ contentRef.current.style.maxHeight = currentH + "px";
399
+ const clip = targetBw - currentW;
400
+ contentRef.current.style.clipPath = rightSide ? `inset(0 0 0 ${clip}px)` : `inset(0 ${clip}px 0 0)`;
401
+ }
402
+ } else {
403
+ const pillW = Math.min(p, b);
404
+ if (wrapperRef.current) {
405
+ wrapperRef.current.style.width = pillW + "px";
406
+ }
407
+ if (contentRef.current) {
408
+ contentRef.current.style.width = "";
409
+ contentRef.current.style.overflow = "hidden";
410
+ contentRef.current.style.maxHeight = PH + "px";
411
+ contentRef.current.style.clipPath = "";
412
+ }
413
+ }
414
+ }, []);
415
+ const measure = useCallback(() => {
416
+ if (!headerRef.current || !contentRef.current) return;
417
+ const wr = wrapperRef.current;
418
+ const savedW = wr?.style.width ?? "";
419
+ const savedOv = contentRef.current.style.overflow;
420
+ const savedMH = contentRef.current.style.maxHeight;
421
+ const savedCW = contentRef.current.style.width;
422
+ if (wr) {
423
+ wr.style.width = "";
424
+ }
425
+ contentRef.current.style.overflow = "";
426
+ contentRef.current.style.maxHeight = "";
427
+ contentRef.current.style.width = "";
428
+ const cs = getComputedStyle(contentRef.current);
429
+ const paddingX = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight);
430
+ const pw2 = headerRef.current.offsetWidth + paddingX;
431
+ const bw2 = contentRef.current.offsetWidth;
432
+ const th2 = contentRef.current.offsetHeight;
433
+ if (wr) {
434
+ wr.style.width = savedW;
435
+ }
436
+ contentRef.current.style.overflow = savedOv;
437
+ contentRef.current.style.maxHeight = savedMH;
438
+ contentRef.current.style.width = savedCW;
439
+ setDims({ pw: pw2, bw: bw2, th: th2 });
440
+ }, []);
441
+ useLayoutEffect(() => {
442
+ measure();
443
+ const t = setTimeout(measure, 100);
444
+ return () => clearTimeout(t);
445
+ }, [effectiveTitle, effectivePhase, isExpanded, showBody, effectiveDescription, effectiveAction, measure]);
446
+ useEffect(() => {
447
+ if (!contentRef.current) return;
448
+ const ro = new ResizeObserver(measure);
449
+ ro.observe(contentRef.current);
450
+ return () => ro.disconnect();
451
+ }, [measure]);
452
+ const { pw, bw, th } = dims;
453
+ const hasDims = pw > 0 && bw > 0 && th > 0;
454
+ const blobSquishCtrl = useRef(null);
455
+ const expandDur = timing?.expandDuration ?? DEFAULT_EXPAND_DUR;
456
+ const collapseDur = timing?.collapseDuration ?? DEFAULT_COLLAPSE_DUR;
457
+ const lastSquishTime = useRef(0);
458
+ const triggerLandingSquish = useCallback((phase2 = "mount") => {
459
+ if (!wrapperRef.current || prefersReducedMotion) return;
460
+ const now = Date.now();
461
+ if (now - lastSquishTime.current < 300) return;
462
+ lastSquishTime.current = now;
463
+ blobSquishCtrl.current?.stop();
464
+ const el = wrapperRef.current;
465
+ const spring = phase2 === "collapse" ? squishSpring(collapseDur, DEFAULT_COLLAPSE_DUR) : squishSpring(expandDur, DEFAULT_EXPAND_DUR);
466
+ const compressY = phase2 === "collapse" ? 0.07 : 0.12;
467
+ const expandX = phase2 === "collapse" ? 0.035 : 0.06;
468
+ blobSquishCtrl.current = animate(0, 1, {
469
+ ...spring,
470
+ onUpdate: (v) => {
471
+ const intensity = Math.sin(v * Math.PI);
472
+ const sy = 1 - compressY * intensity;
473
+ const sx = 1 + expandX * intensity;
474
+ const mirror = el.style.transform?.includes("scaleX(-1)") ? "scaleX(-1) " : "";
475
+ el.style.transformOrigin = "center bottom";
476
+ el.style.transform = mirror + `scaleX(${sx}) scaleY(${sy})`;
477
+ },
478
+ onComplete: () => {
479
+ const right = el.style.transform?.includes("scaleX(-1)");
480
+ el.style.transform = right ? "scaleX(-1)" : "";
481
+ el.style.transformOrigin = "";
482
+ }
483
+ });
484
+ }, [prefersReducedMotion, expandDur, collapseDur]);
485
+ useLayoutEffect(() => {
486
+ if (!hasDims || collapsingRef.current) return;
487
+ const prev = { ...aDims.current };
488
+ const target = { pw, bw, th };
489
+ if (prev.bw <= 0) {
490
+ aDims.current = target;
491
+ flush();
492
+ return;
493
+ }
494
+ if (morphTRef.current > 0 && morphTRef.current < 1) {
495
+ aDims.current = target;
496
+ flush();
497
+ return;
498
+ }
499
+ if (showBody) {
500
+ aDims.current = target;
501
+ flush();
502
+ return;
503
+ }
504
+ if (prev.bw === target.bw && prev.pw === target.pw && prev.th === target.th) return;
505
+ if (prefersReducedMotion) {
506
+ aDims.current = target;
507
+ flush();
508
+ return;
509
+ }
510
+ pillResizeCtrl.current?.stop();
511
+ if (Date.now() - collapseEndTime.current > 500 && !isExpanded) {
512
+ triggerLandingSquish("expand");
513
+ }
514
+ pillResizeCtrl.current = animate(0, 1, {
515
+ type: "spring",
516
+ duration: 0.5,
517
+ bounce: 0.35,
518
+ onUpdate: (t) => {
519
+ aDims.current = {
520
+ pw: prev.pw + (target.pw - prev.pw) * t,
521
+ bw: prev.bw + (target.bw - prev.bw) * t,
522
+ th: prev.th + (target.th - prev.th) * t
523
+ };
524
+ flush();
525
+ }
526
+ });
527
+ }, [pw, bw, th, hasDims, showBody, flush, prefersReducedMotion, triggerLandingSquish]);
528
+ const expandDelayMs = prefersReducedMotion ? 0 : timing?.expandDelay ?? 330;
529
+ const mountSquished = useRef(false);
530
+ useEffect(() => {
531
+ if (hasDims && !mountSquished.current && !isExpanded) {
532
+ mountSquished.current = true;
533
+ const t = setTimeout(triggerLandingSquish, expandDelayMs);
534
+ return () => clearTimeout(t);
535
+ }
536
+ }, [hasDims, expandDelayMs, triggerLandingSquish]);
537
+ const prevShowBody = useRef(false);
538
+ useLayoutEffect(() => {
539
+ if (!prevShowBody.current && showBody) {
540
+ triggerLandingSquish("expand");
541
+ }
542
+ prevShowBody.current = showBody;
543
+ }, [showBody, triggerLandingSquish]);
544
+ const shakeCtrl = useRef(null);
545
+ const prevPhase = useRef(phase);
546
+ useEffect(() => {
547
+ if (phase === "error" && prevPhase.current !== "error" && !dismissing && wrapperRef.current && !prefersReducedMotion) {
548
+ shakeCtrl.current?.stop();
549
+ const el = wrapperRef.current;
550
+ const mirror = el.style.transform?.includes("scaleX(-1)") ? "scaleX(-1) " : "";
551
+ shakeCtrl.current = animate(0, 1, {
552
+ duration: 0.4,
553
+ ease: "easeOut",
554
+ onUpdate: (v) => {
555
+ const decay = 1 - v;
556
+ const shake = Math.sin(v * Math.PI * 6) * decay * 3;
557
+ el.style.transform = mirror + `translateX(${shake}px)`;
558
+ },
559
+ onComplete: () => {
560
+ el.style.transform = mirror.trim() || "";
561
+ }
562
+ });
563
+ }
564
+ prevPhase.current = phase;
565
+ return () => {
566
+ shakeCtrl.current?.stop();
567
+ };
568
+ }, [phase, dismissing, prefersReducedMotion]);
569
+ useEffect(() => {
570
+ if (isExpanded) {
571
+ const delay = prefersReducedMotion ? 0 : timing?.expandDelay ?? 330;
572
+ const t1 = setTimeout(() => setShowBody(true), delay);
573
+ return () => clearTimeout(t1);
574
+ }
575
+ morphCtrl.current?.stop();
576
+ pillResizeCtrl.current?.stop();
577
+ if (morphTRef.current > 0) {
578
+ const csPad = contentRef.current ? getComputedStyle(contentRef.current) : null;
579
+ const padX = csPad ? parseFloat(csPad.paddingLeft) + parseFloat(csPad.paddingRight) : 20;
580
+ const targetPw = headerRef.current ? headerRef.current.offsetWidth + padX : aDims.current.pw;
581
+ const targetDims = { pw: targetPw, bw: targetPw, th: PH };
582
+ if (prefersReducedMotion) {
583
+ morphTRef.current = 0;
584
+ collapsingRef.current = false;
585
+ preDismissRef.current = false;
586
+ setShowBody(false);
587
+ aDims.current = { ...targetDims };
588
+ flush();
589
+ return;
590
+ }
591
+ const savedDims = expandedDimsRef.current.bw > 0 ? { ...expandedDimsRef.current } : { ...aDims.current };
592
+ const isPreDismiss = preDismissRef.current;
593
+ const collapseDur2 = timing?.collapseDuration ?? 0.9;
594
+ const collapseTransition = isPreDismiss ? { duration: collapseDur2, ease: [0.4, 0, 0.2, 1] } : { type: "spring", duration: collapseDur2, bounce: 0.2 };
595
+ triggerLandingSquish("collapse");
596
+ morphCtrl.current = animate(morphTRef.current, 0, {
597
+ ...collapseTransition,
598
+ onUpdate: (t) => {
599
+ morphTRef.current = t;
600
+ aDims.current = {
601
+ pw: targetDims.pw + (savedDims.pw - targetDims.pw) * t,
602
+ bw: targetDims.bw + (savedDims.bw - targetDims.bw) * t,
603
+ th: targetDims.th + (savedDims.th - targetDims.th) * t
604
+ };
605
+ flush();
606
+ },
607
+ onComplete: () => {
608
+ morphTRef.current = 0;
609
+ collapsingRef.current = false;
610
+ preDismissRef.current = false;
611
+ collapseEndTime.current = Date.now();
612
+ aDims.current = { ...targetDims };
613
+ flush();
614
+ setShowBody(false);
615
+ }
616
+ });
617
+ return () => {
618
+ morphCtrl.current?.stop();
619
+ };
620
+ }
621
+ setShowBody(false);
622
+ morphTRef.current = 0;
623
+ flush();
624
+ }, [isExpanded, flush, prefersReducedMotion]);
625
+ useEffect(() => {
626
+ if (!showBody || actionSuccess || dismissing) return;
627
+ const expandDelayMs2 = prefersReducedMotion ? 0 : timing?.expandDelay ?? 330;
628
+ const collapseMs = prefersReducedMotion ? 10 : (timing?.collapseDuration ?? 0.9) * 1e3;
629
+ const displayMs = timing?.displayDuration ?? DEFAULT_DISPLAY_DURATION;
630
+ const delay = displayMs - expandDelayMs2 - collapseMs;
631
+ if (delay <= 0) return;
632
+ const timer = setTimeout(() => {
633
+ expandedDimsRef.current = { ...aDims.current };
634
+ collapsingRef.current = true;
635
+ preDismissRef.current = true;
636
+ setDismissing(true);
637
+ }, delay);
638
+ return () => clearTimeout(timer);
639
+ }, [showBody, actionSuccess, dismissing, prefersReducedMotion]);
640
+ useEffect(() => {
641
+ if (!showBody) {
642
+ morphTRef.current = 0;
643
+ morphCtrl.current?.stop();
644
+ flush();
645
+ return;
646
+ }
647
+ if (prefersReducedMotion) {
648
+ pillResizeCtrl.current?.stop();
649
+ morphCtrl.current?.stop();
650
+ morphTRef.current = 1;
651
+ aDims.current = { ...dimsRef.current };
652
+ flush();
653
+ syncSonnerHeights(wrapperRef.current);
654
+ return;
655
+ }
656
+ const raf = requestAnimationFrame(() => {
657
+ pillResizeCtrl.current?.stop();
658
+ morphCtrl.current?.stop();
659
+ const startDims = { ...aDims.current };
660
+ morphCtrl.current = animate(0, 1, {
661
+ type: "spring",
662
+ duration: timing?.expandDuration ?? 0.9,
663
+ bounce: 0.2,
664
+ onUpdate: (t) => {
665
+ morphTRef.current = t;
666
+ const target = dimsRef.current;
667
+ aDims.current = {
668
+ pw: startDims.pw + (target.pw - startDims.pw) * t,
669
+ bw: startDims.bw + (target.bw - startDims.bw) * t,
670
+ th: startDims.th + (target.th - startDims.th) * t
671
+ };
672
+ flush();
673
+ },
674
+ onComplete: () => {
675
+ morphTRef.current = 1;
676
+ aDims.current = { ...dimsRef.current };
677
+ flush();
678
+ syncSonnerHeights(wrapperRef.current);
679
+ }
680
+ });
681
+ });
682
+ return () => {
683
+ cancelAnimationFrame(raf);
684
+ morphCtrl.current?.stop();
685
+ };
686
+ }, [showBody, flush, prefersReducedMotion]);
687
+ const headerSquished = useRef(false);
688
+ useEffect(() => {
689
+ if (!headerRef.current || prefersReducedMotion) return;
690
+ headerSquishCtrl.current?.stop();
691
+ const el = headerRef.current;
692
+ if (showBody && !dismissing && !actionSuccess) {
693
+ headerSquished.current = true;
694
+ headerSquishCtrl.current = animate(0, 1, {
695
+ ...squishSpring(expandDur, DEFAULT_EXPAND_DUR),
696
+ onUpdate: (v) => {
697
+ const scale = 1 - 0.05 * v;
698
+ const pushY = v * 1;
699
+ el.style.transform = `scale(${scale}) translateY(${pushY}px)`;
700
+ }
701
+ });
702
+ } else if (headerSquished.current) {
703
+ headerSquished.current = false;
704
+ const isSpringCollapse = !preDismissRef.current;
705
+ const transition = isSpringCollapse ? squishSpring(collapseDur, DEFAULT_COLLAPSE_DUR) : { duration: collapseDur * 0.5, ease: [0.4, 0, 0.2, 1] };
706
+ headerSquishCtrl.current = animate(1, 0, {
707
+ ...transition,
708
+ onUpdate: (v) => {
709
+ const scale = 1 - 0.05 * v;
710
+ const pushY = v * 1;
711
+ el.style.transform = `scale(${scale}) translateY(${pushY}px)`;
712
+ },
713
+ onComplete: () => {
714
+ el.style.transform = "";
715
+ }
716
+ });
717
+ }
718
+ return () => {
719
+ headerSquishCtrl.current?.stop();
720
+ };
721
+ }, [showBody, dismissing, actionSuccess, prefersReducedMotion, expandDur, collapseDur]);
722
+ useEffect(() => {
723
+ const wrapper = wrapperRef.current;
724
+ if (!wrapper) return;
725
+ const ol = wrapper.closest("[data-sonner-toast]")?.parentElement;
726
+ if (!ol) return;
727
+ return registerSonnerObserver(ol, () => {
728
+ syncSonnerHeights(wrapper);
729
+ });
730
+ }, []);
731
+ const handleActionClick = useCallback(() => {
732
+ if (!effectiveAction) return;
733
+ effectiveAction.onClick();
734
+ if (effectiveAction.successLabel) {
735
+ expandedDimsRef.current = { ...aDims.current };
736
+ collapsingRef.current = true;
737
+ setActionSuccess(effectiveAction.successLabel);
738
+ }
739
+ }, [effectiveAction]);
740
+ const renderIcon = () => {
741
+ if (!actionSuccess && icon) return icon;
742
+ if (isLoading) return /* @__PURE__ */ jsx(SpinnerIcon, { size: 18, className: GoeyToast_default.spinnerSpin });
743
+ const IconComponent = phaseIconMap[effectivePhase];
744
+ return /* @__PURE__ */ jsx(IconComponent, { size: 18 });
745
+ };
746
+ const iconTransition = prefersReducedMotion ? { duration: 0.01 } : { duration: 0.2 };
747
+ const iconEl = /* @__PURE__ */ jsx("div", { className: `${GoeyToast_default.iconWrapper}${classNames?.icon ? ` ${classNames.icon}` : ""}`, children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
748
+ motion.div,
749
+ {
750
+ initial: prefersReducedMotion ? false : { opacity: 0, scale: 0.5 },
751
+ animate: { opacity: 1, scale: 1 },
752
+ exit: { opacity: 0, scale: 0.5 },
753
+ transition: iconTransition,
754
+ children: renderIcon()
755
+ },
756
+ isLoading ? "spinner" : effectivePhase
757
+ ) }) });
758
+ const titleEl = /* @__PURE__ */ jsx("span", { className: `${GoeyToast_default.title}${classNames?.title ? ` ${classNames.title}` : ""}`, children: effectiveTitle });
759
+ const iconAndTitle = /* @__PURE__ */ jsxs(Fragment, { children: [
760
+ iconEl,
761
+ titleEl
762
+ ] });
763
+ return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: `${GoeyToast_default.wrapper}${classNames?.wrapper ? ` ${classNames.wrapper}` : ""}`, style: isRight ? { marginLeft: "auto", transform: "scaleX(-1)" } : void 0, role: effectivePhase === "error" ? "alert" : "status", "aria-live": effectivePhase === "error" ? "assertive" : "polite", "aria-atomic": "true", children: [
764
+ /* @__PURE__ */ jsx(
765
+ "svg",
766
+ {
767
+ className: GoeyToast_default.blobSvg,
768
+ "aria-hidden": true,
769
+ children: /* @__PURE__ */ jsx(
770
+ "path",
771
+ {
772
+ ref: pathRef,
773
+ fill: fillColor,
774
+ stroke: borderColor || "none",
775
+ strokeWidth: borderColor ? borderWidth ?? 1.5 : 0
776
+ }
777
+ )
778
+ }
779
+ ),
780
+ /* @__PURE__ */ jsxs(
781
+ "div",
782
+ {
783
+ ref: contentRef,
784
+ className: `${GoeyToast_default.content} ${showBody ? GoeyToast_default.contentExpanded : GoeyToast_default.contentCompact}${classNames?.content ? ` ${classNames.content}` : ""}`,
785
+ style: isRight ? { transform: "scaleX(-1)", textAlign: "right" } : { textAlign: "left" },
786
+ children: [
787
+ /* @__PURE__ */ jsx("div", { ref: headerRef, className: `${GoeyToast_default.header} ${titleColorMap[effectivePhase]}${classNames?.header ? ` ${classNames.header}` : ""}`, children: iconAndTitle }),
788
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showBody && hasDescription && !dismissing && /* @__PURE__ */ jsx(
789
+ motion.div,
790
+ {
791
+ className: `${GoeyToast_default.description}${classNames?.description ? ` ${classNames.description}` : ""}`,
792
+ style: { textAlign: "left" },
793
+ initial: prefersReducedMotion ? false : { opacity: 0 },
794
+ animate: { opacity: 1 },
795
+ exit: { opacity: 0 },
796
+ transition: prefersReducedMotion ? { duration: 0.01 } : { duration: 0.35, ease: [0.4, 0, 0.2, 1] },
797
+ children: effectiveDescription
798
+ },
799
+ "description"
800
+ ) }),
801
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showBody && hasAction && effectiveAction && !dismissing && /* @__PURE__ */ jsx(
802
+ motion.div,
803
+ {
804
+ className: `${GoeyToast_default.actionWrapper}${classNames?.actionWrapper ? ` ${classNames.actionWrapper}` : ""}`,
805
+ initial: prefersReducedMotion ? false : { opacity: 0 },
806
+ animate: { opacity: 1 },
807
+ exit: { opacity: 0 },
808
+ transition: prefersReducedMotion ? { duration: 0.01 } : { duration: 0.35, ease: [0.4, 0, 0.2, 1], delay: 0.1 },
809
+ children: /* @__PURE__ */ jsx(
810
+ "button",
811
+ {
812
+ className: `${GoeyToast_default.actionButton} ${actionColorMap[effectivePhase]}${classNames?.actionButton ? ` ${classNames.actionButton}` : ""}`,
813
+ onClick: handleActionClick,
814
+ type: "button",
815
+ "aria-label": effectiveAction.label,
816
+ children: effectiveAction.label
817
+ }
818
+ )
819
+ },
820
+ "action"
821
+ ) })
822
+ ]
823
+ }
824
+ )
825
+ ] });
826
+ };
827
+ var ToastErrorBoundary = class extends Component {
828
+ constructor() {
829
+ super(...arguments);
830
+ this.state = { hasError: false };
831
+ }
832
+ static getDerivedStateFromError() {
833
+ return { hasError: true };
834
+ }
835
+ componentDidCatch(error, errorInfo) {
836
+ if (process.env.NODE_ENV !== "production") {
837
+ console.error("[GoeyToast] Rendering error:", error, errorInfo);
838
+ }
839
+ }
840
+ render() {
841
+ if (this.state.hasError) {
842
+ return null;
843
+ }
844
+ return this.props.children;
845
+ }
846
+ };
847
+ var DEFAULT_EXPANDED_DURATION = 4e3;
848
+ function GoeyToastWrapper({
849
+ initialPhase,
850
+ title,
851
+ type,
852
+ description,
853
+ action,
854
+ icon,
855
+ classNames,
856
+ fillColor,
857
+ borderColor,
858
+ borderWidth,
859
+ timing
860
+ }) {
861
+ return /* @__PURE__ */ jsx(ToastErrorBoundary, { children: /* @__PURE__ */ jsx(
862
+ GoeyToast,
863
+ {
864
+ title,
865
+ description,
866
+ type,
867
+ action,
868
+ icon,
869
+ phase: initialPhase,
870
+ classNames,
871
+ fillColor,
872
+ borderColor,
873
+ borderWidth,
874
+ timing
875
+ }
876
+ ) });
877
+ }
878
+ function PromiseToastWrapper({
879
+ promise,
880
+ data,
881
+ toastId
882
+ }) {
883
+ const [phase, setPhase] = useState("loading");
884
+ const [title, setTitle] = useState(data.loading);
885
+ const [description, setDescription] = useState(data.description?.loading);
886
+ const [action, setAction] = useState(void 0);
887
+ useEffect(() => {
888
+ const resetDuration = (hasExpandedContent) => {
889
+ const baseDuration = data.timing?.displayDuration ?? (hasExpandedContent ? DEFAULT_EXPANDED_DURATION : void 0);
890
+ const collapseDurMs = (data.timing?.collapseDuration ?? 0.9) * 1e3;
891
+ const duration = baseDuration != null && hasExpandedContent ? baseDuration + collapseDurMs : baseDuration;
892
+ if (duration != null) {
893
+ toast.custom(() => /* @__PURE__ */ jsx(PromiseToastWrapper, { promise, data, toastId }), { id: toastId, duration });
894
+ }
895
+ };
896
+ promise.then((result) => {
897
+ const desc = typeof data.description?.success === "function" ? data.description.success(result) : data.description?.success;
898
+ setTitle(
899
+ typeof data.success === "function" ? data.success(result) : data.success
900
+ );
901
+ setDescription(desc);
902
+ setAction(data.action?.success);
903
+ setPhase("success");
904
+ resetDuration(Boolean(desc || data.action?.success));
905
+ }).catch((err) => {
906
+ const desc = typeof data.description?.error === "function" ? data.description.error(err) : data.description?.error;
907
+ setTitle(
908
+ typeof data.error === "function" ? data.error(err) : data.error
909
+ );
910
+ setDescription(desc);
911
+ setAction(data.action?.error);
912
+ setPhase("error");
913
+ resetDuration(Boolean(desc || data.action?.error));
914
+ });
915
+ }, []);
916
+ return /* @__PURE__ */ jsx(ToastErrorBoundary, { children: /* @__PURE__ */ jsx(
917
+ GoeyToast,
918
+ {
919
+ title,
920
+ description,
921
+ type: phase === "loading" ? "info" : phase,
922
+ action,
923
+ phase,
924
+ classNames: data.classNames,
925
+ fillColor: data.fillColor,
926
+ borderColor: data.borderColor,
927
+ borderWidth: data.borderWidth,
928
+ timing: data.timing
929
+ }
930
+ ) });
931
+ }
932
+ function createGoeyToast(title, type, options) {
933
+ const baseDuration = options?.timing?.displayDuration ?? options?.duration ?? (options?.description ? DEFAULT_EXPANDED_DURATION : void 0);
934
+ const hasExpandedContent = Boolean(options?.description || options?.action);
935
+ const collapseDurMs = (options?.timing?.collapseDuration ?? 0.9) * 1e3;
936
+ const duration = baseDuration != null && hasExpandedContent ? baseDuration + collapseDurMs : baseDuration;
937
+ return toast.custom(
938
+ () => /* @__PURE__ */ jsx(
939
+ GoeyToastWrapper,
940
+ {
941
+ initialPhase: type,
942
+ title,
943
+ type,
944
+ description: options?.description,
945
+ action: options?.action,
946
+ icon: options?.icon,
947
+ classNames: options?.classNames,
948
+ fillColor: options?.fillColor,
949
+ borderColor: options?.borderColor,
950
+ borderWidth: options?.borderWidth,
951
+ timing: options?.timing
952
+ }
953
+ ),
954
+ {
955
+ duration,
956
+ id: options?.id
957
+ }
958
+ );
959
+ }
960
+ var goeyToast = Object.assign(
961
+ (title, options) => createGoeyToast(title, "default", options),
962
+ {
963
+ success: (title, options) => createGoeyToast(title, "success", options),
964
+ error: (title, options) => createGoeyToast(title, "error", options),
965
+ warning: (title, options) => createGoeyToast(title, "warning", options),
966
+ info: (title, options) => createGoeyToast(title, "info", options),
967
+ promise: (promise, data) => {
968
+ const id = Math.random().toString(36).slice(2);
969
+ return toast.custom(() => /* @__PURE__ */ jsx(PromiseToastWrapper, { promise, data, toastId: id }), {
970
+ id,
971
+ duration: data.timing?.displayDuration != null || data.description ? Infinity : void 0
972
+ });
973
+ },
974
+ dismiss: toast.dismiss
975
+ }
976
+ );
977
+
978
+ export { GoeyToaster, goeyToast };
979
+ //# sourceMappingURL=index.js.map
980
+ //# sourceMappingURL=index.js.map