toastify-pro 1.3.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.
@@ -15,11 +15,18 @@
15
15
  * - Confirmation dialogs with customizable buttons and callbacks
16
16
  * - Center position support for enhanced focus
17
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
18
21
  *
19
- * @version 1.3.0
22
+ * @version 1.4.0
20
23
  * @author ToastifyPro Team
21
24
  * @license MIT
22
25
  */
26
+
27
+ // Global active confirmation tracker (shared across all instances)
28
+ let globalActiveConfirmation = null;
29
+
23
30
  class ToastifyPro {
24
31
  /**
25
32
  * Creates a new ToastifyPro instance
@@ -299,6 +306,26 @@ class ToastifyPro {
299
306
  100% { transform: translateX(100%); }
300
307
  }
301
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
+
302
329
  .toastify-pro.show {
303
330
  opacity: 1;
304
331
  transform: scale(1);
@@ -401,6 +428,11 @@ class ToastifyPro {
401
428
  opacity: 0;
402
429
  }
403
430
  }
431
+
432
+ @keyframes spin {
433
+ 0% { transform: rotate(0deg); }
434
+ 100% { transform: rotate(360deg); }
435
+ }
404
436
 
405
437
  .toastify-pro .toast-icon svg {
406
438
  width: 18px;
@@ -481,15 +513,23 @@ class ToastifyPro {
481
513
  .toastify-pro-container.bottom-right { right: 16px; }
482
514
  }
483
515
 
484
- /* Confirmation Toast Styles */
516
+ /* Confirmation Toast Styles - Enhanced Modern Design */
485
517
  .toastify-pro.confirmation {
486
- min-width: 320px;
487
- max-width: 450px;
488
- padding: 24px;
518
+ min-width: 380px;
519
+ max-width: 500px;
520
+ padding: 32px 28px 28px;
489
521
  flex-direction: column;
490
522
  align-items: stretch;
491
- gap: 20px;
523
+ gap: 24px;
492
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;
493
533
  }
494
534
 
495
535
  /* Hide progress bar for confirmation toasts */
@@ -497,62 +537,94 @@ class ToastifyPro {
497
537
  display: none;
498
538
  }
499
539
 
540
+ /* Shimmer effect for confirmation toasts */
541
+ .toastify-pro.confirmation::before {
542
+ opacity: 0.5;
543
+ }
544
+
500
545
  /* Close button for confirmation dialogs */
501
546
  .toastify-pro.confirmation .conf-close-btn {
502
547
  position: absolute;
503
- top: 12px;
504
- right: 12px;
548
+ top: 14px;
549
+ right: 14px;
505
550
  cursor: pointer;
506
- font-size: 18px;
551
+ font-size: 20px;
507
552
  color: inherit;
508
- opacity: 0.6;
509
- padding: 4px;
510
- border-radius: 50%;
511
- transition: all 0.2s ease;
512
- width: 24px;
513
- height: 24px;
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;
514
559
  display: flex;
515
560
  align-items: center;
516
561
  justify-content: center;
517
- background: rgba(255, 255, 255, 0.1);
562
+ background: rgba(255, 255, 255, 0.08);
518
563
  backdrop-filter: blur(10px);
519
564
  font-weight: 300;
520
565
  line-height: 1;
521
- border: 1px solid rgba(255, 255, 255, 0.1);
566
+ border: 1px solid rgba(255, 255, 255, 0.12);
522
567
  }
523
568
 
524
569
  .toastify-pro.confirmation .conf-close-btn:hover {
525
- opacity: 1;
526
- background: rgba(255, 255, 255, 0.2);
527
- transform: scale(1.1);
528
- border-color: rgba(255, 255, 255, 0.2);
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);
529
574
  }
530
575
 
531
576
  .toastify-pro.confirmation.light .conf-close-btn {
532
- background: rgba(15, 23, 42, 0.08);
533
- border-color: rgba(15, 23, 42, 0.1);
577
+ background: rgba(15, 23, 42, 0.06);
578
+ border-color: rgba(15, 23, 42, 0.12);
579
+ opacity: 0.6;
534
580
  }
535
581
 
536
582
  .toastify-pro.confirmation.light .conf-close-btn:hover {
537
- background: rgba(15, 23, 42, 0.15);
583
+ background: rgba(15, 23, 42, 0.12);
538
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);
539
607
  }
540
608
 
541
609
  .toastify-pro.confirmation .toast-content {
542
610
  text-align: center;
543
- margin-bottom: 8px;
611
+ margin-bottom: 4px;
544
612
  }
545
613
 
546
614
  .toastify-pro.confirmation .toast-message {
547
- font-weight: 600;
548
- font-size: 16px;
549
- margin-bottom: 6px;
615
+ font-weight: 700;
616
+ font-size: 20px;
617
+ margin-bottom: 8px;
618
+ letter-spacing: -0.02em;
619
+ line-height: 1.3;
550
620
  }
551
621
 
552
622
  .toastify-pro.confirmation .toast-description {
553
- font-size: 14px;
554
- opacity: 0.9;
555
- margin-top: 6px;
623
+ font-size: 15px;
624
+ opacity: 0.85;
625
+ margin-top: 8px;
626
+ line-height: 1.5;
627
+ font-weight: 400;
556
628
  }
557
629
 
558
630
  /* Fix text visibility for dark/light variants */
@@ -568,54 +640,80 @@ class ToastifyPro {
568
640
 
569
641
  .toast-actions {
570
642
  display: flex;
571
- gap: 12px;
572
- margin-top: 8px;
643
+ gap: 14px;
644
+ margin-top: 4px;
573
645
  }
574
646
 
575
647
  .toast-btn {
576
648
  flex: 1;
577
- padding: 10px 16px;
649
+ padding: 14px 20px;
578
650
  border: none;
579
- border-radius: 8px;
651
+ border-radius: 12px;
580
652
  font-weight: 600;
581
- font-size: 14px;
653
+ font-size: 15px;
582
654
  cursor: pointer;
583
- transition: all 0.2s ease;
655
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
584
656
  backdrop-filter: blur(10px);
585
- border: 1px solid rgba(255, 255, 255, 0.2);
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;
586
678
  }
587
679
 
588
680
  .toast-btn:hover {
589
- transform: translateY(-1px);
590
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
681
+ transform: translateY(-2px);
682
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
591
683
  }
592
684
 
593
685
  .toast-btn:active {
594
686
  transform: translateY(0);
687
+ transition: all 0.1s;
595
688
  }
596
689
 
597
690
  .toast-btn-cancel {
598
691
  background: rgba(255, 255, 255, 0.1);
599
- color: rgba(255, 255, 255, 0.8);
600
- border: 1px solid rgba(255, 255, 255, 0.3);
601
- font-weight: 500;
692
+ color: rgba(255, 255, 255, 0.9);
693
+ border: 1.5px solid rgba(255, 255, 255, 0.25);
694
+ font-weight: 600;
602
695
  }
603
696
 
604
697
  .toast-btn-cancel:hover {
605
698
  background: rgba(255, 255, 255, 0.15);
606
- color: rgba(255, 255, 255, 0.9);
607
- border-color: rgba(255, 255, 255, 0.4);
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;
608
707
  }
609
708
 
610
709
  .toast-btn-confirm {
611
710
  color: white;
612
711
  font-weight: 700;
613
- border: 2px solid rgba(255, 255, 255, 0.3);
614
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
712
+ border: 2px solid rgba(255, 255, 255, 0.4);
713
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
615
714
  position: relative;
616
715
  overflow: hidden;
617
- background: linear-gradient(135deg, rgba(15, 23, 42, 0.9), rgba(30, 41, 59, 0.9));
618
- border-color: rgba(148, 163, 184, 0.5);
716
+ background: linear-gradient(135deg, rgba(15, 23, 42, 0.95), rgba(30, 41, 59, 0.95));
619
717
  }
620
718
 
621
719
  .toast-btn-confirm::before {
@@ -625,8 +723,8 @@ class ToastifyPro {
625
723
  left: -100%;
626
724
  width: 100%;
627
725
  height: 100%;
628
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
629
- transition: left 0.5s;
726
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);
727
+ transition: left 0.6s;
630
728
  }
631
729
 
632
730
  .toast-btn-confirm:hover::before {
@@ -635,13 +733,38 @@ class ToastifyPro {
635
733
 
636
734
  .toast-btn-confirm:hover {
637
735
  background: linear-gradient(135deg, rgba(15, 23, 42, 1), rgba(30, 41, 59, 1));
638
- border-color: rgba(148, 163, 184, 0.7);
639
- box-shadow: 0 6px 20px rgba(15, 23, 42, 0.4);
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;
640
763
  }
641
764
 
642
765
  .toastify-pro.light .toast-btn-cancel {
643
766
  background: rgba(15, 23, 42, 0.08);
644
- color: rgba(15, 23, 42, 0.8);
767
+ color: rgba(15, 23, 42, 0.85);
645
768
  border-color: rgba(15, 23, 42, 0.2);
646
769
  }
647
770
 
@@ -653,15 +776,15 @@ class ToastifyPro {
653
776
 
654
777
  /* Enhanced light theme confirm buttons */
655
778
  .toastify-pro.light .toast-btn-confirm {
656
- border-color: rgba(15, 23, 42, 0.3);
657
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(248, 250, 252, 0.9));
658
- color: #1e293b;
779
+ border-color: rgba(15, 23, 42, 0.35);
780
+ background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
781
+ color: white;
659
782
  }
660
783
 
661
784
  .toastify-pro.light .toast-btn-confirm:hover {
662
- background: linear-gradient(135deg, rgba(255, 255, 255, 1), rgba(248, 250, 252, 1));
663
- border-color: rgba(15, 23, 42, 0.4);
664
- box-shadow: 0 6px 20px rgba(15, 23, 42, 0.2);
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);
665
788
  }
666
789
 
667
790
  @media (max-width: 640px) {
@@ -918,6 +1041,25 @@ class ToastifyPro {
918
1041
  * @param {Function} callback - Callback function (if description provided)
919
1042
  */
920
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
+
921
1063
  // Parse arguments to support multiple usage patterns
922
1064
  let description = '';
923
1065
  let options = {};
@@ -961,6 +1103,9 @@ class ToastifyPro {
961
1103
  cancelText: options.cancelText || 'Cancel',
962
1104
  theme: options.theme || options.color || 'dark', // Support both theme and color for backward compatibility
963
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)
964
1109
  ...options
965
1110
  };
966
1111
 
@@ -971,6 +1116,23 @@ class ToastifyPro {
971
1116
  confirmOptions.theme = 'dark'; // Default to dark for all other values
972
1117
  }
973
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
+
974
1136
  // Validate position for confirmation toast
975
1137
  const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center', 'center'];
976
1138
  if (!validPositions.includes(confirmOptions.position)) {
@@ -992,26 +1154,108 @@ class ToastifyPro {
992
1154
  }
993
1155
  }
994
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
+
995
1197
  // Helper function to handle confirmation result
996
- const handleConfirmation = (confirmed) => {
1198
+ const handleConfirmation = async (confirmed) => {
997
1199
  if (confirmed) {
998
1200
  // Call onConfirm if provided
999
1201
  if (options.onConfirm && typeof options.onConfirm === 'function') {
1000
1202
  try {
1001
- options.onConfirm();
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
+ }
1002
1222
  } catch (error) {
1003
1223
  console.error('ToastifyPro: Error in onConfirm callback:', error);
1224
+ setLoading(false);
1225
+ closeConfirmation();
1004
1226
  }
1005
1227
  }
1006
1228
  // Call unified callback if provided
1007
- if (resultCallback && typeof resultCallback === 'function') {
1229
+ else if (resultCallback && typeof resultCallback === 'function') {
1008
1230
  try {
1009
- resultCallback(true);
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
+ }
1010
1246
  } catch (error) {
1011
1247
  console.error('ToastifyPro: Error in confirmation callback:', error);
1248
+ setLoading(false);
1249
+ closeConfirmation();
1012
1250
  }
1251
+ } else {
1252
+ // No callback - just close
1253
+ closeConfirmation();
1013
1254
  }
1014
1255
  } else {
1256
+ // Cancel - no loading needed, check if not currently loading
1257
+ if (isLoading) return; // Don't allow cancel while loading
1258
+
1015
1259
  // Call onCancel if provided
1016
1260
  if (options.onCancel && typeof options.onCancel === 'function') {
1017
1261
  try {
@@ -1028,6 +1272,7 @@ class ToastifyPro {
1028
1272
  console.error('ToastifyPro: Error in confirmation callback:', error);
1029
1273
  }
1030
1274
  }
1275
+ closeConfirmation();
1031
1276
  }
1032
1277
  };
1033
1278
 
@@ -1035,6 +1280,30 @@ class ToastifyPro {
1035
1280
  // Create confirmation toast element
1036
1281
  const toast = document.createElement("div");
1037
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
+ }
1038
1307
 
1039
1308
  // Create close button for confirmation
1040
1309
  const closeBtn = document.createElement("span");
@@ -1042,15 +1311,23 @@ class ToastifyPro {
1042
1311
  closeBtn.innerHTML = "×";
1043
1312
  closeBtn.setAttribute('aria-label', 'Cancel confirmation');
1044
1313
  closeBtn.onclick = () => {
1045
- handleConfirmation(false);
1046
- this.removeToast(toast);
1314
+ if (!isLoading) {
1315
+ handleConfirmation(false);
1316
+ }
1047
1317
  };
1318
+ if (confirmOptions.primaryColor) {
1319
+ closeBtn.style.color = textColor;
1320
+ }
1321
+ closeBtnElement = closeBtn;
1048
1322
  toast.appendChild(closeBtn);
1049
1323
 
1050
1324
  // Create icon wrapper
1051
1325
  const iconWrapper = document.createElement("div");
1052
1326
  iconWrapper.className = "toast-icon";
1053
1327
  iconWrapper.innerHTML = this.getIconSVG('info'); // Default to info icon
1328
+ if (confirmOptions.primaryColor) {
1329
+ iconWrapper.style.color = textColor;
1330
+ }
1054
1331
  toast.appendChild(iconWrapper);
1055
1332
 
1056
1333
  // Create content wrapper
@@ -1061,6 +1338,9 @@ class ToastifyPro {
1061
1338
  const messageElement = document.createElement("div");
1062
1339
  messageElement.className = "toast-message";
1063
1340
  messageElement.textContent = message.substring(0, this.defaultOptions.maxLength);
1341
+ if (confirmOptions.primaryColor) {
1342
+ messageElement.style.color = textColor;
1343
+ }
1064
1344
  contentWrapper.appendChild(messageElement);
1065
1345
 
1066
1346
  // Optional description
@@ -1068,6 +1348,9 @@ class ToastifyPro {
1068
1348
  const descriptionElement = document.createElement("div");
1069
1349
  descriptionElement.className = "toast-description";
1070
1350
  descriptionElement.textContent = description.substring(0, this.defaultOptions.maxLength * 2);
1351
+ if (confirmOptions.primaryColor) {
1352
+ descriptionElement.style.color = textColor;
1353
+ }
1071
1354
  contentWrapper.appendChild(descriptionElement);
1072
1355
  }
1073
1356
 
@@ -1082,18 +1365,64 @@ class ToastifyPro {
1082
1365
  cancelBtn.className = "toast-btn toast-btn-cancel";
1083
1366
  cancelBtn.textContent = confirmOptions.cancelText;
1084
1367
  cancelBtn.onclick = () => {
1085
- handleConfirmation(false);
1086
- this.removeToast(toast);
1368
+ if (!isLoading) {
1369
+ handleConfirmation(false);
1370
+ }
1087
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
+ }
1088
1383
 
1089
1384
  // Confirm button
1090
1385
  const confirmBtn = document.createElement("button");
1091
1386
  confirmBtn.className = `toast-btn toast-btn-confirm`;
1092
- confirmBtn.textContent = confirmOptions.confirmText;
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
+
1093
1399
  confirmBtn.onclick = () => {
1094
- handleConfirmation(true);
1095
- this.removeToast(toast);
1400
+ if (!isLoading) {
1401
+ handleConfirmation(true);
1402
+ }
1096
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
+ }
1097
1426
 
1098
1427
  actionsWrapper.appendChild(cancelBtn);
1099
1428
  actionsWrapper.appendChild(confirmBtn);
@@ -1101,6 +1430,21 @@ class ToastifyPro {
1101
1430
 
1102
1431
  // Add toast to the specified container (not default container)
1103
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
+ }
1104
1448
 
1105
1449
  // Entrance animation
1106
1450
  setTimeout(() => {
@@ -1111,11 +1455,22 @@ class ToastifyPro {
1111
1455
  }
1112
1456
  }, 10);
1113
1457
 
1114
- return toast;
1458
+ // Return control object with toast element and control functions
1459
+ return controlObject;
1115
1460
  } catch (error) {
1116
1461
  console.error('ToastifyPro: Failed to create confirmation toast:', error);
1117
1462
  }
1118
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
+ }
1119
1474
  }
1120
1475
 
1121
1476
  /**