vanilla-jui 1.5.20 → 1.5.21
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/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/package.json +1 -1
- package/src/primitives/toast.ts +43 -0
package/package.json
CHANGED
package/src/primitives/toast.ts
CHANGED
|
@@ -51,6 +51,7 @@ export interface ToastThemeOptions extends ToastClassNameOptions {
|
|
|
51
51
|
|
|
52
52
|
export interface ToastOptions extends ToastThemeOptions {
|
|
53
53
|
duration?: number;
|
|
54
|
+
once?: boolean;
|
|
54
55
|
loading?: MaybeAccessor<boolean>;
|
|
55
56
|
text?: {
|
|
56
57
|
loading?: string;
|
|
@@ -60,6 +61,7 @@ export interface ToastOptions extends ToastThemeOptions {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
export interface ToastConfirmProps extends ToastThemeOptions {
|
|
64
|
+
once?: boolean;
|
|
63
65
|
text?: {
|
|
64
66
|
close?: string;
|
|
65
67
|
confirm?: string;
|
|
@@ -97,6 +99,7 @@ const timers = new Set<string>();
|
|
|
97
99
|
const disposers = new Map<HTMLElement, () => void>();
|
|
98
100
|
const animations = new Map<HTMLElement, Animation>();
|
|
99
101
|
const operations = new Map<HTMLElement, number>();
|
|
102
|
+
const singletonToasts = new Map<'show' | 'confirm', HTMLElement>();
|
|
100
103
|
let classNames = DEFAULT_CLASS_NAMES;
|
|
101
104
|
|
|
102
105
|
function mergeClassNames(value?: ToastClassNameConfig): ToastClassNames {
|
|
@@ -115,6 +118,30 @@ function addToastDisposer(element: HTMLElement, dispose: () => void): void {
|
|
|
115
118
|
});
|
|
116
119
|
}
|
|
117
120
|
|
|
121
|
+
function getSingletonToast(kind: 'show' | 'confirm'): HTMLElement | null {
|
|
122
|
+
const toast = singletonToasts.get(kind);
|
|
123
|
+
if (!toast) return null;
|
|
124
|
+
if (!toast.isConnected || toast.getAttribute('data-mount') === 'false') {
|
|
125
|
+
singletonToasts.delete(kind);
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return toast;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function setSingletonToast(
|
|
132
|
+
kind: 'show' | 'confirm',
|
|
133
|
+
toast: HTMLElement,
|
|
134
|
+
once: boolean
|
|
135
|
+
): void {
|
|
136
|
+
if (once) singletonToasts.set(kind, toast);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function clearSingletonToast(toast: HTMLElement): void {
|
|
140
|
+
for (const [kind, current] of singletonToasts) {
|
|
141
|
+
if (current === toast) singletonToasts.delete(kind);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
118
145
|
function isToastLoading(options: ToastOptions): boolean {
|
|
119
146
|
return !!access(options.loading ?? false);
|
|
120
147
|
}
|
|
@@ -229,6 +256,7 @@ function removeToastElement(element: HTMLElement): void {
|
|
|
229
256
|
element.remove();
|
|
230
257
|
disposers.get(element)?.();
|
|
231
258
|
disposers.delete(element);
|
|
259
|
+
clearSingletonToast(element);
|
|
232
260
|
const container = q<HTMLElement>('[data-toast-container]');
|
|
233
261
|
if (container && container.children.length === 0) container.remove();
|
|
234
262
|
}
|
|
@@ -342,9 +370,15 @@ function hide(toast: HTMLElement | null | undefined): void {
|
|
|
342
370
|
}
|
|
343
371
|
|
|
344
372
|
function show(message = '', options: ToastOptions = {}): HTMLElement {
|
|
373
|
+
const once = options.once ?? false;
|
|
374
|
+
if (once) {
|
|
375
|
+
const toast = getSingletonToast('show');
|
|
376
|
+
if (toast) return toast;
|
|
377
|
+
}
|
|
345
378
|
validateParam('message', message, 'string', 'Toast.show');
|
|
346
379
|
const duration = options.duration ?? 3000;
|
|
347
380
|
const theme = options.theme || 'info';
|
|
381
|
+
validateParam('once', once, 'boolean', 'Toast.show');
|
|
348
382
|
validateParam('duration', duration, TOAST_DURATION_RULE, 'Toast.show');
|
|
349
383
|
validateParam('theme', theme, TOAST_THEME_RULE, 'Toast.show');
|
|
350
384
|
|
|
@@ -380,6 +414,7 @@ function show(message = '', options: ToastOptions = {}): HTMLElement {
|
|
|
380
414
|
() => insert(getOrCreateContainer(names), toast),
|
|
381
415
|
theme === 'error' ? 'assertive' : 'polite'
|
|
382
416
|
);
|
|
417
|
+
setSingletonToast('show', toast, once);
|
|
383
418
|
addToastDisposer(
|
|
384
419
|
toast,
|
|
385
420
|
listen(toast, 'click', () => void closeByUser(toast, options))
|
|
@@ -415,10 +450,16 @@ function lite(
|
|
|
415
450
|
}
|
|
416
451
|
|
|
417
452
|
function confirm(message = '', props: ToastConfirmProps = {}): HTMLElement {
|
|
453
|
+
const once = props.once ?? true;
|
|
454
|
+
if (once) {
|
|
455
|
+
const toast = getSingletonToast('confirm');
|
|
456
|
+
if (toast) return toast;
|
|
457
|
+
}
|
|
418
458
|
validateParam('message', message, 'string', 'Toast.confirm');
|
|
419
459
|
const closeText = props.text?.close || s('Close');
|
|
420
460
|
const confirmText = props.text?.confirm || s('Confirm');
|
|
421
461
|
const theme = props.theme || 'info';
|
|
462
|
+
validateParam('once', once, 'boolean', 'Toast.confirm');
|
|
422
463
|
validateParam('theme', theme, TOAST_THEME_RULE, 'Toast.confirm');
|
|
423
464
|
const names = resolveClassNames(props);
|
|
424
465
|
const id = randomId();
|
|
@@ -468,6 +509,7 @@ function confirm(message = '', props: ToastConfirmProps = {}): HTMLElement {
|
|
|
468
509
|
q<HTMLButtonElement>('[data-toast-button="confirm"]', element)?.focus();
|
|
469
510
|
}
|
|
470
511
|
);
|
|
512
|
+
setSingletonToast('confirm', element, once);
|
|
471
513
|
return element;
|
|
472
514
|
}
|
|
473
515
|
|
|
@@ -479,6 +521,7 @@ function clearAll(): void {
|
|
|
479
521
|
for (const animation of animations.values()) animation.cancel();
|
|
480
522
|
animations.clear();
|
|
481
523
|
operations.clear();
|
|
524
|
+
singletonToasts.clear();
|
|
482
525
|
q<HTMLElement>('[data-toast-container]')?.remove();
|
|
483
526
|
q<HTMLElement>('[data-toast-lite]')?.remove();
|
|
484
527
|
}
|