toastify-pro 1.4.0 → 1.5.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.
@@ -9,17 +9,16 @@
9
9
  * - Position-aware car swipe exit animations
10
10
  * - Description support for enhanced messaging
11
11
  * - Six theme variants (success, error, info, warning, dark, light)
12
+ * - Custom color toasts with gradient support (custom method)
12
13
  * - Progress bar with shimmer effects
13
14
  * - Responsive design for mobile devices
14
15
  * - Framework agnostic (works with React, Vue, Angular, etc.)
15
16
  * - Confirmation dialogs with customizable buttons and callbacks
17
+ * - Confirmation overlay with blur effect for focus
16
18
  * - Center position support for enhanced focus
17
19
  * - Independent positioning for confirmations
18
- * - Loading states for async operations
19
- * - Custom gradient colors with primaryColor/secondaryColor
20
- * - Single instance mode with shake animation
21
20
  *
22
- * @version 1.4.0
21
+ * @version 1.5.0
23
22
  * @author ToastifyPro Team
24
23
  * @license MIT
25
24
  */
@@ -35,6 +34,8 @@ class ToastifyPro {
35
34
  * @param {number} options.timeout - Auto-dismiss timeout in milliseconds (0 to disable)
36
35
  * @param {boolean} options.allowClose - Whether to show close button
37
36
  * @param {number} options.maxLength - Maximum message length
37
+ * @param {string} options.primaryColor - Primary color for custom() method
38
+ * @param {string} options.secondaryColor - Secondary color for gradient in custom() method
38
39
  */
39
40
  constructor(options = {}) {
40
41
  // Validate options parameter
@@ -49,6 +50,8 @@ class ToastifyPro {
49
50
  timeout: options.timeout || 3000,
50
51
  allowClose: options.allowClose !== false, // default true
51
52
  maxLength: options.maxLength || 100,
53
+ primaryColor: options.primaryColor || null, // Custom primary color for custom() method
54
+ secondaryColor: options.secondaryColor || null, // Custom secondary color for gradient
52
55
  };
53
56
 
54
57
  // Validate position
@@ -793,6 +796,58 @@ class ToastifyPro {
793
796
  max-width: calc(100vw - 32px);
794
797
  }
795
798
  }
799
+
800
+ /* Custom toast type */
801
+ .toastify-pro.custom {
802
+ border-color: rgba(255, 255, 255, 0.2);
803
+ }
804
+
805
+ .toastify-pro.custom.light-text {
806
+ color: #1e293b;
807
+ }
808
+
809
+ .toastify-pro.custom.light-text .toast-icon {
810
+ background: rgba(15, 23, 42, 0.1);
811
+ }
812
+
813
+ .toastify-pro.custom.light-text .close-btn {
814
+ background: rgba(15, 23, 42, 0.08);
815
+ }
816
+
817
+ .toastify-pro.custom.light-text .close-btn:hover {
818
+ background: rgba(15, 23, 42, 0.15);
819
+ }
820
+
821
+ .toastify-pro.custom.light-text::before {
822
+ background: linear-gradient(90deg,
823
+ rgba(30, 41, 59, 0.8) 0%,
824
+ rgba(30, 41, 59, 0.4) 50%,
825
+ rgba(30, 41, 59, 0.8) 100%);
826
+ }
827
+
828
+ .toastify-pro.custom.light-text::after {
829
+ background: rgba(30, 41, 59, 0.6);
830
+ }
831
+
832
+ /* Confirmation Overlay */
833
+ .toastify-pro-overlay {
834
+ position: fixed;
835
+ top: 0;
836
+ left: 0;
837
+ right: 0;
838
+ bottom: 0;
839
+ background: rgba(0, 0, 0, 0.5);
840
+ backdrop-filter: blur(8px);
841
+ -webkit-backdrop-filter: blur(8px);
842
+ z-index: 9998;
843
+ opacity: 0;
844
+ transition: opacity 0.3s ease;
845
+ pointer-events: auto;
846
+ }
847
+
848
+ .toastify-pro-overlay.show {
849
+ opacity: 1;
850
+ }
796
851
  `;
797
852
  document.head.appendChild(style);
798
853
  } catch (error) {
@@ -1034,6 +1089,168 @@ class ToastifyPro {
1034
1089
  this.show(msg, "light", opts);
1035
1090
  }
1036
1091
 
1092
+ /**
1093
+ * Shows a custom-colored toast notification with gradient support
1094
+ * @param {string} msg - Main message
1095
+ * @param {string|Object} opts - Description string or options object
1096
+ * @param {string} opts.primaryColor - Primary color for the toast
1097
+ * @param {string} opts.secondaryColor - Secondary color for gradient (optional)
1098
+ */
1099
+ custom(msg, opts) {
1100
+ if (typeof opts === 'string') {
1101
+ opts = { description: opts };
1102
+ }
1103
+
1104
+ opts = opts || {};
1105
+
1106
+ // Get colors from options or use default options
1107
+ const primaryColor = opts.primaryColor || this.defaultOptions.primaryColor;
1108
+ const secondaryColor = opts.secondaryColor || this.defaultOptions.secondaryColor;
1109
+
1110
+ // If no custom colors provided, fallback to success style
1111
+ if (!primaryColor) {
1112
+ return this.success(msg, opts);
1113
+ }
1114
+
1115
+ // Helper function to determine if a color is light
1116
+ const isLightColor = (color) => {
1117
+ if (!color) return false;
1118
+ const hex = color.replace('#', '');
1119
+ const r = parseInt(hex.substr(0, 2), 16);
1120
+ const g = parseInt(hex.substr(2, 2), 16);
1121
+ const b = parseInt(hex.substr(4, 2), 16);
1122
+ const brightness = ((r * 299) + (g * 587) + (b * 114)) / 1000;
1123
+ return brightness > 155;
1124
+ };
1125
+
1126
+ // Helper function to lighten or darken a color
1127
+ const adjustColor = (color, percent) => {
1128
+ const hex = color.replace('#', '');
1129
+ const r = parseInt(hex.substr(0, 2), 16);
1130
+ const g = parseInt(hex.substr(2, 2), 16);
1131
+ const b = parseInt(hex.substr(4, 2), 16);
1132
+
1133
+ const adjust = (c) => {
1134
+ const adjusted = Math.round(c + (percent / 100) * (percent > 0 ? (255 - c) : c));
1135
+ return Math.max(0, Math.min(255, adjusted));
1136
+ };
1137
+
1138
+ const newR = adjust(r).toString(16).padStart(2, '0');
1139
+ const newG = adjust(g).toString(16).padStart(2, '0');
1140
+ const newB = adjust(b).toString(16).padStart(2, '0');
1141
+
1142
+ return `#${newR}${newG}${newB}`;
1143
+ };
1144
+
1145
+ // Determine gradient colors
1146
+ let gradientStart = primaryColor;
1147
+ let gradientEnd;
1148
+
1149
+ if (secondaryColor) {
1150
+ // Both colors provided
1151
+ gradientEnd = secondaryColor;
1152
+ } else {
1153
+ // Only primary color - create gradient with lighter/darker shade
1154
+ const isLight = isLightColor(primaryColor);
1155
+ gradientEnd = isLight ? adjustColor(primaryColor, -25) : adjustColor(primaryColor, 25);
1156
+ }
1157
+
1158
+ // Determine text color
1159
+ const needsLightText = isLightColor(primaryColor);
1160
+
1161
+ // Create custom options
1162
+ const customOpts = {
1163
+ ...opts,
1164
+ customGradient: `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)`,
1165
+ customTextLight: needsLightText
1166
+ };
1167
+
1168
+ this.showCustom(msg, customOpts);
1169
+ }
1170
+
1171
+ /**
1172
+ * Internal method to show a custom-styled toast
1173
+ * @param {string} message - Main message text
1174
+ * @param {Object} opts - Options including customGradient and customTextLight
1175
+ */
1176
+ showCustom(message, opts = {}) {
1177
+ if (typeof message !== 'string') {
1178
+ message = String(message);
1179
+ }
1180
+
1181
+ if (!message.trim()) {
1182
+ console.warn('ToastifyPro: Empty message provided.');
1183
+ return;
1184
+ }
1185
+
1186
+ const options = { ...this.defaultOptions, ...opts };
1187
+
1188
+ try {
1189
+ const toast = document.createElement("div");
1190
+ toast.className = `toastify-pro custom${options.customTextLight ? ' light-text' : ''}`;
1191
+
1192
+ // Apply custom gradient
1193
+ if (options.customGradient) {
1194
+ toast.style.background = options.customGradient;
1195
+ }
1196
+
1197
+ if (options.timeout > 0) {
1198
+ toast.style.setProperty('--duration', `${options.timeout}ms`);
1199
+ }
1200
+
1201
+ // Create icon wrapper
1202
+ const iconWrapper = document.createElement("div");
1203
+ iconWrapper.className = "toast-icon";
1204
+ iconWrapper.innerHTML = this.getIconSVG('success'); // Use success icon for custom
1205
+ toast.appendChild(iconWrapper);
1206
+
1207
+ // Create content wrapper
1208
+ const contentWrapper = document.createElement("div");
1209
+ contentWrapper.className = "toast-content";
1210
+
1211
+ const messageElement = document.createElement("div");
1212
+ messageElement.className = "toast-message";
1213
+ messageElement.textContent = message.substring(0, options.maxLength);
1214
+ contentWrapper.appendChild(messageElement);
1215
+
1216
+ if (options.description && typeof options.description === 'string') {
1217
+ const descriptionElement = document.createElement("div");
1218
+ descriptionElement.className = "toast-description";
1219
+ descriptionElement.textContent = options.description.substring(0, options.maxLength * 2);
1220
+ contentWrapper.appendChild(descriptionElement);
1221
+ }
1222
+
1223
+ toast.appendChild(contentWrapper);
1224
+
1225
+ if (options.allowClose) {
1226
+ const closeBtn = document.createElement("span");
1227
+ closeBtn.className = "close-btn";
1228
+ closeBtn.innerHTML = "×";
1229
+ closeBtn.setAttribute('aria-label', 'Close notification');
1230
+ closeBtn.onclick = () => this.removeToast(toast);
1231
+ toast.appendChild(closeBtn);
1232
+ }
1233
+
1234
+ this.container.appendChild(toast);
1235
+
1236
+ setTimeout(() => {
1237
+ toast.classList.add("show");
1238
+ const icon = toast.querySelector('.toast-icon');
1239
+ if (icon) {
1240
+ icon.style.animation = 'iconBounce 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
1241
+ }
1242
+ }, 10);
1243
+
1244
+ if (options.timeout > 0) {
1245
+ setTimeout(() => this.removeToast(toast), options.timeout);
1246
+ }
1247
+
1248
+ return toast;
1249
+ } catch (error) {
1250
+ console.error('ToastifyPro: Failed to create custom toast:', error);
1251
+ }
1252
+ }
1253
+
1037
1254
  /**
1038
1255
  * Shows a confirmation toast with confirm/cancel buttons
1039
1256
  * @param {string} message - Main confirmation question
@@ -1161,6 +1378,34 @@ class ToastifyPro {
1161
1378
  let isLoading = false;
1162
1379
  let useLoading = false; // Track if user wants loading behavior
1163
1380
  let toastElement = null; // Reference to toast element
1381
+ let overlayElement = null; // Reference to overlay element
1382
+
1383
+ // Create overlay for confirmation
1384
+ const createOverlay = () => {
1385
+ overlayElement = document.createElement("div");
1386
+ overlayElement.className = "toastify-pro-overlay";
1387
+ document.body.appendChild(overlayElement);
1388
+
1389
+ // Trigger show animation
1390
+ setTimeout(() => {
1391
+ overlayElement.classList.add("show");
1392
+ }, 10);
1393
+
1394
+ return overlayElement;
1395
+ };
1396
+
1397
+ // Remove overlay
1398
+ const removeOverlay = () => {
1399
+ if (overlayElement && overlayElement.parentNode) {
1400
+ overlayElement.classList.remove("show");
1401
+ setTimeout(() => {
1402
+ if (overlayElement && overlayElement.parentNode) {
1403
+ overlayElement.remove();
1404
+ }
1405
+ overlayElement = null;
1406
+ }, 300);
1407
+ }
1408
+ };
1164
1409
 
1165
1410
  const setLoading = (loading) => {
1166
1411
  useLoading = true; // User is manually controlling loading
@@ -1190,6 +1435,7 @@ class ToastifyPro {
1190
1435
  const closeConfirmation = () => {
1191
1436
  if (toastElement && toastElement.parentNode) {
1192
1437
  globalActiveConfirmation = null;
1438
+ removeOverlay(); // Remove the overlay when closing
1193
1439
  this.removeToast(toastElement);
1194
1440
  }
1195
1441
  };
@@ -1277,6 +1523,9 @@ class ToastifyPro {
1277
1523
  };
1278
1524
 
1279
1525
  try {
1526
+ // Create overlay first
1527
+ createOverlay();
1528
+
1280
1529
  // Create confirmation toast element
1281
1530
  const toast = document.createElement("div");
1282
1531
  toast.className = `toastify-pro confirmation ${confirmOptions.theme}`;