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
@@ -8,23 +8,20 @@ import {
8
8
  } from 'vanilla-signal';
9
9
 
10
10
  import Component, { type ComponentDOM } from '../core/Component.ts';
11
+ import { joinClasses } from '../utilities/class-name.ts';
11
12
  import {
12
- type ResolveSchema,
13
- hasOwn,
14
- isPlainObject,
15
- randomId,
16
- resolveProps,
17
- validateParam,
18
- } from '../utilities/core.ts';
19
- import {
20
- type DOMReference,
21
13
  type RenderableContent,
22
14
  all,
23
- isRenderableContent,
24
- normalizeContentNodes,
25
- requireContainer,
15
+ normalizeRenderableContentNodes,
26
16
  } from '../utilities/dom.ts';
27
- import { Validator } from './validator.ts';
17
+ import { randomId } from '../utilities/id.ts';
18
+ import { isPlainObject } from '../utilities/object.ts';
19
+ import {
20
+ type ResolveSchema,
21
+ resolveProps,
22
+ validateParam,
23
+ } from '../utilities/types.ts';
24
+ import { createValidator } from '../validation/validator.ts';
28
25
 
29
26
  type FormValue = string | number | boolean;
30
27
  type FormOptionInput = FormValue | FormOption;
@@ -35,7 +32,6 @@ type FormControlElement =
35
32
  type FormStyle = string | Partial<CSSStyleDeclaration> | null;
36
33
  export type FormDataValue = FormDataEntryValue | FormDataEntryValue[];
37
34
  export type FormDataRecord = Record<string, FormDataValue>;
38
- type FormContainer = Element | DocumentFragment | false | null;
39
35
  type FormControlType =
40
36
  | 'checkbox'
41
37
  | 'custom'
@@ -159,7 +155,6 @@ interface FormState extends ResolvedFormProps {
159
155
 
160
156
  interface FormDOM extends ComponentDOM {
161
157
  root: HTMLFormElement | null;
162
- container: DOMReference | DocumentFragment;
163
158
  fields: Map<string, FormControlElement>;
164
159
  }
165
160
 
@@ -169,8 +164,8 @@ interface FormCache {
169
164
  }
170
165
 
171
166
  interface ValidatorInstance {
172
- root: Element | null;
173
- options: FormValidatorConfig & { onSubmit: null };
167
+ dom: { root: Element | null };
168
+ props: (FormValidatorConfig & { onSubmit: null }) | null;
174
169
  validate: () => boolean;
175
170
  reset: () => void;
176
171
  destroy: () => void;
@@ -251,11 +246,6 @@ const FORM_PROPS_SCHEMA = {
251
246
  onReset: { default: null, types: ['function', 'null'] },
252
247
  } satisfies ResolveSchema<FormProps>;
253
248
 
254
- const FORM_UPDATE_RULE = {
255
- validate: (value: unknown) => isPlainObject(value),
256
- message: 'expects a props object.',
257
- };
258
-
259
249
  function cloneOptions(
260
250
  options: readonly FormOptionInput[] | undefined
261
251
  ): FormOptionInput[] {
@@ -296,14 +286,6 @@ function stringifyFormValue(
296
286
  return value.name;
297
287
  }
298
288
 
299
- function contentView<TContext>(
300
- content: unknown,
301
- context: TContext
302
- ): Node[] | null {
303
- if (!isRenderableContent(content)) return null;
304
- return normalizeContentNodes(content, context);
305
- }
306
-
307
289
  function fieldIsRequired(
308
290
  field: FormField,
309
291
  rules: FormValidatorConfig['rules']
@@ -312,14 +294,10 @@ function fieldIsRequired(
312
294
  return !!field.required || !!rules?.[field.name]?.required;
313
295
  }
314
296
 
315
- function resolveClassName(...parts: Array<string | false | null | undefined>) {
316
- return parts.filter(Boolean).join(' ');
317
- }
318
-
319
297
  function resolveClassNames(value: unknown): FormClassNames {
320
298
  return {
321
299
  ...DEFAULT_CLASS_NAMES,
322
- ...(isPlainObject(value) ? value : {}),
300
+ ...(isPlainObject(value) ? (value as Partial<FormClassNames>) : {}),
323
301
  } as FormClassNames;
324
302
  }
325
303
 
@@ -331,7 +309,8 @@ function setElementStyle(element: HTMLElement, style: FormStyle): void {
331
309
  return;
332
310
  }
333
311
  if (!isPlainObject(style)) return;
334
- for (const [key, value] of Object.entries(style)) {
312
+ const styleRecord = style as Record<string, unknown>;
313
+ for (const [key, value] of Object.entries(styleRecord)) {
335
314
  if (value == null) continue;
336
315
  if (typeof value !== 'string' && typeof value !== 'number') continue;
337
316
  const name = key.startsWith('--')
@@ -342,7 +321,9 @@ function setElementStyle(element: HTMLElement, style: FormStyle): void {
342
321
  }
343
322
 
344
323
  function cloneValidator(validator: unknown): FormValidatorConfig {
345
- const source = isPlainObject(validator) ? validator : {};
324
+ const source = (
325
+ isPlainObject(validator) ? validator : {}
326
+ ) as Partial<FormValidatorConfig> & Record<string, unknown>;
346
327
  return {
347
328
  ...source,
348
329
  rules: isPlainObject(source.rules)
@@ -371,16 +352,15 @@ function normalizeProps(input: FormProps): ResolvedFormProps {
371
352
  };
372
353
  }
373
354
 
374
- export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
355
+ class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
375
356
  declare state: FormState;
376
357
  validator: ValidatorInstance | null;
377
358
  cache: FormCache;
378
359
 
379
- constructor(input: FormProps = {}, container: DOMReference = false) {
360
+ constructor(input: FormProps = {}) {
380
361
  const props = normalizeProps(input);
381
362
  super(props);
382
363
 
383
- this.dom.container = container;
384
364
  this.dom.fields = new Map();
385
365
  this.validator = null;
386
366
  this.cleanup.view = null;
@@ -397,43 +377,23 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
397
377
  });
398
378
  }
399
379
 
400
- override get root(): HTMLFormElement | null {
380
+ get root(): HTMLFormElement | null {
401
381
  return this.dom?.root || null;
402
382
  }
403
383
 
404
- override set root(value: HTMLFormElement | null) {
384
+ set root(value: HTMLFormElement | null) {
405
385
  this.dom.root = value;
406
386
  }
407
387
 
408
- protected onInit(): void {
409
- if (
410
- this.dom.container === false ||
411
- this.dom.container == null ||
412
- typeof this.dom.container === 'string'
413
- ) {
414
- return;
415
- }
416
- this.mount(this.dom.container as FormContainer);
417
- }
418
-
419
- build(container: DOMReference = this.dom.container): this {
388
+ build(): this {
420
389
  if (this.runtime.destroyed)
421
390
  throw new Error('Form.build: instance destroyed');
422
391
  if (this.cleanup.view) return this;
423
- this.dom.container =
424
- container === false
425
- ? document.createDocumentFragment()
426
- : requireContainer(container, 'Form.container');
427
392
  this.init(this.props);
428
- return this;
429
- }
430
-
431
- mount(container: FormContainer = this.dom.container as FormContainer): this {
432
- if (this.cleanup.view) return this;
433
- if (!container) return this;
434
393
 
394
+ const host = document.createElement('div');
435
395
  this.cleanup.view = createRoot((dispose) => {
436
- const viewDispose = render(() => this.view(), container as Element);
396
+ const viewDispose = render(() => this.view(), host);
437
397
  onCleanup(viewDispose);
438
398
  return dispose;
439
399
  });
@@ -445,7 +405,7 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
445
405
  const className = this.state.className;
446
406
  return jsx('form', {
447
407
  className: () =>
448
- resolveClassName(
408
+ joinClasses(
449
409
  this.state.className.form,
450
410
  this.state.vertical ? className.vertical : className.horizontal,
451
411
  this.state.itemVertical
@@ -510,7 +470,7 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
510
470
  const className = this.state.className;
511
471
 
512
472
  return jsx('label', {
513
- className: resolveClassName(
473
+ className: joinClasses(
514
474
  className.label,
515
475
  fieldIsRequired(field, this.state.validator?.rules)
516
476
  ? className.required
@@ -541,7 +501,11 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
541
501
  case 'switch':
542
502
  return this.switchView(field, id);
543
503
  case 'custom':
544
- return contentView(field.content, { form: this, field, index });
504
+ return normalizeRenderableContentNodes(field.content, {
505
+ form: this,
506
+ field,
507
+ index,
508
+ });
545
509
  default:
546
510
  return this.inputView(field, id);
547
511
  }
@@ -601,7 +565,7 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
601
565
  ): HTMLElement {
602
566
  const direction = field.vertical ? 'vertical' : 'horizontal';
603
567
  const classNameConfig = this.state.className;
604
- const className = resolveClassName(
568
+ const className = joinClasses(
605
569
  type === 'radio' ? classNameConfig.radio : classNameConfig.checkbox,
606
570
  direction === 'vertical'
607
571
  ? classNameConfig.choiceVertical
@@ -649,7 +613,7 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
649
613
  switchView(field: FormField, id: string): HTMLLabelElement {
650
614
  const className = this.state.className;
651
615
  return jsx('label', {
652
- className: resolveClassName(
616
+ className: joinClasses(
653
617
  className.switch,
654
618
  field.variant ? `is-${field.variant}` : className.switchDefault,
655
619
  field.size ? `is-${field.size}` : className.switchSizeMd
@@ -684,7 +648,7 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
684
648
  children: this.state.buttons.map((button) =>
685
649
  jsx('button', {
686
650
  type: button.type || 'button',
687
- className: resolveClassName(
651
+ className: joinClasses(
688
652
  className.button,
689
653
  button.theme ? `is-${button.theme}` : '',
690
654
  button.className || ''
@@ -743,13 +707,13 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
743
707
  onSubmit: null,
744
708
  };
745
709
 
746
- if (this.validator?.root === this.root) {
747
- this.validator.options = options;
710
+ if (this.validator?.dom.root === this.root) {
711
+ this.validator.props = options;
748
712
  return;
749
713
  }
750
714
 
751
715
  this.validator?.destroy();
752
- this.validator = new Validator(
716
+ this.validator = createValidator(
753
717
  this.root,
754
718
  options,
755
719
  false
@@ -825,7 +789,7 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
825
789
  const formData = new FormData(form);
826
790
 
827
791
  for (const [key, value] of formData.entries()) {
828
- if (hasOwn(data, key)) {
792
+ if (Object.hasOwn(data, key)) {
829
793
  data[key] = Array.isArray(data[key])
830
794
  ? [...data[key], value]
831
795
  : [data[key], value];
@@ -849,34 +813,15 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
849
813
  return this;
850
814
  }
851
815
 
852
- override update(
853
- patch: Partial<FormProps> | null | undefined = {},
854
- _options = {}
855
- ): this {
856
- const propsPatch = patch || {};
857
- validateParam('props', propsPatch, FORM_UPDATE_RULE, 'Form.update');
858
- const nextProps = normalizeProps(Object.assign({}, this.props, propsPatch));
859
-
860
- this.props = this.cloneProps(nextProps);
816
+ setFields(fields: readonly FormField[]): this {
817
+ validateParam('fields', fields, 'array', 'Form.setFields');
861
818
  this.cache.fieldIds.clear();
862
- super.update(nextProps);
863
-
864
819
  flushSync(() => {
865
- const statePatch = this.cloneProps(nextProps);
866
- for (const key of Object.keys(statePatch) as Array<
867
- keyof ResolvedFormProps
868
- >) {
869
- this.state[key] = statePatch[key] as FormState[typeof key];
870
- }
820
+ this.state.fields = cloneFields(fields);
871
821
  });
872
822
  return this;
873
823
  }
874
824
 
875
- setFields(fields: readonly FormField[]): this {
876
- validateParam('fields', fields, 'array', 'Form.setFields');
877
- return this.update({ fields });
878
- }
879
-
880
825
  resetFields(): this {
881
826
  return this.setFields(this.cache.initial.fields);
882
827
  }
@@ -945,9 +890,6 @@ export class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
945
890
  }
946
891
  }
947
892
 
948
- export function createForm(
949
- props: FormProps = {},
950
- container: DOMReference = false
951
- ): Form {
952
- return new Form(props, container);
893
+ export function createForm(props: FormProps = {}): Form {
894
+ return new Form(props);
953
895
  }