flashpop 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.
@@ -0,0 +1,807 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var reactDom = require('react-dom');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ const SuccessIcon = ({
8
+ size = 20,
9
+ className = '',
10
+ ...props
11
+ }) => /*#__PURE__*/jsxRuntime.jsxs("svg", {
12
+ width: size,
13
+ height: size,
14
+ viewBox: "0 0 24 24",
15
+ fill: "none",
16
+ stroke: "currentColor",
17
+ strokeWidth: "2",
18
+ strokeLinecap: "round",
19
+ strokeLinejoin: "round",
20
+ className: `easy-toast-icon easy-toast-icon--success ${className}`,
21
+ "aria-hidden": "true",
22
+ ...props,
23
+ children: [/*#__PURE__*/jsxRuntime.jsx("path", {
24
+ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14"
25
+ }), /*#__PURE__*/jsxRuntime.jsx("polyline", {
26
+ points: "22 4 12 14.01 9 11.01"
27
+ })]
28
+ });
29
+
30
+ /**
31
+ * Error Icon SVG
32
+ */
33
+ const ErrorIcon = ({
34
+ size = 20,
35
+ className = '',
36
+ ...props
37
+ }) => /*#__PURE__*/jsxRuntime.jsxs("svg", {
38
+ width: size,
39
+ height: size,
40
+ viewBox: "0 0 24 24",
41
+ fill: "none",
42
+ stroke: "currentColor",
43
+ strokeWidth: "2",
44
+ strokeLinecap: "round",
45
+ strokeLinejoin: "round",
46
+ className: `easy-toast-icon easy-toast-icon--error ${className}`,
47
+ "aria-hidden": "true",
48
+ ...props,
49
+ children: [/*#__PURE__*/jsxRuntime.jsx("circle", {
50
+ cx: "12",
51
+ cy: "12",
52
+ r: "10"
53
+ }), /*#__PURE__*/jsxRuntime.jsx("line", {
54
+ x1: "15",
55
+ y1: "9",
56
+ x2: "9",
57
+ y2: "15"
58
+ }), /*#__PURE__*/jsxRuntime.jsx("line", {
59
+ x1: "9",
60
+ y1: "9",
61
+ x2: "15",
62
+ y2: "15"
63
+ })]
64
+ });
65
+
66
+ /**
67
+ * Warning Icon SVG
68
+ */
69
+ const WarningIcon = ({
70
+ size = 20,
71
+ className = '',
72
+ ...props
73
+ }) => /*#__PURE__*/jsxRuntime.jsxs("svg", {
74
+ width: size,
75
+ height: size,
76
+ viewBox: "0 0 24 24",
77
+ fill: "none",
78
+ stroke: "currentColor",
79
+ strokeWidth: "2",
80
+ strokeLinecap: "round",
81
+ strokeLinejoin: "round",
82
+ className: `easy-toast-icon easy-toast-icon--warning ${className}`,
83
+ "aria-hidden": "true",
84
+ ...props,
85
+ children: [/*#__PURE__*/jsxRuntime.jsx("path", {
86
+ 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"
87
+ }), /*#__PURE__*/jsxRuntime.jsx("line", {
88
+ x1: "12",
89
+ y1: "9",
90
+ x2: "12",
91
+ y2: "13"
92
+ }), /*#__PURE__*/jsxRuntime.jsx("line", {
93
+ x1: "12",
94
+ y1: "17",
95
+ x2: "12.01",
96
+ y2: "17"
97
+ })]
98
+ });
99
+
100
+ /**
101
+ * Info Icon SVG
102
+ */
103
+ const InfoIcon = ({
104
+ size = 20,
105
+ className = '',
106
+ ...props
107
+ }) => /*#__PURE__*/jsxRuntime.jsxs("svg", {
108
+ width: size,
109
+ height: size,
110
+ viewBox: "0 0 24 24",
111
+ fill: "none",
112
+ stroke: "currentColor",
113
+ strokeWidth: "2",
114
+ strokeLinecap: "round",
115
+ strokeLinejoin: "round",
116
+ className: `easy-toast-icon easy-toast-icon--info ${className}`,
117
+ "aria-hidden": "true",
118
+ ...props,
119
+ children: [/*#__PURE__*/jsxRuntime.jsx("circle", {
120
+ cx: "12",
121
+ cy: "12",
122
+ r: "10"
123
+ }), /*#__PURE__*/jsxRuntime.jsx("line", {
124
+ x1: "12",
125
+ y1: "16",
126
+ x2: "12",
127
+ y2: "12"
128
+ }), /*#__PURE__*/jsxRuntime.jsx("line", {
129
+ x1: "12",
130
+ y1: "8",
131
+ x2: "12.01",
132
+ y2: "8"
133
+ })]
134
+ });
135
+
136
+ /**
137
+ * Loading Spinner SVG
138
+ */
139
+ const LoadingIcon = ({
140
+ size = 20,
141
+ className = '',
142
+ ...props
143
+ }) => /*#__PURE__*/jsxRuntime.jsx("svg", {
144
+ width: size,
145
+ height: size,
146
+ viewBox: "0 0 24 24",
147
+ fill: "none",
148
+ stroke: "currentColor",
149
+ strokeWidth: "2.5",
150
+ strokeLinecap: "round",
151
+ strokeLinejoin: "round",
152
+ className: `easy-toast-icon easy-toast-icon--loading ${className}`,
153
+ "aria-hidden": "true",
154
+ ...props,
155
+ children: /*#__PURE__*/jsxRuntime.jsx("path", {
156
+ d: "M21 12a9 9 0 1 1-6.219-8.56"
157
+ })
158
+ });
159
+
160
+ /**
161
+ * Close (Dismiss) Button Icon SVG
162
+ */
163
+ const CloseIcon = ({
164
+ size = 16,
165
+ className = '',
166
+ ...props
167
+ }) => /*#__PURE__*/jsxRuntime.jsxs("svg", {
168
+ width: size,
169
+ height: size,
170
+ viewBox: "0 0 24 24",
171
+ fill: "none",
172
+ stroke: "currentColor",
173
+ strokeWidth: "2",
174
+ strokeLinecap: "round",
175
+ strokeLinejoin: "round",
176
+ className: `easy-toast-close-icon ${className}`,
177
+ "aria-hidden": "true",
178
+ ...props,
179
+ children: [/*#__PURE__*/jsxRuntime.jsx("line", {
180
+ x1: "18",
181
+ y1: "6",
182
+ x2: "6",
183
+ y2: "18"
184
+ }), /*#__PURE__*/jsxRuntime.jsx("line", {
185
+ x1: "6",
186
+ y1: "6",
187
+ x2: "18",
188
+ y2: "18"
189
+ })]
190
+ });
191
+
192
+ const ToastItem = ({
193
+ id,
194
+ type = 'default',
195
+ title,
196
+ message,
197
+ duration = 3000,
198
+ showProgressBar = true,
199
+ pauseOnHover = true,
200
+ closeButton = true,
201
+ icon,
202
+ action,
203
+ onClose,
204
+ onAccept,
205
+ onCancel,
206
+ acceptLabel,
207
+ cancelLabel = 'Dismiss',
208
+ swipeable = true,
209
+ swipeThreshold = 70,
210
+ theme = 'light',
211
+ className = '',
212
+ style = {},
213
+ onDismiss
214
+ }) => {
215
+ const [isExiting, setIsExiting] = React.useState(false);
216
+ const [exitDirection, setExitDirection] = React.useState(null); // 'left' | 'right' | null
217
+ const [isPaused, setIsPaused] = React.useState(false);
218
+
219
+ // Swipe state
220
+ const [isDragging, setIsDragging] = React.useState(false);
221
+ const [dragOffset, setDragOffset] = React.useState(0);
222
+ const startXRef = React.useRef(0);
223
+ const isPointerDownRef = React.useRef(false);
224
+
225
+ // Ref to track remaining duration when paused
226
+ const remainingTimeRef = React.useRef(duration);
227
+ const startTimeRef = React.useRef(null);
228
+ const timerRef = React.useRef(null);
229
+
230
+ // Check if this toast supports an "accept" action
231
+ const hasAcceptAction = Boolean(onAccept || action && action.onClick);
232
+ const resolvedAcceptLabel = acceptLabel || action && action.label || 'Accept';
233
+
234
+ // Trigger graceful exit animation before removal
235
+ const handleDismiss = React.useCallback((direction = null) => {
236
+ if (isExiting) return;
237
+ setIsExiting(true);
238
+ if (direction) {
239
+ setExitDirection(direction);
240
+ }
241
+ if (onClose) {
242
+ onClose(id);
243
+ }
244
+ // Match the CSS exit animation duration
245
+ setTimeout(() => {
246
+ onDismiss(id);
247
+ }, 280);
248
+ }, [id, isExiting, onClose, onDismiss]);
249
+
250
+ // Trigger accept action
251
+ const handleAccept = React.useCallback(() => {
252
+ if (isExiting) return;
253
+ if (onAccept) {
254
+ onAccept(id);
255
+ } else if (action && action.onClick) {
256
+ action.onClick(id);
257
+ }
258
+ handleDismiss('right');
259
+ }, [action, handleDismiss, id, isExiting, onAccept]);
260
+
261
+ // Handle auto-dismiss timer
262
+ React.useEffect(() => {
263
+ if (duration === false || duration === Infinity || duration <= 0) {
264
+ return;
265
+ }
266
+ const startTimer = time => {
267
+ startTimeRef.current = Date.now();
268
+ timerRef.current = setTimeout(() => {
269
+ handleDismiss();
270
+ }, time);
271
+ };
272
+ if (!isPaused && !isDragging) {
273
+ startTimer(remainingTimeRef.current);
274
+ }
275
+ return () => {
276
+ if (timerRef.current) {
277
+ clearTimeout(timerRef.current);
278
+ }
279
+ };
280
+ }, [duration, isPaused, isDragging, handleDismiss]);
281
+
282
+ // Pause on mouse enter
283
+ const handleMouseEnter = () => {
284
+ if (!pauseOnHover || duration === false || duration === Infinity || duration <= 0) {
285
+ return;
286
+ }
287
+ if (timerRef.current) {
288
+ clearTimeout(timerRef.current);
289
+ const elapsed = Date.now() - startTimeRef.current;
290
+ remainingTimeRef.current = Math.max(0, remainingTimeRef.current - elapsed);
291
+ }
292
+ setIsPaused(true);
293
+ };
294
+
295
+ // Resume on mouse leave
296
+ const handleMouseLeave = () => {
297
+ if (!pauseOnHover || duration === false || duration === Infinity || duration <= 0 || isDragging) {
298
+ return;
299
+ }
300
+ setIsPaused(false);
301
+ };
302
+
303
+ // --- Pointer Swipe Handlers ---
304
+ const handlePointerDown = e => {
305
+ if (!swipeable || isExiting) return;
306
+ // Don't drag if clicking buttons or interactive elements
307
+ if (e.target.closest('button') || e.target.closest('a')) return;
308
+ if (e.button !== undefined && e.button !== 0) return; // Only primary mouse button
309
+
310
+ isPointerDownRef.current = true;
311
+ startXRef.current = e.clientX;
312
+ setIsDragging(true);
313
+
314
+ // Pause timer while swiping
315
+ if (timerRef.current) {
316
+ clearTimeout(timerRef.current);
317
+ const elapsed = Date.now() - (startTimeRef.current || Date.now());
318
+ remainingTimeRef.current = Math.max(0, remainingTimeRef.current - elapsed);
319
+ }
320
+ try {
321
+ e.currentTarget.setPointerCapture(e.pointerId);
322
+ } catch (err) {
323
+ // Ignore if setPointerCapture not supported
324
+ }
325
+ };
326
+ const handlePointerMove = e => {
327
+ if (!isPointerDownRef.current || !isDragging) return;
328
+ const currentX = e.clientX;
329
+ const diff = currentX - startXRef.current;
330
+ setDragOffset(diff);
331
+ };
332
+ const handlePointerUp = e => {
333
+ if (!isPointerDownRef.current) return;
334
+ isPointerDownRef.current = false;
335
+ setIsDragging(false);
336
+ try {
337
+ e.currentTarget.releasePointerCapture(e.pointerId);
338
+ } catch (err) {
339
+ // Ignore
340
+ }
341
+
342
+ // Check if swipe distance exceeded threshold
343
+ if (dragOffset >= swipeThreshold) {
344
+ // Swiped Right
345
+ if (hasAcceptAction) {
346
+ handleAccept();
347
+ } else {
348
+ if (onCancel) onCancel(id);
349
+ handleDismiss('right');
350
+ }
351
+ } else if (dragOffset <= -swipeThreshold) {
352
+ // Swiped Left
353
+ if (onCancel) onCancel(id);
354
+ handleDismiss('left');
355
+ } else {
356
+ // Reset back to center
357
+ setDragOffset(0);
358
+ setIsPaused(false);
359
+ }
360
+ };
361
+ const handlePointerCancel = () => {
362
+ isPointerDownRef.current = false;
363
+ setIsDragging(false);
364
+ setDragOffset(0);
365
+ setIsPaused(false);
366
+ };
367
+
368
+ // Resolve appropriate icon
369
+ const renderIcon = () => {
370
+ if (icon === false) return null;
371
+ if (icon && /*#__PURE__*/React.isValidElement(icon)) {
372
+ return /*#__PURE__*/jsxRuntime.jsx("span", {
373
+ className: "easy-toast-custom-icon",
374
+ children: icon
375
+ });
376
+ }
377
+ switch (type) {
378
+ case 'success':
379
+ return /*#__PURE__*/jsxRuntime.jsx(SuccessIcon, {});
380
+ case 'error':
381
+ return /*#__PURE__*/jsxRuntime.jsx(ErrorIcon, {});
382
+ case 'warning':
383
+ return /*#__PURE__*/jsxRuntime.jsx(WarningIcon, {});
384
+ case 'info':
385
+ return /*#__PURE__*/jsxRuntime.jsx(InfoIcon, {});
386
+ case 'loading':
387
+ return /*#__PURE__*/jsxRuntime.jsx(LoadingIcon, {});
388
+ default:
389
+ return null;
390
+ }
391
+ };
392
+ const hasIcon = icon !== false && (icon || type !== 'default');
393
+ const hasProgressBar = showProgressBar && duration && duration !== Infinity && duration > 0;
394
+
395
+ // Compute inline drag transform
396
+ const getDragStyle = () => {
397
+ if (isDragging) {
398
+ return {
399
+ transform: `translateX(${dragOffset}px)`,
400
+ transition: 'none',
401
+ opacity: Math.max(0.45, 1 - Math.abs(dragOffset) / 280)
402
+ };
403
+ }
404
+ if (exitDirection === 'left') {
405
+ return {
406
+ transform: 'translateX(-115%) scale(0.9)',
407
+ opacity: 0,
408
+ transition: 'transform 0.28s cubic-bezier(0.4, 0, 1, 1), opacity 0.28s ease'
409
+ };
410
+ }
411
+ if (exitDirection === 'right') {
412
+ return {
413
+ transform: 'translateX(115%) scale(0.9)',
414
+ opacity: 0,
415
+ transition: 'transform 0.28s cubic-bezier(0.4, 0, 1, 1), opacity 0.28s ease'
416
+ };
417
+ }
418
+ return {
419
+ transition: 'transform 0.24s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.24s ease'
420
+ };
421
+ };
422
+
423
+ // Visual cues during swipe
424
+ const isSwipingAccept = dragOffset > 25 && hasAcceptAction;
425
+ const isSwipingDismissRight = dragOffset > 25 && !hasAcceptAction;
426
+ const isSwipingCancel = dragOffset < -25;
427
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
428
+ role: type === 'error' ? 'alert' : 'status',
429
+ "aria-live": type === 'error' ? 'assertive' : 'polite',
430
+ className: `easy-toast-item easy-toast--${type} easy-toast--theme-${theme} ${isExiting ? 'easy-toast--exit' : 'easy-toast--enter'} ${swipeable ? 'easy-toast--swipeable' : ''} ${isDragging ? 'easy-toast--dragging' : ''} ${isSwipingAccept ? 'easy-toast--swipe-accept' : ''} ${isSwipingCancel || isSwipingDismissRight ? 'easy-toast--swipe-cancel' : ''} ${className}`,
431
+ style: {
432
+ ...style,
433
+ ...getDragStyle()
434
+ },
435
+ onMouseEnter: handleMouseEnter,
436
+ onMouseLeave: handleMouseLeave,
437
+ onPointerDown: handlePointerDown,
438
+ onPointerMove: handlePointerMove,
439
+ onPointerUp: handlePointerUp,
440
+ onPointerCancel: handlePointerCancel,
441
+ children: [isSwipingAccept && /*#__PURE__*/jsxRuntime.jsxs("div", {
442
+ className: "easy-toast-swipe-badge easy-toast-swipe-badge--accept",
443
+ children: ["\u2713 ", resolvedAcceptLabel]
444
+ }), (isSwipingCancel || isSwipingDismissRight) && /*#__PURE__*/jsxRuntime.jsxs("div", {
445
+ className: "easy-toast-swipe-badge easy-toast-swipe-badge--cancel",
446
+ children: ["\u2715 ", cancelLabel]
447
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
448
+ className: "easy-toast-content-wrapper",
449
+ children: [hasIcon && /*#__PURE__*/jsxRuntime.jsx("div", {
450
+ className: "easy-toast-icon-container",
451
+ children: renderIcon()
452
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
453
+ className: "easy-toast-text-container",
454
+ children: [title && /*#__PURE__*/jsxRuntime.jsx("div", {
455
+ className: "easy-toast-title",
456
+ children: title
457
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
458
+ className: "easy-toast-message",
459
+ children: message
460
+ })]
461
+ }), action && /*#__PURE__*/jsxRuntime.jsx("div", {
462
+ className: "easy-toast-action-container",
463
+ children: /*#__PURE__*/jsxRuntime.jsx("button", {
464
+ type: "button",
465
+ className: "easy-toast-action-button",
466
+ onClick: () => {
467
+ action.onClick?.(id);
468
+ if (action.dismissOnClick !== false) {
469
+ handleDismiss('right');
470
+ }
471
+ },
472
+ children: action.label
473
+ })
474
+ }), closeButton && /*#__PURE__*/jsxRuntime.jsx("button", {
475
+ type: "button",
476
+ className: "easy-toast-close-button",
477
+ "aria-label": "Close notification",
478
+ onClick: () => handleDismiss(),
479
+ children: /*#__PURE__*/jsxRuntime.jsx(CloseIcon, {})
480
+ })]
481
+ }), hasProgressBar && /*#__PURE__*/jsxRuntime.jsx("div", {
482
+ className: "easy-toast-progress-bar-track",
483
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
484
+ className: "easy-toast-progress-bar",
485
+ style: {
486
+ animationDuration: `${duration}ms`,
487
+ animationPlayState: isPaused || isDragging ? 'paused' : 'running'
488
+ }
489
+ })
490
+ })]
491
+ });
492
+ };
493
+
494
+ const VALID_POSITIONS = ['top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'];
495
+
496
+ /**
497
+ * ToastContainer Component
498
+ * Renders toast groups in their respective screen positions via a React Portal.
499
+ */
500
+ const ToastContainer = ({
501
+ toasts = [],
502
+ onDismiss,
503
+ position: defaultPosition = 'top-right',
504
+ theme: defaultTheme = 'light',
505
+ newestOnTop = false,
506
+ pauseOnHover = true,
507
+ showProgressBar = true,
508
+ swipeable = true,
509
+ swipeThreshold = 70,
510
+ limit,
511
+ className = '',
512
+ style = {}
513
+ }) => {
514
+ // Guard for SSR (Next.js, Gatsby, Remix)
515
+ const isMounted = typeof window !== 'undefined' && typeof document !== 'undefined';
516
+ if (!isMounted) {
517
+ return null;
518
+ }
519
+
520
+ // Filter toasts if limit is set
521
+ const visibleToasts = limit && limit > 0 ? toasts.slice(-limit) : toasts;
522
+
523
+ // Group toasts by position
524
+ const groupedToasts = visibleToasts.reduce((acc, toast) => {
525
+ const pos = toast.position || defaultPosition;
526
+ const validPos = VALID_POSITIONS.includes(pos) ? pos : 'top-right';
527
+ if (!acc[validPos]) {
528
+ acc[validPos] = [];
529
+ }
530
+ acc[validPos].push(toast);
531
+ return acc;
532
+ }, {});
533
+ const portalContent = /*#__PURE__*/jsxRuntime.jsx("div", {
534
+ className: `easy-toast-portal ${className}`,
535
+ style: style,
536
+ children: Object.entries(groupedToasts).map(([pos, items]) => {
537
+ const sortedItems = newestOnTop ? [...items].reverse() : items;
538
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
539
+ className: `easy-toast-container easy-toast-container--${pos}`,
540
+ "aria-live": "polite",
541
+ children: sortedItems.map(toast => /*#__PURE__*/jsxRuntime.jsx(ToastItem, {
542
+ ...toast,
543
+ pauseOnHover: toast.pauseOnHover ?? pauseOnHover,
544
+ showProgressBar: toast.showProgressBar ?? showProgressBar,
545
+ swipeable: toast.swipeable ?? swipeable,
546
+ swipeThreshold: toast.swipeThreshold ?? swipeThreshold,
547
+ theme: toast.theme || defaultTheme,
548
+ onDismiss: onDismiss
549
+ }, toast.id))
550
+ }, pos);
551
+ })
552
+ });
553
+ return /*#__PURE__*/reactDom.createPortal(portalContent, document.body);
554
+ };
555
+
556
+ const ToastContext = /*#__PURE__*/React.createContext(null);
557
+ let toastCount = 0;
558
+ const generateId = () => {
559
+ toastCount += 1;
560
+ return `easy-toast-${Date.now()}-${toastCount}`;
561
+ };
562
+
563
+ /**
564
+ * ToastProvider Component
565
+ * Wraps your application to provide toast functionality anywhere in the component tree.
566
+ */
567
+ const ToastProvider = ({
568
+ children,
569
+ position = 'top-right',
570
+ autoClose = 3000,
571
+ pauseOnHover = true,
572
+ showProgressBar = true,
573
+ swipeable = true,
574
+ swipeThreshold = 70,
575
+ theme = 'light',
576
+ limit,
577
+ newestOnTop = false,
578
+ containerClassName = '',
579
+ containerStyle = {}
580
+ }) => {
581
+ const [toasts, setToasts] = React.useState([]);
582
+ const toastsRef = React.useRef(toasts);
583
+ toastsRef.current = toasts;
584
+
585
+ // Add a new toast
586
+ const addToast = React.useCallback((messageOrOptions, maybeOptions = {}) => {
587
+ let options = {};
588
+ if (typeof messageOrOptions === 'object' && ! /*#__PURE__*/React.isValidElement(messageOrOptions)) {
589
+ options = {
590
+ ...messageOrOptions
591
+ };
592
+ } else {
593
+ options = {
594
+ ...maybeOptions,
595
+ message: messageOrOptions
596
+ };
597
+ }
598
+ const id = options.id || generateId();
599
+ const newToast = {
600
+ id,
601
+ type: options.type || 'default',
602
+ message: options.message,
603
+ title: options.title,
604
+ duration: options.duration !== undefined ? options.duration : autoClose,
605
+ showProgressBar: options.showProgressBar !== undefined ? options.showProgressBar : showProgressBar,
606
+ pauseOnHover: options.pauseOnHover !== undefined ? options.pauseOnHover : pauseOnHover,
607
+ swipeable: options.swipeable !== undefined ? options.swipeable : swipeable,
608
+ swipeThreshold: options.swipeThreshold !== undefined ? options.swipeThreshold : swipeThreshold,
609
+ closeButton: options.closeButton !== undefined ? options.closeButton : true,
610
+ icon: options.icon,
611
+ action: options.action,
612
+ onClose: options.onClose,
613
+ onAccept: options.onAccept,
614
+ onCancel: options.onCancel,
615
+ acceptLabel: options.acceptLabel,
616
+ cancelLabel: options.cancelLabel,
617
+ position: options.position || position,
618
+ theme: options.theme || theme,
619
+ className: options.className || '',
620
+ style: options.style || {}
621
+ };
622
+ setToasts(prev => {
623
+ // If updating an existing toast with same id
624
+ const existingIndex = prev.findIndex(t => t.id === id);
625
+ if (existingIndex > -1) {
626
+ const updated = [...prev];
627
+ updated[existingIndex] = {
628
+ ...updated[existingIndex],
629
+ ...newToast
630
+ };
631
+ return updated;
632
+ }
633
+
634
+ // Apply limit if specified
635
+ if (limit && limit > 0 && prev.length >= limit) {
636
+ return [...prev.slice(prev.length - limit + 1), newToast];
637
+ }
638
+ return [...prev, newToast];
639
+ });
640
+ return id;
641
+ }, [autoClose, showProgressBar, pauseOnHover, position, theme, limit]);
642
+
643
+ // Update an existing toast (e.g. for promises)
644
+ const updateToast = React.useCallback((id, updatedOptions) => {
645
+ setToasts(prev => prev.map(t => {
646
+ if (t.id === id) {
647
+ return {
648
+ ...t,
649
+ ...updatedOptions,
650
+ id // preserve id
651
+ };
652
+ }
653
+ return t;
654
+ }));
655
+ }, []);
656
+
657
+ // Dismiss a specific toast
658
+ const dismissToast = React.useCallback(id => {
659
+ setToasts(prev => prev.filter(t => t.id !== id));
660
+ }, []);
661
+
662
+ // Dismiss all toasts
663
+ const dismissAll = React.useCallback(() => {
664
+ setToasts([]);
665
+ }, []);
666
+
667
+ // Context value with core functions
668
+ const contextValue = React.useMemo(() => ({
669
+ addToast,
670
+ updateToast,
671
+ dismissToast,
672
+ dismissAll,
673
+ toasts
674
+ }), [addToast, updateToast, dismissToast, dismissAll, toasts]);
675
+ return /*#__PURE__*/jsxRuntime.jsxs(ToastContext.Provider, {
676
+ value: contextValue,
677
+ children: [children, /*#__PURE__*/jsxRuntime.jsx(ToastContainer, {
678
+ toasts: toasts,
679
+ onDismiss: dismissToast,
680
+ position: position,
681
+ theme: theme,
682
+ limit: limit,
683
+ newestOnTop: newestOnTop,
684
+ pauseOnHover: pauseOnHover,
685
+ showProgressBar: showProgressBar,
686
+ swipeable: swipeable,
687
+ swipeThreshold: swipeThreshold,
688
+ className: containerClassName,
689
+ style: containerStyle
690
+ })]
691
+ });
692
+ };
693
+
694
+ /**
695
+ * Custom Hook: useToast
696
+ * Exposes an intuitive API to trigger and manage toast notifications.
697
+ *
698
+ * Example:
699
+ * const toast = useToast();
700
+ * toast.success('Profile updated!');
701
+ * toast.error('Something went wrong');
702
+ */
703
+ const useToast = () => {
704
+ const context = React.useContext(ToastContext);
705
+ if (!context) {
706
+ throw new Error('useToast must be used within a <ToastProvider>. Please wrap your root component in <ToastProvider>.');
707
+ }
708
+ const {
709
+ addToast,
710
+ updateToast,
711
+ dismissToast,
712
+ dismissAll
713
+ } = context;
714
+
715
+ // Base callable toast function
716
+ const toast = React.useCallback((message, options) => {
717
+ return addToast(message, options);
718
+ }, [addToast]);
719
+
720
+ // Helper: toast.success
721
+ toast.success = React.useCallback((message, options = {}) => {
722
+ return addToast(message, {
723
+ ...options,
724
+ type: 'success'
725
+ });
726
+ }, [addToast]);
727
+
728
+ // Helper: toast.error
729
+ toast.error = React.useCallback((message, options = {}) => {
730
+ return addToast(message, {
731
+ ...options,
732
+ type: 'error'
733
+ });
734
+ }, [addToast]);
735
+
736
+ // Helper: toast.warning
737
+ toast.warning = React.useCallback((message, options = {}) => {
738
+ return addToast(message, {
739
+ ...options,
740
+ type: 'warning'
741
+ });
742
+ }, [addToast]);
743
+
744
+ // Helper: toast.info
745
+ toast.info = React.useCallback((message, options = {}) => {
746
+ return addToast(message, {
747
+ ...options,
748
+ type: 'info'
749
+ });
750
+ }, [addToast]);
751
+
752
+ // Helper: toast.loading
753
+ toast.loading = React.useCallback((message, options = {}) => {
754
+ return addToast(message, {
755
+ ...options,
756
+ type: 'loading',
757
+ duration: false,
758
+ // does not auto-dismiss
759
+ showProgressBar: false
760
+ });
761
+ }, [addToast]);
762
+
763
+ // Helper: toast.promise
764
+ toast.promise = React.useCallback((promise, messages, options = {}) => {
765
+ const id = toast.loading(messages.loading || 'Loading...', options);
766
+ const p = promise instanceof Promise ? promise : Promise.resolve(promise);
767
+ p.then(result => {
768
+ const successMsg = typeof messages.success === 'function' ? messages.success(result) : messages.success || 'Success!';
769
+ updateToast(id, {
770
+ type: 'success',
771
+ message: successMsg,
772
+ duration: options.duration !== undefined ? options.duration : 3000,
773
+ showProgressBar: options.showProgressBar !== undefined ? options.showProgressBar : true,
774
+ ...options.successOptions
775
+ });
776
+ }).catch(err => {
777
+ const errorMsg = typeof messages.error === 'function' ? messages.error(err) : messages.error || 'Something went wrong';
778
+ updateToast(id, {
779
+ type: 'error',
780
+ message: errorMsg,
781
+ duration: options.duration !== undefined ? options.duration : 4000,
782
+ showProgressBar: options.showProgressBar !== undefined ? options.showProgressBar : true,
783
+ ...options.errorOptions
784
+ });
785
+ });
786
+ return p;
787
+ }, [toast, updateToast]);
788
+
789
+ // Programmatic dismiss
790
+ toast.dismiss = dismissToast;
791
+ toast.dismissAll = dismissAll;
792
+ toast.update = updateToast;
793
+ return toast;
794
+ };
795
+
796
+ exports.CloseIcon = CloseIcon;
797
+ exports.ErrorIcon = ErrorIcon;
798
+ exports.InfoIcon = InfoIcon;
799
+ exports.LoadingIcon = LoadingIcon;
800
+ exports.SuccessIcon = SuccessIcon;
801
+ exports.ToastContainer = ToastContainer;
802
+ exports.ToastContext = ToastContext;
803
+ exports.ToastItem = ToastItem;
804
+ exports.ToastProvider = ToastProvider;
805
+ exports.WarningIcon = WarningIcon;
806
+ exports.useToast = useToast;
807
+ //# sourceMappingURL=index.cjs.js.map