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.
@@ -17,16 +17,26 @@
17
17
  * - Progress bar with shimmer effects
18
18
  * - Responsive design for mobile devices
19
19
  * - Framework agnostic (works with React, Vue, Angular, etc.)
20
+ * - Confirmation dialogs with customizable buttons and callbacks
21
+ * - Center position support for enhanced focus
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
20
26
  *
21
- * @version 1.2.0
27
+ * @version 1.4.0
22
28
  * @author ToastifyPro Team
23
29
  * @license MIT
24
30
  */
31
+
32
+ // Global active confirmation tracker (shared across all instances)
33
+ let globalActiveConfirmation = null;
34
+
25
35
  class ToastifyPro {
26
36
  /**
27
37
  * Creates a new ToastifyPro instance
28
38
  * @param {Object} options - Configuration options
29
- * @param {string} options.position - Toast position (top-left, top-right, bottom-left, bottom-right, top-center, bottom-center)
39
+ * @param {string} options.position - Toast position (top-left, top-right, bottom-left, bottom-right, top-center, bottom-center, center)
30
40
  * @param {number} options.timeout - Auto-dismiss timeout in milliseconds (0 to disable)
31
41
  * @param {boolean} options.allowClose - Whether to show close button
32
42
  * @param {number} options.maxLength - Maximum message length
@@ -40,14 +50,14 @@
40
50
 
41
51
  // Merge with default options
42
52
  this.defaultOptions = {
43
- position: options.position || "bottom-center", // top-left, top-right, bottom-left, bottom-right, top-center, bottom-center
53
+ position: options.position || "bottom-center", // top-left, top-right, bottom-left, bottom-right, top-center, bottom-center, center
44
54
  timeout: options.timeout || 3000,
45
55
  allowClose: options.allowClose !== false, // default true
46
56
  maxLength: options.maxLength || 100,
47
57
  };
48
58
 
49
59
  // Validate position
50
- const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center'];
60
+ const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center', 'center'];
51
61
  if (!validPositions.includes(this.defaultOptions.position)) {
52
62
  console.warn(`ToastifyPro: Invalid position "${this.defaultOptions.position}". Using "bottom-center".`);
53
63
  this.defaultOptions.position = "bottom-center";
@@ -143,6 +153,7 @@
143
153
  .toastify-pro-container.bottom-right { bottom: 50px; right: 24px; align-items: flex-end; }
144
154
  .toastify-pro-container.top-center { top: 50px; left: 50%; transform: translateX(-50%); }
145
155
  .toastify-pro-container.bottom-center { bottom: 50px; left: 50%; transform: translateX(-50%); }
156
+ .toastify-pro-container.center { top: 50%; left: 50%; transform: translate(-50%, -50%); }
146
157
 
147
158
  .toastify-pro {
148
159
  min-width: 280px;
@@ -300,6 +311,26 @@
300
311
  100% { transform: translateX(100%); }
301
312
  }
302
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
+
303
334
  .toastify-pro.show {
304
335
  opacity: 1;
305
336
  transform: scale(1);
@@ -402,6 +433,11 @@
402
433
  opacity: 0;
403
434
  }
404
435
  }
436
+
437
+ @keyframes spin {
438
+ 0% { transform: rotate(0deg); }
439
+ 100% { transform: rotate(360deg); }
440
+ }
405
441
 
406
442
  .toastify-pro .toast-icon svg {
407
443
  width: 18px;
@@ -481,6 +517,287 @@
481
517
  .toastify-pro-container.top-right,
482
518
  .toastify-pro-container.bottom-right { right: 16px; }
483
519
  }
520
+
521
+ /* Confirmation Toast Styles - Enhanced Modern Design */
522
+ .toastify-pro.confirmation {
523
+ min-width: 380px;
524
+ max-width: 500px;
525
+ padding: 32px 28px 28px;
526
+ flex-direction: column;
527
+ align-items: stretch;
528
+ gap: 24px;
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;
538
+ }
539
+
540
+ /* Hide progress bar for confirmation toasts */
541
+ .toastify-pro.confirmation::after {
542
+ display: none;
543
+ }
544
+
545
+ /* Shimmer effect for confirmation toasts */
546
+ .toastify-pro.confirmation::before {
547
+ opacity: 0.5;
548
+ }
549
+
550
+ /* Close button for confirmation dialogs */
551
+ .toastify-pro.confirmation .conf-close-btn {
552
+ position: absolute;
553
+ top: 14px;
554
+ right: 14px;
555
+ cursor: pointer;
556
+ font-size: 20px;
557
+ color: inherit;
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;
564
+ display: flex;
565
+ align-items: center;
566
+ justify-content: center;
567
+ background: rgba(255, 255, 255, 0.08);
568
+ backdrop-filter: blur(10px);
569
+ font-weight: 300;
570
+ line-height: 1;
571
+ border: 1px solid rgba(255, 255, 255, 0.12);
572
+ }
573
+
574
+ .toastify-pro.confirmation .conf-close-btn:hover {
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);
579
+ }
580
+
581
+ .toastify-pro.confirmation.light .conf-close-btn {
582
+ background: rgba(15, 23, 42, 0.06);
583
+ border-color: rgba(15, 23, 42, 0.12);
584
+ opacity: 0.6;
585
+ }
586
+
587
+ .toastify-pro.confirmation.light .conf-close-btn:hover {
588
+ background: rgba(15, 23, 42, 0.12);
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);
612
+ }
613
+
614
+ .toastify-pro.confirmation .toast-content {
615
+ text-align: center;
616
+ margin-bottom: 4px;
617
+ }
618
+
619
+ .toastify-pro.confirmation .toast-message {
620
+ font-weight: 700;
621
+ font-size: 20px;
622
+ margin-bottom: 8px;
623
+ letter-spacing: -0.02em;
624
+ line-height: 1.3;
625
+ }
626
+
627
+ .toastify-pro.confirmation .toast-description {
628
+ font-size: 15px;
629
+ opacity: 0.85;
630
+ margin-top: 8px;
631
+ line-height: 1.5;
632
+ font-weight: 400;
633
+ }
634
+
635
+ /* Fix text visibility for dark/light variants */
636
+ .toastify-pro.confirmation.dark .toast-message,
637
+ .toastify-pro.confirmation.dark .toast-description {
638
+ color: white;
639
+ }
640
+
641
+ .toastify-pro.confirmation.light .toast-message,
642
+ .toastify-pro.confirmation.light .toast-description {
643
+ color: #1e293b;
644
+ }
645
+
646
+ .toast-actions {
647
+ display: flex;
648
+ gap: 14px;
649
+ margin-top: 4px;
650
+ }
651
+
652
+ .toast-btn {
653
+ flex: 1;
654
+ padding: 14px 20px;
655
+ border: none;
656
+ border-radius: 12px;
657
+ font-weight: 600;
658
+ font-size: 15px;
659
+ cursor: pointer;
660
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
661
+ backdrop-filter: blur(10px);
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;
683
+ }
684
+
685
+ .toast-btn:hover {
686
+ transform: translateY(-2px);
687
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
688
+ }
689
+
690
+ .toast-btn:active {
691
+ transform: translateY(0);
692
+ transition: all 0.1s;
693
+ }
694
+
695
+ .toast-btn-cancel {
696
+ background: rgba(255, 255, 255, 0.1);
697
+ color: rgba(255, 255, 255, 0.9);
698
+ border: 1.5px solid rgba(255, 255, 255, 0.25);
699
+ font-weight: 600;
700
+ }
701
+
702
+ .toast-btn-cancel:hover {
703
+ background: rgba(255, 255, 255, 0.15);
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;
712
+ }
713
+
714
+ .toast-btn-confirm {
715
+ color: white;
716
+ font-weight: 700;
717
+ border: 2px solid rgba(255, 255, 255, 0.4);
718
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
719
+ position: relative;
720
+ overflow: hidden;
721
+ background: linear-gradient(135deg, rgba(15, 23, 42, 0.95), rgba(30, 41, 59, 0.95));
722
+ }
723
+
724
+ .toast-btn-confirm::before {
725
+ content: '';
726
+ position: absolute;
727
+ top: 0;
728
+ left: -100%;
729
+ width: 100%;
730
+ height: 100%;
731
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);
732
+ transition: left 0.6s;
733
+ }
734
+
735
+ .toast-btn-confirm:hover::before {
736
+ left: 100%;
737
+ }
738
+
739
+ .toast-btn-confirm:hover {
740
+ background: linear-gradient(135deg, rgba(15, 23, 42, 1), rgba(30, 41, 59, 1));
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;
768
+ }
769
+
770
+ .toastify-pro.light .toast-btn-cancel {
771
+ background: rgba(15, 23, 42, 0.08);
772
+ color: rgba(15, 23, 42, 0.85);
773
+ border-color: rgba(15, 23, 42, 0.2);
774
+ }
775
+
776
+ .toastify-pro.light .toast-btn-cancel:hover {
777
+ background: rgba(15, 23, 42, 0.12);
778
+ color: rgba(15, 23, 42, 1);
779
+ border-color: rgba(15, 23, 42, 0.3);
780
+ }
781
+
782
+ /* Enhanced light theme confirm buttons */
783
+ .toastify-pro.light .toast-btn-confirm {
784
+ border-color: rgba(15, 23, 42, 0.35);
785
+ background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
786
+ color: white;
787
+ }
788
+
789
+ .toastify-pro.light .toast-btn-confirm:hover {
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);
793
+ }
794
+
795
+ @media (max-width: 640px) {
796
+ .toastify-pro.confirmation {
797
+ min-width: 280px;
798
+ max-width: calc(100vw - 32px);
799
+ }
800
+ }
484
801
  `;
485
802
  document.head.appendChild(style);
486
803
  } catch (error) {
@@ -721,6 +1038,444 @@
721
1038
  }
722
1039
  this.show(msg, "light", opts);
723
1040
  }
1041
+
1042
+ /**
1043
+ * Shows a confirmation toast with confirm/cancel buttons
1044
+ * @param {string} message - Main confirmation question
1045
+ * @param {string|Function|Object} descriptionOrCallback - Description text, callback function, or options object
1046
+ * @param {Function} callback - Callback function (if description provided)
1047
+ */
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
+
1068
+ // Parse arguments to support multiple usage patterns
1069
+ let description = '';
1070
+ let options = {};
1071
+ let resultCallback = null;
1072
+
1073
+ // Pattern 1: conf('message', callback)
1074
+ if (typeof descriptionOrCallback === 'function' && !callback) {
1075
+ resultCallback = descriptionOrCallback;
1076
+ }
1077
+ // Pattern 2: conf('message', 'description', callback)
1078
+ else if (typeof descriptionOrCallback === 'string' && typeof callback === 'function') {
1079
+ description = descriptionOrCallback;
1080
+ resultCallback = callback;
1081
+ }
1082
+ // Pattern 3: conf('message', options) with onConfirm/onCancel
1083
+ else if (typeof descriptionOrCallback === 'object' && descriptionOrCallback !== null) {
1084
+ options = descriptionOrCallback;
1085
+ description = options.description || '';
1086
+
1087
+ // Use onConfirm/onCancel if provided, otherwise use callback parameter
1088
+ if (options.onConfirm || options.onCancel) {
1089
+ // Don't use the callback parameter if onConfirm/onCancel are provided
1090
+ resultCallback = null;
1091
+ } else if (typeof callback === 'function') {
1092
+ resultCallback = callback;
1093
+ }
1094
+ }
1095
+ // Pattern 4: conf('message', 'description', options) - legacy support
1096
+ else if (typeof descriptionOrCallback === 'string' && typeof callback === 'object') {
1097
+ description = descriptionOrCallback;
1098
+ options = callback || {};
1099
+ // In this case, no unified callback, rely on onConfirm/onCancel
1100
+ resultCallback = null;
1101
+ }
1102
+
1103
+ // Default options for confirmation
1104
+ const confirmOptions = {
1105
+ timeout: 0, // No auto-dismiss for confirmations
1106
+ allowClose: false, // No close button, must choose
1107
+ confirmText: options.confirmText || 'Confirm',
1108
+ cancelText: options.cancelText || 'Cancel',
1109
+ theme: options.theme || options.color || 'dark', // Support both theme and color for backward compatibility
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)
1114
+ ...options
1115
+ };
1116
+
1117
+ // Validate and set theme to only dark or light
1118
+ if (confirmOptions.theme === 'light' || confirmOptions.theme === 'white') {
1119
+ confirmOptions.theme = 'light';
1120
+ } else {
1121
+ confirmOptions.theme = 'dark'; // Default to dark for all other values
1122
+ }
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
+
1141
+ // Validate position for confirmation toast
1142
+ const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center', 'center'];
1143
+ if (!validPositions.includes(confirmOptions.position)) {
1144
+ console.warn(`ToastifyPro: Invalid confirmation position "${confirmOptions.position}". Using default position.`);
1145
+ confirmOptions.position = this.defaultOptions.position;
1146
+ }
1147
+
1148
+ // Get or create container for the specified position
1149
+ let confirmContainer = document.querySelector(`.toastify-pro-container.${confirmOptions.position}`);
1150
+
1151
+ if (!confirmContainer) {
1152
+ try {
1153
+ confirmContainer = document.createElement("div");
1154
+ confirmContainer.className = `toastify-pro-container ${confirmOptions.position}`;
1155
+ document.body.appendChild(confirmContainer);
1156
+ } catch (error) {
1157
+ console.warn('ToastifyPro: Failed to create confirmation container. Using default container.');
1158
+ confirmContainer = this.container;
1159
+ }
1160
+ }
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
+
1202
+ // Helper function to handle confirmation result
1203
+ const handleConfirmation = async (confirmed) => {
1204
+ if (confirmed) {
1205
+ // Call onConfirm if provided
1206
+ if (options.onConfirm && typeof options.onConfirm === 'function') {
1207
+ try {
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
+ }
1227
+ } catch (error) {
1228
+ console.error('ToastifyPro: Error in onConfirm callback:', error);
1229
+ setLoading(false);
1230
+ closeConfirmation();
1231
+ }
1232
+ }
1233
+ // Call unified callback if provided
1234
+ else if (resultCallback && typeof resultCallback === 'function') {
1235
+ try {
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
+ }
1251
+ } catch (error) {
1252
+ console.error('ToastifyPro: Error in confirmation callback:', error);
1253
+ setLoading(false);
1254
+ closeConfirmation();
1255
+ }
1256
+ } else {
1257
+ // No callback - just close
1258
+ closeConfirmation();
1259
+ }
1260
+ } else {
1261
+ // Cancel - no loading needed, check if not currently loading
1262
+ if (isLoading) return; // Don't allow cancel while loading
1263
+
1264
+ // Call onCancel if provided
1265
+ if (options.onCancel && typeof options.onCancel === 'function') {
1266
+ try {
1267
+ options.onCancel();
1268
+ } catch (error) {
1269
+ console.error('ToastifyPro: Error in onCancel callback:', error);
1270
+ }
1271
+ }
1272
+ // Call unified callback if provided
1273
+ if (resultCallback && typeof resultCallback === 'function') {
1274
+ try {
1275
+ resultCallback(false);
1276
+ } catch (error) {
1277
+ console.error('ToastifyPro: Error in confirmation callback:', error);
1278
+ }
1279
+ }
1280
+ closeConfirmation();
1281
+ }
1282
+ };
1283
+
1284
+ try {
1285
+ // Create confirmation toast element
1286
+ const toast = document.createElement("div");
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
+ }
1312
+
1313
+ // Create close button for confirmation
1314
+ const closeBtn = document.createElement("span");
1315
+ closeBtn.className = "conf-close-btn";
1316
+ closeBtn.innerHTML = "×";
1317
+ closeBtn.setAttribute('aria-label', 'Cancel confirmation');
1318
+ closeBtn.onclick = () => {
1319
+ if (!isLoading) {
1320
+ handleConfirmation(false);
1321
+ }
1322
+ };
1323
+ if (confirmOptions.primaryColor) {
1324
+ closeBtn.style.color = textColor;
1325
+ }
1326
+ closeBtnElement = closeBtn;
1327
+ toast.appendChild(closeBtn);
1328
+
1329
+ // Create icon wrapper
1330
+ const iconWrapper = document.createElement("div");
1331
+ iconWrapper.className = "toast-icon";
1332
+ iconWrapper.innerHTML = this.getIconSVG('info'); // Default to info icon
1333
+ if (confirmOptions.primaryColor) {
1334
+ iconWrapper.style.color = textColor;
1335
+ }
1336
+ toast.appendChild(iconWrapper);
1337
+
1338
+ // Create content wrapper
1339
+ const contentWrapper = document.createElement("div");
1340
+ contentWrapper.className = "toast-content";
1341
+
1342
+ // Main message
1343
+ const messageElement = document.createElement("div");
1344
+ messageElement.className = "toast-message";
1345
+ messageElement.textContent = message.substring(0, this.defaultOptions.maxLength);
1346
+ if (confirmOptions.primaryColor) {
1347
+ messageElement.style.color = textColor;
1348
+ }
1349
+ contentWrapper.appendChild(messageElement);
1350
+
1351
+ // Optional description
1352
+ if (description) {
1353
+ const descriptionElement = document.createElement("div");
1354
+ descriptionElement.className = "toast-description";
1355
+ descriptionElement.textContent = description.substring(0, this.defaultOptions.maxLength * 2);
1356
+ if (confirmOptions.primaryColor) {
1357
+ descriptionElement.style.color = textColor;
1358
+ }
1359
+ contentWrapper.appendChild(descriptionElement);
1360
+ }
1361
+
1362
+ toast.appendChild(contentWrapper);
1363
+
1364
+ // Create action buttons container
1365
+ const actionsWrapper = document.createElement("div");
1366
+ actionsWrapper.className = "toast-actions";
1367
+
1368
+ // Cancel button
1369
+ const cancelBtn = document.createElement("button");
1370
+ cancelBtn.className = "toast-btn toast-btn-cancel";
1371
+ cancelBtn.textContent = confirmOptions.cancelText;
1372
+ cancelBtn.onclick = () => {
1373
+ if (!isLoading) {
1374
+ handleConfirmation(false);
1375
+ }
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
+ }
1388
+
1389
+ // Confirm button
1390
+ const confirmBtn = document.createElement("button");
1391
+ confirmBtn.className = `toast-btn toast-btn-confirm`;
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
+
1404
+ confirmBtn.onclick = () => {
1405
+ if (!isLoading) {
1406
+ handleConfirmation(true);
1407
+ }
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
+ }
1431
+
1432
+ actionsWrapper.appendChild(cancelBtn);
1433
+ actionsWrapper.appendChild(confirmBtn);
1434
+ toast.appendChild(actionsWrapper);
1435
+
1436
+ // Add toast to the specified container (not default container)
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
+ }
1453
+
1454
+ // Entrance animation
1455
+ setTimeout(() => {
1456
+ toast.classList.add("show");
1457
+ const icon = toast.querySelector('.toast-icon');
1458
+ if (icon) {
1459
+ icon.style.animation = 'iconBounce 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
1460
+ }
1461
+ }, 10);
1462
+
1463
+ // Return control object with toast element and control functions
1464
+ return controlObject;
1465
+ } catch (error) {
1466
+ console.error('ToastifyPro: Failed to create confirmation toast:', error);
1467
+ }
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
+ }
724
1479
  }
725
1480
 
726
1481
  /**