@schemx/vue 0.2.0 → 0.2.2

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/dist/index.mjs CHANGED
@@ -1,34 +1,36 @@
1
1
  import "./style.css";
2
- import { computed, provide, onUnmounted, inject, shallowRef, ref, onMounted, watchEffect, onScopeDispose, defineComponent, createVNode, toRef, h, createTextVNode, watch, openBlock, createElementBlock, normalizeClass, Fragment, renderList, unref, createBlock, createSlots, withCtx, renderSlot, mergeProps } from "vue";
2
+ import { onScopeDispose, provide, inject, onUnmounted, shallowRef, computed, ref, onMounted, watchEffect, defineComponent, createVNode, h, toRef, createTextVNode, watch, openBlock, createElementBlock, normalizeStyle, normalizeClass, Fragment, renderList, unref, createBlock, createSlots, withCtx, renderSlot, mergeProps } from "vue";
3
3
  import classnames from "classnames";
4
4
  import { createRendererRegistry, createValidatorsRegistry, createForm, createField, createWatch, createEffect } from "@schemx/core";
5
5
  export * from "@schemx/core";
6
6
  import { omit } from "es-toolkit";
7
7
  const rendererRegistry = createRendererRegistry("input");
8
8
  const validatorRegistry = createValidatorsRegistry();
9
- const SCHEMX_INSTANCE_KEY = Symbol("SCHEMX_INSTANCE");
10
9
  function useForm(options = {}) {
11
- const { ...formOptions } = options;
12
10
  const mergedOptions = {
13
- ...formOptions,
14
- rendererRegistry: formOptions.rendererRegistry ?? rendererRegistry,
15
- validatorRegistry: formOptions.validatorRegistry ?? validatorRegistry
11
+ ...options,
12
+ rendererRegistry: options.rendererRegistry ?? rendererRegistry,
13
+ validatorRegistry: options.validatorRegistry ?? validatorRegistry
16
14
  };
17
- const instance = computed(() => createForm(mergedOptions));
18
- provide(SCHEMX_INSTANCE_KEY, instance.value);
19
- onUnmounted(() => {
20
- instance.value.destroy();
15
+ const instance = createForm(mergedOptions);
16
+ onScopeDispose(() => {
17
+ instance.destroy();
21
18
  });
22
- return instance.value;
19
+ return instance;
20
+ }
21
+ const SCHEMX_FORM_INSTANCE_KEY = Symbol("schemx:instance");
22
+ function createFormContext(instance) {
23
+ provide(SCHEMX_FORM_INSTANCE_KEY, instance);
23
24
  }
24
25
  function useFormContext() {
25
- const instance = inject(SCHEMX_INSTANCE_KEY);
26
+ const instance = inject(SCHEMX_FORM_INSTANCE_KEY, null);
26
27
  if (!instance) {
27
- throw new Error("useFormContext must be used within a Form");
28
+ throw new Error(
29
+ "[schemx] useFormContext() must be called inside a <SchemxForm> descendant. Ensure createFormContext(form) is called synchronously during setup()."
30
+ );
28
31
  }
29
32
  return instance;
30
33
  }
31
- const FIELD_CONTEXT_KEY = Symbol("schemx:field");
32
34
  const fieldHookCache = /* @__PURE__ */ new WeakMap();
33
35
  function createFieldHook(form, name) {
34
36
  const field = createField(form, name);
@@ -47,6 +49,7 @@ function createFieldHook(form, name) {
47
49
  });
48
50
  const pending = computed(() => fieldPending.value);
49
51
  const result = {
52
+ name,
50
53
  value: fieldValue,
51
54
  error,
52
55
  dirty,
@@ -86,13 +89,18 @@ const useField = (name) => {
86
89
  });
87
90
  return activeEntry.result;
88
91
  };
92
+ const SCHEMX_FORM_FIELD_KEY = Symbol(
93
+ "schemx:field"
94
+ );
89
95
  function createFieldContext(field) {
90
- provide(FIELD_CONTEXT_KEY, field);
96
+ provide(SCHEMX_FORM_FIELD_KEY, field);
91
97
  }
92
98
  function useFieldContext() {
93
- const field = inject(FIELD_CONTEXT_KEY);
99
+ const field = inject(SCHEMX_FORM_FIELD_KEY);
94
100
  if (!field) {
95
- throw new Error("[schemx] useFieldContext() must be used inside a FormItem tree");
101
+ throw new Error(
102
+ "[schemx] useFieldContext() must be called inside a component tree where createFieldContext(field) has been called."
103
+ );
96
104
  }
97
105
  return field;
98
106
  }
@@ -190,7 +198,7 @@ const useDictionary = (options, fieldName) => {
190
198
  list.value = data;
191
199
  };
192
200
  if ((_a = options.dependsOn) == null ? void 0 : _a.length) {
193
- useWatchFields(options.dependsOn, (_payload, latestSnapshot) => {
201
+ useWatchFields(options.dependsOn, (latestSnapshot, _payload) => {
194
202
  if (typeof options.onDepsChange === "function") {
195
203
  options.onDepsChange(latestSnapshot, instance);
196
204
  }
@@ -208,14 +216,27 @@ const useDictionary = (options, fieldName) => {
208
216
  });
209
217
  return { list, loading, error, loadDict, refresh, mutate };
210
218
  };
211
- const FORM_CONTEXT_KEY = Symbol("FormContext");
212
- const createConfigContext = (props) => {
213
- provide(FORM_CONTEXT_KEY, props);
219
+ const SCHEMX_FORM_CONFIG_KEY = Symbol("schemx:form-config");
220
+ const formConfigContextOmitKey = [
221
+ "form",
222
+ "modelValue",
223
+ "rendererRegistry",
224
+ "validatorRegistry",
225
+ "defaultRendererType",
226
+ "onFinish",
227
+ "onFinishFailed",
228
+ "onValuesChange",
229
+ "onFieldsChange"
230
+ ];
231
+ const createFormConfigContext = (props) => {
232
+ provide(SCHEMX_FORM_CONFIG_KEY, props);
214
233
  };
215
- function useConfigContext() {
216
- const context = inject(FORM_CONTEXT_KEY);
234
+ function useFormConfigContext() {
235
+ const context = inject(SCHEMX_FORM_CONFIG_KEY);
217
236
  if (!context) {
218
- throw new Error("useConfigContext must be used within a Form");
237
+ throw new Error(
238
+ "[schemx] useFormConfigContext() must be called inside a <SchemxForm> descendant. Ensure createFormConfigContext(props) is called synchronously during setup()."
239
+ );
219
240
  }
220
241
  return context;
221
242
  }
@@ -320,6 +341,7 @@ const FormGroup = /* @__PURE__ */ defineComponent((props, {
320
341
  "class": classnames("schemx-group", {
321
342
  "schemx-group--collapsed": collapsed.value
322
343
  }, schema.class),
344
+ "style": schema.style,
323
345
  "data-key": schema.key
324
346
  }, [schema.label && createVNode("div", {
325
347
  "role": collapsible ? "button" : void 0,
@@ -356,138 +378,166 @@ const FormGroup = /* @__PURE__ */ defineComponent((props, {
356
378
  }
357
379
  }
358
380
  });
359
- const FormItem = /* @__PURE__ */ defineComponent((props, {
360
- slots
361
- }) => {
362
- const schemaRef = toRef(props, "schema");
363
- if (isViewGroupSchema(schemaRef.value)) {
381
+ const FormItem = /* @__PURE__ */ defineComponent({
382
+ name: "SchemxItem",
383
+ props: {
384
+ schema: {
385
+ type: Object,
386
+ required: true
387
+ }
388
+ },
389
+ setup(props, {
390
+ slots
391
+ }) {
364
392
  return () => {
365
- return h(FormGroup, {
366
- schema: schemaRef.value
393
+ const schema = props.schema;
394
+ if (isViewGroupSchema(schema)) {
395
+ return h(FormGroup, {
396
+ schema
397
+ }, slots);
398
+ }
399
+ return h(FieldFormItem, {
400
+ schema
367
401
  }, slots);
368
402
  };
369
403
  }
370
- const form = useFormContext();
371
- const formContext = useConfigContext();
372
- const schema = () => schemaRef.value;
373
- const field = useField(schema().name);
374
- createFieldContext(field);
375
- const trigger = computed(() => mergeTrigger(schema().validationTrigger, formContext.validationTrigger, "onChange"));
376
- const canVerified = computed(() => {
377
- const isOperate = schema().visible && !schema().readonly && !schema().disabled;
378
- const rules = schema().rules;
379
- const hasRules = Array.isArray(rules) ? (rules == null ? void 0 : rules.length) > 0 : !!schema().rules;
380
- return isOperate && hasRules;
381
- });
382
- const handleChange = (v) => {
383
- var _a, _b;
384
- field.setValue(v);
385
- (_b = (_a = schema().componentProps) == null ? void 0 : _a.onChange) == null ? void 0 : _b.call(_a, v);
386
- if (canVerified.value && shouldValidateOn("change", trigger.value)) {
387
- field.validate();
388
- }
389
- };
390
- const handleBlur = () => {
391
- if (canVerified.value && shouldValidateOn("blur", trigger.value)) {
392
- field.validate();
393
- }
394
- };
395
- const componentProps = useStableRef(() => ({
396
- ...schema().componentProps,
397
- value: field.getValue(),
398
- onChange: handleChange,
399
- onBlur: handleBlur,
400
- "onUpdate:value": (v) => field.setValue(v)
401
- }));
402
- const renderRequired = () => {
403
- if (!schema().required || schema().disabled || schema().readonly) {
404
- return null;
405
- }
406
- return createVNode("span", {
407
- "class": "schemx-item__required"
408
- }, [createTextVNode("*")]);
409
- };
410
- const renderLabel = () => {
411
- const labelSlot = resolveSlot(slots, `${schema().name}Label`);
412
- if (labelSlot) {
413
- return labelSlot(schema());
414
- }
415
- const labelAlign = schema().labelAlign || formContext.labelAlign;
416
- const labelWidth = schema().labelWidth || formContext.labelWidth;
417
- const colon = schema().colon ?? formContext.colon;
418
- return createVNode("label", {
419
- "class": "schemx-item__label",
420
- "style": {
421
- width: labelWidth,
422
- textAlign: labelAlign
423
- }
424
- }, [renderRequired(), createVNode("span", {
425
- "class": "schemx-item__label-text"
426
- }, [schema().label, colon ? ":" : ""])]);
427
- };
428
- const renderContent = () => {
429
- const component = form.getRenderer(schema().componentType);
430
- if (!component) {
431
- throw new Error(`[schemx] Can not find component renderer of "${schema().componentType}".`);
432
- }
433
- const childSlots = extractChildSlots(normalizeNameKey(schema().name), slots);
434
- const columnElement = h(component, componentProps.value, childSlots);
435
- const contentSlot = resolveSlot(slots, `${schema().name}Content`);
436
- if (contentSlot) {
437
- return contentSlot({
438
- ...schema(),
439
- columnElement
440
- });
441
- }
442
- return createVNode("div", {
443
- "class": "schemx-item__control"
444
- }, [columnElement]);
445
- };
446
- const renderError = () => {
447
- const errorSlot = resolveSlot(slots, `${schema().name}Error`);
448
- if (errorSlot) {
449
- return errorSlot({
450
- ...schema(),
451
- errors: field.error.value
452
- });
453
- }
454
- if (!Array.isArray(field.error.value) || field.error.value.length === 0) {
455
- return null;
456
- }
457
- return createVNode("div", {
458
- "class": "schemx-item__error"
459
- }, [field.error.value[0]]);
460
- };
461
- return () => {
462
- if (!schema().visible) {
463
- return null;
464
- }
465
- const itemSlot = resolveSlot(slots, normalizeNameKey(schema().name));
466
- if (itemSlot) {
467
- return itemSlot(schema());
468
- }
469
- const labelPosition = schema().labelPosition || formContext.labelPosition;
470
- return createVNode("div", {
471
- "class": classnames("schemx-item-wrapper")
472
- }, [createVNode("div", {
473
- "class": classnames("schemx-item", `schemx-item--label-${labelPosition}`, schema().class, {
474
- "is-readonly": schema().readonly,
475
- "is-disabled": schema().disabled
476
- }),
477
- "style": {
478
- ...schema().style ?? {}
479
- }
480
- }, [renderLabel(), createVNode("div", {
481
- "class": "schemx-item__content"
482
- }, [renderContent(), renderError()])])]);
483
- };
484
- }, {
485
- name: "SchemxItem",
404
+ });
405
+ const FieldFormItem = /* @__PURE__ */ defineComponent({
406
+ name: "SchemxFieldItem",
486
407
  props: {
487
408
  schema: {
488
409
  type: Object,
489
410
  required: true
490
411
  }
412
+ },
413
+ setup(props, {
414
+ slots
415
+ }) {
416
+ const schemaRef = toRef(props, "schema");
417
+ const form = useFormContext();
418
+ const formContext = useFormConfigContext();
419
+ const schema = () => schemaRef.value;
420
+ const field = useField(schema().name);
421
+ createFieldContext(field);
422
+ const trigger = computed(() => mergeTrigger(schema().validationTrigger, formContext.validationTrigger, "onChange"));
423
+ const canVerified = computed(() => {
424
+ const isOperate = schema().visible && !schema().readonly && !schema().disabled;
425
+ const rules = schema().rules;
426
+ const hasRules = Array.isArray(rules) ? (rules == null ? void 0 : rules.length) > 0 : !!schema().rules;
427
+ return isOperate && hasRules;
428
+ });
429
+ const handleChange = (v) => {
430
+ var _a, _b;
431
+ field.setValue(v);
432
+ (_b = (_a = schema().componentProps) == null ? void 0 : _a.onChange) == null ? void 0 : _b.call(_a, v);
433
+ if (canVerified.value && shouldValidateOn("change", trigger.value)) {
434
+ field.validate();
435
+ }
436
+ };
437
+ const handleBlur = (v) => {
438
+ var _a, _b;
439
+ (_b = (_a = schema().componentProps) == null ? void 0 : _a.onBlur) == null ? void 0 : _b.call(_a, v);
440
+ if (canVerified.value && shouldValidateOn("blur", trigger.value)) {
441
+ field.validate();
442
+ }
443
+ };
444
+ const componentProps = useStableRef(() => {
445
+ const currentSchema = schema();
446
+ return {
447
+ ...currentSchema.componentProps,
448
+ readonly: currentSchema.readonly,
449
+ disabled: currentSchema.disabled,
450
+ placeholder: currentSchema.placeholder,
451
+ formItemProps: currentSchema,
452
+ value: field.getValue(),
453
+ onChange: handleChange,
454
+ onBlur: handleBlur,
455
+ "onUpdate:value": (v) => field.setValue(v)
456
+ };
457
+ });
458
+ const renderRequired = () => {
459
+ if (!schema().required || schema().disabled || schema().readonly) {
460
+ return null;
461
+ }
462
+ return createVNode("span", {
463
+ "class": "schemx-item__required"
464
+ }, [createTextVNode("*")]);
465
+ };
466
+ const renderLabel = () => {
467
+ const labelSlot = resolveSlot(slots, `${schema().name}Label`);
468
+ if (labelSlot) {
469
+ return labelSlot(schema());
470
+ }
471
+ const labelAlign = schema().labelAlign || formContext.labelAlign;
472
+ const labelWidth = schema().labelWidth || formContext.labelWidth;
473
+ const colon = schema().colon ?? formContext.colon;
474
+ return createVNode("label", {
475
+ "class": "schemx-item__label",
476
+ "style": {
477
+ width: labelWidth,
478
+ textAlign: labelAlign
479
+ }
480
+ }, [renderRequired(), createVNode("span", {
481
+ "class": "schemx-item__label-text"
482
+ }, [schema().label, colon ? ":" : ""])]);
483
+ };
484
+ const renderContent = () => {
485
+ const component = form.getRenderer(schema().componentType);
486
+ if (!component) {
487
+ throw new Error(`[schemx] Can not find component renderer of "${schema().componentType}".`);
488
+ }
489
+ const childSlots = extractChildSlots(normalizeNameKey(schema().name), slots);
490
+ const columnElement = h(component, componentProps.value, childSlots);
491
+ const contentSlot = resolveSlot(slots, `${schema().name}Content`);
492
+ if (contentSlot) {
493
+ return contentSlot({
494
+ ...schema(),
495
+ columnElement
496
+ });
497
+ }
498
+ return createVNode("div", {
499
+ "class": "schemx-item__control"
500
+ }, [columnElement]);
501
+ };
502
+ const renderError = () => {
503
+ const errorSlot = resolveSlot(slots, `${schema().name}Error`);
504
+ if (errorSlot) {
505
+ return errorSlot({
506
+ ...schema(),
507
+ errors: field.error.value
508
+ });
509
+ }
510
+ if (!Array.isArray(field.error.value) || field.error.value.length === 0) {
511
+ return null;
512
+ }
513
+ return createVNode("div", {
514
+ "class": "schemx-item__error"
515
+ }, [field.error.value[0]]);
516
+ };
517
+ return () => {
518
+ if (!schema().visible) {
519
+ return null;
520
+ }
521
+ const itemSlot = resolveSlot(slots, normalizeNameKey(schema().name));
522
+ if (itemSlot) {
523
+ return itemSlot(schema());
524
+ }
525
+ const labelPosition = schema().labelPosition || formContext.labelPosition;
526
+ return createVNode("div", {
527
+ "class": classnames("schemx-item-wrapper"),
528
+ "style": schema().style
529
+ }, [createVNode("div", {
530
+ "class": classnames("schemx-item", `schemx-item--label-${labelPosition}`, schema().class, {
531
+ "is-readonly": schema().readonly,
532
+ "is-disabled": schema().disabled
533
+ }),
534
+ "style": {
535
+ ...schema().style ?? {}
536
+ }
537
+ }, [renderLabel(), createVNode("div", {
538
+ "class": "schemx-item__content"
539
+ }, [renderContent(), renderError()])])]);
540
+ };
491
541
  }
492
542
  });
493
543
  const isViewGroupSchema = (schema) => {
@@ -540,11 +590,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
540
590
  __name: "form",
541
591
  props: {
542
592
  class: { default: "" },
543
- style: { default: () => ({}) },
593
+ style: { type: [Boolean, null, String, Object, Array], default: () => ({}) },
544
594
  required: { type: Boolean },
545
595
  readonly: { type: Boolean },
546
596
  disabled: { type: Boolean },
547
- visible: { type: Boolean },
597
+ visible: { type: Boolean, default: true },
548
598
  labelIcon: {},
549
599
  labelAlign: {},
550
600
  labelPosition: {},
@@ -568,22 +618,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
568
618
  setup(__props, { expose: __expose, emit: __emit }) {
569
619
  const props = __props;
570
620
  const emit = __emit;
571
- createConfigContext(
572
- omit(props, [
573
- "form",
574
- "modelValue",
575
- "rendererRegistry",
576
- "defaultRendererType",
577
- "validatorRegistry",
578
- "onFinish",
579
- "onFinishFailed",
580
- "onValuesChange",
581
- "onFieldsChange"
582
- ])
583
- );
621
+ createFormConfigContext(omit(props, formConfigContextOmitKey));
584
622
  const form = props.form ? props.form : useForm({
585
623
  schemas: props.schemas,
586
- initialValues: props.initialValues,
624
+ initialValues: Object.keys(props.modelValue).length > 0 ? props.modelValue : props.initialValues,
587
625
  rendererRegistry: props.rendererRegistry,
588
626
  defaultRendererType: props.defaultRendererType,
589
627
  validatorRegistry: props.validatorRegistry,
@@ -599,7 +637,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
599
637
  },
600
638
  onValuesChange: (changedValues, latestSnapshot) => {
601
639
  var _a;
602
- emit("update:modelValue", latestSnapshot);
603
640
  (_a = props.onValuesChange) == null ? void 0 : _a.call(props, changedValues, latestSnapshot);
604
641
  },
605
642
  onFieldsChange: (changedPaths, allPaths) => {
@@ -607,6 +644,21 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
607
644
  (_a = props.onFieldsChange) == null ? void 0 : _a.call(props, changedPaths, allPaths);
608
645
  }
609
646
  });
647
+ createFormContext(form);
648
+ let syncingFromModel = false;
649
+ watch(
650
+ () => props.modelValue,
651
+ (values) => {
652
+ syncingFromModel = true;
653
+ form.setFieldsValue(values);
654
+ syncingFromModel = false;
655
+ }
656
+ );
657
+ const disposeWatch = createWatch(form, (latestSnapshot) => {
658
+ if (syncingFromModel) return;
659
+ emit("update:modelValue", latestSnapshot);
660
+ });
661
+ onUnmounted(disposeWatch);
610
662
  watch(
611
663
  () => props.schemas,
612
664
  (schemas) => {
@@ -633,7 +685,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
633
685
  });
634
686
  return (_ctx, _cache) => {
635
687
  return openBlock(), createElementBlock("div", {
636
- class: normalizeClass(["schemx", props.class])
688
+ class: normalizeClass(["schemx", props.class]),
689
+ style: normalizeStyle(props.style)
637
690
  }, [
638
691
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(viewSchemas), (schema) => {
639
692
  return openBlock(), createBlock(unref(FormItem), {
@@ -651,7 +704,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
651
704
  })
652
705
  ]), 1032, ["schema", "class"]);
653
706
  }), 128))
654
- ], 2);
707
+ ], 6);
655
708
  };
656
709
  }
657
710
  });
@@ -681,7 +734,8 @@ function WithRemoteOptions(WrappedComponent) {
681
734
  }
682
735
  },
683
736
  setup(props, { attrs, slots }) {
684
- const dictResult = props.dict ? useDictionary(props.dict, props.fieldName) : null;
737
+ const fieldName = props.fieldName ?? (props.dict ? useFieldContext().name : void 0);
738
+ const dictResult = props.dict ? useDictionary(props.dict, fieldName) : null;
685
739
  const childrenProps = computed(() => {
686
740
  return {
687
741
  ...attrs,
@@ -698,16 +752,22 @@ export {
698
752
  FormGroup,
699
753
  FormItem,
700
754
  WithRemoteOptions,
755
+ createFieldContext,
756
+ createFormConfigContext,
757
+ createFormContext,
701
758
  SchemxFormExport as default,
759
+ formConfigContextOmitKey,
702
760
  rendererRegistry,
703
761
  SchemxFormExport as schemxForm,
704
- useConfigContext,
705
762
  useDictionary,
706
763
  useEffect,
707
764
  useField,
708
765
  useFieldContext,
709
766
  useForm,
767
+ useFormConfigContext,
710
768
  useFormContext,
769
+ useStableRef,
770
+ useViewSchemas,
711
771
  useWatch,
712
772
  useWatchAll,
713
773
  useWatchField,