@cookified/toastify 1.0.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,742 @@
1
+ // src/toastify/Toaster.tsx
2
+ import { AnimatePresence, motion as motion4 } from "motion/react";
3
+ import { useEffect, useRef, useState } from "react";
4
+
5
+ // src/toastify/Toast.tsx
6
+ import { Check, Loader2, X } from "lucide-react";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ function DefaultToastIcon({ type }) {
9
+ const badgeClasses = "flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-neutral-900 text-white shadow-xs dark:bg-white dark:text-neutral-950";
10
+ switch (type) {
11
+ case "loading":
12
+ return /* @__PURE__ */ jsx("span", { className: badgeClasses, children: /* @__PURE__ */ jsx(Loader2, { size: 12, strokeWidth: 2.5, className: "animate-spin" }) });
13
+ case "success":
14
+ return /* @__PURE__ */ jsx("span", { className: badgeClasses, children: /* @__PURE__ */ jsx(Check, { size: 12, strokeWidth: 2.5 }) });
15
+ case "error":
16
+ return /* @__PURE__ */ jsx("span", { className: badgeClasses, children: /* @__PURE__ */ jsx(X, { size: 12, strokeWidth: 2.5 }) });
17
+ case "warning":
18
+ return /* @__PURE__ */ jsx("span", { className: badgeClasses, children: /* @__PURE__ */ jsxs(
19
+ "svg",
20
+ {
21
+ width: "12",
22
+ height: "12",
23
+ viewBox: "0 0 24 24",
24
+ fill: "none",
25
+ stroke: "currentColor",
26
+ strokeWidth: 2.5,
27
+ strokeLinecap: "round",
28
+ strokeLinejoin: "round",
29
+ "aria-hidden": "true",
30
+ children: [
31
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
32
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
33
+ ]
34
+ }
35
+ ) });
36
+ case "info":
37
+ case "neutral":
38
+ default:
39
+ return /* @__PURE__ */ jsx("span", { className: badgeClasses, children: /* @__PURE__ */ jsxs(
40
+ "svg",
41
+ {
42
+ width: "12",
43
+ height: "12",
44
+ viewBox: "0 0 24 24",
45
+ fill: "none",
46
+ stroke: "currentColor",
47
+ strokeWidth: 2.5,
48
+ strokeLinecap: "round",
49
+ strokeLinejoin: "round",
50
+ "aria-hidden": "true",
51
+ children: [
52
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
53
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
54
+ ]
55
+ }
56
+ ) });
57
+ }
58
+ }
59
+ function Toast({
60
+ toast: toast2,
61
+ onDismiss,
62
+ closeOnClick = false,
63
+ globalIcons,
64
+ globalOptions
65
+ }) {
66
+ if (toast2.customRenderer) {
67
+ return /* @__PURE__ */ jsx(Fragment, { children: toast2.customRenderer(toast2.id) });
68
+ }
69
+ const icon = toast2.icon ?? globalIcons?.[toast2.type] ?? /* @__PURE__ */ jsx(DefaultToastIcon, { type: toast2.type });
70
+ return /* @__PURE__ */ jsxs(
71
+ "div",
72
+ {
73
+ onClick: () => closeOnClick && onDismiss(),
74
+ className: `toastify-toast flex h-14 w-full select-none items-center justify-between gap-3 rounded-md border border-neutral-200/90 bg-white px-3.5 shadow-md shadow-neutral-950/5 dark:border-neutral-800/90 dark:bg-neutral-900 dark:shadow-neutral-950/40 ${globalOptions?.className ?? ""} ${toast2.className ?? ""}`,
75
+ style: { ...globalOptions?.style, ...toast2.style },
76
+ children: [
77
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
78
+ icon,
79
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col justify-center", children: [
80
+ /* @__PURE__ */ jsx("div", { className: "truncate text-[13px] font-medium leading-4 text-neutral-900 dark:text-neutral-100", children: toast2.title }),
81
+ toast2.description && /* @__PURE__ */ jsx("div", { className: "mt-0.5 truncate text-[12px] leading-4 text-neutral-500 dark:text-neutral-400", children: toast2.description })
82
+ ] })
83
+ ] }),
84
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
85
+ toast2.cancel && /* @__PURE__ */ jsx(
86
+ "button",
87
+ {
88
+ type: "button",
89
+ onClick: (event) => {
90
+ event.stopPropagation();
91
+ try {
92
+ toast2.cancel?.onClick(event);
93
+ } catch (err) {
94
+ console.error("Toast cancel handler failed:", err);
95
+ }
96
+ onDismiss();
97
+ },
98
+ className: "cursor-pointer rounded px-2 py-1 text-xs font-medium text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-neutral-900 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-100",
99
+ children: toast2.cancel.label
100
+ }
101
+ ),
102
+ toast2.action && /* @__PURE__ */ jsx(
103
+ "button",
104
+ {
105
+ type: "button",
106
+ onClick: (event) => {
107
+ event.stopPropagation();
108
+ try {
109
+ toast2.action?.onClick(event);
110
+ } catch (err) {
111
+ console.error("Toast action handler failed:", err);
112
+ }
113
+ onDismiss();
114
+ },
115
+ className: "cursor-pointer rounded px-2.5 py-1 text-xs font-medium bg-neutral-900 text-white transition-all hover:bg-neutral-800 shadow-[inset_0_-2px_4px_rgba(0,0,0,0.5),0_1px_2px_rgba(0,0,0,0.08)] active:shadow-[inset_0_2px_4px_rgba(0,0,0,0.4)] dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-neutral-200 dark:shadow-[inset_0_-2px_4px_rgba(0,0,0,0.22),0_1px_2px_rgba(0,0,0,0.15)] dark:active:shadow-[inset_0_2px_4px_rgba(0,0,0,0.25)]",
116
+ children: toast2.action.label
117
+ }
118
+ )
119
+ ] })
120
+ ]
121
+ }
122
+ );
123
+ }
124
+
125
+ // src/toastify/animations/FadeAnimation.tsx
126
+ import { motion } from "motion/react";
127
+
128
+ // src/toastify/animations/constants.ts
129
+ var defaultSpring = {
130
+ stiffness: 220,
131
+ damping: 26,
132
+ mass: 1
133
+ };
134
+
135
+ // src/toastify/animations/FadeAnimation.tsx
136
+ import { jsx as jsx2 } from "react/jsx-runtime";
137
+ function FadeAnimation({
138
+ index,
139
+ position,
140
+ isDismissing,
141
+ onDismiss,
142
+ springConfig = defaultSpring,
143
+ children
144
+ }) {
145
+ const isTop = position.startsWith("top");
146
+ const stackStep = 66;
147
+ const targetY = isTop ? index * stackStep : -index * stackStep;
148
+ return /* @__PURE__ */ jsx2(
149
+ motion.article,
150
+ {
151
+ role: "status",
152
+ "aria-live": "polite",
153
+ initial: {
154
+ opacity: 0,
155
+ scale: 0.9,
156
+ y: targetY,
157
+ filter: "blur(4px)"
158
+ },
159
+ animate: {
160
+ opacity: isDismissing ? 0 : 1,
161
+ scale: isDismissing ? 0.9 : 1,
162
+ y: targetY,
163
+ filter: isDismissing ? "blur(4px)" : "blur(0px)",
164
+ zIndex: 50 - index,
165
+ pointerEvents: !isDismissing ? "auto" : "none"
166
+ },
167
+ exit: {
168
+ opacity: 0,
169
+ scale: 0.9,
170
+ filter: "blur(4px)",
171
+ transition: {
172
+ duration: 0.22,
173
+ ease: "easeOut"
174
+ }
175
+ },
176
+ transition: {
177
+ type: "spring",
178
+ ...springConfig
179
+ },
180
+ drag: "x",
181
+ dragConstraints: { left: 0, right: 0 },
182
+ dragElastic: 0.6,
183
+ onDragEnd: (_, info) => {
184
+ if (Math.abs(info.offset.x) > 60 || Math.abs(info.velocity.x) > 500) {
185
+ onDismiss();
186
+ }
187
+ },
188
+ className: "absolute h-14 w-full select-none cursor-grab active:cursor-grabbing",
189
+ style: {
190
+ [isTop ? "top" : "bottom"]: 0,
191
+ left: 0,
192
+ right: 0
193
+ },
194
+ children
195
+ }
196
+ );
197
+ }
198
+
199
+ // src/toastify/animations/FolderStackAnimation.tsx
200
+ import { motion as motion2 } from "motion/react";
201
+ import { jsx as jsx3 } from "react/jsx-runtime";
202
+ function FolderStackAnimation({
203
+ index,
204
+ isHovered,
205
+ position,
206
+ isDismissing,
207
+ onDismiss,
208
+ springConfig = defaultSpring,
209
+ children
210
+ }) {
211
+ const isTop = position.startsWith("top");
212
+ const isCenter = position.endsWith("center");
213
+ const isLeft = position.endsWith("left");
214
+ const collapsed = !isHovered;
215
+ const isVisible = !collapsed || index < 3;
216
+ const stackStep = 66;
217
+ const offset = collapsed ? index * 10 : index * stackStep;
218
+ const targetY = isTop ? offset : -offset;
219
+ const targetScale = collapsed ? Math.max(0.88, 1 - index * 0.05) : 1;
220
+ const targetOpacity = !isVisible ? 0 : collapsed ? index === 0 ? 1 : index === 1 ? 0.94 : 0.84 : 1;
221
+ const exitX = isCenter ? 0 : isLeft ? -36 : 36;
222
+ return /* @__PURE__ */ jsx3(
223
+ motion2.article,
224
+ {
225
+ role: "status",
226
+ "aria-live": "polite",
227
+ initial: {
228
+ opacity: 0,
229
+ x: 0,
230
+ y: isTop ? targetY - 14 : targetY + 14,
231
+ scale: 0.94
232
+ },
233
+ animate: {
234
+ opacity: isDismissing ? 0 : targetOpacity,
235
+ x: isDismissing ? exitX : 0,
236
+ y: targetY,
237
+ scale: isDismissing ? 0.94 : targetScale,
238
+ zIndex: 50 - index,
239
+ pointerEvents: !isDismissing && isVisible ? "auto" : "none"
240
+ },
241
+ exit: {
242
+ opacity: 0,
243
+ x: exitX,
244
+ scale: 0.94,
245
+ transition: {
246
+ duration: 0.24,
247
+ ease: [0.16, 1, 0.3, 1]
248
+ }
249
+ },
250
+ transition: {
251
+ type: "spring",
252
+ ...springConfig
253
+ },
254
+ drag: index === 0 || isHovered ? "x" : false,
255
+ dragConstraints: { left: 0, right: 0 },
256
+ dragElastic: 0.6,
257
+ onDragEnd: (_, info) => {
258
+ if (Math.abs(info.offset.x) > 60 || Math.abs(info.velocity.x) > 500) {
259
+ onDismiss();
260
+ }
261
+ },
262
+ className: "absolute h-14 w-full select-none cursor-grab active:cursor-grabbing",
263
+ style: {
264
+ [isTop ? "top" : "bottom"]: 0,
265
+ left: 0,
266
+ right: 0,
267
+ transformOrigin: isTop ? "top center" : "bottom center"
268
+ },
269
+ children
270
+ }
271
+ );
272
+ }
273
+
274
+ // src/toastify/animations/SlideAnimation.tsx
275
+ import { motion as motion3 } from "motion/react";
276
+ import { jsx as jsx4 } from "react/jsx-runtime";
277
+ function SlideAnimation({
278
+ index,
279
+ position,
280
+ isDismissing,
281
+ onDismiss,
282
+ springConfig = defaultSpring,
283
+ children
284
+ }) {
285
+ const isTop = position.startsWith("top");
286
+ const isCenter = position.endsWith("center");
287
+ const isLeft = position.endsWith("left");
288
+ const stackStep = 66;
289
+ const targetY = isTop ? index * stackStep : -index * stackStep;
290
+ const entryX = isCenter ? 0 : isLeft ? -120 : 120;
291
+ const exitX = isCenter ? 0 : isLeft ? -140 : 140;
292
+ const initialY = isCenter ? isTop ? targetY - 30 : targetY + 30 : targetY;
293
+ return /* @__PURE__ */ jsx4(
294
+ motion3.article,
295
+ {
296
+ role: "status",
297
+ "aria-live": "polite",
298
+ initial: {
299
+ opacity: 0,
300
+ x: entryX,
301
+ y: initialY,
302
+ scale: 0.95
303
+ },
304
+ animate: {
305
+ opacity: isDismissing ? 0 : 1,
306
+ x: isDismissing ? exitX : 0,
307
+ y: targetY,
308
+ scale: 1,
309
+ zIndex: 50 - index,
310
+ pointerEvents: !isDismissing ? "auto" : "none"
311
+ },
312
+ exit: {
313
+ opacity: 0,
314
+ x: exitX,
315
+ scale: 0.95,
316
+ transition: {
317
+ duration: 0.22,
318
+ ease: [0.16, 1, 0.3, 1]
319
+ }
320
+ },
321
+ transition: {
322
+ type: "spring",
323
+ ...springConfig
324
+ },
325
+ drag: "x",
326
+ dragConstraints: { left: 0, right: 0 },
327
+ dragElastic: 0.6,
328
+ onDragEnd: (_, info) => {
329
+ if (Math.abs(info.offset.x) > 60 || Math.abs(info.velocity.x) > 500) {
330
+ onDismiss();
331
+ }
332
+ },
333
+ className: "absolute h-14 w-full select-none cursor-grab active:cursor-grabbing",
334
+ style: {
335
+ [isTop ? "top" : "bottom"]: 0,
336
+ left: 0,
337
+ right: 0
338
+ },
339
+ children
340
+ }
341
+ );
342
+ }
343
+
344
+ // src/toastify/animations/index.tsx
345
+ import { jsx as jsx5 } from "react/jsx-runtime";
346
+ function ToastAnimation({
347
+ animation = "stack",
348
+ ...props
349
+ }) {
350
+ if (typeof animation === "function") {
351
+ const CustomAnimation = animation;
352
+ return /* @__PURE__ */ jsx5(CustomAnimation, { ...props });
353
+ }
354
+ if (animation === "slide") {
355
+ return /* @__PURE__ */ jsx5(SlideAnimation, { ...props });
356
+ }
357
+ if (animation === "fade") {
358
+ return /* @__PURE__ */ jsx5(FadeAnimation, { ...props });
359
+ }
360
+ return /* @__PURE__ */ jsx5(FolderStackAnimation, { ...props });
361
+ }
362
+
363
+ // src/toastify/store.ts
364
+ var MAX_TOASTS = 30;
365
+ var toasts = [];
366
+ var listeners = /* @__PURE__ */ new Set();
367
+ function generateId() {
368
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
369
+ return crypto.randomUUID();
370
+ }
371
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
372
+ }
373
+ function sanitizeDuration(duration) {
374
+ if (duration === false) return false;
375
+ if (typeof duration === "number") {
376
+ return Number.isFinite(duration) && duration >= 0 ? duration : 3500;
377
+ }
378
+ return void 0;
379
+ }
380
+ function emit() {
381
+ const snapshot = [...toasts];
382
+ listeners.forEach((listener) => {
383
+ try {
384
+ listener(snapshot);
385
+ } catch (err) {
386
+ console.error("Toast listener threw an error:", err);
387
+ }
388
+ });
389
+ }
390
+ function subscribe(listener) {
391
+ listeners.add(listener);
392
+ try {
393
+ listener([...toasts]);
394
+ } catch (err) {
395
+ console.error("Initial toast listener threw an error:", err);
396
+ }
397
+ return () => {
398
+ listeners.delete(listener);
399
+ };
400
+ }
401
+ function remove(id) {
402
+ if (id) {
403
+ toasts = toasts.filter((toast2) => toast2.id !== id);
404
+ } else {
405
+ toasts = [];
406
+ }
407
+ emit();
408
+ }
409
+ function update(id, updates) {
410
+ toasts = toasts.map(
411
+ (toast2) => toast2.id === id ? { ...toast2, ...updates } : toast2
412
+ );
413
+ emit();
414
+ }
415
+ function create(type, title, options = {}) {
416
+ const id = options.id ?? generateId();
417
+ const duration = sanitizeDuration(options.autoClose ?? options.duration);
418
+ const existing = toasts.find((t) => t.id === id);
419
+ if (existing) {
420
+ update(id, {
421
+ type,
422
+ title,
423
+ description: options.description,
424
+ action: options.action,
425
+ cancel: options.cancel,
426
+ icon: options.icon,
427
+ className: options.className,
428
+ style: options.style,
429
+ duration
430
+ });
431
+ return id;
432
+ }
433
+ const toastItem = {
434
+ id,
435
+ type,
436
+ title,
437
+ description: options.description,
438
+ action: options.action,
439
+ cancel: options.cancel,
440
+ icon: options.icon,
441
+ className: options.className,
442
+ style: options.style,
443
+ duration,
444
+ createdAt: Date.now()
445
+ };
446
+ toasts = [toastItem, ...toasts].slice(0, MAX_TOASTS);
447
+ emit();
448
+ return id;
449
+ }
450
+ function promise(promiseOrFn, options) {
451
+ const id = create("loading", options.loading, {
452
+ duration: false
453
+ });
454
+ const p = typeof promiseOrFn === "function" ? promiseOrFn() : promiseOrFn;
455
+ const promiseChain = p.then((data) => {
456
+ let successTitle;
457
+ try {
458
+ successTitle = typeof options.success === "function" ? options.success(data) : options.success;
459
+ } catch {
460
+ successTitle = "Completed successfully";
461
+ }
462
+ let description;
463
+ if (options.description) {
464
+ try {
465
+ description = typeof options.description === "function" ? options.description(data) : options.description;
466
+ } catch {
467
+ description = void 0;
468
+ }
469
+ }
470
+ update(id, {
471
+ type: "success",
472
+ title: successTitle,
473
+ description,
474
+ action: options.action,
475
+ cancel: options.cancel,
476
+ duration: 3500
477
+ });
478
+ return data;
479
+ }).catch((err) => {
480
+ let errorTitle;
481
+ try {
482
+ errorTitle = typeof options.error === "function" ? options.error(err) : options.error;
483
+ } catch {
484
+ errorTitle = "An error occurred";
485
+ }
486
+ update(id, {
487
+ type: "error",
488
+ title: errorTitle,
489
+ action: options.action,
490
+ cancel: options.cancel,
491
+ duration: 4e3
492
+ });
493
+ return Promise.reject(err);
494
+ });
495
+ promiseChain.catch(() => {
496
+ });
497
+ return promiseChain;
498
+ }
499
+ var toastStore = {
500
+ subscribe,
501
+ remove,
502
+ update,
503
+ getToasts: () => [...toasts]
504
+ };
505
+ function toastFn(title, options) {
506
+ return create("neutral", title, options);
507
+ }
508
+ var toast = Object.assign(toastFn, {
509
+ show(title, options) {
510
+ return create("neutral", title, options);
511
+ },
512
+ success(title, options) {
513
+ return create("success", title, options);
514
+ },
515
+ error(title, options) {
516
+ return create("error", title, options);
517
+ },
518
+ warning(title, options) {
519
+ return create("warning", title, options);
520
+ },
521
+ info(title, options) {
522
+ return create("info", title, options);
523
+ },
524
+ loading(title, options) {
525
+ return create("loading", title, { ...options, duration: false });
526
+ },
527
+ custom(renderer, options = {}) {
528
+ const id = options.id ?? generateId();
529
+ const duration = sanitizeDuration(options.autoClose ?? options.duration);
530
+ const toastItem = {
531
+ id,
532
+ type: "custom",
533
+ title: "",
534
+ customRenderer: renderer,
535
+ duration,
536
+ className: options.className,
537
+ style: options.style,
538
+ createdAt: Date.now()
539
+ };
540
+ toasts = [toastItem, ...toasts].slice(0, MAX_TOASTS);
541
+ emit();
542
+ return id;
543
+ },
544
+ promise,
545
+ dismiss(id) {
546
+ remove(id);
547
+ },
548
+ update(id, options) {
549
+ update(id, options);
550
+ }
551
+ });
552
+
553
+ // src/toastify/Toaster.tsx
554
+ import { jsx as jsx6 } from "react/jsx-runtime";
555
+ var positionClasses = {
556
+ "top-left": "top-6 left-6",
557
+ "top-center": "top-6 left-0 right-0 mx-auto",
558
+ "top-right": "top-6 right-6",
559
+ "bottom-left": "bottom-6 left-6",
560
+ "bottom-center": "bottom-6 left-0 right-0 mx-auto",
561
+ "bottom-right": "bottom-6 right-6"
562
+ };
563
+ function ToasterItem({
564
+ toast: toast2,
565
+ index,
566
+ totalToasts,
567
+ isHovered,
568
+ position,
569
+ duration = 3500,
570
+ autoClose,
571
+ closeOnClick = false,
572
+ animation = "stack",
573
+ springConfig,
574
+ icons,
575
+ toastOptions,
576
+ onDismiss
577
+ }) {
578
+ const itemDuration = toast2.type === "loading" ? false : toast2.duration !== void 0 ? toast2.duration : toastOptions?.duration !== void 0 ? toastOptions.duration : autoClose ?? duration;
579
+ const onDismissRef = useRef(onDismiss);
580
+ useEffect(() => {
581
+ onDismissRef.current = onDismiss;
582
+ }, [onDismiss]);
583
+ const [isDismissing, setIsDismissing] = useState(false);
584
+ const handleDismiss = () => {
585
+ setIsDismissing(true);
586
+ onDismissRef.current();
587
+ };
588
+ const remainingTimeRef = useRef(itemDuration);
589
+ useEffect(() => {
590
+ remainingTimeRef.current = itemDuration;
591
+ }, [itemDuration]);
592
+ useEffect(() => {
593
+ if (itemDuration === false || isHovered) {
594
+ return;
595
+ }
596
+ const startTime = Date.now();
597
+ const currentRemaining = remainingTimeRef.current === false ? itemDuration : remainingTimeRef.current;
598
+ const timer = window.setTimeout(() => {
599
+ handleDismiss();
600
+ }, currentRemaining);
601
+ return () => {
602
+ window.clearTimeout(timer);
603
+ if (remainingTimeRef.current !== false) {
604
+ const elapsed = Date.now() - startTime;
605
+ remainingTimeRef.current = Math.max(0, remainingTimeRef.current - elapsed);
606
+ }
607
+ };
608
+ }, [isHovered, itemDuration]);
609
+ return /* @__PURE__ */ jsx6(
610
+ ToastAnimation,
611
+ {
612
+ animation,
613
+ toast: toast2,
614
+ index,
615
+ totalToasts,
616
+ isHovered,
617
+ position,
618
+ isDismissing,
619
+ onDismiss: handleDismiss,
620
+ springConfig,
621
+ children: /* @__PURE__ */ jsx6(
622
+ Toast,
623
+ {
624
+ toast: toast2,
625
+ onDismiss: handleDismiss,
626
+ closeOnClick,
627
+ globalIcons: icons,
628
+ globalOptions: toastOptions
629
+ }
630
+ )
631
+ }
632
+ );
633
+ }
634
+ function Toaster({
635
+ position = "bottom-right",
636
+ duration = 3500,
637
+ autoClose,
638
+ theme = "light",
639
+ className,
640
+ style,
641
+ visibleToasts = 5,
642
+ closeOnClick = false,
643
+ animation = "stack",
644
+ springConfig,
645
+ icons,
646
+ toastOptions
647
+ }) {
648
+ const [toasts2, setToasts] = useState([]);
649
+ const [isHovered, setIsHovered] = useState(false);
650
+ const [hoverExpandedCount, setHoverExpandedCount] = useState(null);
651
+ const leaveTimeoutRef = useRef(null);
652
+ useEffect(() => toastStore.subscribe(setToasts), []);
653
+ const isTop = position.startsWith("top");
654
+ const hasToasts = toasts2.length > 0;
655
+ const activeCount = Math.min(toasts2.length, visibleToasts);
656
+ const effectiveCount = isHovered && hoverExpandedCount !== null ? Math.max(activeCount, hoverExpandedCount) : activeCount;
657
+ const containerHeight = hasToasts && isHovered && effectiveCount > 0 ? (effectiveCount - 1) * 66 + 56 : 56;
658
+ const handleMouseEnter = () => {
659
+ if (leaveTimeoutRef.current) {
660
+ window.clearTimeout(leaveTimeoutRef.current);
661
+ leaveTimeoutRef.current = null;
662
+ }
663
+ setHoverExpandedCount((prev) => Math.max(prev ?? 0, activeCount));
664
+ setIsHovered(true);
665
+ };
666
+ const handleMouseLeave = () => {
667
+ leaveTimeoutRef.current = window.setTimeout(() => {
668
+ setIsHovered(false);
669
+ setHoverExpandedCount(null);
670
+ }, 140);
671
+ };
672
+ useEffect(() => {
673
+ return () => {
674
+ if (leaveTimeoutRef.current) {
675
+ window.clearTimeout(leaveTimeoutRef.current);
676
+ }
677
+ };
678
+ }, []);
679
+ return /* @__PURE__ */ jsx6(
680
+ motion4.div,
681
+ {
682
+ "data-toastify-toaster": "true",
683
+ role: "region",
684
+ "aria-label": "Notifications",
685
+ "aria-live": "polite",
686
+ tabIndex: -1,
687
+ className: `toastify-toaster fixed z-50 w-[356px] max-w-[calc(100vw-32px)] ${hasToasts ? "pointer-events-auto" : "pointer-events-none"} ${positionClasses[position]} ${theme === "dark" ? "dark" : ""} ${className ?? ""}`,
688
+ style,
689
+ animate: {
690
+ height: containerHeight
691
+ },
692
+ transition: {
693
+ type: "spring",
694
+ stiffness: 220,
695
+ damping: 26,
696
+ mass: 1,
697
+ ...springConfig
698
+ },
699
+ onMouseEnter: handleMouseEnter,
700
+ onMouseLeave: handleMouseLeave,
701
+ children: /* @__PURE__ */ jsx6(
702
+ "div",
703
+ {
704
+ className: `relative w-full h-full flex ${isTop ? "flex-col" : "flex-col-reverse"}`,
705
+ children: /* @__PURE__ */ jsx6(AnimatePresence, { children: toasts2.slice(0, visibleToasts).map((toastItem, index) => /* @__PURE__ */ jsx6(
706
+ ToasterItem,
707
+ {
708
+ toast: toastItem,
709
+ index,
710
+ totalToasts: toasts2.length,
711
+ isHovered,
712
+ position,
713
+ duration,
714
+ autoClose,
715
+ closeOnClick,
716
+ animation,
717
+ springConfig,
718
+ icons,
719
+ toastOptions,
720
+ onDismiss: () => toastStore.remove(toastItem.id)
721
+ },
722
+ toastItem.id
723
+ )) })
724
+ }
725
+ )
726
+ }
727
+ );
728
+ }
729
+ var ToastContainer = Toaster;
730
+ export {
731
+ DefaultToastIcon,
732
+ FadeAnimation,
733
+ FolderStackAnimation,
734
+ SlideAnimation,
735
+ Toast,
736
+ ToastAnimation,
737
+ ToastContainer,
738
+ Toaster,
739
+ defaultSpring,
740
+ toast,
741
+ toastStore
742
+ };