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.
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/dist/index.cjs.js +807 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +125 -0
- package/dist/index.esm.js +795 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/toast.css +2 -0
- package/dist/toast.css.map +1 -0
- package/package.json +73 -0
- package/src/ToastContainer.jsx +83 -0
- package/src/ToastContext.jsx +262 -0
- package/src/ToastItem.jsx +343 -0
- package/src/icons.jsx +135 -0
- package/src/index.d.ts +125 -0
- package/src/index.js +13 -0
- package/src/toast.css +586 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
SuccessIcon,
|
|
4
|
+
ErrorIcon,
|
|
5
|
+
WarningIcon,
|
|
6
|
+
InfoIcon,
|
|
7
|
+
LoadingIcon,
|
|
8
|
+
CloseIcon,
|
|
9
|
+
} from './icons';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ToastItem Component
|
|
13
|
+
* Renders an individual notification card with animations, timers, actions, and swipe to accept/cancel.
|
|
14
|
+
*/
|
|
15
|
+
export const ToastItem = ({
|
|
16
|
+
id,
|
|
17
|
+
type = 'default',
|
|
18
|
+
title,
|
|
19
|
+
message,
|
|
20
|
+
duration = 3000,
|
|
21
|
+
showProgressBar = true,
|
|
22
|
+
pauseOnHover = true,
|
|
23
|
+
closeButton = true,
|
|
24
|
+
icon,
|
|
25
|
+
action,
|
|
26
|
+
onClose,
|
|
27
|
+
onAccept,
|
|
28
|
+
onCancel,
|
|
29
|
+
acceptLabel,
|
|
30
|
+
cancelLabel = 'Dismiss',
|
|
31
|
+
swipeable = true,
|
|
32
|
+
swipeThreshold = 70,
|
|
33
|
+
theme = 'light',
|
|
34
|
+
className = '',
|
|
35
|
+
style = {},
|
|
36
|
+
onDismiss,
|
|
37
|
+
}) => {
|
|
38
|
+
const [isExiting, setIsExiting] = useState(false);
|
|
39
|
+
const [exitDirection, setExitDirection] = useState(null); // 'left' | 'right' | null
|
|
40
|
+
const [isPaused, setIsPaused] = useState(false);
|
|
41
|
+
|
|
42
|
+
// Swipe state
|
|
43
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
44
|
+
const [dragOffset, setDragOffset] = useState(0);
|
|
45
|
+
const startXRef = useRef(0);
|
|
46
|
+
const isPointerDownRef = useRef(false);
|
|
47
|
+
|
|
48
|
+
// Ref to track remaining duration when paused
|
|
49
|
+
const remainingTimeRef = useRef(duration);
|
|
50
|
+
const startTimeRef = useRef(null);
|
|
51
|
+
const timerRef = useRef(null);
|
|
52
|
+
|
|
53
|
+
// Check if this toast supports an "accept" action
|
|
54
|
+
const hasAcceptAction = Boolean(onAccept || (action && action.onClick));
|
|
55
|
+
const resolvedAcceptLabel = acceptLabel || (action && action.label) || 'Accept';
|
|
56
|
+
|
|
57
|
+
// Trigger graceful exit animation before removal
|
|
58
|
+
const handleDismiss = useCallback((direction = null) => {
|
|
59
|
+
if (isExiting) return;
|
|
60
|
+
setIsExiting(true);
|
|
61
|
+
if (direction) {
|
|
62
|
+
setExitDirection(direction);
|
|
63
|
+
}
|
|
64
|
+
if (onClose) {
|
|
65
|
+
onClose(id);
|
|
66
|
+
}
|
|
67
|
+
// Match the CSS exit animation duration
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
onDismiss(id);
|
|
70
|
+
}, 280);
|
|
71
|
+
}, [id, isExiting, onClose, onDismiss]);
|
|
72
|
+
|
|
73
|
+
// Trigger accept action
|
|
74
|
+
const handleAccept = useCallback(() => {
|
|
75
|
+
if (isExiting) return;
|
|
76
|
+
if (onAccept) {
|
|
77
|
+
onAccept(id);
|
|
78
|
+
} else if (action && action.onClick) {
|
|
79
|
+
action.onClick(id);
|
|
80
|
+
}
|
|
81
|
+
handleDismiss('right');
|
|
82
|
+
}, [action, handleDismiss, id, isExiting, onAccept]);
|
|
83
|
+
|
|
84
|
+
// Handle auto-dismiss timer
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (duration === false || duration === Infinity || duration <= 0) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const startTimer = (time) => {
|
|
91
|
+
startTimeRef.current = Date.now();
|
|
92
|
+
timerRef.current = setTimeout(() => {
|
|
93
|
+
handleDismiss();
|
|
94
|
+
}, time);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (!isPaused && !isDragging) {
|
|
98
|
+
startTimer(remainingTimeRef.current);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return () => {
|
|
102
|
+
if (timerRef.current) {
|
|
103
|
+
clearTimeout(timerRef.current);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}, [duration, isPaused, isDragging, handleDismiss]);
|
|
107
|
+
|
|
108
|
+
// Pause on mouse enter
|
|
109
|
+
const handleMouseEnter = () => {
|
|
110
|
+
if (!pauseOnHover || duration === false || duration === Infinity || duration <= 0) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (timerRef.current) {
|
|
114
|
+
clearTimeout(timerRef.current);
|
|
115
|
+
const elapsed = Date.now() - startTimeRef.current;
|
|
116
|
+
remainingTimeRef.current = Math.max(0, remainingTimeRef.current - elapsed);
|
|
117
|
+
}
|
|
118
|
+
setIsPaused(true);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Resume on mouse leave
|
|
122
|
+
const handleMouseLeave = () => {
|
|
123
|
+
if (!pauseOnHover || duration === false || duration === Infinity || duration <= 0 || isDragging) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
setIsPaused(false);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// --- Pointer Swipe Handlers ---
|
|
130
|
+
const handlePointerDown = (e) => {
|
|
131
|
+
if (!swipeable || isExiting) return;
|
|
132
|
+
// Don't drag if clicking buttons or interactive elements
|
|
133
|
+
if (e.target.closest('button') || e.target.closest('a')) return;
|
|
134
|
+
if (e.button !== undefined && e.button !== 0) return; // Only primary mouse button
|
|
135
|
+
|
|
136
|
+
isPointerDownRef.current = true;
|
|
137
|
+
startXRef.current = e.clientX;
|
|
138
|
+
setIsDragging(true);
|
|
139
|
+
|
|
140
|
+
// Pause timer while swiping
|
|
141
|
+
if (timerRef.current) {
|
|
142
|
+
clearTimeout(timerRef.current);
|
|
143
|
+
const elapsed = Date.now() - (startTimeRef.current || Date.now());
|
|
144
|
+
remainingTimeRef.current = Math.max(0, remainingTimeRef.current - elapsed);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
// Ignore if setPointerCapture not supported
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handlePointerMove = (e) => {
|
|
155
|
+
if (!isPointerDownRef.current || !isDragging) return;
|
|
156
|
+
const currentX = e.clientX;
|
|
157
|
+
const diff = currentX - startXRef.current;
|
|
158
|
+
setDragOffset(diff);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const handlePointerUp = (e) => {
|
|
162
|
+
if (!isPointerDownRef.current) return;
|
|
163
|
+
isPointerDownRef.current = false;
|
|
164
|
+
setIsDragging(false);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
// Ignore
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Check if swipe distance exceeded threshold
|
|
173
|
+
if (dragOffset >= swipeThreshold) {
|
|
174
|
+
// Swiped Right
|
|
175
|
+
if (hasAcceptAction) {
|
|
176
|
+
handleAccept();
|
|
177
|
+
} else {
|
|
178
|
+
if (onCancel) onCancel(id);
|
|
179
|
+
handleDismiss('right');
|
|
180
|
+
}
|
|
181
|
+
} else if (dragOffset <= -swipeThreshold) {
|
|
182
|
+
// Swiped Left
|
|
183
|
+
if (onCancel) onCancel(id);
|
|
184
|
+
handleDismiss('left');
|
|
185
|
+
} else {
|
|
186
|
+
// Reset back to center
|
|
187
|
+
setDragOffset(0);
|
|
188
|
+
setIsPaused(false);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const handlePointerCancel = () => {
|
|
193
|
+
isPointerDownRef.current = false;
|
|
194
|
+
setIsDragging(false);
|
|
195
|
+
setDragOffset(0);
|
|
196
|
+
setIsPaused(false);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Resolve appropriate icon
|
|
200
|
+
const renderIcon = () => {
|
|
201
|
+
if (icon === false) return null;
|
|
202
|
+
if (icon && React.isValidElement(icon)) {
|
|
203
|
+
return <span className="easy-toast-custom-icon">{icon}</span>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
switch (type) {
|
|
207
|
+
case 'success':
|
|
208
|
+
return <SuccessIcon />;
|
|
209
|
+
case 'error':
|
|
210
|
+
return <ErrorIcon />;
|
|
211
|
+
case 'warning':
|
|
212
|
+
return <WarningIcon />;
|
|
213
|
+
case 'info':
|
|
214
|
+
return <InfoIcon />;
|
|
215
|
+
case 'loading':
|
|
216
|
+
return <LoadingIcon />;
|
|
217
|
+
default:
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const hasIcon = icon !== false && (icon || type !== 'default');
|
|
223
|
+
const hasProgressBar = showProgressBar && duration && duration !== Infinity && duration > 0;
|
|
224
|
+
|
|
225
|
+
// Compute inline drag transform
|
|
226
|
+
const getDragStyle = () => {
|
|
227
|
+
if (isDragging) {
|
|
228
|
+
return {
|
|
229
|
+
transform: `translateX(${dragOffset}px)`,
|
|
230
|
+
transition: 'none',
|
|
231
|
+
opacity: Math.max(0.45, 1 - Math.abs(dragOffset) / 280),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (exitDirection === 'left') {
|
|
235
|
+
return {
|
|
236
|
+
transform: 'translateX(-115%) scale(0.9)',
|
|
237
|
+
opacity: 0,
|
|
238
|
+
transition: 'transform 0.28s cubic-bezier(0.4, 0, 1, 1), opacity 0.28s ease',
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (exitDirection === 'right') {
|
|
242
|
+
return {
|
|
243
|
+
transform: 'translateX(115%) scale(0.9)',
|
|
244
|
+
opacity: 0,
|
|
245
|
+
transition: 'transform 0.28s cubic-bezier(0.4, 0, 1, 1), opacity 0.28s ease',
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
transition: 'transform 0.24s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.24s ease',
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// Visual cues during swipe
|
|
254
|
+
const isSwipingAccept = dragOffset > 25 && hasAcceptAction;
|
|
255
|
+
const isSwipingDismissRight = dragOffset > 25 && !hasAcceptAction;
|
|
256
|
+
const isSwipingCancel = dragOffset < -25;
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<div
|
|
260
|
+
role={type === 'error' ? 'alert' : 'status'}
|
|
261
|
+
aria-live={type === 'error' ? 'assertive' : 'polite'}
|
|
262
|
+
className={`easy-toast-item easy-toast--${type} easy-toast--theme-${theme} ${
|
|
263
|
+
isExiting ? 'easy-toast--exit' : 'easy-toast--enter'
|
|
264
|
+
} ${swipeable ? 'easy-toast--swipeable' : ''} ${
|
|
265
|
+
isDragging ? 'easy-toast--dragging' : ''
|
|
266
|
+
} ${isSwipingAccept ? 'easy-toast--swipe-accept' : ''} ${
|
|
267
|
+
isSwipingCancel || isSwipingDismissRight ? 'easy-toast--swipe-cancel' : ''
|
|
268
|
+
} ${className}`}
|
|
269
|
+
style={{
|
|
270
|
+
...style,
|
|
271
|
+
...getDragStyle(),
|
|
272
|
+
}}
|
|
273
|
+
onMouseEnter={handleMouseEnter}
|
|
274
|
+
onMouseLeave={handleMouseLeave}
|
|
275
|
+
onPointerDown={handlePointerDown}
|
|
276
|
+
onPointerMove={handlePointerMove}
|
|
277
|
+
onPointerUp={handlePointerUp}
|
|
278
|
+
onPointerCancel={handlePointerCancel}
|
|
279
|
+
>
|
|
280
|
+
{/* Swipe Badges/Hints */}
|
|
281
|
+
{isSwipingAccept && (
|
|
282
|
+
<div className="easy-toast-swipe-badge easy-toast-swipe-badge--accept">
|
|
283
|
+
✓ {resolvedAcceptLabel}
|
|
284
|
+
</div>
|
|
285
|
+
)}
|
|
286
|
+
|
|
287
|
+
{(isSwipingCancel || isSwipingDismissRight) && (
|
|
288
|
+
<div className="easy-toast-swipe-badge easy-toast-swipe-badge--cancel">
|
|
289
|
+
✕ {cancelLabel}
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
292
|
+
|
|
293
|
+
<div className="easy-toast-content-wrapper">
|
|
294
|
+
{hasIcon && <div className="easy-toast-icon-container">{renderIcon()}</div>}
|
|
295
|
+
|
|
296
|
+
<div className="easy-toast-text-container">
|
|
297
|
+
{title && <div className="easy-toast-title">{title}</div>}
|
|
298
|
+
<div className="easy-toast-message">{message}</div>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
{action && (
|
|
302
|
+
<div className="easy-toast-action-container">
|
|
303
|
+
<button
|
|
304
|
+
type="button"
|
|
305
|
+
className="easy-toast-action-button"
|
|
306
|
+
onClick={() => {
|
|
307
|
+
action.onClick?.(id);
|
|
308
|
+
if (action.dismissOnClick !== false) {
|
|
309
|
+
handleDismiss('right');
|
|
310
|
+
}
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
{action.label}
|
|
314
|
+
</button>
|
|
315
|
+
</div>
|
|
316
|
+
)}
|
|
317
|
+
|
|
318
|
+
{closeButton && (
|
|
319
|
+
<button
|
|
320
|
+
type="button"
|
|
321
|
+
className="easy-toast-close-button"
|
|
322
|
+
aria-label="Close notification"
|
|
323
|
+
onClick={() => handleDismiss()}
|
|
324
|
+
>
|
|
325
|
+
<CloseIcon />
|
|
326
|
+
</button>
|
|
327
|
+
)}
|
|
328
|
+
</div>
|
|
329
|
+
|
|
330
|
+
{hasProgressBar && (
|
|
331
|
+
<div className="easy-toast-progress-bar-track">
|
|
332
|
+
<div
|
|
333
|
+
className="easy-toast-progress-bar"
|
|
334
|
+
style={{
|
|
335
|
+
animationDuration: `${duration}ms`,
|
|
336
|
+
animationPlayState: isPaused || isDragging ? 'paused' : 'running',
|
|
337
|
+
}}
|
|
338
|
+
/>
|
|
339
|
+
</div>
|
|
340
|
+
)}
|
|
341
|
+
</div>
|
|
342
|
+
);
|
|
343
|
+
};
|
package/src/icons.jsx
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Success Icon SVG
|
|
5
|
+
*/
|
|
6
|
+
export const SuccessIcon = ({ size = 20, className = '', ...props }) => (
|
|
7
|
+
<svg
|
|
8
|
+
width={size}
|
|
9
|
+
height={size}
|
|
10
|
+
viewBox="0 0 24 24"
|
|
11
|
+
fill="none"
|
|
12
|
+
stroke="currentColor"
|
|
13
|
+
strokeWidth="2"
|
|
14
|
+
strokeLinecap="round"
|
|
15
|
+
strokeLinejoin="round"
|
|
16
|
+
className={`easy-toast-icon easy-toast-icon--success ${className}`}
|
|
17
|
+
aria-hidden="true"
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
21
|
+
<polyline points="22 4 12 14.01 9 11.01" />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Error Icon SVG
|
|
27
|
+
*/
|
|
28
|
+
export const ErrorIcon = ({ size = 20, className = '', ...props }) => (
|
|
29
|
+
<svg
|
|
30
|
+
width={size}
|
|
31
|
+
height={size}
|
|
32
|
+
viewBox="0 0 24 24"
|
|
33
|
+
fill="none"
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
strokeWidth="2"
|
|
36
|
+
strokeLinecap="round"
|
|
37
|
+
strokeLinejoin="round"
|
|
38
|
+
className={`easy-toast-icon easy-toast-icon--error ${className}`}
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
{...props}
|
|
41
|
+
>
|
|
42
|
+
<circle cx="12" cy="12" r="10" />
|
|
43
|
+
<line x1="15" y1="9" x2="9" y2="15" />
|
|
44
|
+
<line x1="9" y1="9" x2="15" y2="15" />
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Warning Icon SVG
|
|
50
|
+
*/
|
|
51
|
+
export const WarningIcon = ({ size = 20, className = '', ...props }) => (
|
|
52
|
+
<svg
|
|
53
|
+
width={size}
|
|
54
|
+
height={size}
|
|
55
|
+
viewBox="0 0 24 24"
|
|
56
|
+
fill="none"
|
|
57
|
+
stroke="currentColor"
|
|
58
|
+
strokeWidth="2"
|
|
59
|
+
strokeLinecap="round"
|
|
60
|
+
strokeLinejoin="round"
|
|
61
|
+
className={`easy-toast-icon easy-toast-icon--warning ${className}`}
|
|
62
|
+
aria-hidden="true"
|
|
63
|
+
{...props}
|
|
64
|
+
>
|
|
65
|
+
<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" />
|
|
66
|
+
<line x1="12" y1="9" x2="12" y2="13" />
|
|
67
|
+
<line x1="12" y1="17" x2="12.01" y2="17" />
|
|
68
|
+
</svg>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Info Icon SVG
|
|
73
|
+
*/
|
|
74
|
+
export const InfoIcon = ({ size = 20, className = '', ...props }) => (
|
|
75
|
+
<svg
|
|
76
|
+
width={size}
|
|
77
|
+
height={size}
|
|
78
|
+
viewBox="0 0 24 24"
|
|
79
|
+
fill="none"
|
|
80
|
+
stroke="currentColor"
|
|
81
|
+
strokeWidth="2"
|
|
82
|
+
strokeLinecap="round"
|
|
83
|
+
strokeLinejoin="round"
|
|
84
|
+
className={`easy-toast-icon easy-toast-icon--info ${className}`}
|
|
85
|
+
aria-hidden="true"
|
|
86
|
+
{...props}
|
|
87
|
+
>
|
|
88
|
+
<circle cx="12" cy="12" r="10" />
|
|
89
|
+
<line x1="12" y1="16" x2="12" y2="12" />
|
|
90
|
+
<line x1="12" y1="8" x2="12.01" y2="8" />
|
|
91
|
+
</svg>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Loading Spinner SVG
|
|
96
|
+
*/
|
|
97
|
+
export const LoadingIcon = ({ size = 20, className = '', ...props }) => (
|
|
98
|
+
<svg
|
|
99
|
+
width={size}
|
|
100
|
+
height={size}
|
|
101
|
+
viewBox="0 0 24 24"
|
|
102
|
+
fill="none"
|
|
103
|
+
stroke="currentColor"
|
|
104
|
+
strokeWidth="2.5"
|
|
105
|
+
strokeLinecap="round"
|
|
106
|
+
strokeLinejoin="round"
|
|
107
|
+
className={`easy-toast-icon easy-toast-icon--loading ${className}`}
|
|
108
|
+
aria-hidden="true"
|
|
109
|
+
{...props}
|
|
110
|
+
>
|
|
111
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
112
|
+
</svg>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Close (Dismiss) Button Icon SVG
|
|
117
|
+
*/
|
|
118
|
+
export const CloseIcon = ({ size = 16, className = '', ...props }) => (
|
|
119
|
+
<svg
|
|
120
|
+
width={size}
|
|
121
|
+
height={size}
|
|
122
|
+
viewBox="0 0 24 24"
|
|
123
|
+
fill="none"
|
|
124
|
+
stroke="currentColor"
|
|
125
|
+
strokeWidth="2"
|
|
126
|
+
strokeLinecap="round"
|
|
127
|
+
strokeLinejoin="round"
|
|
128
|
+
className={`easy-toast-close-icon ${className}`}
|
|
129
|
+
aria-hidden="true"
|
|
130
|
+
{...props}
|
|
131
|
+
>
|
|
132
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
133
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
134
|
+
</svg>
|
|
135
|
+
);
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info' | 'loading';
|
|
4
|
+
|
|
5
|
+
export type ToastPosition =
|
|
6
|
+
| 'top-left'
|
|
7
|
+
| 'top-center'
|
|
8
|
+
| 'top-right'
|
|
9
|
+
| 'bottom-left'
|
|
10
|
+
| 'bottom-center'
|
|
11
|
+
| 'bottom-right';
|
|
12
|
+
|
|
13
|
+
export type ToastTheme = 'light' | 'dark' | 'colored';
|
|
14
|
+
|
|
15
|
+
export interface ToastAction {
|
|
16
|
+
label: string;
|
|
17
|
+
onClick: (id: string) => void;
|
|
18
|
+
dismissOnClick?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ToastOptions {
|
|
22
|
+
id?: string;
|
|
23
|
+
type?: ToastType;
|
|
24
|
+
title?: React.ReactNode;
|
|
25
|
+
duration?: number | false;
|
|
26
|
+
showProgressBar?: boolean;
|
|
27
|
+
pauseOnHover?: boolean;
|
|
28
|
+
closeButton?: boolean;
|
|
29
|
+
icon?: React.ReactNode | false;
|
|
30
|
+
action?: ToastAction;
|
|
31
|
+
position?: ToastPosition;
|
|
32
|
+
theme?: ToastTheme;
|
|
33
|
+
className?: string;
|
|
34
|
+
style?: React.CSSProperties;
|
|
35
|
+
onClose?: (id: string) => void;
|
|
36
|
+
swipeable?: boolean;
|
|
37
|
+
swipeThreshold?: number;
|
|
38
|
+
onAccept?: (id: string) => void;
|
|
39
|
+
onCancel?: (id: string) => void;
|
|
40
|
+
acceptLabel?: string;
|
|
41
|
+
cancelLabel?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ToastItemProps extends ToastOptions {
|
|
45
|
+
id: string;
|
|
46
|
+
message: React.ReactNode;
|
|
47
|
+
onDismiss: (id: string) => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface ToastContainerProps {
|
|
51
|
+
toasts?: ToastItemProps[];
|
|
52
|
+
onDismiss?: (id: string) => void;
|
|
53
|
+
position?: ToastPosition;
|
|
54
|
+
theme?: ToastTheme;
|
|
55
|
+
newestOnTop?: boolean;
|
|
56
|
+
pauseOnHover?: boolean;
|
|
57
|
+
showProgressBar?: boolean;
|
|
58
|
+
swipeable?: boolean;
|
|
59
|
+
swipeThreshold?: number;
|
|
60
|
+
limit?: number;
|
|
61
|
+
className?: string;
|
|
62
|
+
style?: React.CSSProperties;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ToastProviderProps {
|
|
66
|
+
children: React.ReactNode;
|
|
67
|
+
position?: ToastPosition;
|
|
68
|
+
autoClose?: number | false;
|
|
69
|
+
pauseOnHover?: boolean;
|
|
70
|
+
showProgressBar?: boolean;
|
|
71
|
+
swipeable?: boolean;
|
|
72
|
+
swipeThreshold?: number;
|
|
73
|
+
theme?: ToastTheme;
|
|
74
|
+
limit?: number;
|
|
75
|
+
newestOnTop?: boolean;
|
|
76
|
+
containerClassName?: string;
|
|
77
|
+
containerStyle?: React.CSSProperties;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface ToastPromiseOptions {
|
|
81
|
+
loading?: React.ReactNode;
|
|
82
|
+
success?: React.ReactNode | ((data: any) => React.ReactNode);
|
|
83
|
+
error?: React.ReactNode | ((err: any) => React.ReactNode);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ToastPromiseExtraOptions {
|
|
87
|
+
duration?: number;
|
|
88
|
+
showProgressBar?: boolean;
|
|
89
|
+
successOptions?: Partial<ToastOptions>;
|
|
90
|
+
errorOptions?: Partial<ToastOptions>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ToastMethods {
|
|
94
|
+
(message: React.ReactNode, options?: ToastOptions): string;
|
|
95
|
+
success: (message: React.ReactNode, options?: ToastOptions) => string;
|
|
96
|
+
error: (message: React.ReactNode, options?: ToastOptions) => string;
|
|
97
|
+
warning: (message: React.ReactNode, options?: ToastOptions) => string;
|
|
98
|
+
info: (message: React.ReactNode, options?: ToastOptions) => string;
|
|
99
|
+
loading: (message: React.ReactNode, options?: ToastOptions) => string;
|
|
100
|
+
promise: <T>(
|
|
101
|
+
promise: Promise<T>,
|
|
102
|
+
messages: ToastPromiseOptions,
|
|
103
|
+
options?: ToastPromiseExtraOptions
|
|
104
|
+
) => Promise<T>;
|
|
105
|
+
dismiss: (id: string) => void;
|
|
106
|
+
dismissAll: () => void;
|
|
107
|
+
update: (id: string, options: Partial<ToastOptions> & { message?: React.ReactNode }) => void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export declare const ToastContext: React.Context<any>;
|
|
111
|
+
|
|
112
|
+
export declare const ToastProvider: React.FC<ToastProviderProps>;
|
|
113
|
+
|
|
114
|
+
export declare const ToastContainer: React.FC<ToastContainerProps>;
|
|
115
|
+
|
|
116
|
+
export declare const ToastItem: React.FC<ToastItemProps>;
|
|
117
|
+
|
|
118
|
+
export declare const useToast: () => ToastMethods;
|
|
119
|
+
|
|
120
|
+
export declare const SuccessIcon: React.FC<React.SVGProps<SVGSVGElement> & { size?: number }>;
|
|
121
|
+
export declare const ErrorIcon: React.FC<React.SVGProps<SVGSVGElement> & { size?: number }>;
|
|
122
|
+
export declare const WarningIcon: React.FC<React.SVGProps<SVGSVGElement> & { size?: number }>;
|
|
123
|
+
export declare const InfoIcon: React.FC<React.SVGProps<SVGSVGElement> & { size?: number }>;
|
|
124
|
+
export declare const LoadingIcon: React.FC<React.SVGProps<SVGSVGElement> & { size?: number }>;
|
|
125
|
+
export declare const CloseIcon: React.FC<React.SVGProps<SVGSVGElement> & { size?: number }>;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import './toast.css';
|
|
2
|
+
|
|
3
|
+
export { ToastProvider, useToast, ToastContext } from './ToastContext';
|
|
4
|
+
export { ToastContainer } from './ToastContainer';
|
|
5
|
+
export { ToastItem } from './ToastItem';
|
|
6
|
+
export {
|
|
7
|
+
SuccessIcon,
|
|
8
|
+
ErrorIcon,
|
|
9
|
+
WarningIcon,
|
|
10
|
+
InfoIcon,
|
|
11
|
+
LoadingIcon,
|
|
12
|
+
CloseIcon,
|
|
13
|
+
} from './icons';
|