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