toastify-pro 1.1.0 → 1.3.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.
@@ -1,157 +1,1148 @@
1
+ /**
2
+ * ToastifyPro - Modern Toast Notification Library
3
+ * A beautiful, customizable toast notification library with glassmorphism design,
4
+ * Apple AirDrop-style animations, and car swipe exit effects.
5
+ *
6
+ * Features:
7
+ * - Glassmorphism design with backdrop-filter effects
8
+ * - Apple AirDrop-style entrance animations
9
+ * - Position-aware car swipe exit animations
10
+ * - Description support for enhanced messaging
11
+ * - Six theme variants (success, error, info, warning, dark, light)
12
+ * - Progress bar with shimmer effects
13
+ * - Responsive design for mobile devices
14
+ * - Framework agnostic (works with React, Vue, Angular, etc.)
15
+ * - Confirmation dialogs with customizable buttons and callbacks
16
+ * - Center position support for enhanced focus
17
+ * - Independent positioning for confirmations
18
+ *
19
+ * @version 1.3.0
20
+ * @author ToastifyPro Team
21
+ * @license MIT
22
+ */
1
23
  class ToastifyPro {
24
+ /**
25
+ * Creates a new ToastifyPro instance
26
+ * @param {Object} options - Configuration options
27
+ * @param {string} options.position - Toast position (top-left, top-right, bottom-left, bottom-right, top-center, bottom-center, center)
28
+ * @param {number} options.timeout - Auto-dismiss timeout in milliseconds (0 to disable)
29
+ * @param {boolean} options.allowClose - Whether to show close button
30
+ * @param {number} options.maxLength - Maximum message length
31
+ */
2
32
  constructor(options = {}) {
33
+ // Validate options parameter
34
+ if (typeof options !== 'object' || options === null) {
35
+ console.warn('ToastifyPro: Invalid options parameter. Using defaults.');
36
+ options = {};
37
+ }
38
+
39
+ // Merge with default options
3
40
  this.defaultOptions = {
4
- position: options.position || "bottom-center", // top-left, top-right, bottom-left, bottom-right, top-center, bottom-center
41
+ position: options.position || "bottom-center", // top-left, top-right, bottom-left, bottom-right, top-center, bottom-center, center
5
42
  timeout: options.timeout || 3000,
6
43
  allowClose: options.allowClose !== false, // default true
7
44
  maxLength: options.maxLength || 100,
8
45
  };
9
46
 
10
- // create container only once
47
+ // Validate position
48
+ const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center', 'center'];
49
+ if (!validPositions.includes(this.defaultOptions.position)) {
50
+ console.warn(`ToastifyPro: Invalid position "${this.defaultOptions.position}". Using "bottom-center".`);
51
+ this.defaultOptions.position = "bottom-center";
52
+ }
53
+
54
+ // Check if we're in a browser environment
55
+ if (typeof document === 'undefined') {
56
+ throw new Error('ToastifyPro: This library requires a DOM environment (browser).');
57
+ }
58
+
59
+ // Create or reuse container for this position
11
60
  const existing = document.querySelector(
12
61
  `.toastify-pro-container.${this.defaultOptions.position}`
13
62
  );
63
+
14
64
  if (existing) {
15
65
  this.container = existing;
16
66
  } else {
17
- this.container = document.createElement("div");
18
- this.container.className = `toastify-pro-container ${this.defaultOptions.position}`;
19
- document.body.appendChild(this.container);
67
+ try {
68
+ this.container = document.createElement("div");
69
+ this.container.className = `toastify-pro-container ${this.defaultOptions.position}`;
70
+ document.body.appendChild(this.container);
71
+ } catch (error) {
72
+ throw new Error('ToastifyPro: Failed to create container element. DOM may not be ready.');
73
+ }
20
74
  }
21
75
 
76
+ // Inject styles once
22
77
  this.injectStyles();
23
78
  }
24
79
 
80
+ /**
81
+ * Returns the SVG icon for a given toast type
82
+ * @param {string} type - Toast type (success, error, info, warning, dark, light)
83
+ * @returns {string} SVG icon markup
84
+ */
85
+ getIconSVG(type) {
86
+ // Validate type parameter
87
+ if (typeof type !== 'string') {
88
+ console.warn('ToastifyPro: Invalid icon type. Using default info icon.');
89
+ type = 'info';
90
+ }
91
+
92
+ const icons = {
93
+ success: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
94
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor"/>
95
+ </svg>`,
96
+ error: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
97
+ <path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z" fill="currentColor"/>
98
+ </svg>`,
99
+ info: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
100
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z" fill="currentColor"/>
101
+ </svg>`,
102
+ warning: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
103
+ <path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" fill="currentColor"/>
104
+ </svg>`,
105
+ dark: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
106
+ <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" fill="currentColor"/>
107
+ </svg>`,
108
+ light: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
109
+ <path d="M9 11H7v6h2v-6zm4 0h-2v6h2v-6zm4 0h-2v6h2v-6zm2.5-9H19V1h-2v1H7V1H5v1H4.5C3.11 2 2 3.11 2 4.5v14C2 19.89 3.11 21 4.5 21h15c1.39 0 2.5-1.11 2.5-2.5v-14C22 3.11 20.89 2 19.5 2zm0 16h-15v-11h15v11z" fill="currentColor"/>
110
+ </svg>`
111
+ };
112
+
113
+ return icons[type] || icons.info;
114
+ }
115
+
116
+ /**
117
+ * Injects the CSS styles into the document head
118
+ * Styles include glassmorphism design, animations, and responsive layout
119
+ */
25
120
  injectStyles() {
26
- if (document.getElementById("toastify-pro-styles")) return; // load once
27
- const style = document.createElement("style");
28
- style.id = "toastify-pro-styles";
29
- style.textContent = `
121
+ // Prevent duplicate style injection
122
+ if (document.getElementById("toastify-pro-styles")) return;
123
+
124
+ try {
125
+ const style = document.createElement("style");
126
+ style.id = "toastify-pro-styles";
127
+ style.textContent = `
128
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
129
+
30
130
  .toastify-pro-container {
31
131
  position: fixed;
32
132
  z-index: 9999;
33
133
  display: flex;
34
134
  flex-direction: column;
35
- gap: 10px;
135
+ gap: 16px;
36
136
  pointer-events: none;
37
137
  }
38
- .toastify-pro-container.top-left { top: 20px; left: 20px; align-items: flex-start; }
39
- .toastify-pro-container.top-right { top: 20px; right: 20px; align-items: flex-end; }
40
- .toastify-pro-container.bottom-left { bottom: 20px; left: 20px; align-items: flex-start; }
41
- .toastify-pro-container.bottom-right { bottom: 20px; right: 20px; align-items: flex-end; }
42
- .toastify-pro-container.top-center { top: 20px; left: 50%; transform: translateX(-50%); }
43
- .toastify-pro-container.bottom-center { bottom: 150px; left: 50%; transform: translateX(-50%); }
138
+ .toastify-pro-container.top-left { top: 50px; left: 24px; align-items: flex-start; }
139
+ .toastify-pro-container.top-right { top: 50px; right: 24px; align-items: flex-end; }
140
+ .toastify-pro-container.bottom-left { bottom: 50px; left: 24px; align-items: flex-start; }
141
+ .toastify-pro-container.bottom-right { bottom: 50px; right: 24px; align-items: flex-end; }
142
+ .toastify-pro-container.top-center { top: 50px; left: 50%; transform: translateX(-50%); }
143
+ .toastify-pro-container.bottom-center { bottom: 50px; left: 50%; transform: translateX(-50%); }
144
+ .toastify-pro-container.center { top: 50%; left: 50%; transform: translate(-50%, -50%); }
44
145
 
45
146
  .toastify-pro {
46
- min-width: 220px;
47
- max-width: 320px;
48
- padding: 12px 18px;
49
- border-radius: 20px;
50
- font-size: 14px;
51
- font-family: sans-serif;
147
+ min-width: 280px;
148
+ max-width: 400px;
149
+ padding: 20px 24px;
150
+ border-radius: 16px;
151
+ font-size: 15px;
152
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
153
+ font-weight: 500;
52
154
  color: white;
53
155
  opacity: 0;
54
- transform: translateY(20px);
55
- transition: all 0.3s ease;
156
+ transform: scale(0.3);
157
+ transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
56
158
  pointer-events: auto;
57
159
  position: relative;
58
160
  display: flex;
59
161
  align-items: center;
60
- justify-content: space-between;
61
- gap: 12px;
162
+ gap: 16px;
163
+ backdrop-filter: blur(20px);
164
+ border: 1px solid rgba(255, 255, 255, 0.1);
165
+ box-shadow:
166
+ 0 20px 25px -5px rgba(0, 0, 0, 0.1),
167
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04),
168
+ 0 0 0 1px rgba(255, 255, 255, 0.05);
169
+ overflow: hidden;
170
+ }
171
+
172
+ .toastify-pro::before {
173
+ content: '';
174
+ position: absolute;
175
+ top: 0;
176
+ left: 0;
177
+ right: 0;
178
+ height: 3px;
179
+ background: linear-gradient(90deg,
180
+ rgba(255, 255, 255, 0.8) 0%,
181
+ rgba(255, 255, 255, 0.4) 50%,
182
+ rgba(255, 255, 255, 0.8) 100%);
183
+ animation: shimmer 2s infinite;
184
+ transition: opacity 0.8s ease;
185
+ }
186
+
187
+ .toastify-pro::after {
188
+ content: '';
189
+ position: absolute;
190
+ bottom: 0;
191
+ left: 0;
192
+ height: 3px;
193
+ background: rgba(255, 255, 255, 0.6);
194
+ animation: progress var(--duration, 5s) linear;
195
+ border-radius: 0 0 16px 16px;
196
+ }
197
+
198
+ @keyframes airdropPop {
199
+ 0% {
200
+ opacity: 0;
201
+ transform: scale(0.3) rotateY(-20deg);
202
+ }
203
+ 30% {
204
+ opacity: 0.8;
205
+ transform: scale(1.1) rotateY(10deg);
206
+ }
207
+ 60% {
208
+ opacity: 1;
209
+ transform: scale(0.98) rotateY(-3deg);
210
+ }
211
+ 100% {
212
+ opacity: 1;
213
+ transform: scale(1) rotateY(0deg);
214
+ }
215
+ }
216
+
217
+ @keyframes carSwipeBottom {
218
+ 0% {
219
+ opacity: 1;
220
+ transform: scale(1) translateY(0);
221
+ }
222
+ 15% {
223
+ opacity: 1;
224
+ transform: scale(1.02) translateY(-8px);
225
+ }
226
+ 100% {
227
+ opacity: 0;
228
+ transform: scale(0.8) translateY(200px);
229
+ }
230
+ }
231
+
232
+ @keyframes carSwipeTop {
233
+ 0% {
234
+ opacity: 1;
235
+ transform: scale(1) translateY(0);
236
+ }
237
+ 15% {
238
+ opacity: 1;
239
+ transform: scale(1.02) translateY(8px);
240
+ }
241
+ 100% {
242
+ opacity: 0;
243
+ transform: scale(0.8) translateY(-200px);
244
+ }
245
+ }
246
+
247
+ @keyframes carSwipeLeft {
248
+ 0% {
249
+ opacity: 1;
250
+ transform: scale(1) translateX(0);
251
+ }
252
+ 15% {
253
+ opacity: 1;
254
+ transform: scale(1.02) translateX(8px);
255
+ }
256
+ 100% {
257
+ opacity: 0;
258
+ transform: scale(0.8) translateX(-300px);
259
+ }
260
+ }
261
+
262
+ @keyframes carSwipeRight {
263
+ 0% {
264
+ opacity: 1;
265
+ transform: scale(1) translateX(0);
266
+ }
267
+ 15% {
268
+ opacity: 1;
269
+ transform: scale(1.02) translateX(-8px);
270
+ }
271
+ 100% {
272
+ opacity: 0;
273
+ transform: scale(0.8) translateX(300px);
274
+ }
275
+ }
276
+
277
+ @keyframes carSwipeCenter {
278
+ 0% {
279
+ opacity: 1;
280
+ transform: scale(1) translateY(0);
281
+ }
282
+ 15% {
283
+ opacity: 1;
284
+ transform: scale(1.02) translateY(-5px);
285
+ }
286
+ 100% {
287
+ opacity: 0;
288
+ transform: scale(0.6) translateY(150px);
289
+ }
290
+ }
291
+
292
+ @keyframes progress {
293
+ 0% { width: 100%; }
294
+ 100% { width: 0%; }
295
+ }
296
+
297
+ @keyframes shimmer {
298
+ 0% { transform: translateX(-100%); }
299
+ 100% { transform: translateX(100%); }
300
+ }
301
+
302
+ .toastify-pro.show {
303
+ opacity: 1;
304
+ transform: scale(1);
305
+ animation: airdropPop 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
306
+ }
307
+
308
+ .toastify-pro.success {
309
+ background: linear-gradient(135deg,
310
+ rgba(34, 197, 94, 0.9) 0%,
311
+ rgba(21, 128, 61, 0.9) 100%);
312
+ border-color: rgba(34, 197, 94, 0.3);
313
+ }
314
+
315
+ .toastify-pro.error {
316
+ background: linear-gradient(135deg,
317
+ rgba(239, 68, 68, 0.9) 0%,
318
+ rgba(185, 28, 28, 0.9) 100%);
319
+ border-color: rgba(239, 68, 68, 0.3);
320
+ }
321
+
322
+ .toastify-pro.info {
323
+ background: linear-gradient(135deg,
324
+ rgba(59, 130, 246, 0.9) 0%,
325
+ rgba(29, 78, 216, 0.9) 100%);
326
+ border-color: rgba(59, 130, 246, 0.3);
327
+ }
328
+
329
+ .toastify-pro.warning {
330
+ background: linear-gradient(135deg,
331
+ rgba(245, 158, 11, 0.9) 0%,
332
+ rgba(217, 119, 6, 0.9) 100%);
333
+ border-color: rgba(245, 158, 11, 0.3);
334
+ }
335
+
336
+ .toastify-pro.dark {
337
+ background: linear-gradient(135deg,
338
+ rgba(15, 23, 42, 0.95) 0%,
339
+ rgba(30, 41, 59, 0.95) 100%);
340
+ border-color: rgba(148, 163, 184, 0.2);
341
+ }
342
+
343
+ .toastify-pro.light {
344
+ background: linear-gradient(135deg,
345
+ rgba(255, 255, 255, 0.95) 0%,
346
+ rgba(248, 250, 252, 0.95) 100%);
347
+ color: #1e293b;
348
+ border-color: rgba(226, 232, 240, 0.8);
349
+ box-shadow:
350
+ 0 20px 25px -5px rgba(0, 0, 0, 0.08),
351
+ 0 10px 10px -5px rgba(0, 0, 0, 0.03);
352
+ }
353
+
354
+ .toastify-pro.light::before {
355
+ background: linear-gradient(90deg,
356
+ rgba(30, 41, 59, 0.8) 0%,
357
+ rgba(30, 41, 59, 0.4) 50%,
358
+ rgba(30, 41, 59, 0.8) 100%);
359
+ }
360
+
361
+ .toastify-pro.light::after {
362
+ background: rgba(30, 41, 59, 0.6);
363
+ }
364
+
365
+ .toastify-pro .toast-icon {
366
+ flex-shrink: 0;
367
+ display: flex;
368
+ align-items: center;
369
+ justify-content: center;
370
+ width: 28px;
371
+ height: 28px;
372
+ border-radius: 50%;
373
+ background: rgba(255, 255, 255, 0.2);
374
+ backdrop-filter: blur(10px);
375
+ animation: iconPulse 2s infinite;
376
+ }
377
+
378
+ @keyframes iconPulse {
379
+ 0%, 100% { transform: scale(1); }
380
+ 50% { transform: scale(1.05); }
381
+ }
382
+
383
+ @keyframes iconBounce {
384
+ 0% { transform: scale(0.2) rotate(-15deg); }
385
+ 40% { transform: scale(1.2) rotate(8deg); }
386
+ 70% { transform: scale(0.95) rotate(-3deg); }
387
+ 100% { transform: scale(1) rotate(0deg); }
388
+ }
389
+
390
+ @keyframes iconCarExit {
391
+ 0% {
392
+ transform: scale(1) rotate(0deg);
393
+ opacity: 1;
394
+ }
395
+ 20% {
396
+ transform: scale(1.1) rotate(-10deg);
397
+ opacity: 0.9;
398
+ }
399
+ 100% {
400
+ transform: scale(0.3) rotate(180deg);
401
+ opacity: 0;
402
+ }
403
+ }
404
+
405
+ .toastify-pro .toast-icon svg {
406
+ width: 18px;
407
+ height: 18px;
408
+ color: inherit;
409
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
410
+ }
411
+
412
+ .toastify-pro.light .toast-icon {
413
+ background: rgba(15, 23, 42, 0.1);
62
414
  }
63
- .toastify-pro.show { opacity: 1; transform: translateY(0); }
64
- .toastify-pro.success { background: rgba(76, 175, 80, 0.9); }
65
- .toastify-pro.error { background: rgba(244, 67, 54, 0.9); }
66
- .toastify-pro.info { background: rgba(33, 150, 243, 0.9); }
67
- .toastify-pro.warning { background: rgba(255, 152, 0, 0.9); }
68
- .toastify-pro.dark { background: rgba(0,0,0,0.85); }
69
- .toastify-pro.light { background: rgba(255,255,255,0.9); color: #000; }
70
415
 
71
416
  .toastify-pro .toast-content {
72
417
  flex: 1;
73
- padding-right: 8px;
418
+ line-height: 1.5;
419
+ font-weight: 500;
420
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
421
+ }
422
+
423
+ .toastify-pro .toast-message {
424
+ font-size: 15px;
425
+ font-weight: 500;
426
+ margin-bottom: 0;
427
+ }
428
+
429
+ .toastify-pro .toast-description {
430
+ font-size: 13px;
431
+ font-weight: 400;
432
+ opacity: 0.85;
433
+ margin-top: 4px;
74
434
  line-height: 1.4;
75
435
  }
76
436
 
77
437
  .toastify-pro .close-btn {
78
438
  cursor: pointer;
79
- font-size: 18px;
439
+ font-size: 20px;
80
440
  color: inherit;
81
- opacity: 0.8;
82
- padding: 2px 4px;
83
- border-radius: 3px;
84
- transition: opacity 0.2s ease, background-color 0.2s ease;
441
+ opacity: 0.7;
442
+ padding: 8px;
443
+ border-radius: 50%;
444
+ transition: all 0.2s ease;
85
445
  flex-shrink: 0;
86
- min-width: 20px;
87
- text-align: center;
446
+ width: 32px;
447
+ height: 32px;
448
+ display: flex;
449
+ align-items: center;
450
+ justify-content: center;
451
+ background: rgba(255, 255, 255, 0.1);
452
+ backdrop-filter: blur(10px);
453
+ font-weight: 300;
88
454
  line-height: 1;
89
455
  }
456
+
90
457
  .toastify-pro .close-btn:hover {
91
458
  opacity: 1;
92
- background-color: rgba(0,0,0,0.1);
459
+ background: rgba(255, 255, 255, 0.2);
460
+ transform: scale(1.1);
93
461
  }
462
+
463
+ .toastify-pro.light .close-btn {
464
+ background: rgba(15, 23, 42, 0.08);
465
+ }
466
+
94
467
  .toastify-pro.light .close-btn:hover {
95
- background-color: rgba(0,0,0,0.05);
468
+ background: rgba(15, 23, 42, 0.15);
469
+ }
470
+
471
+ @media (max-width: 640px) {
472
+ .toastify-pro {
473
+ min-width: 260px;
474
+ max-width: calc(100vw - 48px);
475
+ margin: 0 8px;
476
+ }
477
+
478
+ .toastify-pro-container.top-left,
479
+ .toastify-pro-container.bottom-left { left: 16px; }
480
+ .toastify-pro-container.top-right,
481
+ .toastify-pro-container.bottom-right { right: 16px; }
482
+ }
483
+
484
+ /* Confirmation Toast Styles */
485
+ .toastify-pro.confirmation {
486
+ min-width: 320px;
487
+ max-width: 450px;
488
+ padding: 24px;
489
+ flex-direction: column;
490
+ align-items: stretch;
491
+ gap: 20px;
492
+ position: relative;
493
+ }
494
+
495
+ /* Hide progress bar for confirmation toasts */
496
+ .toastify-pro.confirmation::after {
497
+ display: none;
498
+ }
499
+
500
+ /* Close button for confirmation dialogs */
501
+ .toastify-pro.confirmation .conf-close-btn {
502
+ position: absolute;
503
+ top: 12px;
504
+ right: 12px;
505
+ cursor: pointer;
506
+ font-size: 18px;
507
+ 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;
514
+ display: flex;
515
+ align-items: center;
516
+ justify-content: center;
517
+ background: rgba(255, 255, 255, 0.1);
518
+ backdrop-filter: blur(10px);
519
+ font-weight: 300;
520
+ line-height: 1;
521
+ border: 1px solid rgba(255, 255, 255, 0.1);
522
+ }
523
+
524
+ .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);
529
+ }
530
+
531
+ .toastify-pro.confirmation.light .conf-close-btn {
532
+ background: rgba(15, 23, 42, 0.08);
533
+ border-color: rgba(15, 23, 42, 0.1);
534
+ }
535
+
536
+ .toastify-pro.confirmation.light .conf-close-btn:hover {
537
+ background: rgba(15, 23, 42, 0.15);
538
+ border-color: rgba(15, 23, 42, 0.2);
539
+ }
540
+
541
+ .toastify-pro.confirmation .toast-content {
542
+ text-align: center;
543
+ margin-bottom: 8px;
544
+ }
545
+
546
+ .toastify-pro.confirmation .toast-message {
547
+ font-weight: 600;
548
+ font-size: 16px;
549
+ margin-bottom: 6px;
550
+ }
551
+
552
+ .toastify-pro.confirmation .toast-description {
553
+ font-size: 14px;
554
+ opacity: 0.9;
555
+ margin-top: 6px;
556
+ }
557
+
558
+ /* Fix text visibility for dark/light variants */
559
+ .toastify-pro.confirmation.dark .toast-message,
560
+ .toastify-pro.confirmation.dark .toast-description {
561
+ color: white;
562
+ }
563
+
564
+ .toastify-pro.confirmation.light .toast-message,
565
+ .toastify-pro.confirmation.light .toast-description {
566
+ color: #1e293b;
567
+ }
568
+
569
+ .toast-actions {
570
+ display: flex;
571
+ gap: 12px;
572
+ margin-top: 8px;
573
+ }
574
+
575
+ .toast-btn {
576
+ flex: 1;
577
+ padding: 10px 16px;
578
+ border: none;
579
+ border-radius: 8px;
580
+ font-weight: 600;
581
+ font-size: 14px;
582
+ cursor: pointer;
583
+ transition: all 0.2s ease;
584
+ backdrop-filter: blur(10px);
585
+ border: 1px solid rgba(255, 255, 255, 0.2);
586
+ }
587
+
588
+ .toast-btn:hover {
589
+ transform: translateY(-1px);
590
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
591
+ }
592
+
593
+ .toast-btn:active {
594
+ transform: translateY(0);
595
+ }
596
+
597
+ .toast-btn-cancel {
598
+ 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;
602
+ }
603
+
604
+ .toast-btn-cancel:hover {
605
+ background: rgba(255, 255, 255, 0.15);
606
+ color: rgba(255, 255, 255, 0.9);
607
+ border-color: rgba(255, 255, 255, 0.4);
608
+ }
609
+
610
+ .toast-btn-confirm {
611
+ color: white;
612
+ 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);
615
+ position: relative;
616
+ 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);
619
+ }
620
+
621
+ .toast-btn-confirm::before {
622
+ content: '';
623
+ position: absolute;
624
+ top: 0;
625
+ left: -100%;
626
+ width: 100%;
627
+ height: 100%;
628
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
629
+ transition: left 0.5s;
630
+ }
631
+
632
+ .toast-btn-confirm:hover::before {
633
+ left: 100%;
634
+ }
635
+
636
+ .toast-btn-confirm:hover {
637
+ 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);
640
+ }
641
+
642
+ .toastify-pro.light .toast-btn-cancel {
643
+ background: rgba(15, 23, 42, 0.08);
644
+ color: rgba(15, 23, 42, 0.8);
645
+ border-color: rgba(15, 23, 42, 0.2);
646
+ }
647
+
648
+ .toastify-pro.light .toast-btn-cancel:hover {
649
+ background: rgba(15, 23, 42, 0.12);
650
+ color: rgba(15, 23, 42, 1);
651
+ border-color: rgba(15, 23, 42, 0.3);
652
+ }
653
+
654
+ /* Enhanced light theme confirm buttons */
655
+ .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;
659
+ }
660
+
661
+ .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);
665
+ }
666
+
667
+ @media (max-width: 640px) {
668
+ .toastify-pro.confirmation {
669
+ min-width: 280px;
670
+ max-width: calc(100vw - 32px);
671
+ }
96
672
  }
97
673
  `;
98
- document.head.appendChild(style);
674
+ document.head.appendChild(style);
675
+ } catch (error) {
676
+ console.error('ToastifyPro: Failed to inject styles:', error);
677
+ }
99
678
  }
100
679
 
680
+ /**
681
+ * Creates and displays a toast notification
682
+ * @param {string} message - Main message text
683
+ * @param {string} type - Toast type (success, error, info, warning, dark, light)
684
+ * @param {Object} opts - Additional options
685
+ * @param {string} opts.description - Optional description text
686
+ * @param {number} opts.timeout - Override default timeout
687
+ * @param {boolean} opts.allowClose - Override close button setting
688
+ * @param {number} opts.maxLength - Override max message length
689
+ */
101
690
  show(message, type = "dark", opts = {}) {
102
- const options = { ...this.defaultOptions, ...opts };
691
+ // Input validation
692
+ if (typeof message !== 'string') {
693
+ console.warn('ToastifyPro: Message must be a string. Converting to string.');
694
+ message = String(message);
695
+ }
103
696
 
104
- const toast = document.createElement("div");
105
- toast.className = `toastify-pro ${type}`;
697
+ if (!message.trim()) {
698
+ console.warn('ToastifyPro: Empty message provided.');
699
+ return;
700
+ }
106
701
 
107
- // Create content wrapper for the message
108
- const contentWrapper = document.createElement("div");
109
- contentWrapper.className = "toast-content";
110
- contentWrapper.textContent = message.substring(0, options.maxLength);
111
- toast.appendChild(contentWrapper);
702
+ // Validate type
703
+ const validTypes = ['success', 'error', 'info', 'warning', 'dark', 'light'];
704
+ if (!validTypes.includes(type)) {
705
+ console.warn(`ToastifyPro: Invalid type "${type}". Using "dark".`);
706
+ type = 'dark';
707
+ }
112
708
 
113
- if (options.allowClose) {
114
- const closeBtn = document.createElement("span");
115
- closeBtn.className = "close-btn";
116
- closeBtn.innerHTML = "&times;";
117
- closeBtn.onclick = () => this.removeToast(toast);
118
- toast.appendChild(closeBtn);
709
+ // Validate and merge options
710
+ if (typeof opts !== 'object' || opts === null) {
711
+ console.warn('ToastifyPro: Invalid options parameter. Using defaults.');
712
+ opts = {};
119
713
  }
120
714
 
121
- this.container.appendChild(toast);
715
+ const options = { ...this.defaultOptions, ...opts };
716
+
717
+ try {
718
+ // Create toast element
719
+ const toast = document.createElement("div");
720
+ toast.className = `toastify-pro ${type}`;
721
+
722
+ // Set duration for progress bar animation
723
+ if (options.timeout > 0) {
724
+ toast.style.setProperty('--duration', `${options.timeout}ms`);
725
+ }
726
+
727
+ // Create icon wrapper
728
+ const iconWrapper = document.createElement("div");
729
+ iconWrapper.className = "toast-icon";
730
+ iconWrapper.innerHTML = this.getIconSVG(type);
731
+ toast.appendChild(iconWrapper);
732
+
733
+ // Create content wrapper for the message and description
734
+ const contentWrapper = document.createElement("div");
735
+ contentWrapper.className = "toast-content";
736
+
737
+ // Main message
738
+ const messageElement = document.createElement("div");
739
+ messageElement.className = "toast-message";
740
+ messageElement.textContent = message.substring(0, options.maxLength);
741
+ contentWrapper.appendChild(messageElement);
742
+
743
+ // Optional description (if provided)
744
+ if (options.description && typeof options.description === 'string') {
745
+ const descriptionElement = document.createElement("div");
746
+ descriptionElement.className = "toast-description";
747
+ descriptionElement.textContent = options.description.substring(0, options.maxLength * 2);
748
+ contentWrapper.appendChild(descriptionElement);
749
+ }
750
+
751
+ toast.appendChild(contentWrapper);
752
+
753
+ // Add close button if enabled
754
+ if (options.allowClose) {
755
+ const closeBtn = document.createElement("span");
756
+ closeBtn.className = "close-btn";
757
+ closeBtn.innerHTML = "&times;";
758
+ closeBtn.setAttribute('aria-label', 'Close notification');
759
+ closeBtn.onclick = () => this.removeToast(toast);
760
+ toast.appendChild(closeBtn);
761
+ }
762
+
763
+ // Add toast to container
764
+ this.container.appendChild(toast);
122
765
 
123
- // show animation
124
- setTimeout(() => toast.classList.add("show"), 50);
766
+ // Apple AirDrop-style entrance animation
767
+ setTimeout(() => {
768
+ toast.classList.add("show");
769
+ // Add icon bounce effect with Apple-style timing
770
+ const icon = toast.querySelector('.toast-icon');
771
+ if (icon) {
772
+ icon.style.animation = 'iconBounce 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
773
+ }
774
+ }, 10);
125
775
 
126
- // auto remove
127
- if (options.timeout > 0) {
128
- setTimeout(() => this.removeToast(toast), options.timeout);
776
+ // Auto-remove after timeout
777
+ if (options.timeout > 0) {
778
+ setTimeout(() => this.removeToast(toast), options.timeout);
779
+ }
780
+
781
+ return toast; // Return element for potential future manipulation
782
+ } catch (error) {
783
+ console.error('ToastifyPro: Failed to create toast:', error);
129
784
  }
130
785
  }
131
786
 
787
+ /**
788
+ * Removes a toast with position-aware car swipe animation
789
+ * @param {HTMLElement} toast - Toast element to remove
790
+ */
132
791
  removeToast(toast) {
133
- toast.classList.remove("show");
134
- setTimeout(() => toast.remove(), 300);
792
+ if (!toast || !toast.parentNode) {
793
+ console.warn('ToastifyPro: Invalid toast element for removal.');
794
+ return;
795
+ }
796
+
797
+ try {
798
+ // Detect position to choose the right swipe direction
799
+ const container = toast.parentNode;
800
+ const position = container.className.split(' ')[1]; // get position class
801
+
802
+ let swipeAnimation = 'carSwipeBottom'; // default fallback
803
+
804
+ // Choose animation based on position - car swipes away from screen edge
805
+ if (position.includes('bottom')) {
806
+ swipeAnimation = 'carSwipeBottom'; // swipe down off screen
807
+ } else if (position.includes('top')) {
808
+ swipeAnimation = 'carSwipeTop'; // swipe up off screen
809
+ } else if (position.includes('left')) {
810
+ swipeAnimation = 'carSwipeLeft'; // swipe left off screen
811
+ } else if (position.includes('right')) {
812
+ swipeAnimation = 'carSwipeRight'; // swipe right off screen
813
+ } else if (position.includes('center')) {
814
+ swipeAnimation = 'carSwipeCenter'; // swipe down for center
815
+ }
816
+
817
+ // Apply fast car swipe animation with improved easing
818
+ toast.style.animation = `${swipeAnimation} 0.4s cubic-bezier(0.4, 0.0, 1, 1) forwards`;
819
+
820
+ // Add spinning icon animation for extra polish
821
+ const icon = toast.querySelector('.toast-icon');
822
+ if (icon) {
823
+ icon.style.animation = 'iconCarExit 0.4s cubic-bezier(0.4, 0.0, 1, 1) forwards';
824
+ }
825
+
826
+ // Remove element after animation completes
827
+ setTimeout(() => {
828
+ if (toast.parentNode) {
829
+ toast.remove();
830
+ }
831
+ }, 400);
832
+ } catch (error) {
833
+ console.error('ToastifyPro: Error removing toast:', error);
834
+ // Fallback: remove immediately if animation fails
835
+ if (toast.parentNode) {
836
+ toast.remove();
837
+ }
838
+ }
135
839
  }
136
840
 
841
+ /**
842
+ * Shows a success toast notification
843
+ * @param {string} msg - Main message
844
+ * @param {string|Object} opts - Description string or options object
845
+ */
137
846
  success(msg, opts) {
847
+ // Handle both (message) and (message, description) formats
848
+ if (typeof opts === 'string') {
849
+ opts = { description: opts };
850
+ }
138
851
  this.show(msg, "success", opts);
139
852
  }
853
+
854
+ /**
855
+ * Shows an error toast notification
856
+ * @param {string} msg - Main message
857
+ * @param {string|Object} opts - Description string or options object
858
+ */
140
859
  error(msg, opts) {
860
+ if (typeof opts === 'string') {
861
+ opts = { description: opts };
862
+ }
141
863
  this.show(msg, "error", opts);
142
864
  }
865
+
866
+ /**
867
+ * Shows an info toast notification
868
+ * @param {string} msg - Main message
869
+ * @param {string|Object} opts - Description string or options object
870
+ */
143
871
  info(msg, opts) {
872
+ if (typeof opts === 'string') {
873
+ opts = { description: opts };
874
+ }
144
875
  this.show(msg, "info", opts);
145
876
  }
877
+
878
+ /**
879
+ * Shows a warning toast notification
880
+ * @param {string} msg - Main message
881
+ * @param {string|Object} opts - Description string or options object
882
+ */
146
883
  warning(msg, opts) {
884
+ if (typeof opts === 'string') {
885
+ opts = { description: opts };
886
+ }
147
887
  this.show(msg, "warning", opts);
148
888
  }
889
+
890
+ /**
891
+ * Shows a dark-themed toast notification
892
+ * @param {string} msg - Main message
893
+ * @param {string|Object} opts - Description string or options object
894
+ */
149
895
  dark(msg, opts) {
896
+ if (typeof opts === 'string') {
897
+ opts = { description: opts };
898
+ }
150
899
  this.show(msg, "dark", opts);
151
900
  }
901
+
902
+ /**
903
+ * Shows a light-themed toast notification
904
+ * @param {string} msg - Main message
905
+ * @param {string|Object} opts - Description string or options object
906
+ */
152
907
  light(msg, opts) {
908
+ if (typeof opts === 'string') {
909
+ opts = { description: opts };
910
+ }
153
911
  this.show(msg, "light", opts);
154
912
  }
913
+
914
+ /**
915
+ * Shows a confirmation toast with confirm/cancel buttons
916
+ * @param {string} message - Main confirmation question
917
+ * @param {string|Function|Object} descriptionOrCallback - Description text, callback function, or options object
918
+ * @param {Function} callback - Callback function (if description provided)
919
+ */
920
+ conf(message, descriptionOrCallback, callback) {
921
+ // Parse arguments to support multiple usage patterns
922
+ let description = '';
923
+ let options = {};
924
+ let resultCallback = null;
925
+
926
+ // Pattern 1: conf('message', callback)
927
+ if (typeof descriptionOrCallback === 'function' && !callback) {
928
+ resultCallback = descriptionOrCallback;
929
+ }
930
+ // Pattern 2: conf('message', 'description', callback)
931
+ else if (typeof descriptionOrCallback === 'string' && typeof callback === 'function') {
932
+ description = descriptionOrCallback;
933
+ resultCallback = callback;
934
+ }
935
+ // Pattern 3: conf('message', options) with onConfirm/onCancel
936
+ else if (typeof descriptionOrCallback === 'object' && descriptionOrCallback !== null) {
937
+ options = descriptionOrCallback;
938
+ description = options.description || '';
939
+
940
+ // Use onConfirm/onCancel if provided, otherwise use callback parameter
941
+ if (options.onConfirm || options.onCancel) {
942
+ // Don't use the callback parameter if onConfirm/onCancel are provided
943
+ resultCallback = null;
944
+ } else if (typeof callback === 'function') {
945
+ resultCallback = callback;
946
+ }
947
+ }
948
+ // Pattern 4: conf('message', 'description', options) - legacy support
949
+ else if (typeof descriptionOrCallback === 'string' && typeof callback === 'object') {
950
+ description = descriptionOrCallback;
951
+ options = callback || {};
952
+ // In this case, no unified callback, rely on onConfirm/onCancel
953
+ resultCallback = null;
954
+ }
955
+
956
+ // Default options for confirmation
957
+ const confirmOptions = {
958
+ timeout: 0, // No auto-dismiss for confirmations
959
+ allowClose: false, // No close button, must choose
960
+ confirmText: options.confirmText || 'Confirm',
961
+ cancelText: options.cancelText || 'Cancel',
962
+ theme: options.theme || options.color || 'dark', // Support both theme and color for backward compatibility
963
+ position: options.position || 'center', // Default to center for confirmations
964
+ ...options
965
+ };
966
+
967
+ // Validate and set theme to only dark or light
968
+ if (confirmOptions.theme === 'light' || confirmOptions.theme === 'white') {
969
+ confirmOptions.theme = 'light';
970
+ } else {
971
+ confirmOptions.theme = 'dark'; // Default to dark for all other values
972
+ }
973
+
974
+ // Validate position for confirmation toast
975
+ const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center', 'center'];
976
+ if (!validPositions.includes(confirmOptions.position)) {
977
+ console.warn(`ToastifyPro: Invalid confirmation position "${confirmOptions.position}". Using default position.`);
978
+ confirmOptions.position = this.defaultOptions.position;
979
+ }
980
+
981
+ // Get or create container for the specified position
982
+ let confirmContainer = document.querySelector(`.toastify-pro-container.${confirmOptions.position}`);
983
+
984
+ if (!confirmContainer) {
985
+ try {
986
+ confirmContainer = document.createElement("div");
987
+ confirmContainer.className = `toastify-pro-container ${confirmOptions.position}`;
988
+ document.body.appendChild(confirmContainer);
989
+ } catch (error) {
990
+ console.warn('ToastifyPro: Failed to create confirmation container. Using default container.');
991
+ confirmContainer = this.container;
992
+ }
993
+ }
994
+
995
+ // Helper function to handle confirmation result
996
+ const handleConfirmation = (confirmed) => {
997
+ if (confirmed) {
998
+ // Call onConfirm if provided
999
+ if (options.onConfirm && typeof options.onConfirm === 'function') {
1000
+ try {
1001
+ options.onConfirm();
1002
+ } catch (error) {
1003
+ console.error('ToastifyPro: Error in onConfirm callback:', error);
1004
+ }
1005
+ }
1006
+ // Call unified callback if provided
1007
+ if (resultCallback && typeof resultCallback === 'function') {
1008
+ try {
1009
+ resultCallback(true);
1010
+ } catch (error) {
1011
+ console.error('ToastifyPro: Error in confirmation callback:', error);
1012
+ }
1013
+ }
1014
+ } else {
1015
+ // Call onCancel if provided
1016
+ if (options.onCancel && typeof options.onCancel === 'function') {
1017
+ try {
1018
+ options.onCancel();
1019
+ } catch (error) {
1020
+ console.error('ToastifyPro: Error in onCancel callback:', error);
1021
+ }
1022
+ }
1023
+ // Call unified callback if provided
1024
+ if (resultCallback && typeof resultCallback === 'function') {
1025
+ try {
1026
+ resultCallback(false);
1027
+ } catch (error) {
1028
+ console.error('ToastifyPro: Error in confirmation callback:', error);
1029
+ }
1030
+ }
1031
+ }
1032
+ };
1033
+
1034
+ try {
1035
+ // Create confirmation toast element
1036
+ const toast = document.createElement("div");
1037
+ toast.className = `toastify-pro confirmation ${confirmOptions.theme}`;
1038
+
1039
+ // Create close button for confirmation
1040
+ const closeBtn = document.createElement("span");
1041
+ closeBtn.className = "conf-close-btn";
1042
+ closeBtn.innerHTML = "&times;";
1043
+ closeBtn.setAttribute('aria-label', 'Cancel confirmation');
1044
+ closeBtn.onclick = () => {
1045
+ handleConfirmation(false);
1046
+ this.removeToast(toast);
1047
+ };
1048
+ toast.appendChild(closeBtn);
1049
+
1050
+ // Create icon wrapper
1051
+ const iconWrapper = document.createElement("div");
1052
+ iconWrapper.className = "toast-icon";
1053
+ iconWrapper.innerHTML = this.getIconSVG('info'); // Default to info icon
1054
+ toast.appendChild(iconWrapper);
1055
+
1056
+ // Create content wrapper
1057
+ const contentWrapper = document.createElement("div");
1058
+ contentWrapper.className = "toast-content";
1059
+
1060
+ // Main message
1061
+ const messageElement = document.createElement("div");
1062
+ messageElement.className = "toast-message";
1063
+ messageElement.textContent = message.substring(0, this.defaultOptions.maxLength);
1064
+ contentWrapper.appendChild(messageElement);
1065
+
1066
+ // Optional description
1067
+ if (description) {
1068
+ const descriptionElement = document.createElement("div");
1069
+ descriptionElement.className = "toast-description";
1070
+ descriptionElement.textContent = description.substring(0, this.defaultOptions.maxLength * 2);
1071
+ contentWrapper.appendChild(descriptionElement);
1072
+ }
1073
+
1074
+ toast.appendChild(contentWrapper);
1075
+
1076
+ // Create action buttons container
1077
+ const actionsWrapper = document.createElement("div");
1078
+ actionsWrapper.className = "toast-actions";
1079
+
1080
+ // Cancel button
1081
+ const cancelBtn = document.createElement("button");
1082
+ cancelBtn.className = "toast-btn toast-btn-cancel";
1083
+ cancelBtn.textContent = confirmOptions.cancelText;
1084
+ cancelBtn.onclick = () => {
1085
+ handleConfirmation(false);
1086
+ this.removeToast(toast);
1087
+ };
1088
+
1089
+ // Confirm button
1090
+ const confirmBtn = document.createElement("button");
1091
+ confirmBtn.className = `toast-btn toast-btn-confirm`;
1092
+ confirmBtn.textContent = confirmOptions.confirmText;
1093
+ confirmBtn.onclick = () => {
1094
+ handleConfirmation(true);
1095
+ this.removeToast(toast);
1096
+ };
1097
+
1098
+ actionsWrapper.appendChild(cancelBtn);
1099
+ actionsWrapper.appendChild(confirmBtn);
1100
+ toast.appendChild(actionsWrapper);
1101
+
1102
+ // Add toast to the specified container (not default container)
1103
+ confirmContainer.appendChild(toast);
1104
+
1105
+ // Entrance animation
1106
+ setTimeout(() => {
1107
+ toast.classList.add("show");
1108
+ const icon = toast.querySelector('.toast-icon');
1109
+ if (icon) {
1110
+ icon.style.animation = 'iconBounce 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
1111
+ }
1112
+ }, 10);
1113
+
1114
+ return toast;
1115
+ } catch (error) {
1116
+ console.error('ToastifyPro: Failed to create confirmation toast:', error);
1117
+ }
1118
+ }
1119
+ }
1120
+
1121
+ /**
1122
+ * Export for different environments
1123
+ * Supports CommonJS (Node.js), AMD, and browser globals
1124
+ */
1125
+
1126
+ // CommonJS export (Node.js/npm)
1127
+ if (typeof module !== 'undefined' && module.exports) {
1128
+ module.exports = ToastifyPro;
1129
+ }
1130
+
1131
+ // ES6 module export
1132
+ if (typeof exports !== 'undefined') {
1133
+ exports.ToastifyPro = ToastifyPro;
1134
+ exports.default = ToastifyPro;
1135
+ }
1136
+
1137
+ // AMD export (RequireJS)
1138
+ if (typeof define === 'function' && define.amd) {
1139
+ define(function() {
1140
+ return ToastifyPro;
1141
+ });
1142
+ }
1143
+
1144
+ // Browser global
1145
+ if (typeof window !== 'undefined') {
1146
+ window.ToastifyPro = ToastifyPro;
155
1147
  }
156
-
157
- export { ToastifyPro as default };
1148
+ //# sourceMappingURL=toastify-pro.esm.js.map