vanilla-jui 1.4.8 → 1.5.1
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 +10 -4
- package/dist/index.d.ts +402 -977
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -3
- package/src/components/accordion.ts +467 -349
- package/src/components/flow.ts +779 -1133
- package/src/components/form.ts +666 -509
- package/src/components/menu.ts +199 -326
- package/src/components/modal.ts +510 -747
- package/src/components/offcanvas.ts +281 -361
- package/src/components/pagination.ts +207 -374
- package/src/components/swiper.ts +688 -1168
- package/src/components/tabs.ts +468 -788
- package/src/components/toc.ts +191 -244
- package/src/core/component.ts +332 -0
- package/src/core/motion.ts +298 -0
- package/src/core/presence.ts +233 -0
- package/src/core/scheduler.ts +91 -0
- package/src/core/view.ts +31 -0
- package/src/css/accordion.css +94 -3
- package/src/css/content.css +18 -24
- package/src/css/drop.css +4 -1
- package/src/css/form.css +8 -0
- package/src/css/menu.css +10 -5
- package/src/css/modal.css +2 -1
- package/src/css/offcanvas.css +5 -17
- package/src/css/swiper.css +2 -1
- package/src/css/tabs.css +8 -17
- package/src/css/themes/default/accordion.css +0 -48
- package/src/css/themes/default/swiper.css +1 -1
- package/src/css/themes/default/tabs.css +4 -6
- package/src/css/toast.css +1 -17
- package/src/css/tooltip.css +2 -1
- package/src/index.ts +8 -3
- package/src/locales/index.ts +3 -0
- package/src/primitives/drop.ts +253 -262
- package/src/primitives/loading.ts +22 -4
- package/src/primitives/parabola.ts +123 -164
- package/src/primitives/sticky.ts +297 -0
- package/src/primitives/theme.ts +164 -222
- package/src/primitives/toast.ts +309 -284
- package/src/primitives/tooltip.ts +65 -66
- package/src/utilities/browser.ts +38 -0
- package/src/utilities/dom.ts +0 -54
- package/src/utilities/refs.ts +61 -0
- package/src/utilities/state.ts +73 -0
- package/src/utilities/timer.ts +11 -31
- package/src/validation/validator.ts +289 -389
- package/src/components/sticky.ts +0 -297
- package/src/core/Component.ts +0 -377
- package/src/utilities/scheduler.ts +0 -156
package/src/primitives/toast.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx } from 'vanilla-signal';
|
|
1
|
+
import { insert, jsx } from 'vanilla-signal';
|
|
2
2
|
import { t } from 'vanilla-signal-i18n';
|
|
3
3
|
|
|
4
4
|
import locales from '../locales/index.ts';
|
|
@@ -37,6 +37,7 @@ export interface ToastOptions {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export interface ToastActionProps extends ToastOptions {
|
|
40
|
+
type?: ToastType;
|
|
40
41
|
text?: {
|
|
41
42
|
close?: string;
|
|
42
43
|
action?: string;
|
|
@@ -45,10 +46,6 @@ export interface ToastActionProps extends ToastOptions {
|
|
|
45
46
|
onClose?: () => void | Promise<void>;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
interface ResolvedToastOptions {
|
|
49
|
-
className: ToastClassNames;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
49
|
const DEFAULT_CLASS_NAMES: ToastClassNames = {
|
|
53
50
|
container: 'j-toast-container',
|
|
54
51
|
toast: 'j-toast',
|
|
@@ -71,309 +68,337 @@ const TOAST_TYPE_RULE = {
|
|
|
71
68
|
type: 'string',
|
|
72
69
|
enum: ['info', 'success', 'warning', 'error', 'primary'],
|
|
73
70
|
};
|
|
71
|
+
const TOAST_DURATION_RULE = { type: 'number', min: 0 };
|
|
72
|
+
const LITE_DURATION_RULE = { type: 'number', greaterThan: 0 };
|
|
74
73
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const LITE_DURATION_RULE = {
|
|
81
|
-
type: 'number',
|
|
82
|
-
greaterThan: 0,
|
|
83
|
-
};
|
|
74
|
+
const timers = new Set<string>();
|
|
75
|
+
const disposers = new Map<HTMLElement, () => void>();
|
|
76
|
+
const animations = new Map<HTMLElement, Animation>();
|
|
77
|
+
const operations = new Map<HTMLElement, number>();
|
|
78
|
+
let classNames = DEFAULT_CLASS_NAMES;
|
|
84
79
|
|
|
85
|
-
function mergeClassNames(
|
|
86
|
-
return { ...DEFAULT_CLASS_NAMES, ...
|
|
80
|
+
function mergeClassNames(value?: ToastClassNameConfig): ToastClassNames {
|
|
81
|
+
return { ...DEFAULT_CLASS_NAMES, ...value };
|
|
87
82
|
}
|
|
88
83
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
* 以静态方法方式使用,支持多类型堆叠消息和单实例轻提示。
|
|
93
|
-
*/
|
|
94
|
-
export class Toast {
|
|
95
|
-
static timers = new Set<string>();
|
|
96
|
-
static disposers = new Map<HTMLElement, () => void>();
|
|
97
|
-
private static classNames = new WeakMap<HTMLElement, ToastClassNames>();
|
|
98
|
-
private static options: ResolvedToastOptions = {
|
|
99
|
-
className: DEFAULT_CLASS_NAMES,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
static configure(options: ToastOptions = {}): ToastOptions {
|
|
103
|
-
Toast.options = {
|
|
104
|
-
className: mergeClassNames(options.className),
|
|
105
|
-
};
|
|
106
|
-
return Toast.options;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private static resolveClassNames(options?: ToastOptions): ToastClassNames {
|
|
110
|
-
return mergeClassNames({
|
|
111
|
-
...Toast.options.className,
|
|
112
|
-
...options?.className,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
static show(
|
|
117
|
-
message = '',
|
|
118
|
-
duration = 3000,
|
|
119
|
-
type: ToastType = 'info',
|
|
120
|
-
options: ToastOptions = {}
|
|
121
|
-
): HTMLElement {
|
|
122
|
-
validateParam('message', message, 'string', 'Toast.show');
|
|
123
|
-
validateParam('duration', duration, TOAST_DURATION_RULE, 'Toast.show');
|
|
124
|
-
validateParam('type', type, TOAST_TYPE_RULE, 'Toast.show');
|
|
84
|
+
function resolveClassNames(options?: ToastOptions): ToastClassNames {
|
|
85
|
+
return mergeClassNames({ ...classNames, ...options?.className });
|
|
86
|
+
}
|
|
125
87
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
className:
|
|
131
|
-
'data-toast':
|
|
132
|
-
role: 'alert',
|
|
133
|
-
'aria-atomic': 'true',
|
|
134
|
-
children: [
|
|
135
|
-
jsx('span', {
|
|
136
|
-
className: className.icon,
|
|
137
|
-
'data-toast-icon': id,
|
|
138
|
-
children: icon(type === 'primary' ? 'info' : type),
|
|
139
|
-
}),
|
|
140
|
-
jsx('span', {
|
|
141
|
-
className: className.message,
|
|
142
|
-
'data-toast-message': id,
|
|
143
|
-
children: message,
|
|
144
|
-
}),
|
|
145
|
-
],
|
|
88
|
+
function getOrCreateContainer(names: ToastClassNames): HTMLElement {
|
|
89
|
+
let container = q<HTMLElement>('[data-toast-container]');
|
|
90
|
+
if (!container) {
|
|
91
|
+
container = jsx('div', {
|
|
92
|
+
className: names.container,
|
|
93
|
+
'data-toast-container': '',
|
|
146
94
|
}) as HTMLElement;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const v = type === 'error' ? 'assertive' : 'polite';
|
|
152
|
-
Toast.setTimer(id, 'show', () => toast.setAttribute('aria-live', v), 10);
|
|
153
|
-
|
|
154
|
-
if (duration > 0) {
|
|
155
|
-
Toast.setTimer(id, 'hide', () => Toast.hide(toast), duration);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const disposeClick = listen(toast, 'click', () => Toast.hide(toast));
|
|
159
|
-
Toast.disposers.set(toast, disposeClick);
|
|
160
|
-
|
|
161
|
-
return toast;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
static success(
|
|
165
|
-
message = '',
|
|
166
|
-
duration = 3000,
|
|
167
|
-
options: ToastOptions = {}
|
|
168
|
-
): HTMLElement {
|
|
169
|
-
return Toast.show(message, duration, 'success', options);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
static info(
|
|
173
|
-
message = '',
|
|
174
|
-
duration = 3000,
|
|
175
|
-
options: ToastOptions = {}
|
|
176
|
-
): HTMLElement {
|
|
177
|
-
return Toast.show(message, duration, 'info', options);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
static primary(
|
|
181
|
-
message = '',
|
|
182
|
-
duration = 3000,
|
|
183
|
-
options: ToastOptions = {}
|
|
184
|
-
): HTMLElement {
|
|
185
|
-
return Toast.show(message, duration, 'primary', options);
|
|
95
|
+
insert(document.body, container);
|
|
96
|
+
} else {
|
|
97
|
+
container.className = names.container;
|
|
186
98
|
}
|
|
99
|
+
return container;
|
|
100
|
+
}
|
|
187
101
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
102
|
+
function setToastTimer(
|
|
103
|
+
id: string,
|
|
104
|
+
action: string,
|
|
105
|
+
callback: () => void,
|
|
106
|
+
delay: number
|
|
107
|
+
): string {
|
|
108
|
+
const key = `${id}-${action}`;
|
|
109
|
+
timers.add(key);
|
|
110
|
+
timer.start(key, delay, () => {
|
|
111
|
+
timers.delete(key);
|
|
112
|
+
callback();
|
|
113
|
+
});
|
|
114
|
+
return key;
|
|
115
|
+
}
|
|
195
116
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return Toast.show(message, duration, 'error', options);
|
|
117
|
+
function cancelToastTimers(id: string): void {
|
|
118
|
+
for (const key of Array.from(timers)) {
|
|
119
|
+
if (!key.startsWith(`${id}-`)) continue;
|
|
120
|
+
timer.cancel(key);
|
|
121
|
+
timers.delete(key);
|
|
202
122
|
}
|
|
123
|
+
}
|
|
203
124
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
125
|
+
function removeToastElement(element: HTMLElement): void {
|
|
126
|
+
animations.get(element)?.cancel();
|
|
127
|
+
animations.delete(element);
|
|
128
|
+
operations.delete(element);
|
|
129
|
+
element.remove();
|
|
130
|
+
disposers.get(element)?.();
|
|
131
|
+
disposers.delete(element);
|
|
132
|
+
const container = q<HTMLElement>('[data-toast-container]');
|
|
133
|
+
if (container && container.children.length === 0) container.remove();
|
|
134
|
+
}
|
|
209
135
|
|
|
210
|
-
|
|
211
|
-
|
|
136
|
+
function toastAnimationOptions(
|
|
137
|
+
element: HTMLElement,
|
|
138
|
+
duration: number,
|
|
139
|
+
easing: string
|
|
140
|
+
): KeyframeAnimationOptions {
|
|
141
|
+
const reducedMotion =
|
|
142
|
+
element.ownerDocument.defaultView?.matchMedia?.(
|
|
143
|
+
'(prefers-reduced-motion: reduce)'
|
|
144
|
+
).matches || false;
|
|
145
|
+
return {
|
|
146
|
+
duration: reducedMotion ? 0 : duration,
|
|
147
|
+
easing,
|
|
148
|
+
fill: 'both',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
212
151
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
152
|
+
function waitForAnimation(animation: Animation): Promise<void> {
|
|
153
|
+
const finished = animation.finished.then(
|
|
154
|
+
() => undefined,
|
|
155
|
+
() => undefined
|
|
156
|
+
);
|
|
157
|
+
return finished;
|
|
158
|
+
}
|
|
219
159
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
160
|
+
function toastKeyframes(
|
|
161
|
+
element: HTMLElement,
|
|
162
|
+
phase: 'enter' | 'leave'
|
|
163
|
+
): Keyframe[] {
|
|
164
|
+
const lite = element.hasAttribute('data-toast-lite');
|
|
165
|
+
if (lite && phase === 'enter') {
|
|
166
|
+
return [{ opacity: 0 }, { opacity: 1 }];
|
|
227
167
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
validateParam('message', message, 'string', 'Toast.lite');
|
|
235
|
-
validateParam('duration', duration, LITE_DURATION_RULE, 'Toast.lite');
|
|
236
|
-
|
|
237
|
-
const className = Toast.resolveClassNames(options);
|
|
238
|
-
q<HTMLElement>('[data-toast-lite]')?.remove();
|
|
239
|
-
|
|
240
|
-
const id = randomId();
|
|
241
|
-
const lite = jsx('div', {
|
|
242
|
-
className: className.lite,
|
|
243
|
-
'data-toast': id,
|
|
244
|
-
'data-toast-lite': '',
|
|
245
|
-
children: message,
|
|
246
|
-
}) as HTMLElement;
|
|
247
|
-
|
|
248
|
-
Toast.classNames.set(lite, className);
|
|
249
|
-
document.body.appendChild(lite);
|
|
250
|
-
|
|
251
|
-
Toast.setTimer(
|
|
252
|
-
id,
|
|
253
|
-
'show',
|
|
254
|
-
() => lite.setAttribute('aria-live', 'polite'),
|
|
255
|
-
10
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
Toast.setTimer(
|
|
259
|
-
id,
|
|
260
|
-
'hide',
|
|
261
|
-
() => {
|
|
262
|
-
lite.removeAttribute('aria-live');
|
|
263
|
-
lite.setAttribute('aria-hidden', 'true');
|
|
264
|
-
Toast.setTimer(id, 'remove', () => lite.remove(), 300);
|
|
168
|
+
if (lite) {
|
|
169
|
+
return [
|
|
170
|
+
{ opacity: 1, transform: 'translate(-50%, -50%)' },
|
|
171
|
+
{
|
|
172
|
+
opacity: 0,
|
|
173
|
+
transform: 'translate(-50%, calc(-50% - 8px))',
|
|
265
174
|
},
|
|
266
|
-
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
return lite;
|
|
175
|
+
];
|
|
270
176
|
}
|
|
177
|
+
return phase === 'enter'
|
|
178
|
+
? [
|
|
179
|
+
{ opacity: 0, transform: 'translate3d(0, -16px, 0)' },
|
|
180
|
+
{ opacity: 1, transform: 'translate3d(0, 0, 0)' },
|
|
181
|
+
]
|
|
182
|
+
: [
|
|
183
|
+
{ opacity: 1, transform: 'translate3d(0, 0, 0)' },
|
|
184
|
+
{ opacity: 0, transform: 'translate3d(0, -16px, 0)' },
|
|
185
|
+
];
|
|
186
|
+
}
|
|
271
187
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
'aria-label': props.text?.close || c,
|
|
298
|
-
onClick: async () => {
|
|
299
|
-
await props.onClose?.();
|
|
300
|
-
Toast.hide(action);
|
|
301
|
-
},
|
|
302
|
-
}),
|
|
303
|
-
jsx('button', {
|
|
304
|
-
className: joinClasses(className.button, className.actionBtn),
|
|
305
|
-
children: props.text?.action || a,
|
|
306
|
-
'data-action': 'toast-action',
|
|
307
|
-
'aria-label': props.text?.action || a,
|
|
308
|
-
onClick: async () => {
|
|
309
|
-
await props.onAction?.();
|
|
310
|
-
Toast.hide(action);
|
|
311
|
-
},
|
|
312
|
-
}),
|
|
313
|
-
],
|
|
314
|
-
}),
|
|
315
|
-
],
|
|
316
|
-
}) as HTMLElement;
|
|
317
|
-
|
|
318
|
-
Toast.classNames.set(action, className);
|
|
319
|
-
toastContainer.appendChild(action);
|
|
188
|
+
async function playToastAnimation(
|
|
189
|
+
element: HTMLElement,
|
|
190
|
+
phase: 'enter' | 'leave'
|
|
191
|
+
): Promise<void> {
|
|
192
|
+
const operation = (operations.get(element) || 0) + 1;
|
|
193
|
+
operations.set(element, operation);
|
|
194
|
+
animations.get(element)?.cancel();
|
|
195
|
+
animations.delete(element);
|
|
196
|
+
if (typeof element.animate !== 'function') return;
|
|
197
|
+
const lite = element.hasAttribute('data-toast-lite');
|
|
198
|
+
const animation = element.animate(
|
|
199
|
+
toastKeyframes(element, phase),
|
|
200
|
+
toastAnimationOptions(
|
|
201
|
+
element,
|
|
202
|
+
phase === 'enter' ? (lite ? 150 : 240) : lite ? 120 : 180,
|
|
203
|
+
phase === 'enter'
|
|
204
|
+
? 'cubic-bezier(0.16, 1, 0.3, 1)'
|
|
205
|
+
: 'cubic-bezier(0.4, 0, 1, 1)'
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
animations.set(element, animation);
|
|
209
|
+
await waitForAnimation(animation);
|
|
210
|
+
if (operations.get(element) !== operation) return;
|
|
211
|
+
if (animations.get(element) === animation) animations.delete(element);
|
|
212
|
+
}
|
|
320
213
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
214
|
+
function mountToast(
|
|
215
|
+
element: HTMLElement,
|
|
216
|
+
mount: () => void,
|
|
217
|
+
live: 'assertive' | 'polite',
|
|
218
|
+
onEntered?: () => void
|
|
219
|
+
): void {
|
|
220
|
+
element.removeAttribute('data-mount');
|
|
221
|
+
element.setAttribute('aria-live', live);
|
|
222
|
+
mount();
|
|
223
|
+
element.getBoundingClientRect();
|
|
224
|
+
void playToastAnimation(element, 'enter').then(() => {
|
|
225
|
+
if (!element.isConnected || element.dataset.mount === 'false') return;
|
|
226
|
+
onEntered?.();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
330
229
|
|
|
331
|
-
|
|
332
|
-
|
|
230
|
+
function hide(toast: HTMLElement | null | undefined): void {
|
|
231
|
+
if (!toast) return;
|
|
232
|
+
if (toast.getAttribute('data-mount') === 'false') return;
|
|
233
|
+
disposers.get(toast)?.();
|
|
234
|
+
disposers.delete(toast);
|
|
235
|
+
const id = toast.dataset.toast || randomId();
|
|
236
|
+
cancelToastTimers(id);
|
|
237
|
+
toast.removeAttribute('aria-live');
|
|
238
|
+
toast.setAttribute('data-mount', 'false');
|
|
239
|
+
void playToastAnimation(toast, 'leave').then(() => {
|
|
240
|
+
if (toast.dataset.mount === 'false') removeToastElement(toast);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
333
243
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
244
|
+
function show(
|
|
245
|
+
message = '',
|
|
246
|
+
duration = 3000,
|
|
247
|
+
type: ToastType = 'info',
|
|
248
|
+
options: ToastOptions = {}
|
|
249
|
+
): HTMLElement {
|
|
250
|
+
validateParam('message', message, 'string', 'Toast.show');
|
|
251
|
+
validateParam('duration', duration, TOAST_DURATION_RULE, 'Toast.show');
|
|
252
|
+
validateParam('type', type, TOAST_TYPE_RULE, 'Toast.show');
|
|
253
|
+
|
|
254
|
+
const names = resolveClassNames(options);
|
|
255
|
+
const id = randomId();
|
|
256
|
+
const toast = jsx('div', {
|
|
257
|
+
className: joinClasses(names.toast, names[type]),
|
|
258
|
+
'data-toast': id,
|
|
259
|
+
role: 'alert',
|
|
260
|
+
'aria-atomic': 'true',
|
|
261
|
+
children: [
|
|
262
|
+
jsx('span', {
|
|
263
|
+
className: names.icon,
|
|
264
|
+
'data-toast-icon': id,
|
|
265
|
+
children: icon(type === 'primary' ? 'info' : type),
|
|
266
|
+
}),
|
|
267
|
+
jsx('span', {
|
|
268
|
+
className: names.message,
|
|
269
|
+
'data-toast-message': id,
|
|
270
|
+
children: message,
|
|
271
|
+
}),
|
|
272
|
+
],
|
|
273
|
+
}) as HTMLElement;
|
|
274
|
+
mountToast(
|
|
275
|
+
toast,
|
|
276
|
+
() => insert(getOrCreateContainer(names), toast),
|
|
277
|
+
type === 'error' ? 'assertive' : 'polite'
|
|
278
|
+
);
|
|
279
|
+
if (duration > 0) setToastTimer(id, 'hide', () => hide(toast), duration);
|
|
280
|
+
disposers.set(
|
|
281
|
+
toast,
|
|
282
|
+
listen(toast, 'click', () => hide(toast))
|
|
283
|
+
);
|
|
284
|
+
return toast;
|
|
285
|
+
}
|
|
347
286
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
287
|
+
function lite(
|
|
288
|
+
message = '',
|
|
289
|
+
duration = 2000,
|
|
290
|
+
options: ToastOptions = {}
|
|
291
|
+
): HTMLElement {
|
|
292
|
+
validateParam('message', message, 'string', 'Toast.lite');
|
|
293
|
+
validateParam('duration', duration, LITE_DURATION_RULE, 'Toast.lite');
|
|
294
|
+
|
|
295
|
+
const names = resolveClassNames(options);
|
|
296
|
+
const previous = q<HTMLElement>('[data-toast-lite]');
|
|
297
|
+
if (previous) {
|
|
298
|
+
cancelToastTimers(previous.dataset.toast || '');
|
|
299
|
+
animations.get(previous)?.cancel();
|
|
300
|
+
removeToastElement(previous);
|
|
361
301
|
}
|
|
302
|
+
const id = randomId();
|
|
303
|
+
const element = jsx('div', {
|
|
304
|
+
className: names.lite,
|
|
305
|
+
'data-toast': id,
|
|
306
|
+
'data-toast-lite': '',
|
|
307
|
+
children: message,
|
|
308
|
+
}) as HTMLElement;
|
|
309
|
+
mountToast(element, () => insert(document.body, element), 'polite');
|
|
310
|
+
setToastTimer(id, 'hide', () => hide(element), duration);
|
|
311
|
+
return element;
|
|
312
|
+
}
|
|
362
313
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
314
|
+
function action(message = '', props: ToastActionProps = {}): HTMLElement {
|
|
315
|
+
validateParam('message', message, 'string', 'Toast.action');
|
|
316
|
+
const closeText = props.text?.close || t('Close', locales);
|
|
317
|
+
const actionText = props.text?.action || t('Confirm', locales);
|
|
318
|
+
const names = resolveClassNames(props);
|
|
319
|
+
const id = randomId();
|
|
320
|
+
const element = jsx('div', {
|
|
321
|
+
className: joinClasses(names.action, names[props.type || 'info']),
|
|
322
|
+
'data-toast': id,
|
|
323
|
+
'data-toast-action': '',
|
|
324
|
+
children: [
|
|
325
|
+
jsx('div', {
|
|
326
|
+
className: names.message,
|
|
327
|
+
'data-toast-message': id,
|
|
328
|
+
children: message,
|
|
329
|
+
}),
|
|
330
|
+
jsx('div', {
|
|
331
|
+
className: names.actions,
|
|
332
|
+
'data-toast-actions': id,
|
|
333
|
+
children: [
|
|
334
|
+
jsx('button', {
|
|
335
|
+
className: joinClasses(names.button, names.closeBtn),
|
|
336
|
+
children: closeText,
|
|
337
|
+
'data-action': 'close',
|
|
338
|
+
'aria-label': closeText,
|
|
339
|
+
onClick: async () => {
|
|
340
|
+
await props.onClose?.();
|
|
341
|
+
hide(element);
|
|
342
|
+
},
|
|
343
|
+
}),
|
|
344
|
+
jsx('button', {
|
|
345
|
+
className: joinClasses(names.button, names.actionBtn),
|
|
346
|
+
children: actionText,
|
|
347
|
+
'data-action': 'toast-action',
|
|
348
|
+
'aria-label': actionText,
|
|
349
|
+
onClick: async () => {
|
|
350
|
+
await props.onAction?.();
|
|
351
|
+
hide(element);
|
|
352
|
+
},
|
|
353
|
+
}),
|
|
354
|
+
],
|
|
355
|
+
}),
|
|
356
|
+
],
|
|
357
|
+
}) as HTMLElement;
|
|
358
|
+
mountToast(
|
|
359
|
+
element,
|
|
360
|
+
() => insert(getOrCreateContainer(names), element),
|
|
361
|
+
'polite',
|
|
362
|
+
() => {
|
|
363
|
+
q<HTMLButtonElement>('[data-action="toast-action"]', element)?.focus();
|
|
366
364
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
Toast.disposers.clear();
|
|
371
|
-
|
|
372
|
-
q<HTMLElement>('[data-toast-container]')?.remove();
|
|
373
|
-
q<HTMLElement>('[data-toast-lite]')?.remove();
|
|
374
|
-
}
|
|
365
|
+
);
|
|
366
|
+
return element;
|
|
367
|
+
}
|
|
375
368
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
369
|
+
function clearAll(): void {
|
|
370
|
+
for (const key of timers) timer.cancel(key);
|
|
371
|
+
timers.clear();
|
|
372
|
+
for (const dispose of disposers.values()) dispose();
|
|
373
|
+
disposers.clear();
|
|
374
|
+
for (const animation of animations.values()) animation.cancel();
|
|
375
|
+
animations.clear();
|
|
376
|
+
operations.clear();
|
|
377
|
+
q<HTMLElement>('[data-toast-container]')?.remove();
|
|
378
|
+
q<HTMLElement>('[data-toast-lite]')?.remove();
|
|
379
379
|
}
|
|
380
|
+
|
|
381
|
+
export const Toast = {
|
|
382
|
+
timers,
|
|
383
|
+
disposers,
|
|
384
|
+
configure(options: ToastOptions = {}): ToastOptions {
|
|
385
|
+
classNames = mergeClassNames(options.className);
|
|
386
|
+
return { className: classNames };
|
|
387
|
+
},
|
|
388
|
+
show,
|
|
389
|
+
success: (message = '', duration = 3000, options: ToastOptions = {}) =>
|
|
390
|
+
show(message, duration, 'success', options),
|
|
391
|
+
info: (message = '', duration = 3000, options: ToastOptions = {}) =>
|
|
392
|
+
show(message, duration, 'info', options),
|
|
393
|
+
primary: (message = '', duration = 3000, options: ToastOptions = {}) =>
|
|
394
|
+
show(message, duration, 'primary', options),
|
|
395
|
+
warning: (message = '', duration = 3000, options: ToastOptions = {}) =>
|
|
396
|
+
show(message, duration, 'warning', options),
|
|
397
|
+
error: (message = '', duration = 3000, options: ToastOptions = {}) =>
|
|
398
|
+
show(message, duration, 'error', options),
|
|
399
|
+
hide,
|
|
400
|
+
lite,
|
|
401
|
+
action,
|
|
402
|
+
clearAll,
|
|
403
|
+
destroyAll: clearAll,
|
|
404
|
+
};
|