@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/Readme.md +34 -4
- package/assets/css/toast.css +9 -0
- package/assets/css/toast.min.css +1 -1
- package/package.json +29 -13
- package/toast.d.ts +102 -0
- package/toast.js +249 -74
- package/toast.min.js +1 -1
- package/toast.module.js +717 -512
- package/LICENCE +0 -16
- package/assets/img/old/error.gif +0 -0
- package/assets/img/old/info.gif +0 -0
- package/assets/img/old/success.gif +0 -0
- package/assets/img/old/warning.gif +0 -0
package/toast.js
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (
|
|
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.
|
|
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
|
-
|
|
239
|
-
|
|
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 (
|
|
242
|
-
if (!
|
|
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
|
-
|
|
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
|
-
|
|
279
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
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
|
-
|
|
416
|
-
toastElement.
|
|
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
|
-
|
|
441
|
-
|
|
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.
|
|
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
|
-
|
|
539
|
-
toastElement.
|
|
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);
|