toastify-pro 1.2.0 → 1.4.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 +400 -41
- package/dist/toastify-pro.esm.js +759 -4
- package/dist/toastify-pro.esm.js.map +1 -1
- package/dist/toastify-pro.umd.js +759 -4
- package/dist/toastify-pro.umd.js.map +1 -1
- package/dist/toastify-pro.umd.min.js +8 -2
- package/dist/toastify-pro.umd.min.js.map +1 -1
- package/package.json +2 -2
- package/src/toastify-pro.js +759 -4
package/dist/toastify-pro.esm.js
CHANGED
|
@@ -12,16 +12,26 @@
|
|
|
12
12
|
* - Progress bar with shimmer effects
|
|
13
13
|
* - Responsive design for mobile devices
|
|
14
14
|
* - Framework agnostic (works with React, Vue, Angular, etc.)
|
|
15
|
+
* - Confirmation dialogs with customizable buttons and callbacks
|
|
16
|
+
* - Center position support for enhanced focus
|
|
17
|
+
* - Independent positioning for confirmations
|
|
18
|
+
* - Loading states for async operations
|
|
19
|
+
* - Custom gradient colors with primaryColor/secondaryColor
|
|
20
|
+
* - Single instance mode with shake animation
|
|
15
21
|
*
|
|
16
|
-
* @version 1.
|
|
22
|
+
* @version 1.4.0
|
|
17
23
|
* @author ToastifyPro Team
|
|
18
24
|
* @license MIT
|
|
19
25
|
*/
|
|
26
|
+
|
|
27
|
+
// Global active confirmation tracker (shared across all instances)
|
|
28
|
+
let globalActiveConfirmation = null;
|
|
29
|
+
|
|
20
30
|
class ToastifyPro {
|
|
21
31
|
/**
|
|
22
32
|
* Creates a new ToastifyPro instance
|
|
23
33
|
* @param {Object} options - Configuration options
|
|
24
|
-
* @param {string} options.position - Toast position (top-left, top-right, bottom-left, bottom-right, top-center, bottom-center)
|
|
34
|
+
* @param {string} options.position - Toast position (top-left, top-right, bottom-left, bottom-right, top-center, bottom-center, center)
|
|
25
35
|
* @param {number} options.timeout - Auto-dismiss timeout in milliseconds (0 to disable)
|
|
26
36
|
* @param {boolean} options.allowClose - Whether to show close button
|
|
27
37
|
* @param {number} options.maxLength - Maximum message length
|
|
@@ -35,14 +45,14 @@ class ToastifyPro {
|
|
|
35
45
|
|
|
36
46
|
// Merge with default options
|
|
37
47
|
this.defaultOptions = {
|
|
38
|
-
position: options.position || "bottom-center", // top-left, top-right, bottom-left, bottom-right, top-center, bottom-center
|
|
48
|
+
position: options.position || "bottom-center", // top-left, top-right, bottom-left, bottom-right, top-center, bottom-center, center
|
|
39
49
|
timeout: options.timeout || 3000,
|
|
40
50
|
allowClose: options.allowClose !== false, // default true
|
|
41
51
|
maxLength: options.maxLength || 100,
|
|
42
52
|
};
|
|
43
53
|
|
|
44
54
|
// Validate position
|
|
45
|
-
const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center'];
|
|
55
|
+
const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center', 'center'];
|
|
46
56
|
if (!validPositions.includes(this.defaultOptions.position)) {
|
|
47
57
|
console.warn(`ToastifyPro: Invalid position "${this.defaultOptions.position}". Using "bottom-center".`);
|
|
48
58
|
this.defaultOptions.position = "bottom-center";
|
|
@@ -138,6 +148,7 @@ class ToastifyPro {
|
|
|
138
148
|
.toastify-pro-container.bottom-right { bottom: 50px; right: 24px; align-items: flex-end; }
|
|
139
149
|
.toastify-pro-container.top-center { top: 50px; left: 50%; transform: translateX(-50%); }
|
|
140
150
|
.toastify-pro-container.bottom-center { bottom: 50px; left: 50%; transform: translateX(-50%); }
|
|
151
|
+
.toastify-pro-container.center { top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
|
141
152
|
|
|
142
153
|
.toastify-pro {
|
|
143
154
|
min-width: 280px;
|
|
@@ -295,6 +306,26 @@ class ToastifyPro {
|
|
|
295
306
|
100% { transform: translateX(100%); }
|
|
296
307
|
}
|
|
297
308
|
|
|
309
|
+
@keyframes shake {
|
|
310
|
+
0%, 100% { transform: translate(0, 0); }
|
|
311
|
+
10%, 30%, 50%, 70%, 90% { transform: translate(-10px, 0); }
|
|
312
|
+
20%, 40%, 60%, 80% { transform: translate(10px, 0); }
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
@keyframes shakeCenter {
|
|
316
|
+
0%, 100% { transform: scale(1) translateX(0); }
|
|
317
|
+
10%, 30%, 50%, 70%, 90% { transform: scale(1) translateX(-10px); }
|
|
318
|
+
20%, 40%, 60%, 80% { transform: scale(1) translateX(10px); }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.toastify-pro.shake {
|
|
322
|
+
animation: shake 0.6s cubic-bezier(0.36, 0.07, 0.19, 0.97) !important;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.toastify-pro-container.center .toastify-pro.shake {
|
|
326
|
+
animation: shakeCenter 0.6s cubic-bezier(0.36, 0.07, 0.19, 0.97) !important;
|
|
327
|
+
}
|
|
328
|
+
|
|
298
329
|
.toastify-pro.show {
|
|
299
330
|
opacity: 1;
|
|
300
331
|
transform: scale(1);
|
|
@@ -397,6 +428,11 @@ class ToastifyPro {
|
|
|
397
428
|
opacity: 0;
|
|
398
429
|
}
|
|
399
430
|
}
|
|
431
|
+
|
|
432
|
+
@keyframes spin {
|
|
433
|
+
0% { transform: rotate(0deg); }
|
|
434
|
+
100% { transform: rotate(360deg); }
|
|
435
|
+
}
|
|
400
436
|
|
|
401
437
|
.toastify-pro .toast-icon svg {
|
|
402
438
|
width: 18px;
|
|
@@ -476,6 +512,287 @@ class ToastifyPro {
|
|
|
476
512
|
.toastify-pro-container.top-right,
|
|
477
513
|
.toastify-pro-container.bottom-right { right: 16px; }
|
|
478
514
|
}
|
|
515
|
+
|
|
516
|
+
/* Confirmation Toast Styles - Enhanced Modern Design */
|
|
517
|
+
.toastify-pro.confirmation {
|
|
518
|
+
min-width: 380px;
|
|
519
|
+
max-width: 500px;
|
|
520
|
+
padding: 32px 28px 28px;
|
|
521
|
+
flex-direction: column;
|
|
522
|
+
align-items: stretch;
|
|
523
|
+
gap: 24px;
|
|
524
|
+
position: relative;
|
|
525
|
+
backdrop-filter: blur(24px) saturate(180%);
|
|
526
|
+
box-shadow:
|
|
527
|
+
0 24px 48px -12px rgba(0, 0, 0, 0.25),
|
|
528
|
+
0 12px 24px -8px rgba(0, 0, 0, 0.15),
|
|
529
|
+
0 0 0 1px rgba(255, 255, 255, 0.08),
|
|
530
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.1);
|
|
531
|
+
border: 1.5px solid rgba(255, 255, 255, 0.15);
|
|
532
|
+
border-radius: 20px;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/* Hide progress bar for confirmation toasts */
|
|
536
|
+
.toastify-pro.confirmation::after {
|
|
537
|
+
display: none;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/* Shimmer effect for confirmation toasts */
|
|
541
|
+
.toastify-pro.confirmation::before {
|
|
542
|
+
opacity: 0.5;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/* Close button for confirmation dialogs */
|
|
546
|
+
.toastify-pro.confirmation .conf-close-btn {
|
|
547
|
+
position: absolute;
|
|
548
|
+
top: 14px;
|
|
549
|
+
right: 14px;
|
|
550
|
+
cursor: pointer;
|
|
551
|
+
font-size: 20px;
|
|
552
|
+
color: inherit;
|
|
553
|
+
opacity: 0.5;
|
|
554
|
+
padding: 6px;
|
|
555
|
+
border-radius: 8px;
|
|
556
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
557
|
+
width: 32px;
|
|
558
|
+
height: 32px;
|
|
559
|
+
display: flex;
|
|
560
|
+
align-items: center;
|
|
561
|
+
justify-content: center;
|
|
562
|
+
background: rgba(255, 255, 255, 0.08);
|
|
563
|
+
backdrop-filter: blur(10px);
|
|
564
|
+
font-weight: 300;
|
|
565
|
+
line-height: 1;
|
|
566
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.toastify-pro.confirmation .conf-close-btn:hover {
|
|
570
|
+
opacity: 0.9;
|
|
571
|
+
background: rgba(255, 255, 255, 0.15);
|
|
572
|
+
transform: scale(1.1) rotate(90deg);
|
|
573
|
+
border-color: rgba(255, 255, 255, 0.25);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.toastify-pro.confirmation.light .conf-close-btn {
|
|
577
|
+
background: rgba(15, 23, 42, 0.06);
|
|
578
|
+
border-color: rgba(15, 23, 42, 0.12);
|
|
579
|
+
opacity: 0.6;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.toastify-pro.confirmation.light .conf-close-btn:hover {
|
|
583
|
+
background: rgba(15, 23, 42, 0.12);
|
|
584
|
+
border-color: rgba(15, 23, 42, 0.2);
|
|
585
|
+
opacity: 1;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/* Icon styling for confirmation */
|
|
589
|
+
.toastify-pro.confirmation .toast-icon {
|
|
590
|
+
width: 56px;
|
|
591
|
+
height: 56px;
|
|
592
|
+
margin: 0 auto 8px;
|
|
593
|
+
background: rgba(255, 255, 255, 0.15);
|
|
594
|
+
backdrop-filter: blur(12px);
|
|
595
|
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
596
|
+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.toastify-pro.confirmation .toast-icon svg {
|
|
600
|
+
width: 28px;
|
|
601
|
+
height: 28px;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.toastify-pro.confirmation.light .toast-icon {
|
|
605
|
+
background: rgba(15, 23, 42, 0.08);
|
|
606
|
+
border-color: rgba(15, 23, 42, 0.15);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.toastify-pro.confirmation .toast-content {
|
|
610
|
+
text-align: center;
|
|
611
|
+
margin-bottom: 4px;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.toastify-pro.confirmation .toast-message {
|
|
615
|
+
font-weight: 700;
|
|
616
|
+
font-size: 20px;
|
|
617
|
+
margin-bottom: 8px;
|
|
618
|
+
letter-spacing: -0.02em;
|
|
619
|
+
line-height: 1.3;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.toastify-pro.confirmation .toast-description {
|
|
623
|
+
font-size: 15px;
|
|
624
|
+
opacity: 0.85;
|
|
625
|
+
margin-top: 8px;
|
|
626
|
+
line-height: 1.5;
|
|
627
|
+
font-weight: 400;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/* Fix text visibility for dark/light variants */
|
|
631
|
+
.toastify-pro.confirmation.dark .toast-message,
|
|
632
|
+
.toastify-pro.confirmation.dark .toast-description {
|
|
633
|
+
color: white;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.toastify-pro.confirmation.light .toast-message,
|
|
637
|
+
.toastify-pro.confirmation.light .toast-description {
|
|
638
|
+
color: #1e293b;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.toast-actions {
|
|
642
|
+
display: flex;
|
|
643
|
+
gap: 14px;
|
|
644
|
+
margin-top: 4px;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.toast-btn {
|
|
648
|
+
flex: 1;
|
|
649
|
+
padding: 14px 20px;
|
|
650
|
+
border: none;
|
|
651
|
+
border-radius: 12px;
|
|
652
|
+
font-weight: 600;
|
|
653
|
+
font-size: 15px;
|
|
654
|
+
cursor: pointer;
|
|
655
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
656
|
+
backdrop-filter: blur(10px);
|
|
657
|
+
position: relative;
|
|
658
|
+
overflow: hidden;
|
|
659
|
+
letter-spacing: 0.01em;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.toast-btn::after {
|
|
663
|
+
content: '';
|
|
664
|
+
position: absolute;
|
|
665
|
+
top: 50%;
|
|
666
|
+
left: 50%;
|
|
667
|
+
width: 0;
|
|
668
|
+
height: 0;
|
|
669
|
+
border-radius: 50%;
|
|
670
|
+
background: rgba(255, 255, 255, 0.3);
|
|
671
|
+
transform: translate(-50%, -50%);
|
|
672
|
+
transition: width 0.6s, height 0.6s;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.toast-btn:hover::after {
|
|
676
|
+
width: 300px;
|
|
677
|
+
height: 300px;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.toast-btn:hover {
|
|
681
|
+
transform: translateY(-2px);
|
|
682
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.toast-btn:active {
|
|
686
|
+
transform: translateY(0);
|
|
687
|
+
transition: all 0.1s;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
.toast-btn-cancel {
|
|
691
|
+
background: rgba(255, 255, 255, 0.1);
|
|
692
|
+
color: rgba(255, 255, 255, 0.9);
|
|
693
|
+
border: 1.5px solid rgba(255, 255, 255, 0.25);
|
|
694
|
+
font-weight: 600;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.toast-btn-cancel:hover {
|
|
698
|
+
background: rgba(255, 255, 255, 0.15);
|
|
699
|
+
color: white;
|
|
700
|
+
border-color: rgba(255, 255, 255, 0.35);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.toast-btn-cancel:disabled {
|
|
704
|
+
opacity: 0.5;
|
|
705
|
+
cursor: not-allowed;
|
|
706
|
+
pointer-events: none;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.toast-btn-confirm {
|
|
710
|
+
color: white;
|
|
711
|
+
font-weight: 700;
|
|
712
|
+
border: 2px solid rgba(255, 255, 255, 0.4);
|
|
713
|
+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
|
|
714
|
+
position: relative;
|
|
715
|
+
overflow: hidden;
|
|
716
|
+
background: linear-gradient(135deg, rgba(15, 23, 42, 0.95), rgba(30, 41, 59, 0.95));
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.toast-btn-confirm::before {
|
|
720
|
+
content: '';
|
|
721
|
+
position: absolute;
|
|
722
|
+
top: 0;
|
|
723
|
+
left: -100%;
|
|
724
|
+
width: 100%;
|
|
725
|
+
height: 100%;
|
|
726
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);
|
|
727
|
+
transition: left 0.6s;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.toast-btn-confirm:hover::before {
|
|
731
|
+
left: 100%;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.toast-btn-confirm:hover {
|
|
735
|
+
background: linear-gradient(135deg, rgba(15, 23, 42, 1), rgba(30, 41, 59, 1));
|
|
736
|
+
border-color: rgba(255, 255, 255, 0.5);
|
|
737
|
+
box-shadow: 0 8px 28px rgba(15, 23, 42, 0.5);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
.toast-btn-confirm.loading {
|
|
741
|
+
opacity: 0.7;
|
|
742
|
+
cursor: not-allowed;
|
|
743
|
+
pointer-events: none;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.toast-btn-confirm .btn-spinner {
|
|
747
|
+
display: none;
|
|
748
|
+
width: 16px;
|
|
749
|
+
height: 16px;
|
|
750
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
751
|
+
border-top-color: white;
|
|
752
|
+
border-radius: 50%;
|
|
753
|
+
animation: spin 0.6s linear infinite;
|
|
754
|
+
margin-right: 8px;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.toast-btn-confirm.loading .btn-spinner {
|
|
758
|
+
display: inline-block;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.toast-btn-confirm.loading .btn-text {
|
|
762
|
+
opacity: 0.7;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.toastify-pro.light .toast-btn-cancel {
|
|
766
|
+
background: rgba(15, 23, 42, 0.08);
|
|
767
|
+
color: rgba(15, 23, 42, 0.85);
|
|
768
|
+
border-color: rgba(15, 23, 42, 0.2);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.toastify-pro.light .toast-btn-cancel:hover {
|
|
772
|
+
background: rgba(15, 23, 42, 0.12);
|
|
773
|
+
color: rgba(15, 23, 42, 1);
|
|
774
|
+
border-color: rgba(15, 23, 42, 0.3);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/* Enhanced light theme confirm buttons */
|
|
778
|
+
.toastify-pro.light .toast-btn-confirm {
|
|
779
|
+
border-color: rgba(15, 23, 42, 0.35);
|
|
780
|
+
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
|
781
|
+
color: white;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.toastify-pro.light .toast-btn-confirm:hover {
|
|
785
|
+
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
|
786
|
+
border-color: rgba(15, 23, 42, 0.5);
|
|
787
|
+
box-shadow: 0 8px 28px rgba(15, 23, 42, 0.3);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
@media (max-width: 640px) {
|
|
791
|
+
.toastify-pro.confirmation {
|
|
792
|
+
min-width: 280px;
|
|
793
|
+
max-width: calc(100vw - 32px);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
479
796
|
`;
|
|
480
797
|
document.head.appendChild(style);
|
|
481
798
|
} catch (error) {
|
|
@@ -716,6 +1033,444 @@ class ToastifyPro {
|
|
|
716
1033
|
}
|
|
717
1034
|
this.show(msg, "light", opts);
|
|
718
1035
|
}
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Shows a confirmation toast with confirm/cancel buttons
|
|
1039
|
+
* @param {string} message - Main confirmation question
|
|
1040
|
+
* @param {string|Function|Object} descriptionOrCallback - Description text, callback function, or options object
|
|
1041
|
+
* @param {Function} callback - Callback function (if description provided)
|
|
1042
|
+
*/
|
|
1043
|
+
conf(message, descriptionOrCallback, callback) {
|
|
1044
|
+
// Check if there's already an active confirmation (GLOBAL CHECK)
|
|
1045
|
+
if (globalActiveConfirmation && globalActiveConfirmation.element && globalActiveConfirmation.element.parentNode) {
|
|
1046
|
+
// Trigger shake animation on existing confirmation toast element
|
|
1047
|
+
const existingToast = globalActiveConfirmation.element;
|
|
1048
|
+
existingToast.classList.remove('shake');
|
|
1049
|
+
// Force reflow to restart animation
|
|
1050
|
+
void existingToast.offsetWidth;
|
|
1051
|
+
existingToast.classList.add('shake');
|
|
1052
|
+
|
|
1053
|
+
// Remove shake class after animation completes
|
|
1054
|
+
setTimeout(() => {
|
|
1055
|
+
if (existingToast && existingToast.parentNode) {
|
|
1056
|
+
existingToast.classList.remove('shake');
|
|
1057
|
+
}
|
|
1058
|
+
}, 600);
|
|
1059
|
+
|
|
1060
|
+
return globalActiveConfirmation;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// Parse arguments to support multiple usage patterns
|
|
1064
|
+
let description = '';
|
|
1065
|
+
let options = {};
|
|
1066
|
+
let resultCallback = null;
|
|
1067
|
+
|
|
1068
|
+
// Pattern 1: conf('message', callback)
|
|
1069
|
+
if (typeof descriptionOrCallback === 'function' && !callback) {
|
|
1070
|
+
resultCallback = descriptionOrCallback;
|
|
1071
|
+
}
|
|
1072
|
+
// Pattern 2: conf('message', 'description', callback)
|
|
1073
|
+
else if (typeof descriptionOrCallback === 'string' && typeof callback === 'function') {
|
|
1074
|
+
description = descriptionOrCallback;
|
|
1075
|
+
resultCallback = callback;
|
|
1076
|
+
}
|
|
1077
|
+
// Pattern 3: conf('message', options) with onConfirm/onCancel
|
|
1078
|
+
else if (typeof descriptionOrCallback === 'object' && descriptionOrCallback !== null) {
|
|
1079
|
+
options = descriptionOrCallback;
|
|
1080
|
+
description = options.description || '';
|
|
1081
|
+
|
|
1082
|
+
// Use onConfirm/onCancel if provided, otherwise use callback parameter
|
|
1083
|
+
if (options.onConfirm || options.onCancel) {
|
|
1084
|
+
// Don't use the callback parameter if onConfirm/onCancel are provided
|
|
1085
|
+
resultCallback = null;
|
|
1086
|
+
} else if (typeof callback === 'function') {
|
|
1087
|
+
resultCallback = callback;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
// Pattern 4: conf('message', 'description', options) - legacy support
|
|
1091
|
+
else if (typeof descriptionOrCallback === 'string' && typeof callback === 'object') {
|
|
1092
|
+
description = descriptionOrCallback;
|
|
1093
|
+
options = callback || {};
|
|
1094
|
+
// In this case, no unified callback, rely on onConfirm/onCancel
|
|
1095
|
+
resultCallback = null;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// Default options for confirmation
|
|
1099
|
+
const confirmOptions = {
|
|
1100
|
+
timeout: 0, // No auto-dismiss for confirmations
|
|
1101
|
+
allowClose: false, // No close button, must choose
|
|
1102
|
+
confirmText: options.confirmText || 'Confirm',
|
|
1103
|
+
cancelText: options.cancelText || 'Cancel',
|
|
1104
|
+
theme: options.theme || options.color || 'dark', // Support both theme and color for backward compatibility
|
|
1105
|
+
position: options.position || 'center', // Default to center for confirmations
|
|
1106
|
+
primaryColor: options.primaryColor || null,
|
|
1107
|
+
secondaryColor: options.secondaryColor || null,
|
|
1108
|
+
loading: options.loading || false, // Support external loading state (for React/Vue)
|
|
1109
|
+
...options
|
|
1110
|
+
};
|
|
1111
|
+
|
|
1112
|
+
// Validate and set theme to only dark or light
|
|
1113
|
+
if (confirmOptions.theme === 'light' || confirmOptions.theme === 'white') {
|
|
1114
|
+
confirmOptions.theme = 'light';
|
|
1115
|
+
} else {
|
|
1116
|
+
confirmOptions.theme = 'dark'; // Default to dark for all other values
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// Helper function to determine if a color is light or dark
|
|
1120
|
+
const isLightColor = (color) => {
|
|
1121
|
+
if (!color) return false;
|
|
1122
|
+
const hex = color.replace('#', '');
|
|
1123
|
+
const r = parseInt(hex.substr(0, 2), 16);
|
|
1124
|
+
const g = parseInt(hex.substr(2, 2), 16);
|
|
1125
|
+
const b = parseInt(hex.substr(4, 2), 16);
|
|
1126
|
+
const brightness = ((r * 299) + (g * 587) + (b * 114)) / 1000;
|
|
1127
|
+
return brightness > 155;
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
// Determine text color based on background
|
|
1131
|
+
let textColor = confirmOptions.theme === 'light' ? '#1e293b' : 'white';
|
|
1132
|
+
if (confirmOptions.primaryColor) {
|
|
1133
|
+
textColor = isLightColor(confirmOptions.primaryColor) ? '#1e293b' : 'white';
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// Validate position for confirmation toast
|
|
1137
|
+
const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center', 'center'];
|
|
1138
|
+
if (!validPositions.includes(confirmOptions.position)) {
|
|
1139
|
+
console.warn(`ToastifyPro: Invalid confirmation position "${confirmOptions.position}". Using default position.`);
|
|
1140
|
+
confirmOptions.position = this.defaultOptions.position;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// Get or create container for the specified position
|
|
1144
|
+
let confirmContainer = document.querySelector(`.toastify-pro-container.${confirmOptions.position}`);
|
|
1145
|
+
|
|
1146
|
+
if (!confirmContainer) {
|
|
1147
|
+
try {
|
|
1148
|
+
confirmContainer = document.createElement("div");
|
|
1149
|
+
confirmContainer.className = `toastify-pro-container ${confirmOptions.position}`;
|
|
1150
|
+
document.body.appendChild(confirmContainer);
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
console.warn('ToastifyPro: Failed to create confirmation container. Using default container.');
|
|
1153
|
+
confirmContainer = this.container;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// Create control functions for loading state
|
|
1158
|
+
let confirmBtnElement = null;
|
|
1159
|
+
let cancelBtnElement = null;
|
|
1160
|
+
let closeBtnElement = null;
|
|
1161
|
+
let isLoading = false;
|
|
1162
|
+
let useLoading = false; // Track if user wants loading behavior
|
|
1163
|
+
let toastElement = null; // Reference to toast element
|
|
1164
|
+
|
|
1165
|
+
const setLoading = (loading) => {
|
|
1166
|
+
useLoading = true; // User is manually controlling loading
|
|
1167
|
+
isLoading = loading;
|
|
1168
|
+
if (confirmBtnElement) {
|
|
1169
|
+
if (loading) {
|
|
1170
|
+
confirmBtnElement.classList.add('loading');
|
|
1171
|
+
confirmBtnElement.disabled = true;
|
|
1172
|
+
} else {
|
|
1173
|
+
confirmBtnElement.classList.remove('loading');
|
|
1174
|
+
confirmBtnElement.disabled = false;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
// Disable/enable cancel and close buttons during loading
|
|
1178
|
+
if (cancelBtnElement) {
|
|
1179
|
+
cancelBtnElement.disabled = loading;
|
|
1180
|
+
cancelBtnElement.style.opacity = loading ? '0.5' : '1';
|
|
1181
|
+
cancelBtnElement.style.cursor = loading ? 'not-allowed' : 'pointer';
|
|
1182
|
+
}
|
|
1183
|
+
if (closeBtnElement) {
|
|
1184
|
+
closeBtnElement.style.opacity = loading ? '0.3' : '0.5';
|
|
1185
|
+
closeBtnElement.style.cursor = loading ? 'not-allowed' : 'pointer';
|
|
1186
|
+
closeBtnElement.style.pointerEvents = loading ? 'none' : 'auto';
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
const closeConfirmation = () => {
|
|
1191
|
+
if (toastElement && toastElement.parentNode) {
|
|
1192
|
+
globalActiveConfirmation = null;
|
|
1193
|
+
this.removeToast(toastElement);
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
|
|
1197
|
+
// Helper function to handle confirmation result
|
|
1198
|
+
const handleConfirmation = async (confirmed) => {
|
|
1199
|
+
if (confirmed) {
|
|
1200
|
+
// Call onConfirm if provided
|
|
1201
|
+
if (options.onConfirm && typeof options.onConfirm === 'function') {
|
|
1202
|
+
try {
|
|
1203
|
+
const result = options.onConfirm({ setLoading, close: closeConfirmation });
|
|
1204
|
+
// Check if it's a promise
|
|
1205
|
+
if (result && typeof result.then === 'function') {
|
|
1206
|
+
// If user didn't manually set loading, auto-set it
|
|
1207
|
+
if (!useLoading) {
|
|
1208
|
+
setLoading(true);
|
|
1209
|
+
}
|
|
1210
|
+
await result;
|
|
1211
|
+
// Auto-close after promise resolves if user didn't manually close
|
|
1212
|
+
if (toastElement && toastElement.parentNode) {
|
|
1213
|
+
setLoading(false);
|
|
1214
|
+
closeConfirmation();
|
|
1215
|
+
}
|
|
1216
|
+
} else {
|
|
1217
|
+
// Synchronous callback - only close if user didn't start loading
|
|
1218
|
+
if (!useLoading) {
|
|
1219
|
+
closeConfirmation();
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
console.error('ToastifyPro: Error in onConfirm callback:', error);
|
|
1224
|
+
setLoading(false);
|
|
1225
|
+
closeConfirmation();
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
// Call unified callback if provided
|
|
1229
|
+
else if (resultCallback && typeof resultCallback === 'function') {
|
|
1230
|
+
try {
|
|
1231
|
+
const result = resultCallback(true, { setLoading, close: closeConfirmation });
|
|
1232
|
+
if (result && typeof result.then === 'function') {
|
|
1233
|
+
if (!useLoading) {
|
|
1234
|
+
setLoading(true);
|
|
1235
|
+
}
|
|
1236
|
+
await result;
|
|
1237
|
+
if (toastElement && toastElement.parentNode) {
|
|
1238
|
+
setLoading(false);
|
|
1239
|
+
closeConfirmation();
|
|
1240
|
+
}
|
|
1241
|
+
} else {
|
|
1242
|
+
if (!useLoading) {
|
|
1243
|
+
closeConfirmation();
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
console.error('ToastifyPro: Error in confirmation callback:', error);
|
|
1248
|
+
setLoading(false);
|
|
1249
|
+
closeConfirmation();
|
|
1250
|
+
}
|
|
1251
|
+
} else {
|
|
1252
|
+
// No callback - just close
|
|
1253
|
+
closeConfirmation();
|
|
1254
|
+
}
|
|
1255
|
+
} else {
|
|
1256
|
+
// Cancel - no loading needed, check if not currently loading
|
|
1257
|
+
if (isLoading) return; // Don't allow cancel while loading
|
|
1258
|
+
|
|
1259
|
+
// Call onCancel if provided
|
|
1260
|
+
if (options.onCancel && typeof options.onCancel === 'function') {
|
|
1261
|
+
try {
|
|
1262
|
+
options.onCancel();
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
console.error('ToastifyPro: Error in onCancel callback:', error);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
// Call unified callback if provided
|
|
1268
|
+
if (resultCallback && typeof resultCallback === 'function') {
|
|
1269
|
+
try {
|
|
1270
|
+
resultCallback(false);
|
|
1271
|
+
} catch (error) {
|
|
1272
|
+
console.error('ToastifyPro: Error in confirmation callback:', error);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
closeConfirmation();
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
try {
|
|
1280
|
+
// Create confirmation toast element
|
|
1281
|
+
const toast = document.createElement("div");
|
|
1282
|
+
toast.className = `toastify-pro confirmation ${confirmOptions.theme}`;
|
|
1283
|
+
|
|
1284
|
+
// Store reference to toast element
|
|
1285
|
+
toastElement = toast;
|
|
1286
|
+
|
|
1287
|
+
// Apply custom colors if provided
|
|
1288
|
+
if (confirmOptions.primaryColor) {
|
|
1289
|
+
const primary = confirmOptions.primaryColor;
|
|
1290
|
+
const secondary = confirmOptions.secondaryColor;
|
|
1291
|
+
|
|
1292
|
+
if (secondary) {
|
|
1293
|
+
// Both colors provided - create gradient
|
|
1294
|
+
toast.style.background = `linear-gradient(135deg, ${primary} 0%, ${secondary} 100%)`;
|
|
1295
|
+
} else {
|
|
1296
|
+
// Only primary color - solid with slight transparency
|
|
1297
|
+
toast.style.background = primary;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Set text color based on background brightness
|
|
1301
|
+
toast.style.color = textColor;
|
|
1302
|
+
|
|
1303
|
+
// Adjust border color to match
|
|
1304
|
+
const borderOpacity = isLightColor(primary) ? '0.2' : '0.15';
|
|
1305
|
+
toast.style.borderColor = `rgba(255, 255, 255, ${borderOpacity})`;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// Create close button for confirmation
|
|
1309
|
+
const closeBtn = document.createElement("span");
|
|
1310
|
+
closeBtn.className = "conf-close-btn";
|
|
1311
|
+
closeBtn.innerHTML = "×";
|
|
1312
|
+
closeBtn.setAttribute('aria-label', 'Cancel confirmation');
|
|
1313
|
+
closeBtn.onclick = () => {
|
|
1314
|
+
if (!isLoading) {
|
|
1315
|
+
handleConfirmation(false);
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
if (confirmOptions.primaryColor) {
|
|
1319
|
+
closeBtn.style.color = textColor;
|
|
1320
|
+
}
|
|
1321
|
+
closeBtnElement = closeBtn;
|
|
1322
|
+
toast.appendChild(closeBtn);
|
|
1323
|
+
|
|
1324
|
+
// Create icon wrapper
|
|
1325
|
+
const iconWrapper = document.createElement("div");
|
|
1326
|
+
iconWrapper.className = "toast-icon";
|
|
1327
|
+
iconWrapper.innerHTML = this.getIconSVG('info'); // Default to info icon
|
|
1328
|
+
if (confirmOptions.primaryColor) {
|
|
1329
|
+
iconWrapper.style.color = textColor;
|
|
1330
|
+
}
|
|
1331
|
+
toast.appendChild(iconWrapper);
|
|
1332
|
+
|
|
1333
|
+
// Create content wrapper
|
|
1334
|
+
const contentWrapper = document.createElement("div");
|
|
1335
|
+
contentWrapper.className = "toast-content";
|
|
1336
|
+
|
|
1337
|
+
// Main message
|
|
1338
|
+
const messageElement = document.createElement("div");
|
|
1339
|
+
messageElement.className = "toast-message";
|
|
1340
|
+
messageElement.textContent = message.substring(0, this.defaultOptions.maxLength);
|
|
1341
|
+
if (confirmOptions.primaryColor) {
|
|
1342
|
+
messageElement.style.color = textColor;
|
|
1343
|
+
}
|
|
1344
|
+
contentWrapper.appendChild(messageElement);
|
|
1345
|
+
|
|
1346
|
+
// Optional description
|
|
1347
|
+
if (description) {
|
|
1348
|
+
const descriptionElement = document.createElement("div");
|
|
1349
|
+
descriptionElement.className = "toast-description";
|
|
1350
|
+
descriptionElement.textContent = description.substring(0, this.defaultOptions.maxLength * 2);
|
|
1351
|
+
if (confirmOptions.primaryColor) {
|
|
1352
|
+
descriptionElement.style.color = textColor;
|
|
1353
|
+
}
|
|
1354
|
+
contentWrapper.appendChild(descriptionElement);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
toast.appendChild(contentWrapper);
|
|
1358
|
+
|
|
1359
|
+
// Create action buttons container
|
|
1360
|
+
const actionsWrapper = document.createElement("div");
|
|
1361
|
+
actionsWrapper.className = "toast-actions";
|
|
1362
|
+
|
|
1363
|
+
// Cancel button
|
|
1364
|
+
const cancelBtn = document.createElement("button");
|
|
1365
|
+
cancelBtn.className = "toast-btn toast-btn-cancel";
|
|
1366
|
+
cancelBtn.textContent = confirmOptions.cancelText;
|
|
1367
|
+
cancelBtn.onclick = () => {
|
|
1368
|
+
if (!isLoading) {
|
|
1369
|
+
handleConfirmation(false);
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1373
|
+
// Store cancel button reference
|
|
1374
|
+
cancelBtnElement = cancelBtn;
|
|
1375
|
+
|
|
1376
|
+
// Style cancel button with custom colors
|
|
1377
|
+
if (confirmOptions.primaryColor) {
|
|
1378
|
+
const isLight = isLightColor(confirmOptions.primaryColor);
|
|
1379
|
+
cancelBtn.style.background = isLight ? 'rgba(15, 23, 42, 0.08)' : 'rgba(255, 255, 255, 0.1)';
|
|
1380
|
+
cancelBtn.style.color = textColor;
|
|
1381
|
+
cancelBtn.style.borderColor = isLight ? 'rgba(15, 23, 42, 0.2)' : 'rgba(255, 255, 255, 0.25)';
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Confirm button
|
|
1385
|
+
const confirmBtn = document.createElement("button");
|
|
1386
|
+
confirmBtn.className = `toast-btn toast-btn-confirm`;
|
|
1387
|
+
|
|
1388
|
+
// Create spinner element
|
|
1389
|
+
const spinner = document.createElement("span");
|
|
1390
|
+
spinner.className = "btn-spinner";
|
|
1391
|
+
confirmBtn.appendChild(spinner);
|
|
1392
|
+
|
|
1393
|
+
// Create text wrapper
|
|
1394
|
+
const textWrapper = document.createElement("span");
|
|
1395
|
+
textWrapper.className = "btn-text";
|
|
1396
|
+
textWrapper.textContent = confirmOptions.confirmText;
|
|
1397
|
+
confirmBtn.appendChild(textWrapper);
|
|
1398
|
+
|
|
1399
|
+
confirmBtn.onclick = () => {
|
|
1400
|
+
if (!isLoading) {
|
|
1401
|
+
handleConfirmation(true);
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
|
|
1405
|
+
// Store reference for loading state control
|
|
1406
|
+
confirmBtnElement = confirmBtn;
|
|
1407
|
+
|
|
1408
|
+
// Style confirm button with custom colors
|
|
1409
|
+
if (confirmOptions.primaryColor) {
|
|
1410
|
+
const primary = confirmOptions.primaryColor;
|
|
1411
|
+
const secondary = confirmOptions.secondaryColor;
|
|
1412
|
+
const isLight = isLightColor(primary);
|
|
1413
|
+
|
|
1414
|
+
if (secondary) {
|
|
1415
|
+
// Gradient confirm button
|
|
1416
|
+
confirmBtn.style.background = `linear-gradient(135deg, ${primary} 0%, ${secondary} 100%)`;
|
|
1417
|
+
} else {
|
|
1418
|
+
// Solid color confirm button with enhanced styling
|
|
1419
|
+
confirmBtn.style.background = primary;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// Determine button text color (always use contrasting color for readability)
|
|
1423
|
+
confirmBtn.style.color = isLight ? '#1e293b' : 'white';
|
|
1424
|
+
confirmBtn.style.borderColor = isLight ? 'rgba(15, 23, 42, 0.3)' : 'rgba(255, 255, 255, 0.4)';
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
actionsWrapper.appendChild(cancelBtn);
|
|
1428
|
+
actionsWrapper.appendChild(confirmBtn);
|
|
1429
|
+
toast.appendChild(actionsWrapper);
|
|
1430
|
+
|
|
1431
|
+
// Add toast to the specified container (not default container)
|
|
1432
|
+
confirmContainer.appendChild(toast);
|
|
1433
|
+
|
|
1434
|
+
// Create control object
|
|
1435
|
+
const controlObject = {
|
|
1436
|
+
element: toast,
|
|
1437
|
+
setLoading: setLoading,
|
|
1438
|
+
close: closeConfirmation
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
// Store as global active confirmation (with control object)
|
|
1442
|
+
globalActiveConfirmation = controlObject;
|
|
1443
|
+
|
|
1444
|
+
// Apply initial loading state if provided (for React/Vue)
|
|
1445
|
+
if (confirmOptions.loading) {
|
|
1446
|
+
setLoading(true);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// Entrance animation
|
|
1450
|
+
setTimeout(() => {
|
|
1451
|
+
toast.classList.add("show");
|
|
1452
|
+
const icon = toast.querySelector('.toast-icon');
|
|
1453
|
+
if (icon) {
|
|
1454
|
+
icon.style.animation = 'iconBounce 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
|
|
1455
|
+
}
|
|
1456
|
+
}, 10);
|
|
1457
|
+
|
|
1458
|
+
// Return control object with toast element and control functions
|
|
1459
|
+
return controlObject;
|
|
1460
|
+
} catch (error) {
|
|
1461
|
+
console.error('ToastifyPro: Failed to create confirmation toast:', error);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/**
|
|
1466
|
+
* Alias for conf() method - shows a confirmation toast
|
|
1467
|
+
* @param {string} message - Main confirmation question
|
|
1468
|
+
* @param {string|Function|Object} descriptionOrCallback - Description text, callback function, or options object
|
|
1469
|
+
* @param {Function} callback - Callback function (if description provided)
|
|
1470
|
+
*/
|
|
1471
|
+
confirm(message, descriptionOrCallback, callback) {
|
|
1472
|
+
return this.conf(message, descriptionOrCallback, callback);
|
|
1473
|
+
}
|
|
719
1474
|
}
|
|
720
1475
|
|
|
721
1476
|
/**
|