vanilla-jui 1.3.11 → 1.4.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.
Files changed (106) hide show
  1. package/dist/index.d.ts +2921 -0
  2. package/dist/index.js +1 -1
  3. package/dist/index.umd.js +1 -1
  4. package/dist/style.css +1 -1
  5. package/package.json +4 -3
  6. package/src/components/accordion.ts +522 -0
  7. package/src/components/drop.ts +497 -0
  8. package/src/components/flow.ts +1579 -0
  9. package/src/components/form.ts +953 -0
  10. package/src/components/{icons.js → icons.ts} +24 -11
  11. package/src/components/menu.ts +615 -0
  12. package/src/components/{modal.js → modal.ts} +560 -237
  13. package/src/components/offcanvas.ts +457 -0
  14. package/src/components/{pagination.js → pagination.ts} +251 -76
  15. package/src/components/parabola.ts +335 -0
  16. package/src/components/{sticky.js → sticky.ts} +123 -98
  17. package/src/components/{swiper.js → swiper.ts} +510 -232
  18. package/src/components/tabs.ts +984 -0
  19. package/src/components/theme.ts +437 -0
  20. package/src/components/toast.ts +371 -0
  21. package/src/components/toc.ts +385 -0
  22. package/src/components/tooltip.ts +179 -0
  23. package/src/components/validator.ts +596 -0
  24. package/src/core/{Component.js → Component.ts} +134 -27
  25. package/src/css/accordion.css +0 -45
  26. package/src/css/animation.css +31 -1
  27. package/src/css/avatar.css +7 -33
  28. package/src/css/badge.css +11 -59
  29. package/src/css/breadcrumb.css +9 -32
  30. package/src/css/button.css +23 -224
  31. package/src/css/card.css +1 -1
  32. package/src/css/color.css +863 -1013
  33. package/src/css/color_p3.css +822 -975
  34. package/src/css/components.css +1 -0
  35. package/src/css/drop.css +7 -7
  36. package/src/css/flow.css +1 -1
  37. package/src/css/form.css +1 -1
  38. package/src/css/icon.css +7 -12
  39. package/src/css/index.css +5 -2
  40. package/src/css/loading.css +36 -20
  41. package/src/css/menu.css +1 -1
  42. package/src/css/modal.css +15 -44
  43. package/src/css/offcanvas.css +14 -40
  44. package/src/css/popup.css +4 -4
  45. package/src/css/status.css +0 -2
  46. package/src/css/swiper.css +35 -50
  47. package/src/css/tabs.css +9 -31
  48. package/src/css/tag.css +16 -77
  49. package/src/css/themes/default/accordion.css +62 -0
  50. package/src/css/themes/default/avatar.css +39 -0
  51. package/src/css/themes/default/badge.css +63 -0
  52. package/src/css/themes/default/breadcrumb.css +28 -0
  53. package/src/css/themes/default/button.css +204 -0
  54. package/src/css/themes/default/drop.css +11 -0
  55. package/src/css/themes/default/index.css +16 -0
  56. package/src/css/themes/default/loading.css +23 -0
  57. package/src/css/themes/default/modal.css +40 -0
  58. package/src/css/themes/default/offcanvas.css +13 -0
  59. package/src/css/themes/default/popup.css +7 -0
  60. package/src/css/themes/default/swiper.css +35 -0
  61. package/src/css/themes/default/tabs.css +37 -0
  62. package/src/css/themes/default/tag.css +85 -0
  63. package/src/css/themes/default/tip.css +49 -0
  64. package/src/css/themes/default/toast.css +70 -0
  65. package/src/css/themes/default/tooltip.css +14 -0
  66. package/src/css/themes/index.css +1 -0
  67. package/src/css/tip.css +20 -39
  68. package/src/css/toast.css +18 -80
  69. package/src/css/tokens.css +8 -3
  70. package/src/css/tooltip.css +10 -10
  71. package/src/index.ts +37 -0
  72. package/src/locales/{index.js → index.ts} +1 -2
  73. package/src/types.d.ts +1 -0
  74. package/src/utilities/browser.ts +131 -0
  75. package/src/utilities/cache.ts +57 -0
  76. package/src/utilities/core.ts +614 -0
  77. package/src/utilities/dom.ts +468 -0
  78. package/src/utilities/events.ts +106 -0
  79. package/src/utilities/http.ts +35 -0
  80. package/src/utilities/storage.ts +46 -0
  81. package/src/components/accordion.js +0 -373
  82. package/src/components/components.js +0 -0
  83. package/src/components/drop.js +0 -437
  84. package/src/components/flow.js +0 -1133
  85. package/src/components/form.js +0 -585
  86. package/src/components/menu.js +0 -422
  87. package/src/components/offcanvas.js +0 -332
  88. package/src/components/parabola.js +0 -295
  89. package/src/components/tabs.js +0 -746
  90. package/src/components/theme.js +0 -353
  91. package/src/components/toast.js +0 -329
  92. package/src/components/toc.js +0 -226
  93. package/src/components/tooltip.js +0 -86
  94. package/src/components/validator.js +0 -586
  95. package/src/index.js +0 -45
  96. package/src/locales/index.min.js +0 -1
  97. package/src/utilities/browser.js +0 -111
  98. package/src/utilities/cache.js +0 -159
  99. package/src/utilities/core.js +0 -502
  100. package/src/utilities/debug.js +0 -103
  101. package/src/utilities/dom.js +0 -313
  102. package/src/utilities/events.js +0 -69
  103. package/src/utilities/http.js +0 -30
  104. package/src/utilities/legacy-dom.js +0 -179
  105. package/src/utilities/storage.js +0 -46
  106. /package/src/icons/{index.js → index.ts} +0 -0
@@ -0,0 +1,371 @@
1
+ import { jsx } from 'vanilla-signal';
2
+
3
+ import { randomId, timer, validateParam } from '../utilities/core.ts';
4
+ import { q } from '../utilities/dom.ts';
5
+ import { listen } from '../utilities/events.ts';
6
+ import { icon } from './icons.ts';
7
+
8
+ export type ToastType = 'info' | 'success' | 'warning' | 'error' | 'primary';
9
+
10
+ export interface ToastClassNames {
11
+ container: string;
12
+ toast: string;
13
+ icon: string;
14
+ message: string;
15
+ shown: string;
16
+ hidden: string;
17
+ lite: string;
18
+ action: string;
19
+ actions: string;
20
+ button: string;
21
+ cancelButton: string;
22
+ actionButton: string;
23
+ info: string;
24
+ success: string;
25
+ warning: string;
26
+ error: string;
27
+ primary: string;
28
+ }
29
+
30
+ export type ToastClassNameConfig = Partial<ToastClassNames>;
31
+
32
+ export interface ToastOptions {
33
+ className?: ToastClassNameConfig;
34
+ }
35
+
36
+ export interface ToastActionProps extends ToastOptions {
37
+ text?: {
38
+ cancel?: string;
39
+ action?: string;
40
+ };
41
+ onAction?: () => void | Promise<void>;
42
+ }
43
+
44
+ interface ResolvedToastOptions {
45
+ className: ToastClassNames;
46
+ }
47
+
48
+ const DEFAULT_CLASS_NAMES: ToastClassNames = {
49
+ container: 'j-toast-container',
50
+ toast: 'j-toast',
51
+ icon: 'toast-icon',
52
+ message: 'toast-message',
53
+ shown: 'is-shown',
54
+ hidden: 'is-hidden',
55
+ lite: 'j-toast-lite',
56
+ action: 'is-action',
57
+ actions: 'toast-actions',
58
+ button: 'j-button is-sm',
59
+ cancelButton: 'is-ghost',
60
+ actionButton: 'is-outline',
61
+ info: 'is-info',
62
+ success: 'is-success',
63
+ warning: 'is-warning',
64
+ error: 'is-error',
65
+ primary: 'is-primary',
66
+ };
67
+
68
+ const TOAST_TYPE_RULE = {
69
+ type: 'string',
70
+ enum: ['info', 'success', 'warning', 'error', 'primary'],
71
+ };
72
+
73
+ const TOAST_DURATION_RULE = {
74
+ type: 'number',
75
+ validate: (value: unknown) => typeof value === 'number' && value >= 0,
76
+ message: 'expects a positive number or 0.',
77
+ };
78
+
79
+ const LITE_DURATION_RULE = {
80
+ type: 'number',
81
+ validate: (value: unknown) => typeof value === 'number' && value > 0,
82
+ message: 'expects a number greater than 0.',
83
+ };
84
+
85
+ function mergeClassNames(className?: ToastClassNameConfig): ToastClassNames {
86
+ return { ...DEFAULT_CLASS_NAMES, ...className };
87
+ }
88
+
89
+ function joinClasses(
90
+ ...classes: Array<string | null | undefined | false>
91
+ ): string {
92
+ return classes.filter(Boolean).join(' ');
93
+ }
94
+
95
+ /**
96
+ * Toast 消息提示工具。
97
+ *
98
+ * 以静态方法方式使用,支持多类型堆叠消息和单实例轻提示。
99
+ */
100
+ export class Toast {
101
+ static timers = new Set<string>();
102
+ static disposers = new Map<HTMLElement, () => void>();
103
+ private static classNames = new WeakMap<HTMLElement, ToastClassNames>();
104
+ private static options: ResolvedToastOptions = {
105
+ className: DEFAULT_CLASS_NAMES,
106
+ };
107
+
108
+ static configure(options: ToastOptions = {}): ToastOptions {
109
+ Toast.options = {
110
+ className: mergeClassNames(options.className),
111
+ };
112
+ return Toast.options;
113
+ }
114
+
115
+ private static resolveClassNames(options?: ToastOptions): ToastClassNames {
116
+ return mergeClassNames({
117
+ ...Toast.options.className,
118
+ ...options?.className,
119
+ });
120
+ }
121
+
122
+ static show(
123
+ message = '',
124
+ duration = 3000,
125
+ type: ToastType = 'info',
126
+ options: ToastOptions = {}
127
+ ): HTMLElement {
128
+ validateParam('message', message, 'string', 'Toast.show');
129
+ validateParam('duration', duration, TOAST_DURATION_RULE, 'Toast.show');
130
+ validateParam('type', type, TOAST_TYPE_RULE, 'Toast.show');
131
+
132
+ const className = Toast.resolveClassNames(options);
133
+ const toastContainer = Toast.getOrCreateContainer(className);
134
+ const id = randomId();
135
+ const toast = jsx('div', {
136
+ className: joinClasses(className.toast, className[type]),
137
+ 'data-toast': id,
138
+ children: [
139
+ jsx('span', {
140
+ className: className.icon,
141
+ 'data-toast-icon': id,
142
+ children: icon(type === 'primary' ? 'info' : type),
143
+ }),
144
+ jsx('span', {
145
+ className: className.message,
146
+ 'data-toast-message': id,
147
+ children: message,
148
+ }),
149
+ ],
150
+ }) as HTMLElement;
151
+
152
+ Toast.classNames.set(toast, className);
153
+ toastContainer.appendChild(toast);
154
+
155
+ Toast.setTimer(id, 'show', () => toast.classList.add(className.shown), 10);
156
+
157
+ if (duration > 0) {
158
+ Toast.setTimer(id, 'hide', () => Toast.hide(toast), duration);
159
+ }
160
+
161
+ const disposeClick = listen(toast, 'click', () => Toast.hide(toast));
162
+ Toast.disposers.set(toast, disposeClick);
163
+
164
+ return toast;
165
+ }
166
+
167
+ static success(
168
+ message = '',
169
+ duration = 3000,
170
+ options: ToastOptions = {}
171
+ ): HTMLElement {
172
+ return Toast.show(message, duration, 'success', options);
173
+ }
174
+
175
+ static info(
176
+ message = '',
177
+ duration = 3000,
178
+ options: ToastOptions = {}
179
+ ): HTMLElement {
180
+ return Toast.show(message, duration, 'info', options);
181
+ }
182
+
183
+ static primary(
184
+ message = '',
185
+ duration = 3000,
186
+ options: ToastOptions = {}
187
+ ): HTMLElement {
188
+ return Toast.show(message, duration, 'primary', options);
189
+ }
190
+
191
+ static warning(
192
+ message = '',
193
+ duration = 3000,
194
+ options: ToastOptions = {}
195
+ ): HTMLElement {
196
+ return Toast.show(message, duration, 'warning', options);
197
+ }
198
+
199
+ static error(
200
+ message = '',
201
+ duration = 3000,
202
+ options: ToastOptions = {}
203
+ ): HTMLElement {
204
+ return Toast.show(message, duration, 'error', options);
205
+ }
206
+
207
+ static hide(toast: HTMLElement | null | undefined): void {
208
+ if (!toast) return;
209
+
210
+ const className = Toast.classNames.get(toast) || Toast.options.className;
211
+ Toast.disposers.get(toast)?.();
212
+ Toast.disposers.delete(toast);
213
+
214
+ toast.classList.remove(className.shown);
215
+ toast.classList.add(className.hidden);
216
+
217
+ const id = toast.dataset.toast || randomId();
218
+ Toast.setTimer(
219
+ id,
220
+ 'remove',
221
+ () => {
222
+ toast.remove();
223
+
224
+ const container = q<HTMLElement>('[data-toast-container]');
225
+ if (container && container.children.length === 0) {
226
+ container.remove();
227
+ }
228
+ },
229
+ 300
230
+ );
231
+ }
232
+
233
+ static lite(
234
+ message = '',
235
+ duration = 2000,
236
+ options: ToastOptions = {}
237
+ ): HTMLElement {
238
+ validateParam('message', message, 'string', 'Toast.lite');
239
+ validateParam('duration', duration, LITE_DURATION_RULE, 'Toast.lite');
240
+
241
+ const className = Toast.resolveClassNames(options);
242
+ q<HTMLElement>('[data-toast-lite]')?.remove();
243
+
244
+ const id = randomId();
245
+ const lite = jsx('div', {
246
+ className: className.lite,
247
+ 'data-toast': id,
248
+ 'data-toast-lite': '',
249
+ children: message,
250
+ }) as HTMLElement;
251
+
252
+ Toast.classNames.set(lite, className);
253
+ document.body.appendChild(lite);
254
+
255
+ Toast.setTimer(id, 'show', () => lite.classList.add(className.shown), 10);
256
+ Toast.setTimer(
257
+ id,
258
+ 'hide',
259
+ () => {
260
+ lite.classList.remove(className.shown);
261
+ lite.classList.add(className.hidden);
262
+ Toast.setTimer(id, 'remove', () => lite.remove(), 300);
263
+ },
264
+ duration
265
+ );
266
+
267
+ return lite;
268
+ }
269
+
270
+ static action(message = '', props: ToastActionProps = {}): HTMLElement {
271
+ validateParam('message', message, 'string', 'Toast.action');
272
+
273
+ const className = Toast.resolveClassNames(props);
274
+ const toastContainer = Toast.getOrCreateContainer(className);
275
+ const id = randomId();
276
+ const action = jsx('div', {
277
+ className: joinClasses(className.toast, className.action),
278
+ 'data-toast': id,
279
+ 'data-toast-action': '',
280
+ children: [
281
+ jsx('div', {
282
+ className: className.message,
283
+ 'data-toast-message': id,
284
+ children: message,
285
+ }),
286
+ jsx('div', {
287
+ className: className.actions,
288
+ 'data-toast-actions': id,
289
+ children: [
290
+ jsx('button', {
291
+ className: joinClasses(className.button, className.cancelButton),
292
+ children: props.text?.cancel || 'cancel',
293
+ 'data-action': 'cancel',
294
+ onClick: () => Toast.hide(action),
295
+ }),
296
+ jsx('button', {
297
+ className: joinClasses(className.button, className.actionButton),
298
+ children: props.text?.action || 'action',
299
+ 'data-action': 'toast-action',
300
+ onClick: async () => {
301
+ await props.onAction?.();
302
+ Toast.hide(action);
303
+ },
304
+ }),
305
+ ],
306
+ }),
307
+ ],
308
+ }) as HTMLElement;
309
+
310
+ Toast.classNames.set(action, className);
311
+ toastContainer.appendChild(action);
312
+
313
+ Toast.setTimer(
314
+ id,
315
+ 'show',
316
+ () => {
317
+ action.classList.add(className.shown);
318
+ q<HTMLButtonElement>('[data-action="toast-action"]', action)?.focus();
319
+ },
320
+ 10
321
+ );
322
+
323
+ return action;
324
+ }
325
+
326
+ private static getOrCreateContainer(className: ToastClassNames): HTMLElement {
327
+ let toastContainer = q<HTMLElement>('[data-toast-container]');
328
+ if (!toastContainer) {
329
+ toastContainer = jsx('div', {
330
+ className: className.container,
331
+ 'data-toast-container': '',
332
+ }) as HTMLElement;
333
+ document.body.appendChild(toastContainer);
334
+ } else {
335
+ toastContainer.className = className.container;
336
+ }
337
+ return toastContainer;
338
+ }
339
+
340
+ private static setTimer(
341
+ id: string,
342
+ action: string,
343
+ callback: () => void,
344
+ delay: number
345
+ ): string {
346
+ const key = `${id}-${action}`;
347
+ Toast.timers.add(key);
348
+ timer.start(key, delay, () => {
349
+ Toast.timers.delete(key);
350
+ callback();
351
+ });
352
+ return key;
353
+ }
354
+
355
+ static clearAll(): void {
356
+ for (const key of Toast.timers) {
357
+ timer.cancel(key);
358
+ }
359
+ Toast.timers.clear();
360
+
361
+ for (const dispose of Toast.disposers.values()) dispose();
362
+ Toast.disposers.clear();
363
+
364
+ q<HTMLElement>('[data-toast-container]')?.remove();
365
+ q<HTMLElement>('[data-toast-lite]')?.remove();
366
+ }
367
+
368
+ static destroyAll(): void {
369
+ Toast.clearAll();
370
+ }
371
+ }