vanilla-jui 1.4.4 → 1.4.6

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 (103) hide show
  1. package/README.md +6 -0
  2. package/dist/index.d.ts +1273 -1896
  3. package/dist/index.js +1 -1
  4. package/dist/index.umd.js +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +22 -3
  7. package/src/components/accordion.ts +297 -216
  8. package/src/components/flow.ts +222 -258
  9. package/src/components/form.ts +44 -102
  10. package/src/components/menu.ts +268 -356
  11. package/src/components/modal.ts +181 -641
  12. package/src/components/offcanvas.ts +225 -106
  13. package/src/components/pagination.ts +296 -326
  14. package/src/components/sticky.ts +55 -54
  15. package/src/components/swiper.ts +237 -247
  16. package/src/components/tabs.ts +278 -271
  17. package/src/components/toc.ts +42 -46
  18. package/src/core/Component.ts +69 -47
  19. package/src/css/accordion.css +2 -2
  20. package/src/css/animation.css +1 -5
  21. package/src/css/avatar.css +1 -1
  22. package/src/css/badge.css +1 -1
  23. package/src/css/breadcrumb.css +9 -10
  24. package/src/css/button.css +1 -1
  25. package/src/css/card.css +2 -2
  26. package/src/css/color.css +974 -1002
  27. package/src/css/color_p3.css +974 -1004
  28. package/src/css/components.css +1 -1
  29. package/src/css/content.css +72 -63
  30. package/src/css/drop.css +1 -1
  31. package/src/css/editor.css +14 -14
  32. package/src/css/flow.css +4 -14
  33. package/src/css/font.css +5 -4
  34. package/src/css/form.css +76 -52
  35. package/src/css/gradient.css +299 -222
  36. package/src/css/icon.css +57 -57
  37. package/src/css/index.css +1 -1
  38. package/src/css/layout.css +13 -10
  39. package/src/css/loading.css +60 -12
  40. package/src/css/menu.css +262 -188
  41. package/src/css/modal.css +1 -1
  42. package/src/css/offcanvas.css +9 -13
  43. package/src/css/pagination.css +5 -1
  44. package/src/css/popup.css +1 -1
  45. package/src/css/skeleton.css +3 -2
  46. package/src/css/status.css +2 -3
  47. package/src/css/swiper.css +1 -1
  48. package/src/css/table.css +1 -1
  49. package/src/css/tabs.css +12 -11
  50. package/src/css/tag.css +1 -1
  51. package/src/css/theme-palette.css +19 -5
  52. package/src/css/themes/default/accordion.css +61 -61
  53. package/src/css/themes/default/avatar.css +54 -54
  54. package/src/css/themes/default/badge.css +62 -62
  55. package/src/css/themes/default/breadcrumb.css +23 -23
  56. package/src/css/themes/default/button.css +203 -203
  57. package/src/css/themes/default/content.css +53 -53
  58. package/src/css/themes/default/drop.css +10 -10
  59. package/src/css/themes/default/editor.css +9 -9
  60. package/src/css/themes/default/index.css +1 -1
  61. package/src/css/themes/default/loading.css +26 -20
  62. package/src/css/themes/default/menu.css +75 -11
  63. package/src/css/themes/default/modal.css +32 -36
  64. package/src/css/themes/default/pagination.css +7 -7
  65. package/src/css/themes/default/popup.css +6 -6
  66. package/src/css/themes/default/swiper.css +32 -32
  67. package/src/css/themes/default/table.css +16 -16
  68. package/src/css/themes/default/tabs.css +30 -30
  69. package/src/css/themes/default/tag.css +71 -71
  70. package/src/css/themes/default/theme-palette.css +11 -11
  71. package/src/css/themes/default/tip.css +42 -42
  72. package/src/css/themes/default/toast.css +60 -60
  73. package/src/css/themes/default/toolbar.css +23 -29
  74. package/src/css/themes/default/tooltip.css +43 -13
  75. package/src/css/themes/index.css +1 -1
  76. package/src/css/tip.css +2 -2
  77. package/src/css/toast.css +3 -3
  78. package/src/css/tokens.css +12 -17
  79. package/src/css/toolbar.css +9 -10
  80. package/src/css/tooltip.css +3 -2
  81. package/src/index.ts +22 -19
  82. package/src/{components → primitives}/drop.ts +58 -49
  83. package/src/{components → primitives}/icons.ts +1 -1
  84. package/src/primitives/loading.ts +23 -0
  85. package/src/primitives/parabola.ts +388 -0
  86. package/src/primitives/popup.ts +61 -0
  87. package/src/{components → primitives}/theme.ts +88 -57
  88. package/src/{components → primitives}/toast.ts +18 -25
  89. package/src/{components → primitives}/tooltip.ts +100 -25
  90. package/src/utilities/browser.ts +23 -49
  91. package/src/utilities/class-name.ts +5 -0
  92. package/src/utilities/dom.ts +14 -32
  93. package/src/utilities/function.ts +4 -0
  94. package/src/utilities/http.ts +7 -7
  95. package/src/utilities/id.ts +57 -0
  96. package/src/utilities/object.ts +5 -0
  97. package/src/utilities/timer.ts +38 -0
  98. package/src/utilities/types.ts +420 -0
  99. package/src/{components → validation}/validator.ts +64 -53
  100. package/src/components/parabola.ts +0 -335
  101. package/src/utilities/cache.ts +0 -57
  102. package/src/utilities/core.ts +0 -614
  103. package/src/utilities/storage.ts +0 -46
@@ -1,335 +0,0 @@
1
- import { jsx } from 'vanilla-signal';
2
-
3
- import { type ResolveSchema, resolveProps } from '../utilities/core.ts';
4
- import { type DOMReference, isNode, resolveElement } from '../utilities/dom.ts';
5
-
6
- export type ParabolaDirection =
7
- | 'center'
8
- | 'top-right'
9
- | 'top-left'
10
- | 'bottom-right'
11
- | 'bottom-left';
12
-
13
- export interface ParabolaBallOptions {
14
- color: string;
15
- size: string;
16
- }
17
-
18
- export interface ParabolaClassNames {
19
- ball: string;
20
- }
21
-
22
- export type ParabolaClassNameConfig = Partial<ParabolaClassNames>;
23
-
24
- export interface ParabolaOptions extends Record<string, unknown> {
25
- ball?: ParabolaBallOptions;
26
- className?: ParabolaClassNameConfig;
27
- from?: DOMReference;
28
- to?: DOMReference;
29
- direction?: ParabolaDirection;
30
- showDelay?: number;
31
- hideDelay?: number;
32
- onShow?: ((parabola: Parabola) => void) | null;
33
- onHidden?: ((parabola: Parabola) => void) | null;
34
- }
35
-
36
- interface ResolvedParabolaOptions extends Record<string, unknown> {
37
- ball: ParabolaBallOptions;
38
- className: ParabolaClassNames;
39
- from: DOMReference;
40
- to: DOMReference;
41
- direction: ParabolaDirection;
42
- showDelay: number;
43
- hideDelay: number;
44
- onShow: ((parabola: Parabola) => void) | null;
45
- onHidden: ((parabola: Parabola) => void) | null;
46
- }
47
-
48
- interface ParabolaPath {
49
- startX: number;
50
- startY: number;
51
- endX: number;
52
- endY: number;
53
- }
54
-
55
- const DEFAULT_BALL: ParabolaBallOptions = {
56
- color: 'var(--tone-solid)',
57
- size: '12px',
58
- };
59
-
60
- const DEFAULT_CLASS_NAMES: ParabolaClassNames = {
61
- ball: 'parabola-ball',
62
- };
63
-
64
- const ELEMENT_REF_RULE = {
65
- validate: (value: unknown) =>
66
- value == null ||
67
- typeof value === 'string' ||
68
- Array.isArray(value) ||
69
- isNode(value),
70
- message: 'expects Element, Node, selector or JSX node.',
71
- };
72
-
73
- const PARABOLA_OPTIONS_SCHEMA = {
74
- ball: {
75
- default: DEFAULT_BALL,
76
- type: 'object',
77
- normalize: (value: unknown) => ({
78
- ...DEFAULT_BALL,
79
- ...(value && typeof value === 'object' ? value : {}),
80
- }),
81
- validate: (value: unknown) =>
82
- !!value &&
83
- typeof value === 'object' &&
84
- typeof (value as ParabolaBallOptions).color === 'string' &&
85
- typeof (value as ParabolaBallOptions).size === 'string',
86
- message:
87
- 'expects an object with two string properties: "color" and "size".',
88
- },
89
- className: {
90
- default: DEFAULT_CLASS_NAMES,
91
- type: 'object',
92
- normalize: (value: unknown) => ({
93
- ...DEFAULT_CLASS_NAMES,
94
- ...(value && typeof value === 'object' ? value : {}),
95
- }),
96
- },
97
- from: { default: null, ...ELEMENT_REF_RULE },
98
- to: { default: null, ...ELEMENT_REF_RULE },
99
- direction: {
100
- default: 'center',
101
- type: 'string',
102
- enum: ['center', 'top-right', 'top-left', 'bottom-right', 'bottom-left'],
103
- },
104
- showDelay: { default: 0, type: 'number' },
105
- hideDelay: { default: 0, type: 'number' },
106
- onShow: { default: null, types: ['function', 'null'] },
107
- onHidden: { default: null, types: ['function', 'null'] },
108
- } satisfies ResolveSchema<ParabolaOptions>;
109
-
110
- function normalizeOptions(input: ParabolaOptions): ResolvedParabolaOptions {
111
- const options = resolveProps(
112
- input,
113
- PARABOLA_OPTIONS_SCHEMA,
114
- 'Parabola.options'
115
- );
116
- return {
117
- ball: options.ball as ParabolaBallOptions,
118
- className: options.className as ParabolaClassNames,
119
- from: options.from as DOMReference,
120
- to: options.to as DOMReference,
121
- direction: options.direction as ParabolaDirection,
122
- showDelay: options.showDelay as number,
123
- hideDelay: options.hideDelay as number,
124
- onShow: options.onShow as ResolvedParabolaOptions['onShow'],
125
- onHidden: options.onHidden as ResolvedParabolaOptions['onHidden'],
126
- };
127
- }
128
-
129
- /**
130
- * 抛物线动画组件。
131
- *
132
- * 用于实现“加入购物车”等从一个元素飞向另一个元素的小球动画。
133
- */
134
- export class Parabola {
135
- options: ResolvedParabolaOptions;
136
- hidden: boolean;
137
- _ball: HTMLElement | null;
138
- _animationId: number | null;
139
- _fromEl: Element | null;
140
- _toEl: Element | null;
141
- private showTimerId: ReturnType<typeof setTimeout> | null;
142
-
143
- /**
144
- * 创建抛物线动画实例。
145
- * @param {ParabolaOptions} options 动画配置。
146
- */
147
- constructor(options: ParabolaOptions = {}) {
148
- this.options = normalizeOptions(options);
149
- this.hidden = false;
150
- this._ball = null;
151
- this._animationId = null;
152
- this._fromEl = resolveElement(this.options.from);
153
- this._toEl = resolveElement(this.options.to);
154
- this.showTimerId = null;
155
-
156
- this.createBall();
157
- }
158
-
159
- private createBall(): void {
160
- if (this.hidden || this._ball) return;
161
-
162
- const { color, size } = this.options.ball;
163
-
164
- this._ball = jsx('div', {
165
- className: this.options.className.ball,
166
- 'data-parabola': 'ball',
167
- style: {
168
- backgroundColor: color,
169
- width: size,
170
- height: size,
171
- position: 'fixed',
172
- borderRadius: '50%',
173
- transform: 'translate(-50%, -50%)',
174
- pointerEvents: 'none',
175
- zIndex: '9999',
176
- opacity: '1',
177
- },
178
- }) as HTMLElement;
179
- document.body.appendChild(this._ball);
180
- }
181
-
182
- private calculatePath(): ParabolaPath | null {
183
- const fromRect = this._fromEl?.getBoundingClientRect();
184
- const toRect = this._toEl?.getBoundingClientRect();
185
-
186
- if (!fromRect || !toRect) return null;
187
-
188
- const { left: fL, top: fT, width: fW, height: fH } = fromRect;
189
- const { left: tL, top: tT, width: tW, height: tH } = toRect;
190
-
191
- let startX: number;
192
- let startY: number;
193
- switch (this.options.direction) {
194
- case 'top-left':
195
- startX = fL + fW * 0.2;
196
- startY = fT + fH * 0.2;
197
- break;
198
- case 'top-right':
199
- startX = fL + fW * 0.8;
200
- startY = fT + fH * 0.2;
201
- break;
202
- case 'bottom-left':
203
- startX = fL + fW * 0.2;
204
- startY = fT + fH * 0.8;
205
- break;
206
- case 'bottom-right':
207
- startX = fL + fW * 0.8;
208
- startY = fT + fH * 0.8;
209
- break;
210
- default:
211
- startX = fL + fW / 2;
212
- startY = fT + fH / 2;
213
- }
214
-
215
- const endX = tL + tW / 2;
216
- const endY = tT + tH / 2;
217
-
218
- return { startX, startY, endX, endY };
219
- }
220
-
221
- private easeOutCubic(t: number): number {
222
- return 1 - (1 - t) ** 3;
223
- }
224
-
225
- private animate(
226
- startX: number,
227
- startY: number,
228
- endX: number,
229
- endY: number,
230
- duration = 800
231
- ): void {
232
- if (this.hidden || !this._ball) return;
233
-
234
- const startTime = performance.now();
235
- const deltaX = endX - startX;
236
- const deltaY = endY - startY;
237
- const peakOffset = -100;
238
-
239
- const step = (currentTime: number): void => {
240
- if (this.hidden || !this._ball) return;
241
-
242
- const elapsed = currentTime - startTime;
243
- const progress = Math.min(elapsed / duration, 1);
244
- const eased = this.easeOutCubic(progress);
245
-
246
- const currentX = startX + deltaX * eased;
247
- const currentY =
248
- startY + peakOffset * Math.sin(Math.PI * eased) + deltaY * eased;
249
-
250
- const opacity = 1 - progress;
251
-
252
- this._ball.style.left = `${currentX}px`;
253
- this._ball.style.top = `${currentY}px`;
254
- this._ball.style.opacity = `${opacity}`;
255
- this._ball.style.transform = `translate(-50%, -50%) scale(${1 - eased * 0.3})`;
256
-
257
- if (progress < 1) {
258
- this._animationId = requestAnimationFrame(step);
259
- } else {
260
- this.destroy();
261
- }
262
- };
263
-
264
- this._animationId = requestAnimationFrame(step);
265
- }
266
-
267
- /**
268
- * 开始播放动画。
269
- * @returns {Promise<boolean>} 成功开始动画时返回 true;元素缺失或已销毁时返回 false。
270
- */
271
- show(): Promise<boolean> {
272
- if (this.hidden) return Promise.resolve(false);
273
-
274
- return new Promise((resolve) => {
275
- this.showTimerId = setTimeout(() => {
276
- this.showTimerId = null;
277
- if (this.hidden || !this._ball) {
278
- resolve(false);
279
- return;
280
- }
281
-
282
- const path = this.calculatePath();
283
- if (!path) {
284
- this.destroy();
285
- resolve(false);
286
- return;
287
- }
288
-
289
- this._ball.style.left = `${path.startX}px`;
290
- this._ball.style.top = `${path.startY}px`;
291
-
292
- this.options.onShow?.(this);
293
-
294
- this.animate(path.startX, path.startY, path.endX, path.endY);
295
- resolve(true);
296
- }, this.options.showDelay);
297
- });
298
- }
299
-
300
- /**
301
- * 销毁动画实例并清理 DOM、定时器和动画帧。
302
- * @returns {void}
303
- */
304
- destroy(): void {
305
- if (this.hidden) return;
306
-
307
- this._ball?.remove();
308
-
309
- if (this.showTimerId) {
310
- clearTimeout(this.showTimerId);
311
- this.showTimerId = null;
312
- }
313
- if (this._animationId) cancelAnimationFrame(this._animationId);
314
-
315
- this._ball = null;
316
- this._fromEl = null;
317
- this._toEl = null;
318
- this._animationId = null;
319
- this.hidden = true;
320
-
321
- this.options.onHidden?.(this);
322
- }
323
-
324
- /**
325
- * show 的语义化别名。
326
- * @returns {Promise<boolean>}
327
- */
328
- start(): Promise<boolean> {
329
- return this.show();
330
- }
331
- }
332
-
333
- export function createParabola(options: ParabolaOptions = {}): Parabola {
334
- return new Parabola(options);
335
- }
@@ -1,57 +0,0 @@
1
- /**
2
- * 定义可销毁服务的接口契约
3
- */
4
- export interface Destroyable {
5
- destroy?: () => void;
6
- }
7
-
8
- /**
9
- * 简易服务缓存容器。
10
- * 用于按 key 缓存工厂创建的服务实例,并提供销毁能力。
11
- */
12
- export const service = (() => {
13
- // 使用闭包封装 Map,避免 this 指向问题,实现私有化
14
- const instances = new Map<string, Destroyable>();
15
-
16
- return {
17
- /**
18
- * 获取服务实例。如果不存在,则通过工厂函数创建并缓存。
19
- * @template T - 期望返回的服务类型(需继承自 Destroyable)
20
- * @param {string} key - 服务的唯一标识
21
- * @param {() => T} factory - 创建服务实例的工厂函数
22
- * @returns {T} 服务实例
23
- */
24
- get<T extends Destroyable>(key: string, factory: () => T): T {
25
- if (!instances.has(key)) {
26
- instances.set(key, factory());
27
- }
28
- return instances.get(key) as T;
29
- },
30
-
31
- /**
32
- * 销毁指定 key 的服务实例并从缓存中移除。
33
- * @param {string} key - 服务的唯一标识
34
- */
35
- destroy(key: string): void {
36
- const instance = instances.get(key);
37
- if (instance) {
38
- if (typeof instance.destroy === 'function') {
39
- instance.destroy();
40
- }
41
- instances.delete(key);
42
- }
43
- },
44
-
45
- /**
46
- * 销毁所有缓存的服务实例并清空容器。
47
- */
48
- destroyAll(): void {
49
- for (const instance of instances.values()) {
50
- if (typeof instance.destroy === 'function') {
51
- instance.destroy();
52
- }
53
- }
54
- instances.clear();
55
- },
56
- };
57
- })();