toastify-pro 1.5.0 → 1.7.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/README.md +143 -845
- package/assets/screenshots/desktop-demo.png +0 -0
- package/dist/toastify-pro.esm.js +1294 -59
- package/dist/toastify-pro.esm.js.map +1 -1
- package/dist/toastify-pro.umd.js +1294 -59
- package/dist/toastify-pro.umd.js.map +1 -1
- package/dist/toastify-pro.umd.min.js +10 -3
- package/dist/toastify-pro.umd.min.js.map +1 -1
- package/package.json +5 -2
- package/src/toastify-pro.js +1294 -59
- package/assets/site/site.webmanifest +0 -217
package/dist/toastify-pro.umd.js
CHANGED
|
@@ -19,11 +19,18 @@
|
|
|
19
19
|
* - Responsive design for mobile devices
|
|
20
20
|
* - Framework agnostic (works with React, Vue, Angular, etc.)
|
|
21
21
|
* - Confirmation dialogs with customizable buttons and callbacks
|
|
22
|
+
* - Input prompts with validation and async support
|
|
22
23
|
* - Confirmation overlay with blur effect for focus
|
|
23
24
|
* - Center position support for enhanced focus
|
|
24
25
|
* - Independent positioning for confirmations
|
|
26
|
+
* - Action buttons in toasts with customizable callbacks
|
|
27
|
+
* - Pause on hover functionality
|
|
28
|
+
* - Queue management (maxToasts, newestOnTop)
|
|
29
|
+
* - Full accessibility support (ARIA, keyboard navigation, reduced motion)
|
|
30
|
+
* - Focus management for confirmation and input dialogs
|
|
31
|
+
* - Improved dismiss handling (no hover interference)
|
|
25
32
|
*
|
|
26
|
-
* @version 1.
|
|
33
|
+
* @version 1.7.0
|
|
27
34
|
* @author ToastifyPro Team
|
|
28
35
|
* @license MIT
|
|
29
36
|
*/
|
|
@@ -41,6 +48,10 @@
|
|
|
41
48
|
* @param {number} options.maxLength - Maximum message length
|
|
42
49
|
* @param {string} options.primaryColor - Primary color for custom() method
|
|
43
50
|
* @param {string} options.secondaryColor - Secondary color for gradient in custom() method
|
|
51
|
+
* @param {boolean} options.pauseOnHover - Pause timeout when hovering over toast (default: true)
|
|
52
|
+
* @param {number} options.maxToasts - Maximum number of visible toasts (0 for unlimited)
|
|
53
|
+
* @param {boolean} options.newestOnTop - Show newest toasts on top (default: true)
|
|
54
|
+
* @param {boolean} options.ariaLive - ARIA live region setting: 'polite' or 'assertive' (default: 'polite')
|
|
44
55
|
*/
|
|
45
56
|
constructor(options = {}) {
|
|
46
57
|
// Validate options parameter
|
|
@@ -57,7 +68,14 @@
|
|
|
57
68
|
maxLength: options.maxLength || 100,
|
|
58
69
|
primaryColor: options.primaryColor || null, // Custom primary color for custom() method
|
|
59
70
|
secondaryColor: options.secondaryColor || null, // Custom secondary color for gradient
|
|
71
|
+
pauseOnHover: options.pauseOnHover !== false, // default true - pause timeout on hover
|
|
72
|
+
maxToasts: options.maxToasts || 0, // 0 = unlimited
|
|
73
|
+
newestOnTop: options.newestOnTop !== false, // default true
|
|
74
|
+
ariaLive: options.ariaLive || 'polite', // 'polite' or 'assertive'
|
|
60
75
|
};
|
|
76
|
+
|
|
77
|
+
// Track active toasts for queue management
|
|
78
|
+
this.activeToasts = [];
|
|
61
79
|
|
|
62
80
|
// Validate position
|
|
63
81
|
const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center', 'center'];
|
|
@@ -90,6 +108,46 @@
|
|
|
90
108
|
|
|
91
109
|
// Inject styles once
|
|
92
110
|
this.injectStyles();
|
|
111
|
+
|
|
112
|
+
// Setup global keyboard event listener for accessibility
|
|
113
|
+
this.setupKeyboardNavigation();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Sets up keyboard navigation for accessibility
|
|
118
|
+
* - Escape key dismisses the most recent toast or confirmation
|
|
119
|
+
* - Tab key cycles through focusable elements in confirmations
|
|
120
|
+
*/
|
|
121
|
+
setupKeyboardNavigation() {
|
|
122
|
+
// Only setup once globally
|
|
123
|
+
if (window._toastifyProKeyboardSetup) return;
|
|
124
|
+
window._toastifyProKeyboardSetup = true;
|
|
125
|
+
|
|
126
|
+
document.addEventListener('keydown', (e) => {
|
|
127
|
+
// Escape key - dismiss toast or confirmation
|
|
128
|
+
if (e.key === 'Escape') {
|
|
129
|
+
// First check for active confirmation
|
|
130
|
+
if (globalActiveConfirmation && globalActiveConfirmation.element) {
|
|
131
|
+
const loadingBtn = globalActiveConfirmation.element.querySelector('.toast-btn-confirm.loading');
|
|
132
|
+
if (!loadingBtn) {
|
|
133
|
+
globalActiveConfirmation.close();
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Otherwise dismiss the most recent toast
|
|
139
|
+
const containers = document.querySelectorAll('.toastify-pro-container');
|
|
140
|
+
containers.forEach(container => {
|
|
141
|
+
const toasts = container.querySelectorAll('.toastify-pro:not(.confirmation)');
|
|
142
|
+
if (toasts.length > 0) {
|
|
143
|
+
const lastToast = toasts[toasts.length - 1];
|
|
144
|
+
if (lastToast && lastToast._toastInstance) {
|
|
145
|
+
lastToast._toastInstance.removeToast(lastToast);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
93
151
|
}
|
|
94
152
|
|
|
95
153
|
/**
|
|
@@ -213,19 +271,19 @@
|
|
|
213
271
|
@keyframes airdropPop {
|
|
214
272
|
0% {
|
|
215
273
|
opacity: 0;
|
|
216
|
-
transform: scale(0.
|
|
274
|
+
transform: scale(0.5) rotateY(-15deg) translateY(20px);
|
|
217
275
|
}
|
|
218
|
-
|
|
219
|
-
opacity: 0.
|
|
220
|
-
transform: scale(1.
|
|
276
|
+
50% {
|
|
277
|
+
opacity: 0.95;
|
|
278
|
+
transform: scale(1.03) rotateY(5deg) translateY(-3px);
|
|
221
279
|
}
|
|
222
|
-
|
|
280
|
+
75% {
|
|
223
281
|
opacity: 1;
|
|
224
|
-
transform: scale(0.
|
|
282
|
+
transform: scale(0.99) rotateY(-1deg) translateY(1px);
|
|
225
283
|
}
|
|
226
284
|
100% {
|
|
227
285
|
opacity: 1;
|
|
228
|
-
transform: scale(1) rotateY(0deg);
|
|
286
|
+
transform: scale(1) rotateY(0deg) translateY(0);
|
|
229
287
|
}
|
|
230
288
|
}
|
|
231
289
|
|
|
@@ -234,13 +292,13 @@
|
|
|
234
292
|
opacity: 1;
|
|
235
293
|
transform: scale(1) translateY(0);
|
|
236
294
|
}
|
|
237
|
-
|
|
295
|
+
12% {
|
|
238
296
|
opacity: 1;
|
|
239
|
-
transform: scale(1.
|
|
297
|
+
transform: scale(1.015) translateY(-6px);
|
|
240
298
|
}
|
|
241
299
|
100% {
|
|
242
300
|
opacity: 0;
|
|
243
|
-
transform: scale(0.
|
|
301
|
+
transform: scale(0.85) translateY(150px);
|
|
244
302
|
}
|
|
245
303
|
}
|
|
246
304
|
|
|
@@ -249,13 +307,13 @@
|
|
|
249
307
|
opacity: 1;
|
|
250
308
|
transform: scale(1) translateY(0);
|
|
251
309
|
}
|
|
252
|
-
|
|
310
|
+
12% {
|
|
253
311
|
opacity: 1;
|
|
254
|
-
transform: scale(1.
|
|
312
|
+
transform: scale(1.015) translateY(6px);
|
|
255
313
|
}
|
|
256
314
|
100% {
|
|
257
315
|
opacity: 0;
|
|
258
|
-
transform: scale(0.
|
|
316
|
+
transform: scale(0.85) translateY(-150px);
|
|
259
317
|
}
|
|
260
318
|
}
|
|
261
319
|
|
|
@@ -264,13 +322,13 @@
|
|
|
264
322
|
opacity: 1;
|
|
265
323
|
transform: scale(1) translateX(0);
|
|
266
324
|
}
|
|
267
|
-
|
|
325
|
+
12% {
|
|
268
326
|
opacity: 1;
|
|
269
|
-
transform: scale(1.
|
|
327
|
+
transform: scale(1.015) translateX(6px);
|
|
270
328
|
}
|
|
271
329
|
100% {
|
|
272
330
|
opacity: 0;
|
|
273
|
-
transform: scale(0.
|
|
331
|
+
transform: scale(0.85) translateX(-250px);
|
|
274
332
|
}
|
|
275
333
|
}
|
|
276
334
|
|
|
@@ -279,13 +337,13 @@
|
|
|
279
337
|
opacity: 1;
|
|
280
338
|
transform: scale(1) translateX(0);
|
|
281
339
|
}
|
|
282
|
-
|
|
340
|
+
12% {
|
|
283
341
|
opacity: 1;
|
|
284
|
-
transform: scale(1.
|
|
342
|
+
transform: scale(1.015) translateX(-6px);
|
|
285
343
|
}
|
|
286
344
|
100% {
|
|
287
345
|
opacity: 0;
|
|
288
|
-
transform: scale(0.
|
|
346
|
+
transform: scale(0.85) translateX(250px);
|
|
289
347
|
}
|
|
290
348
|
}
|
|
291
349
|
|
|
@@ -294,13 +352,13 @@
|
|
|
294
352
|
opacity: 1;
|
|
295
353
|
transform: scale(1) translateY(0);
|
|
296
354
|
}
|
|
297
|
-
|
|
355
|
+
12% {
|
|
298
356
|
opacity: 1;
|
|
299
|
-
transform: scale(1.
|
|
357
|
+
transform: scale(1.015) translateY(-4px);
|
|
300
358
|
}
|
|
301
359
|
100% {
|
|
302
360
|
opacity: 0;
|
|
303
|
-
transform: scale(0.
|
|
361
|
+
transform: scale(0.7) translateY(120px);
|
|
304
362
|
}
|
|
305
363
|
}
|
|
306
364
|
|
|
@@ -337,7 +395,7 @@
|
|
|
337
395
|
.toastify-pro.show {
|
|
338
396
|
opacity: 1;
|
|
339
397
|
transform: scale(1);
|
|
340
|
-
animation: airdropPop 0.
|
|
398
|
+
animation: airdropPop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
341
399
|
}
|
|
342
400
|
|
|
343
401
|
.toastify-pro.success {
|
|
@@ -416,9 +474,9 @@
|
|
|
416
474
|
}
|
|
417
475
|
|
|
418
476
|
@keyframes iconBounce {
|
|
419
|
-
0% { transform: scale(0.
|
|
420
|
-
|
|
421
|
-
70% { transform: scale(0.
|
|
477
|
+
0% { transform: scale(0.3) rotate(-10deg); opacity: 0; }
|
|
478
|
+
50% { transform: scale(1.1) rotate(5deg); opacity: 1; }
|
|
479
|
+
70% { transform: scale(0.97) rotate(-2deg); }
|
|
422
480
|
100% { transform: scale(1) rotate(0deg); }
|
|
423
481
|
}
|
|
424
482
|
|
|
@@ -427,12 +485,12 @@
|
|
|
427
485
|
transform: scale(1) rotate(0deg);
|
|
428
486
|
opacity: 1;
|
|
429
487
|
}
|
|
430
|
-
|
|
431
|
-
transform: scale(1.
|
|
432
|
-
opacity: 0.
|
|
488
|
+
25% {
|
|
489
|
+
transform: scale(1.08) rotate(-8deg);
|
|
490
|
+
opacity: 0.85;
|
|
433
491
|
}
|
|
434
492
|
100% {
|
|
435
|
-
transform: scale(0.
|
|
493
|
+
transform: scale(0.4) rotate(120deg);
|
|
436
494
|
opacity: 0;
|
|
437
495
|
}
|
|
438
496
|
}
|
|
@@ -441,6 +499,11 @@
|
|
|
441
499
|
0% { transform: rotate(0deg); }
|
|
442
500
|
100% { transform: rotate(360deg); }
|
|
443
501
|
}
|
|
502
|
+
|
|
503
|
+
@keyframes spinFast {
|
|
504
|
+
0% { transform: rotate(0deg); }
|
|
505
|
+
100% { transform: rotate(360deg); }
|
|
506
|
+
}
|
|
444
507
|
|
|
445
508
|
.toastify-pro .toast-icon svg {
|
|
446
509
|
width: 18px;
|
|
@@ -484,6 +547,7 @@
|
|
|
484
547
|
transition: all 0.2s ease;
|
|
485
548
|
flex-shrink: 0;
|
|
486
549
|
width: 32px;
|
|
550
|
+
border: none;
|
|
487
551
|
height: 32px;
|
|
488
552
|
display: flex;
|
|
489
553
|
align-items: center;
|
|
@@ -715,6 +779,9 @@
|
|
|
715
779
|
}
|
|
716
780
|
|
|
717
781
|
.toast-btn-confirm {
|
|
782
|
+
display: flex;
|
|
783
|
+
align-items: center;
|
|
784
|
+
justify-content: center;
|
|
718
785
|
color: white;
|
|
719
786
|
font-weight: 700;
|
|
720
787
|
border: 2px solid rgba(255, 255, 255, 0.4);
|
|
@@ -753,17 +820,20 @@
|
|
|
753
820
|
|
|
754
821
|
.toast-btn-confirm .btn-spinner {
|
|
755
822
|
display: none;
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
823
|
+
align-items: center;
|
|
824
|
+
justify-content: center;
|
|
825
|
+
margin-left: 8px;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.toast-btn-confirm .btn-spinner svg {
|
|
829
|
+
width: 25px;
|
|
830
|
+
height: 25px;
|
|
831
|
+
animation: spinFast 0.5s linear infinite;
|
|
832
|
+
color: currentColor;
|
|
763
833
|
}
|
|
764
834
|
|
|
765
835
|
.toast-btn-confirm.loading .btn-spinner {
|
|
766
|
-
display: inline-
|
|
836
|
+
display: inline-flex;
|
|
767
837
|
}
|
|
768
838
|
|
|
769
839
|
.toast-btn-confirm.loading .btn-text {
|
|
@@ -853,6 +923,356 @@
|
|
|
853
923
|
.toastify-pro-overlay.show {
|
|
854
924
|
opacity: 1;
|
|
855
925
|
}
|
|
926
|
+
|
|
927
|
+
/* Action Button Styles */
|
|
928
|
+
.toastify-pro .toast-action {
|
|
929
|
+
display: inline-flex;
|
|
930
|
+
align-items: center;
|
|
931
|
+
gap: 6px;
|
|
932
|
+
padding: 6px 12px;
|
|
933
|
+
margin-top: 8px;
|
|
934
|
+
border: none;
|
|
935
|
+
border-radius: 8px;
|
|
936
|
+
font-weight: 600;
|
|
937
|
+
font-size: 13px;
|
|
938
|
+
cursor: pointer;
|
|
939
|
+
transition: all 0.2s ease;
|
|
940
|
+
background: rgba(255, 255, 255, 0.2);
|
|
941
|
+
color: inherit;
|
|
942
|
+
backdrop-filter: blur(10px);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
.toastify-pro .toast-action:hover {
|
|
946
|
+
background: rgba(255, 255, 255, 0.3);
|
|
947
|
+
transform: translateY(-1px);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
.toastify-pro .toast-action:active {
|
|
951
|
+
transform: translateY(0);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.toastify-pro.light .toast-action {
|
|
955
|
+
background: rgba(15, 23, 42, 0.1);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
.toastify-pro.light .toast-action:hover {
|
|
959
|
+
background: rgba(15, 23, 42, 0.15);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/* Paused state - pause progress bar */
|
|
963
|
+
.toastify-pro.paused::after {
|
|
964
|
+
animation-play-state: paused;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/* Progress restart - used after hover to restart progress bar only */
|
|
968
|
+
.toastify-pro.progress-restart::after {
|
|
969
|
+
animation: none;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/* Focus styles for accessibility */
|
|
973
|
+
.toastify-pro .close-btn:focus,
|
|
974
|
+
.toastify-pro .toast-action:focus,
|
|
975
|
+
.toast-btn:focus {
|
|
976
|
+
outline: 1px solid rgba(255, 255, 255, 0.8);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.toastify-pro.light .close-btn:focus,
|
|
980
|
+
.toastify-pro.light .toast-action:focus {
|
|
981
|
+
outline-color: 1px solid rgba(15, 23, 42, 0.5);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/* Screen reader only class */
|
|
985
|
+
.sr-only {
|
|
986
|
+
position: absolute;
|
|
987
|
+
width: 1px;
|
|
988
|
+
height: 1px;
|
|
989
|
+
padding: 0;
|
|
990
|
+
margin: -1px;
|
|
991
|
+
overflow: hidden;
|
|
992
|
+
clip: rect(0, 0, 0, 0);
|
|
993
|
+
white-space: nowrap;
|
|
994
|
+
border: 0;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/* ===== INPUT TOAST STYLES ===== */
|
|
998
|
+
.toastify-pro.input-toast {
|
|
999
|
+
min-width: 340px;
|
|
1000
|
+
max-width: 450px;
|
|
1001
|
+
padding: 24px 24px 20px;
|
|
1002
|
+
flex-direction: column;
|
|
1003
|
+
align-items: stretch;
|
|
1004
|
+
gap: 16px;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.toastify-pro.input-toast .toast-input-wrapper {
|
|
1008
|
+
display: flex;
|
|
1009
|
+
flex-direction: column;
|
|
1010
|
+
gap: 16px;
|
|
1011
|
+
width: 100%;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.toastify-pro.input-toast .toast-input-header {
|
|
1015
|
+
display: flex;
|
|
1016
|
+
align-items: flex-start;
|
|
1017
|
+
gap: 12px;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
.toastify-pro.input-toast .toast-input {
|
|
1021
|
+
width: 100%;
|
|
1022
|
+
padding: 12px 16px;
|
|
1023
|
+
border: 1.5px solid rgba(255, 255, 255, 0.2);
|
|
1024
|
+
border-radius: 12px;
|
|
1025
|
+
background: rgba(255, 255, 255, 0.08);
|
|
1026
|
+
backdrop-filter: blur(12px);
|
|
1027
|
+
color: inherit;
|
|
1028
|
+
font-family: inherit;
|
|
1029
|
+
font-size: 14px;
|
|
1030
|
+
font-weight: 450;
|
|
1031
|
+
outline: none;
|
|
1032
|
+
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
1033
|
+
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.toastify-pro.input-toast .toast-input::placeholder {
|
|
1037
|
+
color: rgba(255, 255, 255, 0.5);
|
|
1038
|
+
font-weight: 400;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
.toastify-pro.input-toast .toast-input:focus {
|
|
1042
|
+
border-color: rgba(255, 255, 255, 0.45);
|
|
1043
|
+
background: rgba(255, 255, 255, 0.12);
|
|
1044
|
+
box-shadow:
|
|
1045
|
+
inset 0 1px 2px rgba(0, 0, 0, 0.1),
|
|
1046
|
+
0 0 0 3px rgba(255, 255, 255, 0.08);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.toastify-pro.input-toast .toast-input:hover:not(:focus) {
|
|
1050
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
1051
|
+
background: rgba(255, 255, 255, 0.1);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
.toastify-pro.input-toast.light .toast-input {
|
|
1055
|
+
border-color: rgba(15, 23, 42, 0.15);
|
|
1056
|
+
background: rgba(15, 23, 42, 0.04);
|
|
1057
|
+
box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.06);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
.toastify-pro.input-toast.light .toast-input::placeholder {
|
|
1061
|
+
color: rgba(15, 23, 42, 0.45);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
.toastify-pro.input-toast.light .toast-input:focus {
|
|
1065
|
+
border-color: rgba(15, 23, 42, 0.35);
|
|
1066
|
+
background: rgba(15, 23, 42, 0.06);
|
|
1067
|
+
box-shadow:
|
|
1068
|
+
inset 0 1px 2px rgba(15, 23, 42, 0.06),
|
|
1069
|
+
0 0 0 3px rgba(15, 23, 42, 0.06);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.toastify-pro.input-toast.light .toast-input:hover:not(:focus) {
|
|
1073
|
+
border-color: rgba(15, 23, 42, 0.25);
|
|
1074
|
+
background: rgba(15, 23, 42, 0.06);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
.toastify-pro.input-toast .toast-input-actions {
|
|
1078
|
+
display: flex;
|
|
1079
|
+
gap: 10px;
|
|
1080
|
+
justify-content: flex-end;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
.toastify-pro.input-toast .input-btn {
|
|
1084
|
+
padding: 10px 20px;
|
|
1085
|
+
border-radius: 10px;
|
|
1086
|
+
font-size: 14px;
|
|
1087
|
+
font-weight: 550;
|
|
1088
|
+
cursor: pointer;
|
|
1089
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
1090
|
+
border: 1.5px solid transparent;
|
|
1091
|
+
position: relative;
|
|
1092
|
+
overflow: hidden;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
.toastify-pro.input-toast .input-btn-cancel {
|
|
1096
|
+
background: rgba(255, 255, 255, 0.1);
|
|
1097
|
+
color: rgba(255, 255, 255, 0.9);
|
|
1098
|
+
border-color: rgba(255, 255, 255, 0.2);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
.toastify-pro.input-toast .input-btn-cancel:hover {
|
|
1102
|
+
background: rgba(255, 255, 255, 0.18);
|
|
1103
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.toastify-pro.input-toast .input-btn-submit {
|
|
1107
|
+
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.85) 100%);
|
|
1108
|
+
color: #1e293b;
|
|
1109
|
+
border-color: rgba(255, 255, 255, 0.4);
|
|
1110
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.toastify-pro.input-toast .input-btn-submit:hover {
|
|
1114
|
+
background: linear-gradient(135deg, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.95) 100%);
|
|
1115
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
|
|
1116
|
+
transform: translateY(-1px);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
.toastify-pro.input-toast .input-btn-submit:active {
|
|
1120
|
+
transform: translateY(0);
|
|
1121
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
.toastify-pro.input-toast.light .input-btn-cancel {
|
|
1125
|
+
background: rgba(15, 23, 42, 0.06);
|
|
1126
|
+
color: rgba(15, 23, 42, 0.85);
|
|
1127
|
+
border-color: rgba(15, 23, 42, 0.15);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
.toastify-pro.input-toast.light .input-btn-cancel:hover {
|
|
1131
|
+
background: rgba(15, 23, 42, 0.1);
|
|
1132
|
+
border-color: rgba(15, 23, 42, 0.25);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
.toastify-pro.input-toast.light .input-btn-submit {
|
|
1136
|
+
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
|
1137
|
+
color: white;
|
|
1138
|
+
border-color: rgba(15, 23, 42, 0.3);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
.toastify-pro.input-toast.light .input-btn-submit:hover {
|
|
1142
|
+
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
|
1143
|
+
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.25);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* Input toast loading state */
|
|
1147
|
+
.toastify-pro.input-toast .input-btn-submit.loading {
|
|
1148
|
+
opacity: 0.7;
|
|
1149
|
+
cursor: not-allowed;
|
|
1150
|
+
pointer-events: none;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
.toastify-pro.input-toast .input-btn-submit .btn-spinner {
|
|
1154
|
+
display: none;
|
|
1155
|
+
align-items: center;
|
|
1156
|
+
justify-content: center;
|
|
1157
|
+
margin-left: 6px;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
.toastify-pro.input-toast .input-btn-submit .btn-spinner svg {
|
|
1161
|
+
width: 16px;
|
|
1162
|
+
height: 16px;
|
|
1163
|
+
animation: spinFast 0.5s linear infinite;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
.toastify-pro.input-toast .input-btn-submit.loading .btn-spinner {
|
|
1167
|
+
display: inline-flex;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
.toastify-pro.input-toast .input-btn-submit.loading .btn-text {
|
|
1171
|
+
opacity: 0.7;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/* Input validation error state */
|
|
1175
|
+
.toastify-pro.input-toast .toast-input.error {
|
|
1176
|
+
border-color: rgba(239, 68, 68, 0.6);
|
|
1177
|
+
background: rgba(239, 68, 68, 0.08);
|
|
1178
|
+
animation: inputShake 0.4s cubic-bezier(0.36, 0.07, 0.19, 0.97);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
.toastify-pro.input-toast .toast-input-error {
|
|
1182
|
+
color: #f87171;
|
|
1183
|
+
font-size: 12px;
|
|
1184
|
+
font-weight: 450;
|
|
1185
|
+
margin-top: -8px;
|
|
1186
|
+
opacity: 0;
|
|
1187
|
+
transform: translateY(-4px);
|
|
1188
|
+
transition: all 0.2s ease;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.toastify-pro.input-toast .toast-input-error.visible {
|
|
1192
|
+
opacity: 1;
|
|
1193
|
+
transform: translateY(0);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
.toastify-pro.input-toast.light .toast-input.error {
|
|
1197
|
+
border-color: rgba(239, 68, 68, 0.5);
|
|
1198
|
+
background: rgba(239, 68, 68, 0.06);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
.toastify-pro.input-toast.light .toast-input-error {
|
|
1202
|
+
color: #dc2626;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
@keyframes inputShake {
|
|
1206
|
+
0%, 100% { transform: translateX(0); }
|
|
1207
|
+
20%, 60% { transform: translateX(-6px); }
|
|
1208
|
+
40%, 80% { transform: translateX(6px); }
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/* Hide progress bar for input toasts */
|
|
1212
|
+
.toastify-pro.input-toast::after {
|
|
1213
|
+
display: none;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
@media (max-width: 640px) {
|
|
1217
|
+
.toastify-pro.input-toast {
|
|
1218
|
+
min-width: 280px;
|
|
1219
|
+
max-width: calc(100vw - 32px);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.toastify-pro.input-toast .toast-input-actions {
|
|
1223
|
+
flex-direction: column;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
.toastify-pro.input-toast .input-btn {
|
|
1227
|
+
width: 100%;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/* Reduced motion support */
|
|
1232
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1233
|
+
.toastify-pro {
|
|
1234
|
+
transition: opacity 0.3s ease;
|
|
1235
|
+
transform: none !important;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
.toastify-pro.show {
|
|
1239
|
+
animation: none !important;
|
|
1240
|
+
opacity: 1;
|
|
1241
|
+
transform: none !important;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.toastify-pro .toast-icon {
|
|
1245
|
+
animation: none !important;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.toastify-pro::before {
|
|
1249
|
+
animation: none !important;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
.toastify-pro::after {
|
|
1253
|
+
animation: progress var(--duration, 5s) linear !important;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.toastify-pro-overlay {
|
|
1257
|
+
transition: opacity 0.2s ease;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
.toast-btn::after {
|
|
1261
|
+
display: none;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
.toast-btn:hover {
|
|
1265
|
+
transform: none;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
.toastify-pro.confirmation .conf-close-btn:hover {
|
|
1269
|
+
transform: scale(1.05);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
.btn-spinner svg {
|
|
1273
|
+
animation: spinFast 0.5s linear infinite !important;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
856
1276
|
`;
|
|
857
1277
|
document.head.appendChild(style);
|
|
858
1278
|
} catch (error) {
|
|
@@ -869,6 +1289,9 @@
|
|
|
869
1289
|
* @param {number} opts.timeout - Override default timeout
|
|
870
1290
|
* @param {boolean} opts.allowClose - Override close button setting
|
|
871
1291
|
* @param {number} opts.maxLength - Override max message length
|
|
1292
|
+
* @param {Object} opts.action - Action button configuration { label, onClick }
|
|
1293
|
+
* @param {boolean} opts.pauseOnHover - Pause timeout on hover
|
|
1294
|
+
* @param {string} opts.ariaLive - ARIA live region type ('polite' or 'assertive')
|
|
872
1295
|
*/
|
|
873
1296
|
show(message, type = "dark", opts = {}) {
|
|
874
1297
|
// Input validation
|
|
@@ -898,10 +1321,30 @@
|
|
|
898
1321
|
const options = { ...this.defaultOptions, ...opts };
|
|
899
1322
|
|
|
900
1323
|
try {
|
|
1324
|
+
// Queue management - remove oldest toasts if limit exceeded
|
|
1325
|
+
if (options.maxToasts > 0 && this.activeToasts.length >= options.maxToasts) {
|
|
1326
|
+
const toastsToRemove = this.activeToasts.length - options.maxToasts + 1;
|
|
1327
|
+
for (let i = 0; i < toastsToRemove; i++) {
|
|
1328
|
+
const oldestToast = this.activeToasts.shift();
|
|
1329
|
+
if (oldestToast && oldestToast.element) {
|
|
1330
|
+
this.removeToast(oldestToast.element);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
901
1335
|
// Create toast element
|
|
902
1336
|
const toast = document.createElement("div");
|
|
903
1337
|
toast.className = `toastify-pro ${type}`;
|
|
904
1338
|
|
|
1339
|
+
// Store reference to this instance for keyboard navigation
|
|
1340
|
+
toast._toastInstance = this;
|
|
1341
|
+
|
|
1342
|
+
// ARIA accessibility attributes
|
|
1343
|
+
const ariaLive = type === 'error' || type === 'warning' ? 'assertive' : (options.ariaLive || 'polite');
|
|
1344
|
+
toast.setAttribute('role', type === 'error' ? 'alert' : 'status');
|
|
1345
|
+
toast.setAttribute('aria-live', ariaLive);
|
|
1346
|
+
toast.setAttribute('aria-atomic', 'true');
|
|
1347
|
+
|
|
905
1348
|
// Set duration for progress bar animation
|
|
906
1349
|
if (options.timeout > 0) {
|
|
907
1350
|
toast.style.setProperty('--duration', `${options.timeout}ms`);
|
|
@@ -910,6 +1353,7 @@
|
|
|
910
1353
|
// Create icon wrapper
|
|
911
1354
|
const iconWrapper = document.createElement("div");
|
|
912
1355
|
iconWrapper.className = "toast-icon";
|
|
1356
|
+
iconWrapper.setAttribute('aria-hidden', 'true');
|
|
913
1357
|
iconWrapper.innerHTML = this.getIconSVG(type);
|
|
914
1358
|
toast.appendChild(iconWrapper);
|
|
915
1359
|
|
|
@@ -931,20 +1375,51 @@
|
|
|
931
1375
|
contentWrapper.appendChild(descriptionElement);
|
|
932
1376
|
}
|
|
933
1377
|
|
|
1378
|
+
// Action button support
|
|
1379
|
+
if (options.action && typeof options.action === 'object') {
|
|
1380
|
+
const actionBtn = document.createElement("button");
|
|
1381
|
+
actionBtn.className = "toast-action";
|
|
1382
|
+
actionBtn.textContent = options.action.label || 'Action';
|
|
1383
|
+
actionBtn.setAttribute('type', 'button');
|
|
1384
|
+
if (typeof options.action.onClick === 'function') {
|
|
1385
|
+
actionBtn.onclick = (e) => {
|
|
1386
|
+
e.stopPropagation();
|
|
1387
|
+
options.action.onClick({ close: () => this.removeToast(toast), event: e });
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
contentWrapper.appendChild(actionBtn);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
934
1393
|
toast.appendChild(contentWrapper);
|
|
935
1394
|
|
|
936
1395
|
// Add close button if enabled
|
|
937
1396
|
if (options.allowClose) {
|
|
938
|
-
const closeBtn = document.createElement("
|
|
1397
|
+
const closeBtn = document.createElement("button");
|
|
939
1398
|
closeBtn.className = "close-btn";
|
|
940
1399
|
closeBtn.innerHTML = "×";
|
|
1400
|
+
closeBtn.setAttribute('type', 'button');
|
|
941
1401
|
closeBtn.setAttribute('aria-label', 'Close notification');
|
|
942
1402
|
closeBtn.onclick = () => this.removeToast(toast);
|
|
943
1403
|
toast.appendChild(closeBtn);
|
|
944
1404
|
}
|
|
945
1405
|
|
|
946
|
-
// Add toast to container
|
|
947
|
-
this.container.
|
|
1406
|
+
// Add toast to container (respect newestOnTop setting)
|
|
1407
|
+
if (options.newestOnTop && this.container.firstChild) {
|
|
1408
|
+
this.container.insertBefore(toast, this.container.firstChild);
|
|
1409
|
+
} else {
|
|
1410
|
+
this.container.appendChild(toast);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// Track toast for queue management
|
|
1414
|
+
const toastData = {
|
|
1415
|
+
element: toast,
|
|
1416
|
+
timeout: null,
|
|
1417
|
+
remainingTime: options.timeout,
|
|
1418
|
+
startTime: null,
|
|
1419
|
+
isPaused: false,
|
|
1420
|
+
isRemoving: false // Flag to prevent hover interference during removal
|
|
1421
|
+
};
|
|
1422
|
+
this.activeToasts.push(toastData);
|
|
948
1423
|
|
|
949
1424
|
// Apple AirDrop-style entrance animation
|
|
950
1425
|
setTimeout(() => {
|
|
@@ -956,16 +1431,94 @@
|
|
|
956
1431
|
}
|
|
957
1432
|
}, 10);
|
|
958
1433
|
|
|
1434
|
+
// Pause on hover functionality
|
|
1435
|
+
if (options.pauseOnHover && options.timeout > 0) {
|
|
1436
|
+
toast.addEventListener('mouseenter', () => {
|
|
1437
|
+
// Don't pause if toast is being removed
|
|
1438
|
+
if (toastData.isRemoving) return;
|
|
1439
|
+
if (toastData.timeout) {
|
|
1440
|
+
clearTimeout(toastData.timeout);
|
|
1441
|
+
toastData.isPaused = true;
|
|
1442
|
+
toastData.remainingTime -= (Date.now() - toastData.startTime);
|
|
1443
|
+
toast.classList.add('paused');
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
toast.addEventListener('mouseleave', () => {
|
|
1448
|
+
// Don't restart timer if toast is being removed
|
|
1449
|
+
if (toastData.isRemoving) return;
|
|
1450
|
+
if (toastData.isPaused && toastData.remainingTime > 0) {
|
|
1451
|
+
toastData.isPaused = false;
|
|
1452
|
+
toastData.startTime = Date.now();
|
|
1453
|
+
toast.classList.remove('paused');
|
|
1454
|
+
// Update CSS variable for remaining progress
|
|
1455
|
+
toast.style.setProperty('--duration', `${toastData.remainingTime}ms`);
|
|
1456
|
+
// Restart the progress bar animation only (not the main toast animation)
|
|
1457
|
+
// Using class toggle to reset pseudo-element animation without affecting main element
|
|
1458
|
+
toast.classList.add('progress-restart');
|
|
1459
|
+
void toast.offsetHeight; // Force reflow
|
|
1460
|
+
toast.classList.remove('progress-restart');
|
|
1461
|
+
|
|
1462
|
+
toastData.timeout = setTimeout(() => this.removeToast(toast), toastData.remainingTime);
|
|
1463
|
+
}
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
|
|
959
1467
|
// Auto-remove after timeout
|
|
960
1468
|
if (options.timeout > 0) {
|
|
961
|
-
|
|
1469
|
+
toastData.startTime = Date.now();
|
|
1470
|
+
toastData.timeout = setTimeout(() => this.removeToast(toast), options.timeout);
|
|
962
1471
|
}
|
|
963
1472
|
|
|
964
|
-
|
|
1473
|
+
// Return toast control object
|
|
1474
|
+
return {
|
|
1475
|
+
element: toast,
|
|
1476
|
+
dismiss: () => this.removeToast(toast),
|
|
1477
|
+
update: (newMessage, newOpts) => this.updateToast(toast, newMessage, newOpts)
|
|
1478
|
+
};
|
|
965
1479
|
} catch (error) {
|
|
966
1480
|
console.error('ToastifyPro: Failed to create toast:', error);
|
|
967
1481
|
}
|
|
968
1482
|
}
|
|
1483
|
+
|
|
1484
|
+
/**
|
|
1485
|
+
* Updates an existing toast's content
|
|
1486
|
+
* @param {HTMLElement} toast - Toast element to update
|
|
1487
|
+
* @param {string} message - New message text
|
|
1488
|
+
* @param {Object} opts - Options to update
|
|
1489
|
+
*/
|
|
1490
|
+
updateToast(toast, message, opts = {}) {
|
|
1491
|
+
if (!toast || !toast.parentNode) return;
|
|
1492
|
+
|
|
1493
|
+
const messageEl = toast.querySelector('.toast-message');
|
|
1494
|
+
const descEl = toast.querySelector('.toast-description');
|
|
1495
|
+
|
|
1496
|
+
if (message && messageEl) {
|
|
1497
|
+
messageEl.textContent = message;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
if (opts.description && descEl) {
|
|
1501
|
+
descEl.textContent = opts.description;
|
|
1502
|
+
} else if (opts.description) {
|
|
1503
|
+
const descriptionElement = document.createElement("div");
|
|
1504
|
+
descriptionElement.className = "toast-description";
|
|
1505
|
+
descriptionElement.textContent = opts.description;
|
|
1506
|
+
toast.querySelector('.toast-content')?.appendChild(descriptionElement);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// Update type/style if provided
|
|
1510
|
+
if (opts.type) {
|
|
1511
|
+
const validTypes = ['success', 'error', 'info', 'warning', 'dark', 'light'];
|
|
1512
|
+
if (validTypes.includes(opts.type)) {
|
|
1513
|
+
validTypes.forEach(t => toast.classList.remove(t));
|
|
1514
|
+
toast.classList.add(opts.type);
|
|
1515
|
+
const iconWrapper = toast.querySelector('.toast-icon');
|
|
1516
|
+
if (iconWrapper) {
|
|
1517
|
+
iconWrapper.innerHTML = this.getIconSVG(opts.type);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
969
1522
|
|
|
970
1523
|
/**
|
|
971
1524
|
* Removes a toast with position-aware car swipe animation
|
|
@@ -978,6 +1531,25 @@
|
|
|
978
1531
|
}
|
|
979
1532
|
|
|
980
1533
|
try {
|
|
1534
|
+
// Remove from active toasts tracking and set removal flag
|
|
1535
|
+
const toastIndex = this.activeToasts.findIndex(t => t.element === toast);
|
|
1536
|
+
if (toastIndex > -1) {
|
|
1537
|
+
const toastData = this.activeToasts[toastIndex];
|
|
1538
|
+
// Prevent hover events from interfering during removal
|
|
1539
|
+
toastData.isRemoving = true;
|
|
1540
|
+
if (toastData.timeout) {
|
|
1541
|
+
clearTimeout(toastData.timeout);
|
|
1542
|
+
}
|
|
1543
|
+
this.activeToasts.splice(toastIndex, 1);
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
// Mark the toast element as removing to prevent double-removal
|
|
1547
|
+
if (toast.dataset.removing === 'true') return;
|
|
1548
|
+
toast.dataset.removing = 'true';
|
|
1549
|
+
|
|
1550
|
+
// Disable pointer events during exit animation to prevent hover issues
|
|
1551
|
+
toast.style.pointerEvents = 'none';
|
|
1552
|
+
|
|
981
1553
|
// Detect position to choose the right swipe direction
|
|
982
1554
|
const container = toast.parentNode;
|
|
983
1555
|
const position = container.className.split(' ')[1]; // get position class
|
|
@@ -998,12 +1570,12 @@
|
|
|
998
1570
|
}
|
|
999
1571
|
|
|
1000
1572
|
// Apply fast car swipe animation with improved easing
|
|
1001
|
-
toast.style.animation = `${swipeAnimation} 0.
|
|
1573
|
+
toast.style.animation = `${swipeAnimation} 0.35s cubic-bezier(0.55, 0.0, 0.85, 0.36) forwards`;
|
|
1002
1574
|
|
|
1003
1575
|
// Add spinning icon animation for extra polish
|
|
1004
1576
|
const icon = toast.querySelector('.toast-icon');
|
|
1005
1577
|
if (icon) {
|
|
1006
|
-
icon.style.animation = 'iconCarExit 0.
|
|
1578
|
+
icon.style.animation = 'iconCarExit 0.35s cubic-bezier(0.55, 0.0, 0.85, 0.36) forwards';
|
|
1007
1579
|
}
|
|
1008
1580
|
|
|
1009
1581
|
// Remove element after animation completes
|
|
@@ -1011,7 +1583,7 @@
|
|
|
1011
1583
|
if (toast.parentNode) {
|
|
1012
1584
|
toast.remove();
|
|
1013
1585
|
}
|
|
1014
|
-
},
|
|
1586
|
+
}, 350);
|
|
1015
1587
|
} catch (error) {
|
|
1016
1588
|
console.error('ToastifyPro: Error removing toast:', error);
|
|
1017
1589
|
// Fallback: remove immediately if animation fails
|
|
@@ -1020,6 +1592,33 @@
|
|
|
1020
1592
|
}
|
|
1021
1593
|
}
|
|
1022
1594
|
}
|
|
1595
|
+
|
|
1596
|
+
/**
|
|
1597
|
+
* Dismisses all active toasts
|
|
1598
|
+
* @param {string} type - Optional: only dismiss toasts of this type
|
|
1599
|
+
*/
|
|
1600
|
+
dismissAll(type = null) {
|
|
1601
|
+
const toastsCopy = [...this.activeToasts];
|
|
1602
|
+
toastsCopy.forEach(toastData => {
|
|
1603
|
+
if (toastData.element) {
|
|
1604
|
+
if (type) {
|
|
1605
|
+
if (toastData.element.classList.contains(type)) {
|
|
1606
|
+
this.removeToast(toastData.element);
|
|
1607
|
+
}
|
|
1608
|
+
} else {
|
|
1609
|
+
this.removeToast(toastData.element);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/**
|
|
1616
|
+
* Gets the count of active toasts
|
|
1617
|
+
* @returns {number} Number of active toasts
|
|
1618
|
+
*/
|
|
1619
|
+
getActiveCount() {
|
|
1620
|
+
return this.activeToasts.length;
|
|
1621
|
+
}
|
|
1023
1622
|
|
|
1024
1623
|
/**
|
|
1025
1624
|
* Shows a success toast notification
|
|
@@ -1194,6 +1793,14 @@
|
|
|
1194
1793
|
const toast = document.createElement("div");
|
|
1195
1794
|
toast.className = `toastify-pro custom${options.customTextLight ? ' light-text' : ''}`;
|
|
1196
1795
|
|
|
1796
|
+
// Store reference to this instance
|
|
1797
|
+
toast._toastInstance = this;
|
|
1798
|
+
|
|
1799
|
+
// ARIA accessibility attributes
|
|
1800
|
+
toast.setAttribute('role', 'status');
|
|
1801
|
+
toast.setAttribute('aria-live', options.ariaLive || 'polite');
|
|
1802
|
+
toast.setAttribute('aria-atomic', 'true');
|
|
1803
|
+
|
|
1197
1804
|
// Apply custom gradient
|
|
1198
1805
|
if (options.customGradient) {
|
|
1199
1806
|
toast.style.background = options.customGradient;
|
|
@@ -1206,6 +1813,7 @@
|
|
|
1206
1813
|
// Create icon wrapper
|
|
1207
1814
|
const iconWrapper = document.createElement("div");
|
|
1208
1815
|
iconWrapper.className = "toast-icon";
|
|
1816
|
+
iconWrapper.setAttribute('aria-hidden', 'true');
|
|
1209
1817
|
iconWrapper.innerHTML = this.getIconSVG('success'); // Use success icon for custom
|
|
1210
1818
|
toast.appendChild(iconWrapper);
|
|
1211
1819
|
|
|
@@ -1228,15 +1836,53 @@
|
|
|
1228
1836
|
toast.appendChild(contentWrapper);
|
|
1229
1837
|
|
|
1230
1838
|
if (options.allowClose) {
|
|
1231
|
-
const closeBtn = document.createElement("
|
|
1839
|
+
const closeBtn = document.createElement("button");
|
|
1232
1840
|
closeBtn.className = "close-btn";
|
|
1233
1841
|
closeBtn.innerHTML = "×";
|
|
1842
|
+
closeBtn.setAttribute('type', 'button');
|
|
1234
1843
|
closeBtn.setAttribute('aria-label', 'Close notification');
|
|
1235
1844
|
closeBtn.onclick = () => this.removeToast(toast);
|
|
1236
1845
|
toast.appendChild(closeBtn);
|
|
1237
1846
|
}
|
|
1238
1847
|
|
|
1239
|
-
|
|
1848
|
+
// Add toast to container (respect newestOnTop setting)
|
|
1849
|
+
if (options.newestOnTop && this.container.firstChild) {
|
|
1850
|
+
this.container.insertBefore(toast, this.container.firstChild);
|
|
1851
|
+
} else {
|
|
1852
|
+
this.container.appendChild(toast);
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
// Track toast for queue management
|
|
1856
|
+
const toastData = {
|
|
1857
|
+
element: toast,
|
|
1858
|
+
timeout: null,
|
|
1859
|
+
remainingTime: options.timeout,
|
|
1860
|
+
startTime: null,
|
|
1861
|
+
isPaused: false
|
|
1862
|
+
};
|
|
1863
|
+
this.activeToasts.push(toastData);
|
|
1864
|
+
|
|
1865
|
+
// Pause on hover functionality
|
|
1866
|
+
if (options.pauseOnHover && options.timeout > 0) {
|
|
1867
|
+
toast.addEventListener('mouseenter', () => {
|
|
1868
|
+
if (toastData.timeout) {
|
|
1869
|
+
clearTimeout(toastData.timeout);
|
|
1870
|
+
toastData.isPaused = true;
|
|
1871
|
+
toastData.remainingTime -= (Date.now() - toastData.startTime);
|
|
1872
|
+
toast.classList.add('paused');
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
|
|
1876
|
+
toast.addEventListener('mouseleave', () => {
|
|
1877
|
+
if (toastData.isPaused && toastData.remainingTime > 0) {
|
|
1878
|
+
toastData.isPaused = false;
|
|
1879
|
+
toastData.startTime = Date.now();
|
|
1880
|
+
toast.classList.remove('paused');
|
|
1881
|
+
toast.style.setProperty('--duration', `${toastData.remainingTime}ms`);
|
|
1882
|
+
toastData.timeout = setTimeout(() => this.removeToast(toast), toastData.remainingTime);
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1240
1886
|
|
|
1241
1887
|
setTimeout(() => {
|
|
1242
1888
|
toast.classList.add("show");
|
|
@@ -1247,10 +1893,15 @@
|
|
|
1247
1893
|
}, 10);
|
|
1248
1894
|
|
|
1249
1895
|
if (options.timeout > 0) {
|
|
1250
|
-
|
|
1896
|
+
toastData.startTime = Date.now();
|
|
1897
|
+
toastData.timeout = setTimeout(() => this.removeToast(toast), options.timeout);
|
|
1251
1898
|
}
|
|
1252
1899
|
|
|
1253
|
-
return
|
|
1900
|
+
return {
|
|
1901
|
+
element: toast,
|
|
1902
|
+
dismiss: () => this.removeToast(toast),
|
|
1903
|
+
update: (newMessage, newOpts) => this.updateToast(toast, newMessage, newOpts)
|
|
1904
|
+
};
|
|
1254
1905
|
} catch (error) {
|
|
1255
1906
|
console.error('ToastifyPro: Failed to create custom toast:', error);
|
|
1256
1907
|
}
|
|
@@ -1560,9 +2211,10 @@
|
|
|
1560
2211
|
}
|
|
1561
2212
|
|
|
1562
2213
|
// Create close button for confirmation
|
|
1563
|
-
const closeBtn = document.createElement("
|
|
2214
|
+
const closeBtn = document.createElement("button");
|
|
1564
2215
|
closeBtn.className = "conf-close-btn";
|
|
1565
2216
|
closeBtn.innerHTML = "×";
|
|
2217
|
+
closeBtn.setAttribute('type', 'button');
|
|
1566
2218
|
closeBtn.setAttribute('aria-label', 'Cancel confirmation');
|
|
1567
2219
|
closeBtn.onclick = () => {
|
|
1568
2220
|
if (!isLoading) {
|
|
@@ -1578,6 +2230,7 @@
|
|
|
1578
2230
|
// Create icon wrapper
|
|
1579
2231
|
const iconWrapper = document.createElement("div");
|
|
1580
2232
|
iconWrapper.className = "toast-icon";
|
|
2233
|
+
iconWrapper.setAttribute('aria-hidden', 'true');
|
|
1581
2234
|
iconWrapper.innerHTML = this.getIconSVG('info'); // Default to info icon
|
|
1582
2235
|
if (confirmOptions.primaryColor) {
|
|
1583
2236
|
iconWrapper.style.color = textColor;
|
|
@@ -1598,8 +2251,9 @@
|
|
|
1598
2251
|
contentWrapper.appendChild(messageElement);
|
|
1599
2252
|
|
|
1600
2253
|
// Optional description
|
|
2254
|
+
let descriptionElement = null;
|
|
1601
2255
|
if (description) {
|
|
1602
|
-
|
|
2256
|
+
descriptionElement = document.createElement("div");
|
|
1603
2257
|
descriptionElement.className = "toast-description";
|
|
1604
2258
|
descriptionElement.textContent = description.substring(0, this.defaultOptions.maxLength * 2);
|
|
1605
2259
|
if (confirmOptions.primaryColor) {
|
|
@@ -1617,6 +2271,7 @@
|
|
|
1617
2271
|
// Cancel button
|
|
1618
2272
|
const cancelBtn = document.createElement("button");
|
|
1619
2273
|
cancelBtn.className = "toast-btn toast-btn-cancel";
|
|
2274
|
+
cancelBtn.setAttribute('type', 'button');
|
|
1620
2275
|
cancelBtn.textContent = confirmOptions.cancelText;
|
|
1621
2276
|
cancelBtn.onclick = () => {
|
|
1622
2277
|
if (!isLoading) {
|
|
@@ -1638,18 +2293,25 @@
|
|
|
1638
2293
|
// Confirm button
|
|
1639
2294
|
const confirmBtn = document.createElement("button");
|
|
1640
2295
|
confirmBtn.className = `toast-btn toast-btn-confirm`;
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
const spinner = document.createElement("span");
|
|
1644
|
-
spinner.className = "btn-spinner";
|
|
1645
|
-
confirmBtn.appendChild(spinner);
|
|
1646
|
-
|
|
2296
|
+
confirmBtn.setAttribute('type', 'button');
|
|
2297
|
+
|
|
1647
2298
|
// Create text wrapper
|
|
1648
2299
|
const textWrapper = document.createElement("span");
|
|
1649
2300
|
textWrapper.className = "btn-text";
|
|
1650
2301
|
textWrapper.textContent = confirmOptions.confirmText;
|
|
1651
2302
|
confirmBtn.appendChild(textWrapper);
|
|
1652
2303
|
|
|
2304
|
+
// Create spinner element with custom SVG
|
|
2305
|
+
const spinner = document.createElement("span");
|
|
2306
|
+
spinner.className = "btn-spinner";
|
|
2307
|
+
spinner.innerHTML = `
|
|
2308
|
+
<svg width="25" height="25" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2309
|
+
<path d="M9.5 2.9375V5.5625M9.5 13.4375V16.0625M2.9375 9.5H5.5625M13.4375 9.5H16.0625" stroke="currentColor" stroke-width="1.875" stroke-linecap="round" />
|
|
2310
|
+
<path d="M4.86011 4.85961L6.71627 6.71577M12.2847 12.2842L14.1409 14.1404M4.86011 14.1404L6.71627 12.2842M12.2847 6.71577L14.1409 4.85961" stroke="currentColor" stroke-width="1.875" stroke-linecap="round" />
|
|
2311
|
+
</svg>
|
|
2312
|
+
`;
|
|
2313
|
+
confirmBtn.appendChild(spinner);
|
|
2314
|
+
|
|
1653
2315
|
confirmBtn.onclick = () => {
|
|
1654
2316
|
if (!isLoading) {
|
|
1655
2317
|
handleConfirmation(true);
|
|
@@ -1700,6 +2362,53 @@
|
|
|
1700
2362
|
setLoading(true);
|
|
1701
2363
|
}
|
|
1702
2364
|
|
|
2365
|
+
// ARIA accessibility for confirmation dialog
|
|
2366
|
+
toast.setAttribute('role', 'alertdialog');
|
|
2367
|
+
toast.setAttribute('aria-modal', 'true');
|
|
2368
|
+
toast.setAttribute('aria-labelledby', 'toast-conf-title');
|
|
2369
|
+
if (description) {
|
|
2370
|
+
toast.setAttribute('aria-describedby', 'toast-conf-desc');
|
|
2371
|
+
}
|
|
2372
|
+
messageElement.id = 'toast-conf-title';
|
|
2373
|
+
if (description && descriptionElement) {
|
|
2374
|
+
descriptionElement.id = 'toast-conf-desc';
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
// Store previously focused element for restoration
|
|
2378
|
+
const previouslyFocused = document.activeElement;
|
|
2379
|
+
|
|
2380
|
+
// Focus trap for confirmation dialog
|
|
2381
|
+
const focusableElements = [cancelBtn, confirmBtn, closeBtn].filter(Boolean);
|
|
2382
|
+
let currentFocusIndex = 0;
|
|
2383
|
+
|
|
2384
|
+
const handleTabKey = (e) => {
|
|
2385
|
+
if (e.key === 'Tab' && toastElement && toastElement.parentNode) {
|
|
2386
|
+
e.preventDefault();
|
|
2387
|
+
if (e.shiftKey) {
|
|
2388
|
+
currentFocusIndex = (currentFocusIndex - 1 + focusableElements.length) % focusableElements.length;
|
|
2389
|
+
} else {
|
|
2390
|
+
currentFocusIndex = (currentFocusIndex + 1) % focusableElements.length;
|
|
2391
|
+
}
|
|
2392
|
+
focusableElements[currentFocusIndex]?.focus();
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
document.addEventListener('keydown', handleTabKey);
|
|
2397
|
+
|
|
2398
|
+
// Store cleanup function
|
|
2399
|
+
const originalClose = closeConfirmation;
|
|
2400
|
+
const cleanupAndClose = () => {
|
|
2401
|
+
document.removeEventListener('keydown', handleTabKey);
|
|
2402
|
+
// Restore focus to previously focused element
|
|
2403
|
+
if (previouslyFocused && typeof previouslyFocused.focus === 'function') {
|
|
2404
|
+
setTimeout(() => previouslyFocused.focus(), 100);
|
|
2405
|
+
}
|
|
2406
|
+
originalClose();
|
|
2407
|
+
};
|
|
2408
|
+
|
|
2409
|
+
// Update control object with enhanced close
|
|
2410
|
+
controlObject.close = cleanupAndClose;
|
|
2411
|
+
|
|
1703
2412
|
// Entrance animation
|
|
1704
2413
|
setTimeout(() => {
|
|
1705
2414
|
toast.classList.add("show");
|
|
@@ -1707,6 +2416,11 @@
|
|
|
1707
2416
|
if (icon) {
|
|
1708
2417
|
icon.style.animation = 'iconBounce 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
|
|
1709
2418
|
}
|
|
2419
|
+
|
|
2420
|
+
// Focus the confirm button after animation
|
|
2421
|
+
setTimeout(() => {
|
|
2422
|
+
confirmBtn.focus();
|
|
2423
|
+
}, 100);
|
|
1710
2424
|
}, 10);
|
|
1711
2425
|
|
|
1712
2426
|
// Return control object with toast element and control functions
|
|
@@ -1725,6 +2439,527 @@
|
|
|
1725
2439
|
confirm(message, descriptionOrCallback, callback) {
|
|
1726
2440
|
return this.conf(message, descriptionOrCallback, callback);
|
|
1727
2441
|
}
|
|
2442
|
+
|
|
2443
|
+
/**
|
|
2444
|
+
* Shows an input prompt toast with a text field
|
|
2445
|
+
* @param {string} message - Main prompt question
|
|
2446
|
+
* @param {string|Function|Object} descriptionOrCallback - Description text, callback function, or options object
|
|
2447
|
+
* @param {Function} callback - Callback function (if description provided)
|
|
2448
|
+
* @returns {Object} Control object with element, close, setValue, getValue methods
|
|
2449
|
+
*
|
|
2450
|
+
* Options object:
|
|
2451
|
+
* - description: {string} Optional description text
|
|
2452
|
+
* - placeholder: {string} Input placeholder text (default: 'Enter your response...')
|
|
2453
|
+
* - submitText: {string} Submit button text (default: 'Submit')
|
|
2454
|
+
* - cancelText: {string} Cancel button text (default: 'Cancel')
|
|
2455
|
+
* - defaultValue: {string} Default input value
|
|
2456
|
+
* - required: {boolean} Whether input is required (default: true)
|
|
2457
|
+
* - type: {string} Input type: 'text', 'email', 'number', 'password', 'url' (default: 'text')
|
|
2458
|
+
* - validate: {Function} Custom validation function (receives value, returns true or error string)
|
|
2459
|
+
* - onSubmit: {Function} Called when user submits (receives value, control object)
|
|
2460
|
+
* - onCancel: {Function} Called when user cancels
|
|
2461
|
+
* - theme: {string} Toast theme: 'dark' or 'light' (default: 'dark')
|
|
2462
|
+
* - position: {string} Override default position
|
|
2463
|
+
* - overlay: {boolean} Show overlay behind toast (default: true)
|
|
2464
|
+
* - primaryColor: {string} Custom primary color
|
|
2465
|
+
* - secondaryColor: {string} Custom secondary color for gradient
|
|
2466
|
+
*
|
|
2467
|
+
* @example
|
|
2468
|
+
* // Simple usage
|
|
2469
|
+
* toast.input('What is your name?', (value) => console.log(value));
|
|
2470
|
+
*
|
|
2471
|
+
* // With description
|
|
2472
|
+
* toast.input('Enter your email', 'We will send you updates', (value) => {...});
|
|
2473
|
+
*
|
|
2474
|
+
* // Full options
|
|
2475
|
+
* toast.input('Enter password', {
|
|
2476
|
+
* type: 'password',
|
|
2477
|
+
* placeholder: 'Minimum 8 characters',
|
|
2478
|
+
* validate: (val) => val.length >= 8 || 'Password must be at least 8 characters',
|
|
2479
|
+
* onSubmit: (value) => { ... },
|
|
2480
|
+
* theme: 'light'
|
|
2481
|
+
* });
|
|
2482
|
+
*/
|
|
2483
|
+
input(message, descriptionOrCallback, callback) {
|
|
2484
|
+
try {
|
|
2485
|
+
// Parse arguments like confirm method
|
|
2486
|
+
let description = '';
|
|
2487
|
+
let options = {};
|
|
2488
|
+
let resultCallback = null;
|
|
2489
|
+
|
|
2490
|
+
if (typeof descriptionOrCallback === 'string') {
|
|
2491
|
+
description = descriptionOrCallback;
|
|
2492
|
+
if (typeof callback === 'function') {
|
|
2493
|
+
resultCallback = callback;
|
|
2494
|
+
} else if (typeof callback === 'object') {
|
|
2495
|
+
options = callback || {};
|
|
2496
|
+
resultCallback = options.onSubmit || null;
|
|
2497
|
+
}
|
|
2498
|
+
} else if (typeof descriptionOrCallback === 'function') {
|
|
2499
|
+
resultCallback = descriptionOrCallback;
|
|
2500
|
+
} else if (typeof descriptionOrCallback === 'object') {
|
|
2501
|
+
options = descriptionOrCallback || {};
|
|
2502
|
+
description = options.description || '';
|
|
2503
|
+
resultCallback = options.onSubmit || null;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
// Default options
|
|
2507
|
+
const inputOptions = {
|
|
2508
|
+
placeholder: options.placeholder || 'Enter your response...',
|
|
2509
|
+
submitText: options.submitText || 'Submit',
|
|
2510
|
+
cancelText: options.cancelText || 'Cancel',
|
|
2511
|
+
defaultValue: options.defaultValue || '',
|
|
2512
|
+
required: options.required !== false, // default true
|
|
2513
|
+
type: options.type || 'text',
|
|
2514
|
+
validate: options.validate || null,
|
|
2515
|
+
onCancel: options.onCancel || null,
|
|
2516
|
+
theme: options.theme || 'dark',
|
|
2517
|
+
position: options.position || this.defaultOptions.position,
|
|
2518
|
+
overlay: options.overlay !== false, // default true
|
|
2519
|
+
primaryColor: options.primaryColor || null,
|
|
2520
|
+
secondaryColor: options.secondaryColor || null,
|
|
2521
|
+
};
|
|
2522
|
+
|
|
2523
|
+
// Validate input type
|
|
2524
|
+
const validTypes = ['text', 'email', 'number', 'password', 'url', 'tel'];
|
|
2525
|
+
if (!validTypes.includes(inputOptions.type)) {
|
|
2526
|
+
inputOptions.type = 'text';
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
// Create or get container for the specified position
|
|
2530
|
+
let inputContainer;
|
|
2531
|
+
const positionClass = inputOptions.position.replace(' ', '-');
|
|
2532
|
+
const existingContainer = document.querySelector(
|
|
2533
|
+
`.toastify-pro-container.${positionClass}`
|
|
2534
|
+
);
|
|
2535
|
+
|
|
2536
|
+
if (existingContainer) {
|
|
2537
|
+
inputContainer = existingContainer;
|
|
2538
|
+
} else {
|
|
2539
|
+
inputContainer = document.createElement("div");
|
|
2540
|
+
inputContainer.className = `toastify-pro-container ${positionClass}`;
|
|
2541
|
+
document.body.appendChild(inputContainer);
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
// Create overlay if enabled
|
|
2545
|
+
let overlay = null;
|
|
2546
|
+
if (inputOptions.overlay) {
|
|
2547
|
+
overlay = document.createElement("div");
|
|
2548
|
+
overlay.className = "toastify-pro-overlay";
|
|
2549
|
+
document.body.appendChild(overlay);
|
|
2550
|
+
setTimeout(() => overlay.classList.add("visible"), 10);
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
// Create input toast element
|
|
2554
|
+
const toast = document.createElement("div");
|
|
2555
|
+
toast.className = `toastify-pro input-toast ${inputOptions.theme}`;
|
|
2556
|
+
toast.setAttribute('role', 'dialog');
|
|
2557
|
+
toast.setAttribute('aria-modal', 'true');
|
|
2558
|
+
toast.setAttribute('aria-labelledby', 'toast-input-title');
|
|
2559
|
+
|
|
2560
|
+
// Track state
|
|
2561
|
+
let isLoading = false;
|
|
2562
|
+
let isClosed = false;
|
|
2563
|
+
let inputElement = null;
|
|
2564
|
+
let submitBtnElement = null;
|
|
2565
|
+
let cancelBtnElement = null;
|
|
2566
|
+
let errorElement = null;
|
|
2567
|
+
|
|
2568
|
+
// Helper: Check if a color is light or dark
|
|
2569
|
+
const isLightColor = (color) => {
|
|
2570
|
+
if (!color) return false;
|
|
2571
|
+
const hex = color.replace('#', '');
|
|
2572
|
+
if (hex.length === 3) {
|
|
2573
|
+
const r = parseInt(hex[0] + hex[0], 16);
|
|
2574
|
+
const g = parseInt(hex[1] + hex[1], 16);
|
|
2575
|
+
const b = parseInt(hex[2] + hex[2], 16);
|
|
2576
|
+
return (r * 299 + g * 587 + b * 114) / 1000 > 128;
|
|
2577
|
+
}
|
|
2578
|
+
const r = parseInt(hex.substr(0, 2), 16);
|
|
2579
|
+
const g = parseInt(hex.substr(2, 2), 16);
|
|
2580
|
+
const b = parseInt(hex.substr(4, 2), 16);
|
|
2581
|
+
return (r * 299 + g * 587 + b * 114) / 1000 > 128;
|
|
2582
|
+
};
|
|
2583
|
+
|
|
2584
|
+
// Apply custom colors
|
|
2585
|
+
let textColor = inputOptions.theme === 'light' ? '#1e293b' : 'white';
|
|
2586
|
+
if (inputOptions.primaryColor) {
|
|
2587
|
+
const primary = inputOptions.primaryColor;
|
|
2588
|
+
const secondary = inputOptions.secondaryColor;
|
|
2589
|
+
|
|
2590
|
+
if (secondary) {
|
|
2591
|
+
toast.style.background = `linear-gradient(135deg, ${primary} 0%, ${secondary} 100%)`;
|
|
2592
|
+
} else {
|
|
2593
|
+
toast.style.background = `linear-gradient(135deg, ${primary} 0%, ${primary}dd 100%)`;
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
textColor = isLightColor(primary) ? '#1e293b' : 'white';
|
|
2597
|
+
toast.style.color = textColor;
|
|
2598
|
+
|
|
2599
|
+
const borderOpacity = isLightColor(primary) ? '0.2' : '0.15';
|
|
2600
|
+
toast.style.borderColor = `rgba(255, 255, 255, ${borderOpacity})`;
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// Set loading state
|
|
2604
|
+
const setLoading = (loading) => {
|
|
2605
|
+
isLoading = loading;
|
|
2606
|
+
if (submitBtnElement) {
|
|
2607
|
+
if (loading) {
|
|
2608
|
+
submitBtnElement.classList.add('loading');
|
|
2609
|
+
} else {
|
|
2610
|
+
submitBtnElement.classList.remove('loading');
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
if (cancelBtnElement) {
|
|
2614
|
+
cancelBtnElement.disabled = loading;
|
|
2615
|
+
cancelBtnElement.style.opacity = loading ? '0.5' : '1';
|
|
2616
|
+
cancelBtnElement.style.cursor = loading ? 'not-allowed' : 'pointer';
|
|
2617
|
+
}
|
|
2618
|
+
if (inputElement) {
|
|
2619
|
+
inputElement.disabled = loading;
|
|
2620
|
+
inputElement.style.opacity = loading ? '0.6' : '1';
|
|
2621
|
+
}
|
|
2622
|
+
};
|
|
2623
|
+
|
|
2624
|
+
// Close the input toast
|
|
2625
|
+
const closeInput = () => {
|
|
2626
|
+
if (isClosed) return;
|
|
2627
|
+
isClosed = true;
|
|
2628
|
+
|
|
2629
|
+
// Remove overlay
|
|
2630
|
+
if (overlay) {
|
|
2631
|
+
overlay.classList.remove("visible");
|
|
2632
|
+
setTimeout(() => overlay.remove(), 300);
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
// Exit animation
|
|
2636
|
+
toast.style.pointerEvents = 'none';
|
|
2637
|
+
toast.style.animation = 'carSwipeCenter 0.35s cubic-bezier(0.55, 0.0, 0.85, 0.36) forwards';
|
|
2638
|
+
|
|
2639
|
+
const icon = toast.querySelector('.toast-icon');
|
|
2640
|
+
if (icon) {
|
|
2641
|
+
icon.style.animation = 'iconCarExit 0.35s cubic-bezier(0.55, 0.0, 0.85, 0.36) forwards';
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
setTimeout(() => {
|
|
2645
|
+
if (toast.parentNode) {
|
|
2646
|
+
toast.remove();
|
|
2647
|
+
}
|
|
2648
|
+
}, 350);
|
|
2649
|
+
|
|
2650
|
+
// Restore focus
|
|
2651
|
+
if (previouslyFocused && typeof previouslyFocused.focus === 'function') {
|
|
2652
|
+
setTimeout(() => previouslyFocused.focus(), 100);
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
// Cleanup keyboard handler
|
|
2656
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
2657
|
+
};
|
|
2658
|
+
|
|
2659
|
+
// Validate input
|
|
2660
|
+
const validateInput = (value) => {
|
|
2661
|
+
// Required check
|
|
2662
|
+
if (inputOptions.required && !value.trim()) {
|
|
2663
|
+
return 'This field is required';
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
// Type-specific validation
|
|
2667
|
+
if (value.trim()) {
|
|
2668
|
+
if (inputOptions.type === 'email') {
|
|
2669
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2670
|
+
if (!emailRegex.test(value)) {
|
|
2671
|
+
return 'Please enter a valid email address';
|
|
2672
|
+
}
|
|
2673
|
+
} else if (inputOptions.type === 'url') {
|
|
2674
|
+
try {
|
|
2675
|
+
new URL(value);
|
|
2676
|
+
} catch {
|
|
2677
|
+
return 'Please enter a valid URL';
|
|
2678
|
+
}
|
|
2679
|
+
} else if (inputOptions.type === 'number') {
|
|
2680
|
+
if (isNaN(Number(value))) {
|
|
2681
|
+
return 'Please enter a valid number';
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
// Custom validation
|
|
2687
|
+
if (inputOptions.validate) {
|
|
2688
|
+
const customResult = inputOptions.validate(value);
|
|
2689
|
+
if (customResult !== true && customResult) {
|
|
2690
|
+
return customResult;
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
return null; // Valid
|
|
2695
|
+
};
|
|
2696
|
+
|
|
2697
|
+
// Show error message
|
|
2698
|
+
const showError = (message) => {
|
|
2699
|
+
if (errorElement) {
|
|
2700
|
+
errorElement.textContent = message;
|
|
2701
|
+
errorElement.classList.add('visible');
|
|
2702
|
+
inputElement.classList.add('error');
|
|
2703
|
+
}
|
|
2704
|
+
};
|
|
2705
|
+
|
|
2706
|
+
// Clear error message
|
|
2707
|
+
const clearError = () => {
|
|
2708
|
+
if (errorElement) {
|
|
2709
|
+
errorElement.classList.remove('visible');
|
|
2710
|
+
inputElement.classList.remove('error');
|
|
2711
|
+
}
|
|
2712
|
+
};
|
|
2713
|
+
|
|
2714
|
+
// Handle submit
|
|
2715
|
+
const handleSubmit = async () => {
|
|
2716
|
+
if (isLoading || isClosed) return;
|
|
2717
|
+
|
|
2718
|
+
const value = inputElement.value;
|
|
2719
|
+
const error = validateInput(value);
|
|
2720
|
+
|
|
2721
|
+
if (error) {
|
|
2722
|
+
showError(error);
|
|
2723
|
+
inputElement.focus();
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
clearError();
|
|
2728
|
+
|
|
2729
|
+
if (resultCallback) {
|
|
2730
|
+
const result = resultCallback(value, { setLoading, close: closeInput, setValue: (v) => inputElement.value = v });
|
|
2731
|
+
|
|
2732
|
+
// Handle async callbacks
|
|
2733
|
+
if (result && typeof result.then === 'function') {
|
|
2734
|
+
setLoading(true);
|
|
2735
|
+
try {
|
|
2736
|
+
await result;
|
|
2737
|
+
if (!isClosed) {
|
|
2738
|
+
closeInput();
|
|
2739
|
+
}
|
|
2740
|
+
} catch (err) {
|
|
2741
|
+
setLoading(false);
|
|
2742
|
+
if (err && typeof err === 'string') {
|
|
2743
|
+
showError(err);
|
|
2744
|
+
} else if (err && err.message) {
|
|
2745
|
+
showError(err.message);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
} else if (!isLoading) {
|
|
2749
|
+
closeInput();
|
|
2750
|
+
}
|
|
2751
|
+
} else {
|
|
2752
|
+
closeInput();
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
|
|
2756
|
+
// Handle cancel
|
|
2757
|
+
const handleCancel = () => {
|
|
2758
|
+
if (isLoading || isClosed) return;
|
|
2759
|
+
|
|
2760
|
+
if (inputOptions.onCancel) {
|
|
2761
|
+
inputOptions.onCancel();
|
|
2762
|
+
}
|
|
2763
|
+
closeInput();
|
|
2764
|
+
};
|
|
2765
|
+
|
|
2766
|
+
// Store previously focused element
|
|
2767
|
+
const previouslyFocused = document.activeElement;
|
|
2768
|
+
|
|
2769
|
+
// Keyboard handler
|
|
2770
|
+
const handleKeyDown = (e) => {
|
|
2771
|
+
if (isClosed) return;
|
|
2772
|
+
|
|
2773
|
+
if (e.key === 'Escape' && !isLoading) {
|
|
2774
|
+
handleCancel();
|
|
2775
|
+
} else if (e.key === 'Enter' && e.target === inputElement && !isLoading) {
|
|
2776
|
+
e.preventDefault();
|
|
2777
|
+
handleSubmit();
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2780
|
+
|
|
2781
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
2782
|
+
|
|
2783
|
+
// Build toast content
|
|
2784
|
+
// Header with icon and content
|
|
2785
|
+
const headerWrapper = document.createElement("div");
|
|
2786
|
+
headerWrapper.className = "toast-input-header";
|
|
2787
|
+
|
|
2788
|
+
// Icon
|
|
2789
|
+
const iconWrapper = document.createElement("div");
|
|
2790
|
+
iconWrapper.className = "toast-icon";
|
|
2791
|
+
iconWrapper.setAttribute('aria-hidden', 'true');
|
|
2792
|
+
iconWrapper.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2793
|
+
<path d="M11 4H4C3.46957 4 2.96086 4.21071 2.58579 4.58579C2.21071 4.96086 2 5.46957 2 6V20C2 20.5304 2.21071 21.0391 2.58579 21.4142C2.96086 21.7893 3.46957 22 4 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
2794
|
+
<path d="M18.5 2.50001C18.8978 2.10219 19.4374 1.87869 20 1.87869C20.5626 1.87869 21.1022 2.10219 21.5 2.50001C21.8978 2.89784 22.1213 3.43739 22.1213 4.00001C22.1213 4.56262 21.8978 5.10219 21.5 5.50001L12 15L8 16L9 12L18.5 2.50001Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
2795
|
+
</svg>`;
|
|
2796
|
+
if (inputOptions.primaryColor) {
|
|
2797
|
+
iconWrapper.style.color = textColor;
|
|
2798
|
+
}
|
|
2799
|
+
headerWrapper.appendChild(iconWrapper);
|
|
2800
|
+
|
|
2801
|
+
// Content wrapper
|
|
2802
|
+
const contentWrapper = document.createElement("div");
|
|
2803
|
+
contentWrapper.className = "toast-content";
|
|
2804
|
+
|
|
2805
|
+
// Message
|
|
2806
|
+
const messageElement = document.createElement("div");
|
|
2807
|
+
messageElement.className = "toast-message";
|
|
2808
|
+
messageElement.id = "toast-input-title";
|
|
2809
|
+
messageElement.textContent = message.substring(0, this.defaultOptions.maxLength);
|
|
2810
|
+
if (inputOptions.primaryColor) {
|
|
2811
|
+
messageElement.style.color = textColor;
|
|
2812
|
+
}
|
|
2813
|
+
contentWrapper.appendChild(messageElement);
|
|
2814
|
+
|
|
2815
|
+
// Description
|
|
2816
|
+
if (description) {
|
|
2817
|
+
const descriptionElement = document.createElement("div");
|
|
2818
|
+
descriptionElement.className = "toast-description";
|
|
2819
|
+
descriptionElement.id = "toast-input-desc";
|
|
2820
|
+
descriptionElement.textContent = description.substring(0, this.defaultOptions.maxLength * 2);
|
|
2821
|
+
if (inputOptions.primaryColor) {
|
|
2822
|
+
descriptionElement.style.color = textColor;
|
|
2823
|
+
}
|
|
2824
|
+
contentWrapper.appendChild(descriptionElement);
|
|
2825
|
+
toast.setAttribute('aria-describedby', 'toast-input-desc');
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
headerWrapper.appendChild(contentWrapper);
|
|
2829
|
+
|
|
2830
|
+
// Input wrapper
|
|
2831
|
+
const inputWrapper = document.createElement("div");
|
|
2832
|
+
inputWrapper.className = "toast-input-wrapper";
|
|
2833
|
+
inputWrapper.appendChild(headerWrapper);
|
|
2834
|
+
|
|
2835
|
+
// Input field
|
|
2836
|
+
inputElement = document.createElement("input");
|
|
2837
|
+
inputElement.className = "toast-input";
|
|
2838
|
+
inputElement.type = inputOptions.type;
|
|
2839
|
+
inputElement.placeholder = inputOptions.placeholder;
|
|
2840
|
+
inputElement.value = inputOptions.defaultValue;
|
|
2841
|
+
inputElement.setAttribute('aria-label', message);
|
|
2842
|
+
|
|
2843
|
+
if (inputOptions.primaryColor) {
|
|
2844
|
+
const isLight = isLightColor(inputOptions.primaryColor);
|
|
2845
|
+
inputElement.style.borderColor = isLight ? 'rgba(15, 23, 42, 0.2)' : 'rgba(255, 255, 255, 0.2)';
|
|
2846
|
+
inputElement.style.background = isLight ? 'rgba(15, 23, 42, 0.06)' : 'rgba(255, 255, 255, 0.08)';
|
|
2847
|
+
inputElement.style.color = textColor;
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
// Clear error on input
|
|
2851
|
+
inputElement.addEventListener('input', () => {
|
|
2852
|
+
if (errorElement && errorElement.classList.contains('visible')) {
|
|
2853
|
+
clearError();
|
|
2854
|
+
}
|
|
2855
|
+
});
|
|
2856
|
+
|
|
2857
|
+
inputWrapper.appendChild(inputElement);
|
|
2858
|
+
|
|
2859
|
+
// Error message element
|
|
2860
|
+
errorElement = document.createElement("div");
|
|
2861
|
+
errorElement.className = "toast-input-error";
|
|
2862
|
+
errorElement.setAttribute('role', 'alert');
|
|
2863
|
+
inputWrapper.appendChild(errorElement);
|
|
2864
|
+
|
|
2865
|
+
// Actions wrapper
|
|
2866
|
+
const actionsWrapper = document.createElement("div");
|
|
2867
|
+
actionsWrapper.className = "toast-input-actions";
|
|
2868
|
+
|
|
2869
|
+
// Cancel button
|
|
2870
|
+
cancelBtnElement = document.createElement("button");
|
|
2871
|
+
cancelBtnElement.className = "input-btn input-btn-cancel";
|
|
2872
|
+
cancelBtnElement.type = "button";
|
|
2873
|
+
cancelBtnElement.textContent = inputOptions.cancelText;
|
|
2874
|
+
cancelBtnElement.onclick = handleCancel;
|
|
2875
|
+
|
|
2876
|
+
if (inputOptions.primaryColor) {
|
|
2877
|
+
const isLight = isLightColor(inputOptions.primaryColor);
|
|
2878
|
+
cancelBtnElement.style.background = isLight ? 'rgba(15, 23, 42, 0.08)' : 'rgba(255, 255, 255, 0.1)';
|
|
2879
|
+
cancelBtnElement.style.color = textColor;
|
|
2880
|
+
cancelBtnElement.style.borderColor = isLight ? 'rgba(15, 23, 42, 0.2)' : 'rgba(255, 255, 255, 0.2)';
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
// Submit button
|
|
2884
|
+
submitBtnElement = document.createElement("button");
|
|
2885
|
+
submitBtnElement.className = "input-btn input-btn-submit";
|
|
2886
|
+
submitBtnElement.type = "button";
|
|
2887
|
+
|
|
2888
|
+
const textWrapper = document.createElement("span");
|
|
2889
|
+
textWrapper.className = "btn-text";
|
|
2890
|
+
textWrapper.textContent = inputOptions.submitText;
|
|
2891
|
+
submitBtnElement.appendChild(textWrapper);
|
|
2892
|
+
|
|
2893
|
+
// Spinner
|
|
2894
|
+
const spinner = document.createElement("span");
|
|
2895
|
+
spinner.className = "btn-spinner";
|
|
2896
|
+
spinner.innerHTML = `<svg width="16" height="16" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2897
|
+
<path d="M9.5 2.9375V5.5625M9.5 13.4375V16.0625M2.9375 9.5H5.5625M13.4375 9.5H16.0625" stroke="currentColor" stroke-width="1.875" stroke-linecap="round" />
|
|
2898
|
+
<path d="M4.86011 4.85961L6.71627 6.71577M12.2847 12.2842L14.1409 14.1404M4.86011 14.1404L6.71627 12.2842M12.2847 6.71577L14.1409 4.85961" stroke="currentColor" stroke-width="1.875" stroke-linecap="round" />
|
|
2899
|
+
</svg>`;
|
|
2900
|
+
submitBtnElement.appendChild(spinner);
|
|
2901
|
+
|
|
2902
|
+
submitBtnElement.onclick = handleSubmit;
|
|
2903
|
+
|
|
2904
|
+
if (inputOptions.primaryColor) {
|
|
2905
|
+
const primary = inputOptions.primaryColor;
|
|
2906
|
+
const isLight = isLightColor(primary);
|
|
2907
|
+
submitBtnElement.style.background = isLight
|
|
2908
|
+
? 'linear-gradient(135deg, #1e293b 0%, #334155 100%)'
|
|
2909
|
+
: 'linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.85) 100%)';
|
|
2910
|
+
submitBtnElement.style.color = isLight ? 'white' : '#1e293b';
|
|
2911
|
+
submitBtnElement.style.borderColor = isLight ? 'rgba(15, 23, 42, 0.3)' : 'rgba(255, 255, 255, 0.4)';
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
actionsWrapper.appendChild(cancelBtnElement);
|
|
2915
|
+
actionsWrapper.appendChild(submitBtnElement);
|
|
2916
|
+
inputWrapper.appendChild(actionsWrapper);
|
|
2917
|
+
|
|
2918
|
+
toast.appendChild(inputWrapper);
|
|
2919
|
+
inputContainer.appendChild(toast);
|
|
2920
|
+
|
|
2921
|
+
// Entrance animation
|
|
2922
|
+
setTimeout(() => {
|
|
2923
|
+
toast.classList.add("show");
|
|
2924
|
+
const icon = toast.querySelector('.toast-icon');
|
|
2925
|
+
if (icon) {
|
|
2926
|
+
icon.style.animation = 'iconBounce 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
// Focus input after animation
|
|
2930
|
+
setTimeout(() => {
|
|
2931
|
+
inputElement.focus();
|
|
2932
|
+
// Select default value if present
|
|
2933
|
+
if (inputOptions.defaultValue) {
|
|
2934
|
+
inputElement.select();
|
|
2935
|
+
}
|
|
2936
|
+
}, 150);
|
|
2937
|
+
}, 10);
|
|
2938
|
+
|
|
2939
|
+
// Return control object
|
|
2940
|
+
return {
|
|
2941
|
+
element: toast,
|
|
2942
|
+
close: closeInput,
|
|
2943
|
+
setLoading,
|
|
2944
|
+
getValue: () => inputElement.value,
|
|
2945
|
+
setValue: (value) => { inputElement.value = value; },
|
|
2946
|
+
setError: showError,
|
|
2947
|
+
clearError
|
|
2948
|
+
};
|
|
2949
|
+
} catch (error) {
|
|
2950
|
+
console.error('ToastifyPro: Failed to create input toast:', error);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
/**
|
|
2955
|
+
* Alias for input() method - shows an input prompt
|
|
2956
|
+
* @param {string} message - Main prompt question
|
|
2957
|
+
* @param {string|Function|Object} descriptionOrCallback - Description text, callback function, or options object
|
|
2958
|
+
* @param {Function} callback - Callback function (if description provided)
|
|
2959
|
+
*/
|
|
2960
|
+
prompt(message, descriptionOrCallback, callback) {
|
|
2961
|
+
return this.input(message, descriptionOrCallback, callback);
|
|
2962
|
+
}
|
|
1728
2963
|
}
|
|
1729
2964
|
|
|
1730
2965
|
/**
|