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
@@ -1,373 +0,0 @@
1
- import {
2
- bindAttr,
3
- bindClass,
4
- createDeepStore,
5
- createRoot,
6
- flushSync,
7
- insert,
8
- jsx,
9
- } from 'vanilla-signal';
10
-
11
- import Component from '../core/Component.js';
12
- import { randomId, resolveProps, validateParam } from '../utilities/core.js';
13
- import {
14
- isRenderableContent,
15
- normalizeContentNodes,
16
- requireContainer,
17
- requireRenderDOM,
18
- } from '../utilities/dom.js';
19
- import { icon } from './icons.js';
20
-
21
- const ACCORDION_PROPS_SCHEMA = {
22
- id: {
23
- default: null,
24
- types: ['string', 'null'],
25
- normalize: (value) => {
26
- if (typeof value === 'string')
27
- return value.trim() ? value.trim() : randomId();
28
- if (value == null) return randomId();
29
- return value;
30
- },
31
- },
32
- active: { default: 0, types: ['number', 'string', 'array', 'null'] },
33
- collapsible: { default: false, type: 'boolean' },
34
- multiple: { default: false, type: 'boolean' },
35
- onChange: { default: null, types: ['function', 'null'] },
36
- items: { default: [], type: 'array' },
37
- };
38
-
39
- const ACCORDION_ITEMS_RULE = {
40
- type: 'array',
41
- validate: (value) => value.length > 0,
42
- message: 'expects a non-empty array.',
43
- };
44
-
45
- const ACCORDION_ACTIVE_RULE = {
46
- types: ['number', 'string', 'array', 'null'],
47
- validate: (value) => {
48
- if (value == null) return true;
49
- if (Array.isArray(value)) {
50
- return value.every(
51
- (item) => typeof item === 'number' || typeof item === 'string'
52
- );
53
- }
54
- if (typeof value === 'number') {
55
- return Number.isInteger(value) && value >= 0;
56
- }
57
- return value.trim().length > 0;
58
- },
59
- message: 'expects a positive number, string, array or null.',
60
- };
61
-
62
- function cloneItems(items) {
63
- return Array.isArray(items) ? items.map((item) => ({ ...item })) : [];
64
- }
65
-
66
- function normalizeItems(items) {
67
- validateParam('items', items, ACCORDION_ITEMS_RULE, 'Accordion');
68
-
69
- return items.map((item) => {
70
- if (!item || typeof item !== 'object' || Array.isArray(item)) {
71
- throw new Error('Accordion: item expects an object.');
72
- }
73
- if (item.name != null && typeof item.name !== 'string') {
74
- throw new Error('Accordion: item name expects a string.');
75
- }
76
- if (!isRenderableContent(item.title)) {
77
- throw new Error(
78
- 'Accordion: item title expects string, Node, array, function or null.'
79
- );
80
- }
81
- if (!isRenderableContent(item.content)) {
82
- throw new Error(
83
- 'Accordion: item content expects string, Node, array, function or null.'
84
- );
85
- }
86
- return { ...item, name: item.name || randomId() };
87
- });
88
- }
89
-
90
- /**
91
- * 轻量手风琴组件,继承 Component。
92
- *
93
- * DOM 创建一次,通过 createEffect 细粒度更新 class/ARIA。
94
- */
95
- export class Accordion extends Component {
96
- /**
97
- * @param {Element|Node|string|Array} container 挂载容器(元素、选择器或 JSX/h 返回节点)。
98
- * @param {object} [input={}] 手风琴配置。
99
- */
100
- constructor(container, input = {}) {
101
- requireRenderDOM('Accordion');
102
-
103
- const el = requireContainer(container, 'Accordion');
104
-
105
- const props = resolveProps(input, ACCORDION_PROPS_SCHEMA, 'Accordion');
106
- super(props);
107
-
108
- this.dom.container = el;
109
- this.dom.headers = [];
110
- this.dom.panels = [];
111
-
112
- this.state = createDeepStore({
113
- activeNames: [],
114
- current: { index: null, name: null },
115
- });
116
-
117
- try {
118
- this.onInit(props);
119
- } catch (error) {
120
- this.destroy();
121
- throw error;
122
- }
123
- }
124
-
125
- onInit(props) {
126
- this.root = this.buildRoot(props);
127
- this.buildItems(props);
128
- this.syncActiveNames(this.resolveActiveNames(props.active));
129
- this.bindEvents();
130
- }
131
-
132
- buildRoot(props) {
133
- return jsx('div', { className: 'j-accordion', id: props.id });
134
- }
135
-
136
- /**
137
- * 创建 DOM 节点(一次性)。
138
- * @private
139
- */
140
- buildItems(props) {
141
- const items = normalizeItems(props.items);
142
- const fragment = document.createDocumentFragment();
143
-
144
- this.dom.headers = [];
145
- this.dom.panels = [];
146
-
147
- items.forEach((item, index) => {
148
- const name = item.name || randomId();
149
- const headerId = `${props.id}_header_${index}`;
150
- const panelId = `${props.id}_panel_${index}`;
151
-
152
- const header = jsx('div', {
153
- className: 'accordion-header',
154
- 'data-item': name,
155
- id: headerId,
156
- role: 'button',
157
- tabindex: '0',
158
- 'aria-controls': panelId,
159
- children: [
160
- jsx('span', {
161
- className: 'header-title',
162
- children: this.contentView(item.title, item, index, 'title'),
163
- }),
164
- jsx('span', {
165
- className: 'header-arrow',
166
- 'aria-hidden': 'true',
167
- children: icon('arrow-down'),
168
- }),
169
- ],
170
- });
171
-
172
- const panel = jsx('div', {
173
- className: 'accordion-panel',
174
- id: panelId,
175
- role: 'region',
176
- 'aria-labelledby': headerId,
177
- children: jsx('div', {
178
- className: 'panel-content',
179
- children: this.contentView(item.content, item, index, 'content'),
180
- }),
181
- });
182
-
183
- this.dom.headers.push(header);
184
- this.dom.panels.push(panel);
185
- fragment.append(header, panel);
186
- });
187
-
188
- this.root.append(fragment);
189
-
190
- this.cleanup.bindings?.();
191
- this.cleanup.bindings = createRoot((dispose) => {
192
- this.dom.headers.forEach((header) => {
193
- const name = header.dataset.item;
194
- bindClass(header, 'is-active', () =>
195
- this.state.activeNames.includes(name)
196
- );
197
- bindAttr(header, 'aria-expanded', () =>
198
- this.state.activeNames.includes(name)
199
- );
200
- });
201
- this.dom.panels.forEach((panel, i) => {
202
- const name = this.dom.headers[i]?.dataset.item;
203
- bindClass(panel, 'is-active', () =>
204
- this.state.activeNames.includes(name)
205
- );
206
- bindAttr(panel, 'aria-hidden', () =>
207
- this.state.activeNames.includes(name) ? 'false' : 'true'
208
- );
209
- bindAttr(panel, 'hidden', () => !this.state.activeNames.includes(name));
210
- });
211
- return dispose;
212
- });
213
- }
214
-
215
- contentView(content, item, index, type) {
216
- return normalizeContentNodes(content, {
217
- accordion: this,
218
- item,
219
- index,
220
- type,
221
- active: false,
222
- });
223
- }
224
-
225
- resolveActiveNames(active) {
226
- if (active == null) return [];
227
- const values = Array.isArray(active) ? active : [active];
228
- const names = [];
229
- for (const value of values) {
230
- const index = this.getIndex(value);
231
- if (index < 0 || index >= this.dom.headers.length) continue;
232
- names.push(this.dom.headers[index].dataset.item || String(index));
233
- if (!this.props.multiple) break;
234
- }
235
- return Array.from(new Set(names));
236
- }
237
-
238
- syncActiveNames(names) {
239
- const firstName = names[0] || null;
240
- const index = firstName ? this.getIndex(firstName) : null;
241
- flushSync(() => {
242
- this.state.activeNames = names;
243
- this.state.current = { index, name: firstName };
244
- });
245
- }
246
-
247
- bindEvents() {
248
- this.unbindEvents();
249
-
250
- this.cleanup.events.on('click', this.root, 'click', (event) => {
251
- const header = event.target.closest('.accordion-header');
252
- if (!header) return;
253
- void this.active(header.dataset.item);
254
- });
255
-
256
- this.cleanup.events.on('keydown', this.root, 'keydown', (event) => {
257
- if (event.key !== 'Enter' && event.key !== ' ') return;
258
- const header = event.target.closest('.accordion-header');
259
- if (!header) return;
260
- event.preventDefault();
261
- void this.active(header.dataset.item);
262
- });
263
- }
264
-
265
- unbindEvents() {
266
- this.cleanup.events.clear();
267
- }
268
-
269
- isActive(name) {
270
- return this.state.activeNames.includes(name);
271
- }
272
-
273
- assertActive(method) {
274
- if (this.runtime.destroyed) {
275
- throw new Error(`Accordion.${method}: instance has been destroyed.`);
276
- }
277
- }
278
-
279
- async activateItem(val, fireEvent = true) {
280
- const index = this.getIndex(val);
281
- if (index < 0 || index >= this.dom.headers.length) return;
282
-
283
- const headerEl = this.dom.headers[index];
284
- const panelEl = this.dom.panels[index];
285
- const name = headerEl.dataset.item || String(index);
286
- const active = this.isActive(name);
287
-
288
- if (active && !this.props.multiple && !this.props.collapsible) return;
289
-
290
- let nextNames;
291
- if (this.props.multiple) {
292
- nextNames = active
293
- ? this.state.activeNames.filter((n) => n !== name)
294
- : [...this.state.activeNames, name];
295
- } else if (active) {
296
- nextNames = this.props.collapsible ? [] : this.state.activeNames;
297
- } else {
298
- nextNames = [name];
299
- }
300
-
301
- this.syncActiveNames(nextNames);
302
-
303
- if (fireEvent && this.props.onChange) {
304
- await Promise.resolve(
305
- this.props.onChange(index, name, headerEl, panelEl, this)
306
- );
307
- }
308
- }
309
-
310
- getIndex(val) {
311
- if (typeof val === 'number') return val;
312
- if (typeof val === 'string') {
313
- return this.dom.headers.findIndex(
314
- (header) => header.dataset.item === val
315
- );
316
- }
317
- return -1;
318
- }
319
-
320
- /**
321
- * 激活指定面板。
322
- * @param {number|string} val 面板索引或 `data-item` 名称。
323
- */
324
- async active(val) {
325
- this.assertActive('active');
326
- await this.activateItem(val, true);
327
- }
328
-
329
- /**
330
- * 将组件挂载到构造器指定的容器中。
331
- */
332
- build() {
333
- this.assertActive('build');
334
- insert(this.dom.container, () => this.root);
335
- }
336
-
337
- /**
338
- * 动态替换全部面板条目。
339
- * @param {AccordionItem[]} items 新面板配置。
340
- * @param {number|string|Array<number|string>|null} [active=0] 替换后默认激活项。
341
- */
342
- setItems(items, active = 0) {
343
- this.assertActive('setItems');
344
- validateParam('items', items, ACCORDION_ITEMS_RULE, 'Accordion.setItems');
345
- validateParam(
346
- 'active',
347
- active,
348
- ACCORDION_ACTIVE_RULE,
349
- 'Accordion.setItems'
350
- );
351
-
352
- this.props.items = cloneItems(normalizeItems(items));
353
- this.props.active = active;
354
-
355
- this.root.textContent = '';
356
- this.buildItems(this.props);
357
- this.syncActiveNames(this.resolveActiveNames(active));
358
- this.bindEvents();
359
- }
360
-
361
- onDestroy() {
362
- this.unbindEvents();
363
- this.cleanup.bindings?.();
364
- this.cleanup.bindings = null;
365
- if (this.root?.parentNode) {
366
- this.root.parentNode.removeChild(this.root);
367
- }
368
- }
369
- }
370
-
371
- export function createAccordion(container, input = {}) {
372
- return new Accordion(container, input);
373
- }
File without changes