@tsirosgeorge/toastnotification 5.3.3 → 5.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.
package/toast.js CHANGED
@@ -1,10 +1,44 @@
1
1
  "use strict";
2
2
 
3
- // Dynamically load the external CSS file
4
- const link = document.createElement("link");
5
- link.rel = "stylesheet";
6
- link.href = "https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/css/toast.min.css";
7
- document.head.appendChild(link);
3
+ // Single source of truth for the CDN this build points at.
4
+ // `npm run sync:version` rewrites it from package.json, so it can never go stale.
5
+ const TS_TOAST_VERSION = "5.5.0";
6
+ // Point this at your own copy of assets/ to self-host the CSS and icons
7
+ // (useful offline, behind a strict CSP, or when you don't want a CDN dependency):
8
+ // window.TS_TOAST_ASSET_BASE = '/vendor/toastnotification';
9
+ const TS_TOAST_CDN = (typeof window !== 'undefined' && window.TS_TOAST_ASSET_BASE)
10
+ ? String(window.TS_TOAST_ASSET_BASE).replace(/\/+$/, '')
11
+ : `https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@${TS_TOAST_VERSION}`;
12
+
13
+ // How many confirm dialogs are currently open. The body scroll lock belongs to the
14
+ // group, so only the last dialog to close may release it.
15
+ let tsToastOpenModals = 0;
16
+ let tsToastPrevOverflow = '';
17
+
18
+ const tsToastReducedMotion = () =>
19
+ typeof window !== 'undefined' &&
20
+ typeof window.matchMedia === 'function' &&
21
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches;
22
+
23
+ // Everything inside the dialog a keyboard can reach, in DOM order.
24
+ const tsToastFocusable = (root) => Array.from(
25
+ root.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
26
+ ).filter((el) => !el.disabled && el.offsetParent !== null);
27
+
28
+ let tsToastIdCounter = 0;
29
+
30
+ // Load the stylesheet from the CDN, unless the page opted out by importing it itself
31
+ // (set window.TS_TOAST_NO_CSS = true before loading, or ship assets/css/toast.css yourself).
32
+ (function loadStylesheet() {
33
+ const LINK_ID = 'ts-toast-stylesheet';
34
+ if (typeof window !== 'undefined' && window.TS_TOAST_NO_CSS) return;
35
+ if (document.getElementById(LINK_ID)) return;
36
+ const link = document.createElement("link");
37
+ link.id = LINK_ID;
38
+ link.rel = "stylesheet";
39
+ link.href = `${TS_TOAST_CDN}/assets/css/toast.min.css`;
40
+ document.head.appendChild(link);
41
+ })();
8
42
 
9
43
  // Inject minimal styles for confirm actions and overlay (kept tiny to avoid breaking existing CSS)
10
44
  (function injectInlineStyles() {
@@ -16,6 +50,8 @@ document.head.appendChild(link);
16
50
  /* Ensure center positions exist even if external CSS lacks them */
17
51
  .ts-toast-container.top-center { top: 1rem; left: 50%; transform: translateX(-50%); align-items: center; }
18
52
  .ts-toast-container.bottom-center { bottom: 1rem; left: 50%; transform: translateX(-50%); align-items: center; }
53
+ .ts-toast-container.center { top: 50%; left: 50%; transform: translate(-50%, -50%); align-items: center; }
54
+ .ts-toast-overlay.center { align-items: center; justify-content: center; }
19
55
  .ts-toast-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 2147483646; }
20
56
  .ts-toast.ts-toast-confirm { max-width: min(92vw, 440px); width: max(320px, 60%); flex-direction: column; gap: 12px; padding: 16px 20px; background: var(--toast-bg, #fff); color: var(--toast-color, #000); border: 1px solid var(--toast-border, #e5e7eb); border-radius: 12px; box-shadow: var(--toast-shadow, 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)); text-align: center; }
21
57
  .ts-toast.ts-toast-confirm .ts-toast-content { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; }
@@ -67,6 +103,11 @@ const toast = function (message, options = {}) {
67
103
  useOverlay = true,
68
104
  closeOnOverlayClick = true,
69
105
  showClose = false,
106
+ // Escape cancels a confirm dialog
107
+ closeOnEscape = true,
108
+ // `message` is written as HTML for backwards compatibility. Pass false to
109
+ // render it as plain text, which is what you want for anything user-supplied.
110
+ allowHtml = true,
70
111
  // interactions
71
112
  dismissOnClick = true, // ignored if confirm-mode
72
113
  onClick = null, // Custom onClick event listener
@@ -75,6 +116,7 @@ const toast = function (message, options = {}) {
75
116
  } = options;
76
117
 
77
118
  const isConfirm = (mode === 'confirm' || mode === 'swal');
119
+ const reducedMotion = tsToastReducedMotion();
78
120
 
79
121
  // Pick an animation intelligently when one wasn't explicitly provided
80
122
  const resolvedAnimation = (typeof options.animation === 'string' && options.animation.trim())
@@ -90,10 +132,12 @@ const toast = function (message, options = {}) {
90
132
  };
91
133
  return m[a] || a;
92
134
  })(options.animation.trim())
93
- : (isConfirm ? 'ts-toast-zoom-in' : (position.startsWith('top') ? 'ts-toast-slide-top'
135
+ // 'center' has no edge to slide in from, so it zooms like a dialog.
136
+ : (isConfirm || position === 'center' ? 'ts-toast-zoom-in'
137
+ : position.startsWith('top') ? 'ts-toast-slide-top'
94
138
  : position.startsWith('bottom') ? 'ts-toast-slide-bottom'
95
139
  : position.endsWith('left') ? 'ts-toast-slide-left'
96
- : 'ts-toast-slide-right'));
140
+ : 'ts-toast-slide-right');
97
141
 
98
142
  // helper: remove with smooth CSS transition and cleanup (used by alerts)
99
143
  const removeWithAnimation = (el, callback) => {
@@ -126,13 +170,24 @@ const toast = function (message, options = {}) {
126
170
  el.classList.remove('ts-toast-slide-out');
127
171
  if (el.parentNode) el.parentNode.removeChild(el);
128
172
  if (typeof callback === 'function') callback();
129
- }, 500);
173
+ }, reducedMotion ? 0 : 500);
130
174
  };
131
175
 
132
176
  const toastElement = document.createElement('div');
133
177
  toastElement.className = `ts-toast ts-toast-${type}${isConfirm ? ' ts-toast-confirm' : ''}`;
134
178
  toastElement.dataset.anim = resolvedAnimation;
135
- toastElement.style.animation = `${resolvedAnimation} 0.5s ease`;
179
+ if (!reducedMotion) toastElement.style.animation = `${resolvedAnimation} 0.5s ease`;
180
+
181
+ const uid = `ts-toast-${++tsToastIdCounter}`;
182
+ if (isConfirm) {
183
+ // Without these a screen reader announces nothing, and without tabindex the
184
+ // dialog cannot take focus away from whatever opened it.
185
+ toastElement.setAttribute('role', 'dialog');
186
+ toastElement.setAttribute('aria-modal', 'true');
187
+ toastElement.tabIndex = -1;
188
+ } else if (type === 'error' || type === 'warning') {
189
+ toastElement.setAttribute('role', 'alert');
190
+ }
136
191
  // In confirm mode, we stack content vertically; in alert mode keep original layout
137
192
  if (!isConfirm) {
138
193
  toastElement.style.flexDirection = 'row-reverse';
@@ -147,23 +202,16 @@ const toast = function (message, options = {}) {
147
202
  iconElement.textContent = icon;
148
203
  } else {
149
204
  const img = document.createElement('img');
150
- img.src = '';
205
+ img.alt = '';
206
+ img.setAttribute('aria-hidden', 'true');
151
207
  img.style.width = '30px';
152
208
  img.style.height = '30px';
153
209
  img.style.objectFit = 'contain';
154
210
 
155
- const baseUrl = 'https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/';
156
- const timestamp = new Date().getTime(); // 🔄 force refresh
157
-
158
- if (type === 'success') {
159
- img.src = `${baseUrl}success.gif?t=${timestamp}`;
160
- } else if (type === 'error') {
161
- img.src = `${baseUrl}error.gif?t=${timestamp}`;
162
- } else if (type === 'info') {
163
- img.src = `${baseUrl}info.gif?t=${timestamp}`;
164
- } else if (type === 'warning') {
165
- img.src = `${baseUrl}warning.gif?t=${timestamp}`;
166
- }
211
+ // No cache-buster: these GIFs are immutable per version, so let the
212
+ // browser and the CDN actually cache them.
213
+ const iconFile = { success: 'success.gif', error: 'error.gif', info: 'info.gif', warning: 'warning.gif' }[type];
214
+ if (iconFile) img.src = `${TS_TOAST_CDN}/assets/img/${iconFile}`;
167
215
 
168
216
  iconElement.appendChild(img);
169
217
  }
@@ -171,7 +219,11 @@ const toast = function (message, options = {}) {
171
219
  // Create Body
172
220
  const toastBody = document.createElement('div');
173
221
  toastBody.className = 'ts-toast-body';
174
- toastBody.innerHTML = message; // Allow HTML content in message
222
+ toastBody.id = `${uid}-body`;
223
+ // HTML by default for backwards compatibility; pass allowHtml: false for
224
+ // anything that came from a user.
225
+ if (allowHtml) toastBody.innerHTML = message;
226
+ else toastBody.textContent = message;
175
227
 
176
228
  // Content row for confirm (icon + text side-by-side)
177
229
  let contentRow = null;
@@ -182,10 +234,13 @@ const toast = function (message, options = {}) {
182
234
  if (title) {
183
235
  const titleEl = document.createElement('div');
184
236
  titleEl.className = 'ts-toast-title';
237
+ titleEl.id = `${uid}-title`;
185
238
  titleEl.textContent = title;
186
239
  contentRow.appendChild(titleEl);
240
+ toastElement.setAttribute('aria-labelledby', titleEl.id);
187
241
  }
188
242
  contentRow.appendChild(toastBody);
243
+ toastElement.setAttribute('aria-describedby', toastBody.id);
189
244
  toastElement.appendChild(contentRow);
190
245
  } else {
191
246
  toastElement.appendChild(toastBody);
@@ -204,12 +259,24 @@ const toast = function (message, options = {}) {
204
259
  inputElement.className = 'ts-toast-input';
205
260
  inputElement.placeholder = inputPlaceholder;
206
261
  inputElement.value = inputValue;
262
+ // Enter submits a single-line field, the way a native prompt does.
263
+ // A textarea keeps Enter for newlines.
264
+ if (input !== 'textarea') {
265
+ inputElement.addEventListener('keydown', (e) => {
266
+ if (e.key === 'Enter') { e.preventDefault(); resolveAndClose(true); }
267
+ });
268
+ }
207
269
  toastElement.appendChild(inputElement);
208
270
  }
209
271
 
210
272
  // Actions (for confirm mode)
211
273
  let actionsContainer = null;
212
274
  let resultResolver = null;
275
+ // Assigned in confirm mode; the overlay and (x) handlers below call it so that
276
+ // *every* way of dismissing the dialog settles the promise and the callbacks.
277
+ let resolveAndClose = null;
278
+ // Releases the scroll lock, the key handler and the focus this dialog took.
279
+ let releaseModal = () => {};
213
280
  if (isConfirm) {
214
281
  actionsContainer = document.createElement('div');
215
282
  actionsContainer.className = 'ts-toast-actions';
@@ -235,12 +302,21 @@ const toast = function (message, options = {}) {
235
302
  // Create a Promise that resolves on user choice; expose via property
236
303
  toastElement.result = new Promise((resolve) => { resultResolver = resolve; });
237
304
 
238
- const resolveAndClose = (value) => {
239
- const result = (value && inputElement !== null) ? inputElement.value : value;
305
+ let settled = false;
306
+ resolveAndClose = (confirmed) => {
307
+ if (settled) return; // a button click and a backdrop click can race
308
+ settled = true;
309
+ // With an input, confirming always yields a string (possibly '') and
310
+ // cancelling yields null, so an empty submission stays distinguishable
311
+ // from a cancel. Without an input the contract is still true/false.
312
+ const result = confirmed
313
+ ? (inputElement ? inputElement.value : true)
314
+ : (inputElement ? null : false);
240
315
  if (resultResolver) resultResolver(result);
241
- if (value && typeof onConfirm === 'function') onConfirm((inputElement !== null) ? inputElement.value : value, toastElement);
242
- if (!value && typeof onCancel === 'function') onCancel(toastElement);
316
+ if (confirmed && typeof onConfirm === 'function') onConfirm(result, toastElement);
317
+ if (!confirmed && typeof onCancel === 'function') onCancel(toastElement);
243
318
  if (typeof onResult === 'function') onResult(result, toastElement);
319
+ releaseModal();
244
320
  // Use the same slide+fade removal as alerts
245
321
  removeWithAnimation(toastElement, () => {
246
322
  if (onDismiss && typeof onDismiss === 'function') onDismiss(toastElement);
@@ -265,7 +341,9 @@ const toast = function (message, options = {}) {
265
341
  let overlay = null;
266
342
  if (isConfirm && useOverlay) {
267
343
  overlay = document.createElement('div');
268
- overlay.className = `ts-toast-overlay ${position}`;
344
+ // A modal centres by default. The `position` default of 'top-right' is meant
345
+ // for toasts; applying it here parked the dialog in a corner of the backdrop.
346
+ overlay.className = 'ts-toast-overlay' + (options.position ? ` ${position}` : '');
269
347
  document.body.appendChild(overlay);
270
348
  overlay.appendChild(toastElement);
271
349
  if (showClose) {
@@ -275,28 +353,23 @@ const toast = function (message, options = {}) {
275
353
  closeBtn.innerHTML = '×';
276
354
  closeBtn.addEventListener('click', (e) => {
277
355
  e.stopPropagation();
278
- removeWithAnimation(toastElement, () => {
279
- if (onDismiss && typeof onDismiss === 'function') onDismiss(toastElement);
280
- if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay);
281
- });
356
+ // Dismissing via (x) is a cancel, so it has to settle like one.
357
+ resolveAndClose(false);
282
358
  });
283
359
  toastElement.appendChild(closeBtn);
284
360
  }
285
361
  if (closeOnOverlayClick) {
362
+ // The cancel must both start and end on the backdrop. Selecting text in
363
+ // the input and releasing the mouse outside the card produced a click
364
+ // whose target was the overlay, which threw the dialog away mid-edit.
365
+ let pressedOnBackdrop = false;
366
+ overlay.addEventListener('pointerdown', (e) => { pressedOnBackdrop = e.target === overlay; });
286
367
  overlay.addEventListener('click', (e) => {
287
- if (e.target === overlay) {
288
- // Overlay background click -> cancel
289
- if (toastElement.result) {
290
- // let the confirm logic close and cleanup
291
- if (typeof resultResolver === 'function') {
292
- resultResolver(false);
293
- }
294
- }
295
- removeWithAnimation(toastElement, () => {
296
- if (onDismiss && typeof onDismiss === 'function') onDismiss(toastElement);
297
- if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay);
298
- });
299
- }
368
+ // Backdrop click is a cancel: settle the promise and onCancel/onResult,
369
+ // then close. Previously this resolved only the internal el.result,
370
+ // so `await toast.confirm(...)` hung forever.
371
+ if (e.target === overlay && pressedOnBackdrop) resolveAndClose(false);
372
+ pressedOnBackdrop = false;
300
373
  });
301
374
  }
302
375
  } else {
@@ -307,9 +380,79 @@ const toast = function (message, options = {}) {
307
380
  container.className = `ts-toast-container ${position}`;
308
381
  document.body.appendChild(container);
309
382
  }
383
+ if (!container.hasAttribute('aria-live')) {
384
+ // Without this a toast is invisible to a screen reader.
385
+ container.setAttribute('role', 'status');
386
+ container.setAttribute('aria-live', 'polite');
387
+ container.setAttribute('aria-relevant', 'additions');
388
+ }
310
389
  container.appendChild(toastElement);
311
390
  }
312
391
 
392
+ if (isConfirm) {
393
+ // The dialog has to own the keyboard while it is open. Without this the
394
+ // element that opened it keeps focus, so pressing Enter or Space activates
395
+ // it again and stacks a second dialog on top of the first — and Tab walks
396
+ // through the page behind the backdrop.
397
+ const previouslyFocused = document.activeElement;
398
+
399
+ tsToastOpenModals += 1;
400
+ if (tsToastOpenModals === 1) {
401
+ tsToastPrevOverflow = document.body.style.overflow;
402
+ document.body.style.overflow = 'hidden';
403
+ }
404
+
405
+ // With dialogs stacked, only the top one should answer the keyboard.
406
+ const isTopmost = () => {
407
+ const open = document.querySelectorAll('.ts-toast.ts-toast-confirm');
408
+ return open.length === 0 || open[open.length - 1] === toastElement;
409
+ };
410
+
411
+ const onKeydown = (e) => {
412
+ if (!isTopmost()) return;
413
+
414
+ if (e.key === 'Escape' && closeOnEscape) {
415
+ e.preventDefault();
416
+ resolveAndClose(false);
417
+ return;
418
+ }
419
+ if (e.key !== 'Tab') return;
420
+
421
+ const focusables = tsToastFocusable(toastElement);
422
+ if (!focusables.length) { e.preventDefault(); return; }
423
+
424
+ const first = focusables[0];
425
+ const last = focusables[focusables.length - 1];
426
+
427
+ // Focus can start outside the dialog (the trigger button); pull it back.
428
+ if (!toastElement.contains(document.activeElement)) {
429
+ e.preventDefault();
430
+ (e.shiftKey ? last : first).focus();
431
+ } else if (e.shiftKey && document.activeElement === first) {
432
+ e.preventDefault();
433
+ last.focus();
434
+ } else if (!e.shiftKey && document.activeElement === last) {
435
+ e.preventDefault();
436
+ first.focus();
437
+ }
438
+ };
439
+ document.addEventListener('keydown', onKeydown, true);
440
+
441
+ releaseModal = () => {
442
+ document.removeEventListener('keydown', onKeydown, true);
443
+ tsToastOpenModals = Math.max(0, tsToastOpenModals - 1);
444
+ if (tsToastOpenModals === 0) document.body.style.overflow = tsToastPrevOverflow;
445
+ // Hand the keyboard back to whatever opened the dialog.
446
+ if (previouslyFocused && typeof previouslyFocused.focus === 'function' &&
447
+ document.contains(previouslyFocused)) {
448
+ previouslyFocused.focus();
449
+ }
450
+ };
451
+
452
+ // Land on the input when there is one, otherwise the confirm button.
453
+ (inputElement || toastElement.querySelector('.ts-toast-btn.confirm') || toastElement).focus();
454
+ }
455
+
313
456
  // Trigger the onShow event if provided
314
457
  if (onShow && typeof onShow === 'function') {
315
458
  onShow(toastElement);
@@ -354,8 +497,10 @@ const toast = function (message, options = {}) {
354
497
  if (!isConfirm && dismissOnClick) {
355
498
  toastElement.addEventListener('click', () => {
356
499
  if (toastElement._autoRemove) clearTimeout(toastElement._autoRemove); // Clear the auto-remove timeout
500
+ // onClick belongs to the click, not to the end of the exit animation,
501
+ // which is where it used to fire half a second late.
502
+ if (onClick && typeof onClick === 'function') onClick(toastElement);
357
503
  removeWithAnimation(toastElement, () => {
358
- if (onClick && typeof onClick === 'function') onClick(toastElement);
359
504
  if (onDismiss && typeof onDismiss === 'function') onDismiss(toastElement);
360
505
  });
361
506
  });
@@ -364,15 +509,23 @@ const toast = function (message, options = {}) {
364
509
  // Add swipe event listeners for mobile dismissal
365
510
  if (!isConfirm) {
366
511
  let touchStartX = 0;
512
+ let touchStartY = 0;
367
513
  let touchEndX = 0;
368
514
 
515
+ // Passive: these never preventDefault, and a non-passive touchstart blocks
516
+ // scrolling on the whole toast.
369
517
  toastElement.addEventListener('touchstart', (e) => {
370
518
  touchStartX = e.changedTouches[0].screenX;
371
- });
519
+ touchStartY = e.changedTouches[0].screenY;
520
+ }, { passive: true });
372
521
 
373
522
  toastElement.addEventListener('touchend', (e) => {
374
523
  touchEndX = e.changedTouches[0].screenX;
375
- if (Math.abs(touchStartX - touchEndX) > 50) { // Swipe distance threshold
524
+ const dx = Math.abs(touchStartX - touchEndX);
525
+ const dy = Math.abs(touchStartY - e.changedTouches[0].screenY);
526
+ // Only a mostly-horizontal swipe dismisses, so scrolling the page past a
527
+ // toast no longer throws it away on a bit of sideways drift.
528
+ if (dx > 50 && dx > dy) {
376
529
  if (toastElement._autoRemove) clearTimeout(toastElement._autoRemove);
377
530
  removeWithAnimation(toastElement, () => {
378
531
  if (onDismiss && typeof onDismiss === 'function') onDismiss(toastElement);
@@ -381,15 +534,34 @@ const toast = function (message, options = {}) {
381
534
  });
382
535
  }
383
536
 
537
+ // Let callers dismiss a toast they are holding, instead of only waiting out
538
+ // the duration or making the user click it.
539
+ toastElement.close = () => {
540
+ if (toastElement._autoRemove) clearTimeout(toastElement._autoRemove);
541
+ if (isConfirm) { resolveAndClose(false); return; }
542
+ removeWithAnimation(toastElement, () => {
543
+ if (onDismiss && typeof onDismiss === 'function') onDismiss(toastElement);
544
+ });
545
+ };
546
+
384
547
  return toastElement;
385
548
  };
386
549
 
550
+ // Shorthands return the toast element so it can be passed to toast.update().
387
551
  toast.success = function (message, options) {
388
- toast(message, { ...options, type: 'success' });
552
+ return toast(message, { ...options, type: 'success' });
389
553
  };
390
554
 
391
555
  toast.error = function (message, options) {
392
- toast(message, { ...options, type: 'error' });
556
+ return toast(message, { ...options, type: 'error' });
557
+ };
558
+
559
+ toast.warning = function (message, options) {
560
+ return toast(message, { ...options, type: 'warning' });
561
+ };
562
+
563
+ toast.info = function (message, options) {
564
+ return toast(message, { ...options, type: 'info' });
393
565
  };
394
566
 
395
567
  // Update toast function to handle removal with animation
@@ -399,7 +571,6 @@ const toast = function (message, options = {}) {
399
571
  icon = null,
400
572
  showLoader = false,
401
573
  duration = 3000, // Default duration (in ms)
402
- position = 'top-right', // Default position,
403
574
  onClick = null, // Custom onClick event listener
404
575
  onShow = null, // Custom onShow event listener
405
576
  onDismiss = null // Custom onDismiss event listener
@@ -410,10 +581,13 @@ const toast = function (message, options = {}) {
410
581
  const oldIcon = toastElement.querySelector('.ts-toast-icon');
411
582
  if (oldLoader) oldLoader.remove();
412
583
 
413
- // Update toast class and message
584
+ // Update toast class and message. Swap only the type modifier: overwriting
585
+ // className dropped ts-toast-show (so the toast faded out), dropped
586
+ // ts-toast-confirm (so confirm dialogs lost their layout), and leaked the
587
+ // position onto the toast instead of the container.
414
588
  if (type) {
415
- // keep ts- prefix consistent
416
- toastElement.className = `ts-toast ts-toast-${type} show ${position}`;
589
+ ['success', 'error', 'info', 'warning'].forEach((t) => toastElement.classList.remove(`ts-toast-${t}`));
590
+ toastElement.classList.add('ts-toast', `ts-toast-${type}`, 'ts-toast-show');
417
591
  }
418
592
  const toastBody = toastElement.querySelector('.ts-toast-body');
419
593
  if (toastBody) {
@@ -433,25 +607,21 @@ const toast = function (message, options = {}) {
433
607
  iconElement.textContent = icon;
434
608
  } else {
435
609
  const img = document.createElement('img');
610
+ img.alt = '';
611
+ img.setAttribute('aria-hidden', 'true');
436
612
  img.style.width = '30px';
437
613
  img.style.height = '30px';
438
614
  img.style.objectFit = 'contain';
439
615
 
440
- if (type === 'success') {
441
- img.src = 'https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/success.gif';
442
- } else if (type === 'error') {
443
- img.src = 'https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/error.gif';
444
- } else if (type === 'info') {
445
- img.src = 'https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/info.gif';
446
- } else if (type === 'warning') {
447
- img.src = 'https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/warning.gif';
448
- }
616
+ const iconFile = { success: 'success.gif', error: 'error.gif', info: 'info.gif', warning: 'warning.gif' }[type];
617
+ if (iconFile) img.src = `${TS_TOAST_CDN}/assets/img/${iconFile}`;
449
618
 
450
619
  iconElement.appendChild(img);
451
620
  }
452
621
 
453
- // Append the new icon immediately
454
- toastElement.appendChild(iconElement);
622
+ // Append the new icon immediately (inside the content row for confirm dialogs)
623
+ const contentRow = toastElement.querySelector('.ts-toast-content');
624
+ (contentRow || toastElement).appendChild(iconElement);
455
625
 
456
626
  // Handle loader if requested
457
627
  if (showLoader) {
@@ -534,15 +704,11 @@ const toast = function (message, options = {}) {
534
704
  showLoader: false // Disable loader when updating the message
535
705
  });
536
706
  },
707
+ // Reuse the element's own close so onDismiss fires, which this
708
+ // hand-rolled copy of the removal never did.
537
709
  close: () => {
538
- if (toastElement._autoRemove) clearTimeout(toastElement._autoRemove);
539
- toastElement.classList.add('ts-toast-slide-out');
540
- toastElement.classList.remove('ts-toast-show');
541
- toastElement.style.animation = '';
542
- setTimeout(() => {
543
- toastElement.classList.remove('ts-toast-slide-out');
544
- if (toastElement.parentNode) toastElement.parentNode.removeChild(toastElement);
545
- }, 500);
710
+ toastElement._managedByLoading = false;
711
+ toastElement.close();
546
712
  }
547
713
  };
548
714
  };
@@ -564,12 +730,21 @@ const toast = function (message, options = {}) {
564
730
  });
565
731
  };
566
732
 
733
+ // Close every toast currently on screen. Confirm dialogs settle as a cancel.
734
+ toast.dismissAll = function () {
735
+ document.querySelectorAll('.ts-toast').forEach((el) => {
736
+ if (typeof el.close === 'function') el.close();
737
+ });
738
+ };
739
+
567
740
  // Expose globally for CDN / browser usage
568
741
  if (typeof window !== 'undefined') {
569
742
  window.toast = toast;
570
743
  }
571
744
 
745
+ /* build:cjs-start (stripped when generating toast.module.js) */
572
746
  // CommonJS export for Node/bundlers (safe no-op in browsers)
573
747
  if (typeof module !== 'undefined' && module.exports) {
574
748
  module.exports = toast;
575
- }
749
+ }
750
+ /* build:cjs-end */
package/toast.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";const link=document.createElement("link");link.rel="stylesheet",link.href="https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/css/toast.min.css",document.head.appendChild(link),function(){const t="ts-toast-inline-extras";if(document.getElementById(t))return;const e=document.createElement("style");e.id=t,e.textContent="\n /* Ensure center positions exist even if external CSS lacks them */\n .ts-toast-container.top-center { top: 1rem; left: 50%; transform: translateX(-50%); align-items: center; }\n .ts-toast-container.bottom-center { bottom: 1rem; left: 50%; transform: translateX(-50%); align-items: center; }\n .ts-toast-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 2147483646; }\n .ts-toast.ts-toast-confirm { max-width: min(92vw, 440px); width: max(320px, 60%); flex-direction: column; gap: 12px; padding: 16px 20px; background: var(--toast-bg, #fff); color: var(--toast-color, #000); border: 1px solid var(--toast-border, #e5e7eb); border-radius: 12px; box-shadow: var(--toast-shadow, 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)); text-align: center; }\n .ts-toast.ts-toast-confirm .ts-toast-content { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; }\n .ts-toast-actions { display: flex; gap: 10px; justify-content: center; margin-top: 12px; }\n .ts-toast-btn { appearance: none; border: 0; padding: 8px 12px; border-radius: 8px; font-weight: 600; cursor: pointer; }\n .ts-toast-btn.cancel { background: #e9ecef; color: #1f2937; }\n .ts-toast-btn.confirm { background: #3b82f6; color: #fff; }\n .ts-toast.ts-toast-error .ts-toast-btn.confirm,\n .ts-toast.ts-toast-warning .ts-toast-btn.confirm { background: #ef4444; color: #fff; }\n .ts-toast.ts-toast-confirm .ts-toast-title { font-weight: 700; font-size: 1.05rem; margin-top: 4px; }\n .ts-toast.ts-toast-confirm .ts-toast-close { position: absolute; top: 8px; right: 8px; width: 28px; height: 28px; border-radius: 999px; border: 0; background: transparent; color: #6b7280; font-size: 20px; line-height: 1; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }\n .ts-toast.ts-toast-confirm .ts-toast-close:hover { background: rgba(0,0,0,0.06); }\n .ts-toast.ts-toast-confirm .ts-toast-icon { width: 64px; height: 64px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; }\n .ts-toast.ts-toast-confirm .ts-toast-icon img { width: 36px; height: 36px; }\n .ts-toast.ts-toast-confirm.ts-toast-success .ts-toast-icon { background: #dcfce7; }\n .ts-toast.ts-toast-confirm.ts-toast-info .ts-toast-icon { background: #dbeafe; }\n .ts-toast.ts-toast-confirm.ts-toast-warning .ts-toast-icon { background: #fef3c7; }\n .ts-toast.ts-toast-confirm.ts-toast-error .ts-toast-icon { background: #fee2e2; }\n ",document.head.appendChild(e)}();const toast=function(t,e={}){const{position:o="top-right",animation:s="slide-right",type:n="info",duration:a=3e3,icon:i=null,showLoader:l=!1,mode:r="alert",title:c=null,confirmText:d="Yes",cancelText:m="No",input:u=!1,inputPlaceholder:p="",inputValue:f="",confirmButtonBg:g=null,confirmButtonColor:h=null,cancelButtonBg:y=null,cancelButtonColor:x=null,onConfirm:v=null,onCancel:b=null,onResult:C=null,useOverlay:w=!0,closeOnOverlayClick:L=!0,showClose:E=!1,dismissOnClick:N=!0,onClick:k=null,onShow:T=null,onDismiss:$=null}=e,j="confirm"===r||"swal"===r,_="string"==typeof e.animation&&e.animation.trim()?{"slide-top":"ts-toast-slide-top","slide-bottom":"ts-toast-slide-bottom","slide-left":"ts-toast-slide-left","slide-right":"ts-toast-slide-right","zoom-in":"ts-toast-zoom-in","zoom-out":"ts-toast-zoom-out",flip:"ts-toast-flip"}[R=e.animation.trim()]||R:j?"ts-toast-zoom-in":o.startsWith("top")?"ts-toast-slide-top":o.startsWith("bottom")?"ts-toast-slide-bottom":o.endsWith("left")?"ts-toast-slide-left":"ts-toast-slide-right";var R;const B=(t,e)=>{const o=t.dataset&&t.dataset.anim?t.dataset.anim:t.style.animation||"";let s="";o.includes("ts-toast-slide-top")?s="translateY(-100%)":o.includes("ts-toast-slide-bottom")?s="translateY(100%)":(o.includes("ts-toast-slide-left")||o.includes("ts-toast-slide-right"))&&(s="translateX(100%)"),t.classList.add("ts-toast-slide-out"),t.classList.remove("ts-toast-show"),t.style.animation="",s&&(t.style.transform=s),t.style.opacity="0",setTimeout((()=>{t.classList.remove("ts-toast-slide-out"),t.parentNode&&t.parentNode.removeChild(t),"function"==typeof e&&e()}),500)},S=document.createElement("div");S.className=`ts-toast ts-toast-${n}${j?" ts-toast-confirm":""}`,S.dataset.anim=_,S.style.animation=`${_} 0.5s ease`,j||(S.style.flexDirection="row-reverse",S.style.justifyContent="flex-end");const z=document.createElement("span");if(z.className="ts-toast-icon",z.style.display="flex",i)z.textContent=i;else{const t=document.createElement("img");t.src="",t.style.width="30px",t.style.height="30px",t.style.objectFit="contain";const e="https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/",o=(new Date).getTime();"success"===n?t.src=`${e}success.gif?t=${o}`:"error"===n?t.src=`${e}error.gif?t=${o}`:"info"===n?t.src=`${e}info.gif?t=${o}`:"warning"===n&&(t.src=`${e}warning.gif?t=${o}`),z.appendChild(t)}const q=document.createElement("div");q.className="ts-toast-body",q.innerHTML=t;let P=null;if(j){if(P=document.createElement("div"),P.className="ts-toast-content",P.appendChild(z),c){const t=document.createElement("div");t.className="ts-toast-title",t.textContent=c,P.appendChild(t)}P.appendChild(q),S.appendChild(P)}else S.appendChild(q);let O=null;j&&u&&("textarea"===u?(O=document.createElement("textarea"),O.rows=3):(O=document.createElement("input"),O.type="text"===u||"email"===u||"password"===u||"number"===u?u:"text"),O.className="ts-toast-input",O.placeholder=p,O.value=f,S.appendChild(O));let X=null,D=null;if(j){X=document.createElement("div"),X.className="ts-toast-actions";const t=document.createElement("button");t.className="ts-toast-btn cancel",t.textContent=m;const e=document.createElement("button");e.className="ts-toast-btn confirm",e.textContent=d,y&&(t.style.background=y),x&&(t.style.color=x),g&&(e.style.background=g),h&&(e.style.color=h),X.appendChild(t),X.appendChild(e),S.appendChild(X),S.result=new Promise((t=>{D=t}));const o=t=>{const e=t&&null!==O?O.value:t;D&&D(e),t&&"function"==typeof v&&v(null!==O?O.value:t,S),t||"function"!=typeof b||b(S),"function"==typeof C&&C(e,S),B(S,(()=>{$&&"function"==typeof $&&$(S),F&&F.parentNode&&F.parentNode.removeChild(F)}))};t.addEventListener("click",(t=>{t.stopPropagation(),o(!1)})),e.addEventListener("click",(t=>{t.stopPropagation(),o(!0)}))}let M=null;l&&(M=document.createElement("div"),M.className="ts-toast-loader",S.appendChild(M));let F=null;if(j&&w){if(F=document.createElement("div"),F.className=`ts-toast-overlay ${o}`,document.body.appendChild(F),F.appendChild(S),E){const t=document.createElement("button");t.className="ts-toast-close",t.setAttribute("aria-label","Close"),t.innerHTML="×",t.addEventListener("click",(t=>{t.stopPropagation(),B(S,(()=>{$&&"function"==typeof $&&$(S),F&&F.parentNode&&F.parentNode.removeChild(F)}))})),S.appendChild(t)}L&&F.addEventListener("click",(t=>{t.target===F&&(S.result&&"function"==typeof D&&D(!1),B(S,(()=>{$&&"function"==typeof $&&$(S),F&&F.parentNode&&F.parentNode.removeChild(F)})))}))}else{let t=document.querySelector(`.ts-toast-container.${o}`);t||(t=document.createElement("div"),t.className=`ts-toast-container ${o}`,document.body.appendChild(t)),t.appendChild(S)}if(T&&"function"==typeof T&&T(S),setTimeout((()=>{S.classList.add("ts-toast-show")}),100),l&&M&&setTimeout((()=>{S._managedByLoading||(M.classList.add("done"),M.remove(),S.contains(z)||(j&&P?P.appendChild(z):S.appendChild(z)))}),2e3),l||j||S.contains(z)||S.appendChild(z),!j&&a>0){const t=setTimeout((()=>{B(S,(()=>{$&&"function"==typeof $&&$(S)}))}),a);S._autoRemove=t}if(!j&&N&&S.addEventListener("click",(()=>{S._autoRemove&&clearTimeout(S._autoRemove),B(S,(()=>{k&&"function"==typeof k&&k(S),$&&"function"==typeof $&&$(S)}))})),!j){let t=0,e=0;S.addEventListener("touchstart",(e=>{t=e.changedTouches[0].screenX})),S.addEventListener("touchend",(o=>{e=o.changedTouches[0].screenX,Math.abs(t-e)>50&&(S._autoRemove&&clearTimeout(S._autoRemove),B(S,(()=>{$&&"function"==typeof $&&$(S)})))}))}return S};toast.success=function(t,e){toast(t,{...e,type:"success"})},toast.error=function(t,e){toast(t,{...e,type:"error"})},toast.update=function(t,e,o={}){const{type:s=null,icon:n=null,showLoader:a=!1,duration:i=3e3,position:l="top-right",onClick:r=null,onShow:c=null,onDismiss:d=null}=o,m=t.querySelector(".ts-toast-loader"),u=t.querySelector(".ts-toast-icon");m&&m.remove(),s&&(t.className=`ts-toast ts-toast-${s} show ${l}`);const p=t.querySelector(".ts-toast-body");p&&(p.innerHTML=e),u&&u.remove();const f=document.createElement("span");if(f.className="ts-toast-icon",f.style.display="flex",n)f.textContent=n;else{const t=document.createElement("img");t.style.width="30px",t.style.height="30px",t.style.objectFit="contain","success"===s?t.src="https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/success.gif":"error"===s?t.src="https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/error.gif":"info"===s?t.src="https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/info.gif":"warning"===s&&(t.src="https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.3.0/assets/img/warning.gif"),f.appendChild(t)}if(t.appendChild(f),a){const e=document.createElement("div");e.className="ts-toast-loader",t.appendChild(e),setTimeout((()=>{e.classList.add("done")}),2e3)}t._autoRemove&&clearTimeout(t._autoRemove);const g=setTimeout((()=>{var e,o;o=()=>{d&&"function"==typeof d&&d(t)},(e=t).classList.add("ts-toast-slide-out"),e.classList.remove("ts-toast-show"),e.style.animation="",setTimeout((()=>{e.classList.remove("ts-toast-slide-out"),e.parentNode&&e.parentNode.removeChild(e),"function"==typeof o&&o()}),500)}),i);t._autoRemove=g},toast.loading=function(t,e={}){const o=toast(t,{...e,type:e.type||"info",duration:0,showLoader:!0,icon:null});requestAnimationFrame((()=>{o.classList.add("ts-toast-show")}));const s=o.querySelector(".ts-toast-loader");let n=o.querySelector(".ts-toast-icon");return n||(n=document.createElement("span"),n.className="ts-toast-icon",n.style.display="flex",o.appendChild(n)),o._managedByLoading=!0,s&&setTimeout((()=>{o._managedByLoading||s.classList.add("done")}),2e3),{update:(t,e={})=>{o._managedByLoading=!1,toast.update(o,t,{...e,showLoader:!1})},close:()=>{o._autoRemove&&clearTimeout(o._autoRemove),o.classList.add("ts-toast-slide-out"),o.classList.remove("ts-toast-show"),o.style.animation="",setTimeout((()=>{o.classList.remove("ts-toast-slide-out"),o.parentNode&&o.parentNode.removeChild(o)}),500)}}},toast.confirm=function(t,e={}){return new Promise((o=>{toast(t,{...e,mode:"confirm",duration:0,dismissOnClick:!1,onResult:t=>o(t)})}))},"undefined"!=typeof window&&(window.toast=toast),"undefined"!=typeof module&&module.exports&&(module.exports=toast);
1
+ "use strict";const TS_TOAST_VERSION="5.5.0",TS_TOAST_CDN="undefined"!=typeof window&&window.TS_TOAST_ASSET_BASE?String(window.TS_TOAST_ASSET_BASE).replace(/\/+$/,""):"https://cdn.jsdelivr.net/npm/@tsirosgeorge/toastnotification@5.5.0";let tsToastOpenModals=0,tsToastPrevOverflow="";const tsToastReducedMotion=()=>"undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,tsToastFocusable=t=>Array.from(t.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')).filter((t=>!t.disabled&&null!==t.offsetParent));let tsToastIdCounter=0;!function(){const t="ts-toast-stylesheet";if("undefined"!=typeof window&&window.TS_TOAST_NO_CSS)return;if(document.getElementById(t))return;const e=document.createElement("link");e.id=t,e.rel="stylesheet",e.href=`${TS_TOAST_CDN}/assets/css/toast.min.css`,document.head.appendChild(e)}(),function(){const t="ts-toast-inline-extras";if(document.getElementById(t))return;const e=document.createElement("style");e.id=t,e.textContent="\n /* Ensure center positions exist even if external CSS lacks them */\n .ts-toast-container.top-center { top: 1rem; left: 50%; transform: translateX(-50%); align-items: center; }\n .ts-toast-container.bottom-center { bottom: 1rem; left: 50%; transform: translateX(-50%); align-items: center; }\n .ts-toast-container.center { top: 50%; left: 50%; transform: translate(-50%, -50%); align-items: center; }\n .ts-toast-overlay.center { align-items: center; justify-content: center; }\n .ts-toast-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 2147483646; }\n .ts-toast.ts-toast-confirm { max-width: min(92vw, 440px); width: max(320px, 60%); flex-direction: column; gap: 12px; padding: 16px 20px; background: var(--toast-bg, #fff); color: var(--toast-color, #000); border: 1px solid var(--toast-border, #e5e7eb); border-radius: 12px; box-shadow: var(--toast-shadow, 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)); text-align: center; }\n .ts-toast.ts-toast-confirm .ts-toast-content { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; }\n .ts-toast-actions { display: flex; gap: 10px; justify-content: center; margin-top: 12px; }\n .ts-toast-btn { appearance: none; border: 0; padding: 8px 12px; border-radius: 8px; font-weight: 600; cursor: pointer; }\n .ts-toast-btn.cancel { background: #e9ecef; color: #1f2937; }\n .ts-toast-btn.confirm { background: #3b82f6; color: #fff; }\n .ts-toast.ts-toast-error .ts-toast-btn.confirm,\n .ts-toast.ts-toast-warning .ts-toast-btn.confirm { background: #ef4444; color: #fff; }\n .ts-toast.ts-toast-confirm .ts-toast-title { font-weight: 700; font-size: 1.05rem; margin-top: 4px; }\n .ts-toast.ts-toast-confirm .ts-toast-close { position: absolute; top: 8px; right: 8px; width: 28px; height: 28px; border-radius: 999px; border: 0; background: transparent; color: #6b7280; font-size: 20px; line-height: 1; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }\n .ts-toast.ts-toast-confirm .ts-toast-close:hover { background: rgba(0,0,0,0.06); }\n .ts-toast.ts-toast-confirm .ts-toast-icon { width: 64px; height: 64px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; }\n .ts-toast.ts-toast-confirm .ts-toast-icon img { width: 36px; height: 36px; }\n .ts-toast.ts-toast-confirm.ts-toast-success .ts-toast-icon { background: #dcfce7; }\n .ts-toast.ts-toast-confirm.ts-toast-info .ts-toast-icon { background: #dbeafe; }\n .ts-toast.ts-toast-confirm.ts-toast-warning .ts-toast-icon { background: #fef3c7; }\n .ts-toast.ts-toast-confirm.ts-toast-error .ts-toast-icon { background: #fee2e2; }\n ",document.head.appendChild(e)}();const toast=function(t,e={}){const{position:o="top-right",animation:s="slide-right",type:n="info",duration:a=3e3,icon:i=null,showLoader:r=!1,mode:l="alert",title:c=null,confirmText:d="Yes",cancelText:u="No",input:m=!1,inputPlaceholder:f="",inputValue:p="",confirmButtonBg:y=null,confirmButtonColor:h=null,cancelButtonBg:g=null,cancelButtonColor:b=null,onConfirm:v=null,onCancel:x=null,onResult:w=null,useOverlay:T=!0,closeOnOverlayClick:C=!0,showClose:E=!1,closeOnEscape:S=!0,allowHtml:L=!0,dismissOnClick:_=!0,onClick:k=null,onShow:A=null,onDismiss:N=null}=e,O="confirm"===l||"swal"===l,M=tsToastReducedMotion(),$="string"==typeof e.animation&&e.animation.trim()?{"slide-top":"ts-toast-slide-top","slide-bottom":"ts-toast-slide-bottom","slide-left":"ts-toast-slide-left","slide-right":"ts-toast-slide-right","zoom-in":"ts-toast-zoom-in","zoom-out":"ts-toast-zoom-out",flip:"ts-toast-flip"}[B=e.animation.trim()]||B:O||"center"===o?"ts-toast-zoom-in":o.startsWith("top")?"ts-toast-slide-top":o.startsWith("bottom")?"ts-toast-slide-bottom":o.endsWith("left")?"ts-toast-slide-left":"ts-toast-slide-right";var B;const R=(t,e)=>{const o=t.dataset&&t.dataset.anim?t.dataset.anim:t.style.animation||"";let s="";o.includes("ts-toast-slide-top")?s="translateY(-100%)":o.includes("ts-toast-slide-bottom")?s="translateY(100%)":(o.includes("ts-toast-slide-left")||o.includes("ts-toast-slide-right"))&&(s="translateX(100%)"),t.classList.add("ts-toast-slide-out"),t.classList.remove("ts-toast-show"),t.style.animation="",s&&(t.style.transform=s),t.style.opacity="0",setTimeout((()=>{t.classList.remove("ts-toast-slide-out"),t.parentNode&&t.parentNode.removeChild(t),"function"==typeof e&&e()}),M?0:500)},D=document.createElement("div");D.className=`ts-toast ts-toast-${n}${O?" ts-toast-confirm":""}`,D.dataset.anim=$,M||(D.style.animation=`${$} 0.5s ease`);const q="ts-toast-"+ ++tsToastIdCounter;O?(D.setAttribute("role","dialog"),D.setAttribute("aria-modal","true"),D.tabIndex=-1):"error"!==n&&"warning"!==n||D.setAttribute("role","alert"),O||(D.style.flexDirection="row-reverse",D.style.justifyContent="flex-end");const j=document.createElement("span");if(j.className="ts-toast-icon",j.style.display="flex",i)j.textContent=i;else{const t=document.createElement("img");t.alt="",t.setAttribute("aria-hidden","true"),t.style.width="30px",t.style.height="30px",t.style.objectFit="contain";const e={success:"success.gif",error:"error.gif",info:"info.gif",warning:"warning.gif"}[n];e&&(t.src=`${TS_TOAST_CDN}/assets/img/${e}`),j.appendChild(t)}const P=document.createElement("div");P.className="ts-toast-body",P.id=`${q}-body`,L?P.innerHTML=t:P.textContent=t;let z=null;if(O){if(z=document.createElement("div"),z.className="ts-toast-content",z.appendChild(j),c){const t=document.createElement("div");t.className="ts-toast-title",t.id=`${q}-title`,t.textContent=c,z.appendChild(t),D.setAttribute("aria-labelledby",t.id)}z.appendChild(P),D.setAttribute("aria-describedby",P.id),D.appendChild(z)}else D.appendChild(P);let I=null;O&&m&&("textarea"===m?(I=document.createElement("textarea"),I.rows=3):(I=document.createElement("input"),I.type="text"===m||"email"===m||"password"===m||"number"===m?m:"text"),I.className="ts-toast-input",I.placeholder=f,I.value=p,"textarea"!==m&&I.addEventListener("keydown",(t=>{"Enter"===t.key&&(t.preventDefault(),Y(!0))})),D.appendChild(I));let F=null,X=null,Y=null,H=()=>{};if(O){F=document.createElement("div"),F.className="ts-toast-actions";const t=document.createElement("button");t.className="ts-toast-btn cancel",t.textContent=u;const e=document.createElement("button");e.className="ts-toast-btn confirm",e.textContent=d,g&&(t.style.background=g),b&&(t.style.color=b),y&&(e.style.background=y),h&&(e.style.color=h),F.appendChild(t),F.appendChild(e),D.appendChild(F),D.result=new Promise((t=>{X=t}));let o=!1;Y=t=>{if(o)return;o=!0;const e=t?!I||I.value:!!I&&null;X&&X(e),t&&"function"==typeof v&&v(e,D),t||"function"!=typeof x||x(D),"function"==typeof w&&w(e,D),H(),R(D,(()=>{N&&"function"==typeof N&&N(D),W&&W.parentNode&&W.parentNode.removeChild(W)}))},t.addEventListener("click",(t=>{t.stopPropagation(),Y(!1)})),e.addEventListener("click",(t=>{t.stopPropagation(),Y(!0)}))}let K=null;r&&(K=document.createElement("div"),K.className="ts-toast-loader",D.appendChild(K));let W=null;if(O&&T){if(W=document.createElement("div"),W.className="ts-toast-overlay"+(e.position?` ${o}`:""),document.body.appendChild(W),W.appendChild(D),E){const t=document.createElement("button");t.className="ts-toast-close",t.setAttribute("aria-label","Close"),t.innerHTML="×",t.addEventListener("click",(t=>{t.stopPropagation(),Y(!1)})),D.appendChild(t)}if(C){let t=!1;W.addEventListener("pointerdown",(e=>{t=e.target===W})),W.addEventListener("click",(e=>{e.target===W&&t&&Y(!1),t=!1}))}}else{let t=document.querySelector(`.ts-toast-container.${o}`);t||(t=document.createElement("div"),t.className=`ts-toast-container ${o}`,document.body.appendChild(t)),t.hasAttribute("aria-live")||(t.setAttribute("role","status"),t.setAttribute("aria-live","polite"),t.setAttribute("aria-relevant","additions")),t.appendChild(D)}if(O){const t=document.activeElement;tsToastOpenModals+=1,1===tsToastOpenModals&&(tsToastPrevOverflow=document.body.style.overflow,document.body.style.overflow="hidden");const e=()=>{const t=document.querySelectorAll(".ts-toast.ts-toast-confirm");return 0===t.length||t[t.length-1]===D},o=t=>{if(!e())return;if("Escape"===t.key&&S)return t.preventDefault(),void Y(!1);if("Tab"!==t.key)return;const o=tsToastFocusable(D);if(!o.length)return void t.preventDefault();const s=o[0],n=o[o.length-1];D.contains(document.activeElement)?t.shiftKey&&document.activeElement===s?(t.preventDefault(),n.focus()):t.shiftKey||document.activeElement!==n||(t.preventDefault(),s.focus()):(t.preventDefault(),(t.shiftKey?n:s).focus())};document.addEventListener("keydown",o,!0),H=()=>{document.removeEventListener("keydown",o,!0),tsToastOpenModals=Math.max(0,tsToastOpenModals-1),0===tsToastOpenModals&&(document.body.style.overflow=tsToastPrevOverflow),t&&"function"==typeof t.focus&&document.contains(t)&&t.focus()},(I||D.querySelector(".ts-toast-btn.confirm")||D).focus()}if(A&&"function"==typeof A&&A(D),setTimeout((()=>{D.classList.add("ts-toast-show")}),100),r&&K&&setTimeout((()=>{D._managedByLoading||(K.classList.add("done"),K.remove(),D.contains(j)||(O&&z?z.appendChild(j):D.appendChild(j)))}),2e3),r||O||D.contains(j)||D.appendChild(j),!O&&a>0){const t=setTimeout((()=>{R(D,(()=>{N&&"function"==typeof N&&N(D)}))}),a);D._autoRemove=t}if(!O&&_&&D.addEventListener("click",(()=>{D._autoRemove&&clearTimeout(D._autoRemove),k&&"function"==typeof k&&k(D),R(D,(()=>{N&&"function"==typeof N&&N(D)}))})),!O){let t=0,e=0,o=0;D.addEventListener("touchstart",(o=>{t=o.changedTouches[0].screenX,e=o.changedTouches[0].screenY}),{passive:!0}),D.addEventListener("touchend",(s=>{o=s.changedTouches[0].screenX;const n=Math.abs(t-o),a=Math.abs(e-s.changedTouches[0].screenY);n>50&&n>a&&(D._autoRemove&&clearTimeout(D._autoRemove),R(D,(()=>{N&&"function"==typeof N&&N(D)})))}))}return D.close=()=>{D._autoRemove&&clearTimeout(D._autoRemove),O?Y(!1):R(D,(()=>{N&&"function"==typeof N&&N(D)}))},D};toast.success=function(t,e){return toast(t,{...e,type:"success"})},toast.error=function(t,e){return toast(t,{...e,type:"error"})},toast.warning=function(t,e){return toast(t,{...e,type:"warning"})},toast.info=function(t,e){return toast(t,{...e,type:"info"})},toast.update=function(t,e,o={}){const{type:s=null,icon:n=null,showLoader:a=!1,duration:i=3e3,onClick:r=null,onShow:l=null,onDismiss:c=null}=o,d=t.querySelector(".ts-toast-loader"),u=t.querySelector(".ts-toast-icon");d&&d.remove(),s&&(["success","error","info","warning"].forEach((e=>t.classList.remove(`ts-toast-${e}`))),t.classList.add("ts-toast",`ts-toast-${s}`,"ts-toast-show"));const m=t.querySelector(".ts-toast-body");m&&(m.innerHTML=e),u&&u.remove();const f=document.createElement("span");if(f.className="ts-toast-icon",f.style.display="flex",n)f.textContent=n;else{const t=document.createElement("img");t.alt="",t.setAttribute("aria-hidden","true"),t.style.width="30px",t.style.height="30px",t.style.objectFit="contain";const e={success:"success.gif",error:"error.gif",info:"info.gif",warning:"warning.gif"}[s];e&&(t.src=`${TS_TOAST_CDN}/assets/img/${e}`),f.appendChild(t)}if((t.querySelector(".ts-toast-content")||t).appendChild(f),a){const e=document.createElement("div");e.className="ts-toast-loader",t.appendChild(e),setTimeout((()=>{e.classList.add("done")}),2e3)}t._autoRemove&&clearTimeout(t._autoRemove);const p=setTimeout((()=>{var e,o;o=()=>{c&&"function"==typeof c&&c(t)},(e=t).classList.add("ts-toast-slide-out"),e.classList.remove("ts-toast-show"),e.style.animation="",setTimeout((()=>{e.classList.remove("ts-toast-slide-out"),e.parentNode&&e.parentNode.removeChild(e),"function"==typeof o&&o()}),500)}),i);t._autoRemove=p},toast.loading=function(t,e={}){const o=toast(t,{...e,type:e.type||"info",duration:0,showLoader:!0,icon:null});requestAnimationFrame((()=>{o.classList.add("ts-toast-show")}));const s=o.querySelector(".ts-toast-loader");let n=o.querySelector(".ts-toast-icon");return n||(n=document.createElement("span"),n.className="ts-toast-icon",n.style.display="flex",o.appendChild(n)),o._managedByLoading=!0,s&&setTimeout((()=>{o._managedByLoading||s.classList.add("done")}),2e3),{update:(t,e={})=>{o._managedByLoading=!1,toast.update(o,t,{...e,showLoader:!1})},close:()=>{o._managedByLoading=!1,o.close()}}},toast.confirm=function(t,e={}){return new Promise((o=>{toast(t,{...e,mode:"confirm",duration:0,dismissOnClick:!1,onResult:t=>o(t)})}))},toast.dismissAll=function(){document.querySelectorAll(".ts-toast").forEach((t=>{"function"==typeof t.close&&t.close()}))},"undefined"!=typeof window&&(window.toast=toast),"undefined"!=typeof module&&module.exports&&(module.exports=toast);