@dynamic-field-kit/react 1.5.1 → 1.6.0
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/CHANGELOG.md +166 -0
- package/README.md +76 -14
- package/dist/index.d.mts +79 -5
- package/dist/index.d.ts +79 -5
- package/dist/index.js +247 -100
- package/dist/index.mjs +262 -110
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -32,24 +32,29 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
DynamicFormDevTools: () => DynamicFormDevTools,
|
|
34
34
|
DynamicInput: () => DynamicInput_default,
|
|
35
|
+
FIELD_RENDERER_PROP_KEYS: () => import_core7.FIELD_RENDERER_PROP_KEYS,
|
|
35
36
|
FieldInput: () => FieldInput_default,
|
|
36
37
|
FieldRegistry: () => import_core6.FieldRegistry,
|
|
37
38
|
FieldRegistryProvider: () => FieldRegistryProvider,
|
|
38
39
|
MultiFieldInput: () => MultiFieldInput_default,
|
|
40
|
+
buildFieldRendererProps: () => import_core7.buildFieldRendererProps,
|
|
41
|
+
collectFieldPaths: () => import_core8.collectFieldPaths,
|
|
39
42
|
defaultRenderersMap: () => defaultRenderersMap,
|
|
40
43
|
fieldRegistry: () => fieldRegistry,
|
|
41
44
|
getDefaultRenderer: () => getDefaultRenderer,
|
|
45
|
+
indexGroupPathMap: () => import_core8.indexGroupPathMap,
|
|
42
46
|
layoutRegistry: () => layoutRegistry,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
makeFieldId: () => import_core7.makeFieldId,
|
|
48
|
+
resolveDisabled: () => import_core8.resolveDisabled,
|
|
49
|
+
resolveOptions: () => import_core8.resolveOptions,
|
|
50
|
+
resolveReadOnly: () => import_core8.resolveReadOnly,
|
|
46
51
|
useDynamicForm: () => useDynamicForm,
|
|
47
52
|
useFieldRegistry: () => useFieldRegistry,
|
|
48
|
-
validateField: () =>
|
|
49
|
-
validateFieldAsync: () =>
|
|
50
|
-
validateFields: () =>
|
|
51
|
-
validateFieldsAsync: () =>
|
|
52
|
-
validators: () =>
|
|
53
|
+
validateField: () => import_core8.validateField,
|
|
54
|
+
validateFieldAsync: () => import_core8.validateFieldAsync,
|
|
55
|
+
validateFields: () => import_core8.validateFields,
|
|
56
|
+
validateFieldsAsync: () => import_core8.validateFieldsAsync,
|
|
57
|
+
validators: () => import_core8.validators
|
|
53
58
|
});
|
|
54
59
|
module.exports = __toCommonJS(index_exports);
|
|
55
60
|
|
|
@@ -491,24 +496,10 @@ function useFieldRegistry() {
|
|
|
491
496
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
492
497
|
var DynamicInputInner = ({
|
|
493
498
|
type,
|
|
494
|
-
value,
|
|
495
499
|
onChange,
|
|
496
500
|
onBlur,
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
className,
|
|
500
|
-
description,
|
|
501
|
-
disabled,
|
|
502
|
-
readOnly,
|
|
503
|
-
required,
|
|
504
|
-
touched,
|
|
505
|
-
dirty,
|
|
506
|
-
error,
|
|
507
|
-
id,
|
|
508
|
-
ariaInvalid,
|
|
509
|
-
ariaDescribedBy,
|
|
510
|
-
ariaRequired,
|
|
511
|
-
extraProps
|
|
501
|
+
extraProps,
|
|
502
|
+
...rendererProps
|
|
512
503
|
}) => {
|
|
513
504
|
const registry = useFieldRegistry();
|
|
514
505
|
const Renderer = (0, import_react3.useMemo)(
|
|
@@ -523,23 +514,9 @@ var DynamicInputInner = ({
|
|
|
523
514
|
}
|
|
524
515
|
return import_react3.default.createElement(Renderer, {
|
|
525
516
|
...extraProps,
|
|
526
|
-
|
|
517
|
+
...rendererProps,
|
|
527
518
|
onValueChange: onChange,
|
|
528
|
-
onBlur
|
|
529
|
-
label,
|
|
530
|
-
options,
|
|
531
|
-
className,
|
|
532
|
-
description,
|
|
533
|
-
disabled,
|
|
534
|
-
readOnly,
|
|
535
|
-
required,
|
|
536
|
-
touched,
|
|
537
|
-
dirty,
|
|
538
|
-
error,
|
|
539
|
-
id,
|
|
540
|
-
ariaInvalid,
|
|
541
|
-
ariaDescribedBy,
|
|
542
|
-
ariaRequired
|
|
519
|
+
onBlur
|
|
543
520
|
});
|
|
544
521
|
};
|
|
545
522
|
var DynamicInput = /* @__PURE__ */ import_react3.default.memo(
|
|
@@ -557,7 +534,7 @@ var import_react5 = require("react");
|
|
|
557
534
|
|
|
558
535
|
// src/components/MultiFieldInput.tsx
|
|
559
536
|
var import_core2 = require("@dynamic-field-kit/core");
|
|
560
|
-
var import_react4 = require("react");
|
|
537
|
+
var import_react4 = __toESM(require("react"));
|
|
561
538
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
562
539
|
function resolveLayout(layout) {
|
|
563
540
|
if (!layout) {
|
|
@@ -568,25 +545,39 @@ function resolveLayout(layout) {
|
|
|
568
545
|
}
|
|
569
546
|
return { type: layout.type, config: layout };
|
|
570
547
|
}
|
|
571
|
-
var
|
|
548
|
+
var MultiFieldInputInner = ({
|
|
572
549
|
fieldDescriptions,
|
|
573
550
|
properties,
|
|
574
551
|
onChange,
|
|
575
552
|
layout,
|
|
553
|
+
idPrefix,
|
|
576
554
|
rootData,
|
|
577
555
|
onValidityChange,
|
|
578
|
-
onBlurField
|
|
579
|
-
|
|
556
|
+
onBlurField,
|
|
557
|
+
touched: touchedProp,
|
|
558
|
+
errors: errorsProp,
|
|
559
|
+
onTouchedChange,
|
|
560
|
+
form
|
|
561
|
+
}, ref) => {
|
|
562
|
+
const effectiveProperties = properties ?? form?.data;
|
|
563
|
+
const effectiveOnChange = onChange ?? form?.handleChange;
|
|
564
|
+
const effectiveOnBlurField = onBlurField ?? form?.handleBlur;
|
|
565
|
+
const controlledTouched = touchedProp ?? form?.touched;
|
|
566
|
+
const effectiveErrors = errorsProp ?? form?.errors;
|
|
580
567
|
const [data, setData] = (0, import_react4.useState)({});
|
|
581
|
-
const [
|
|
582
|
-
|
|
583
|
-
);
|
|
584
|
-
const
|
|
568
|
+
const [internalTouched, setInternalTouched] = (0, import_react4.useState)({});
|
|
569
|
+
const initialPropertiesRef = (0, import_react4.useRef)(effectiveProperties ?? {});
|
|
570
|
+
const autoId = (0, import_react4.useId)().replace(/[^a-zA-Z0-9]/g, "");
|
|
571
|
+
const effectiveIdPrefix = idPrefix ?? `dfk-${autoId}`;
|
|
572
|
+
const isTouchedControlled = controlledTouched !== void 0;
|
|
573
|
+
const effectiveTouched = controlledTouched ?? internalTouched;
|
|
585
574
|
(0, import_react4.useEffect)(() => {
|
|
586
|
-
if (
|
|
587
|
-
setData(
|
|
575
|
+
if (effectiveProperties) {
|
|
576
|
+
setData(
|
|
577
|
+
(0, import_core2.applyComputedValues)(fieldDescriptions, effectiveProperties, rootData)
|
|
578
|
+
);
|
|
588
579
|
}
|
|
589
|
-
}, [
|
|
580
|
+
}, [effectiveProperties]);
|
|
590
581
|
const effectiveRoot = rootData ?? data;
|
|
591
582
|
const visibleFields = (0, import_react4.useMemo)(
|
|
592
583
|
() => fieldDescriptions.filter(
|
|
@@ -596,16 +587,22 @@ var MultiFieldInput = ({
|
|
|
596
587
|
);
|
|
597
588
|
const dataRef = (0, import_react4.useRef)(data);
|
|
598
589
|
dataRef.current = data;
|
|
599
|
-
const onChangeRef = (0, import_react4.useRef)(
|
|
600
|
-
onChangeRef.current =
|
|
590
|
+
const onChangeRef = (0, import_react4.useRef)(effectiveOnChange);
|
|
591
|
+
onChangeRef.current = effectiveOnChange;
|
|
601
592
|
const fieldDescriptionsRef = (0, import_react4.useRef)(fieldDescriptions);
|
|
602
593
|
fieldDescriptionsRef.current = fieldDescriptions;
|
|
603
594
|
const rootDataRef = (0, import_react4.useRef)(rootData);
|
|
604
595
|
rootDataRef.current = rootData;
|
|
605
596
|
const onValidityChangeRef = (0, import_react4.useRef)(onValidityChange);
|
|
606
597
|
onValidityChangeRef.current = onValidityChange;
|
|
607
|
-
const onBlurFieldRef = (0, import_react4.useRef)(
|
|
608
|
-
onBlurFieldRef.current =
|
|
598
|
+
const onBlurFieldRef = (0, import_react4.useRef)(effectiveOnBlurField);
|
|
599
|
+
onBlurFieldRef.current = effectiveOnBlurField;
|
|
600
|
+
const onTouchedChangeRef = (0, import_react4.useRef)(onTouchedChange);
|
|
601
|
+
onTouchedChangeRef.current = onTouchedChange;
|
|
602
|
+
const isTouchedControlledRef = (0, import_react4.useRef)(isTouchedControlled);
|
|
603
|
+
isTouchedControlledRef.current = isTouchedControlled;
|
|
604
|
+
const effectiveTouchedRef = (0, import_react4.useRef)(effectiveTouched);
|
|
605
|
+
effectiveTouchedRef.current = effectiveTouched;
|
|
609
606
|
(0, import_react4.useEffect)(() => {
|
|
610
607
|
onValidityChangeRef.current?.(
|
|
611
608
|
(0, import_core2.validateFields)(fieldDescriptions, data, rootData)
|
|
@@ -622,10 +619,39 @@ var MultiFieldInput = ({
|
|
|
622
619
|
setData(next);
|
|
623
620
|
onChangeRef.current?.(next);
|
|
624
621
|
}, []);
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
622
|
+
const markTouched = (0, import_react4.useCallback)((key, isTouched) => {
|
|
623
|
+
const current = effectiveTouchedRef.current;
|
|
624
|
+
if (Boolean(current[key]) === isTouched) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const next = { ...current, [key]: isTouched };
|
|
628
|
+
if (!isTouchedControlledRef.current) {
|
|
629
|
+
effectiveTouchedRef.current = next;
|
|
630
|
+
setInternalTouched(next);
|
|
631
|
+
}
|
|
632
|
+
onTouchedChangeRef.current?.(next);
|
|
628
633
|
}, []);
|
|
634
|
+
const handleBlurField = (0, import_react4.useCallback)(
|
|
635
|
+
(key) => {
|
|
636
|
+
markTouched(key, true);
|
|
637
|
+
onBlurFieldRef.current?.(key);
|
|
638
|
+
},
|
|
639
|
+
[markTouched]
|
|
640
|
+
);
|
|
641
|
+
(0, import_react4.useImperativeHandle)(
|
|
642
|
+
ref,
|
|
643
|
+
() => ({
|
|
644
|
+
resetTouched: () => {
|
|
645
|
+
if (!isTouchedControlledRef.current) {
|
|
646
|
+
effectiveTouchedRef.current = {};
|
|
647
|
+
}
|
|
648
|
+
setInternalTouched({});
|
|
649
|
+
},
|
|
650
|
+
setFieldTouched: (fieldName, isTouched = true) => markTouched(fieldName, isTouched),
|
|
651
|
+
getTouched: () => effectiveTouchedRef.current
|
|
652
|
+
}),
|
|
653
|
+
[markTouched]
|
|
654
|
+
);
|
|
629
655
|
const { type, config } = resolveLayout(layout);
|
|
630
656
|
const Layout = layoutRegistry.get(type);
|
|
631
657
|
if (!Layout) {
|
|
@@ -637,7 +663,10 @@ var MultiFieldInput = ({
|
|
|
637
663
|
fieldDescription: f,
|
|
638
664
|
renderInfos: data,
|
|
639
665
|
rootData: effectiveRoot,
|
|
640
|
-
|
|
666
|
+
idPrefix: effectiveIdPrefix,
|
|
667
|
+
touched: Boolean(effectiveTouched[f.name]),
|
|
668
|
+
touchedMap: (0, import_core2.isFieldGroup)(f) ? effectiveTouched : void 0,
|
|
669
|
+
errors: effectiveErrors,
|
|
641
670
|
dirty: data[f.name] !== initialPropertiesRef.current[f.name],
|
|
642
671
|
onBlurField: handleBlurField,
|
|
643
672
|
onValueChangeField: handleValueChangeField
|
|
@@ -645,14 +674,20 @@ var MultiFieldInput = ({
|
|
|
645
674
|
f.name
|
|
646
675
|
)) });
|
|
647
676
|
};
|
|
677
|
+
var MultiFieldInput = /* @__PURE__ */ import_react4.default.forwardRef(MultiFieldInputInner);
|
|
678
|
+
MultiFieldInput.displayName = "MultiFieldInput";
|
|
648
679
|
var MultiFieldInput_default = MultiFieldInput;
|
|
649
680
|
|
|
650
681
|
// src/components/FieldGroupInput.tsx
|
|
651
682
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
683
|
+
var EMPTY_TOUCHED = Object.freeze({});
|
|
652
684
|
var FieldGroupInput = ({
|
|
653
685
|
fieldDescription,
|
|
654
686
|
items,
|
|
655
687
|
rootData,
|
|
688
|
+
errors,
|
|
689
|
+
touched,
|
|
690
|
+
onBlurField,
|
|
656
691
|
onChange
|
|
657
692
|
}) => {
|
|
658
693
|
const {
|
|
@@ -666,6 +701,14 @@ var FieldGroupInput = ({
|
|
|
666
701
|
const addText = addLabel ?? "Add";
|
|
667
702
|
const removeText = removeLabel ?? "Remove";
|
|
668
703
|
const groupName = label ?? fieldDescription.name;
|
|
704
|
+
const errorsByItem = (0, import_react5.useMemo)(
|
|
705
|
+
() => (0, import_core3.indexGroupPathMap)(errors, fieldDescription.name),
|
|
706
|
+
[errors, fieldDescription.name]
|
|
707
|
+
);
|
|
708
|
+
const touchedByItem = (0, import_react5.useMemo)(
|
|
709
|
+
() => (0, import_core3.indexGroupPathMap)(touched, fieldDescription.name),
|
|
710
|
+
[touched, fieldDescription.name]
|
|
711
|
+
);
|
|
669
712
|
const handleItemChange = (0, import_react5.useCallback)(
|
|
670
713
|
(index, next) => {
|
|
671
714
|
const nextItems = items.slice();
|
|
@@ -702,6 +745,9 @@ var FieldGroupInput = ({
|
|
|
702
745
|
fieldDescriptions: fields,
|
|
703
746
|
properties: item,
|
|
704
747
|
rootData,
|
|
748
|
+
errors: errorsByItem?.[index],
|
|
749
|
+
touched: touched === void 0 ? void 0 : touchedByItem?.[index] ?? EMPTY_TOUCHED,
|
|
750
|
+
onBlurField: (key) => onBlurField?.(`${fieldDescription.name}[${index}].${key}`),
|
|
705
751
|
onChange: (next) => handleItemChange(index, next)
|
|
706
752
|
}
|
|
707
753
|
) }),
|
|
@@ -739,12 +785,15 @@ var FieldInputInner = ({
|
|
|
739
785
|
fieldDescription,
|
|
740
786
|
renderInfos,
|
|
741
787
|
rootData,
|
|
788
|
+
idPrefix = "dfk-field",
|
|
742
789
|
touched,
|
|
790
|
+
touchedMap,
|
|
743
791
|
dirty,
|
|
792
|
+
errors,
|
|
744
793
|
onBlurField,
|
|
745
794
|
onValueChangeField
|
|
746
795
|
}) => {
|
|
747
|
-
const { name,
|
|
796
|
+
const { name, fields } = fieldDescription;
|
|
748
797
|
const handleChange = (0, import_react6.useCallback)(
|
|
749
798
|
(v) => onValueChangeField(v, name),
|
|
750
799
|
[onValueChangeField, name]
|
|
@@ -761,43 +810,27 @@ var FieldInputInner = ({
|
|
|
761
810
|
fieldDescription,
|
|
762
811
|
items,
|
|
763
812
|
rootData,
|
|
813
|
+
errors,
|
|
814
|
+
touched: touchedMap,
|
|
815
|
+
onBlurField,
|
|
764
816
|
onChange: handleChange
|
|
765
817
|
}
|
|
766
818
|
);
|
|
767
819
|
}
|
|
768
|
-
const
|
|
820
|
+
const rendererProps = (0, import_core4.buildFieldRendererProps)({
|
|
769
821
|
fieldDescription,
|
|
770
|
-
renderInfos,
|
|
771
|
-
rootData
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
rootData
|
|
778
|
-
);
|
|
779
|
-
const errors = effectiveDisabled ? [] : (0, import_core4.validateField)(fieldDescription, renderInfos[name], renderInfos, rootData);
|
|
780
|
-
const error = errors.length > 0 ? errors : void 0;
|
|
781
|
-
const fieldId = `dfk-field-${name}`;
|
|
822
|
+
data: renderInfos,
|
|
823
|
+
rootData,
|
|
824
|
+
id: (0, import_core4.makeFieldId)(fieldDescription, idPrefix),
|
|
825
|
+
touched,
|
|
826
|
+
dirty,
|
|
827
|
+
validationErrors: errors === void 0 ? void 0 : errors[name] ?? []
|
|
828
|
+
});
|
|
782
829
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
783
830
|
DynamicInput_default,
|
|
784
831
|
{
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
label,
|
|
788
|
-
value: renderInfos[name],
|
|
789
|
-
options: resolvedOptionsList,
|
|
790
|
-
className,
|
|
791
|
-
description,
|
|
792
|
-
disabled: effectiveDisabled,
|
|
793
|
-
readOnly,
|
|
794
|
-
required,
|
|
795
|
-
touched,
|
|
796
|
-
dirty,
|
|
797
|
-
error,
|
|
798
|
-
ariaInvalid: Boolean(error),
|
|
799
|
-
ariaRequired: Boolean(required),
|
|
800
|
-
extraProps: props,
|
|
832
|
+
...rendererProps,
|
|
833
|
+
description: rendererProps.description,
|
|
801
834
|
onChange: handleChange,
|
|
802
835
|
onBlur: handleBlur
|
|
803
836
|
}
|
|
@@ -805,7 +838,7 @@ var FieldInputInner = ({
|
|
|
805
838
|
};
|
|
806
839
|
var FieldInput = /* @__PURE__ */ import_react6.default.memo(FieldInputInner, (prev, next) => {
|
|
807
840
|
const name = prev.fieldDescription.name;
|
|
808
|
-
return prev.fieldDescription === next.fieldDescription && prev.onValueChangeField === next.onValueChangeField && prev.onBlurField === next.onBlurField && prev.rootData === next.rootData && prev.touched === next.touched && prev.dirty === next.dirty && prev.renderInfos[name] === next.renderInfos[name];
|
|
841
|
+
return prev.fieldDescription === next.fieldDescription && prev.onValueChangeField === next.onValueChangeField && prev.onBlurField === next.onBlurField && prev.rootData === next.rootData && prev.idPrefix === next.idPrefix && prev.touched === next.touched && prev.touchedMap === next.touchedMap && prev.dirty === next.dirty && prev.errors === next.errors && prev.renderInfos[name] === next.renderInfos[name];
|
|
809
842
|
});
|
|
810
843
|
var FieldInput_default = FieldInput;
|
|
811
844
|
|
|
@@ -1012,6 +1045,15 @@ var DynamicFormDevTools = ({
|
|
|
1012
1045
|
// src/useDynamicForm.ts
|
|
1013
1046
|
var import_core5 = require("@dynamic-field-kit/core");
|
|
1014
1047
|
var import_react8 = require("react");
|
|
1048
|
+
function sameStringMap(left, right) {
|
|
1049
|
+
const keys = Object.keys(left);
|
|
1050
|
+
return keys.length === Object.keys(right).length && keys.every(
|
|
1051
|
+
(key) => left[key]?.length === right[key]?.length && left[key]?.every((value, index) => value === right[key]?.[index])
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
function sameValidationResult(left, right) {
|
|
1055
|
+
return left.valid === right.valid && left.complete === right.complete && left.status === right.status && (left.pending ?? []).join("\0") === (right.pending ?? []).join("\0") && sameStringMap(left.errors, right.errors);
|
|
1056
|
+
}
|
|
1015
1057
|
function useDynamicForm({
|
|
1016
1058
|
fields,
|
|
1017
1059
|
initialValues = {},
|
|
@@ -1026,22 +1068,76 @@ function useDynamicForm({
|
|
|
1026
1068
|
const [touched, setTouched] = (0, import_react8.useState)({});
|
|
1027
1069
|
const [isSubmitting, setIsSubmitting] = (0, import_react8.useState)(false);
|
|
1028
1070
|
const [isSubmitted, setIsSubmitted] = (0, import_react8.useState)(false);
|
|
1071
|
+
const [validationResult, setValidationResult] = (0, import_react8.useState)(
|
|
1072
|
+
() => (0, import_core5.validateFields)(fields, data)
|
|
1073
|
+
);
|
|
1074
|
+
const [isValidating, setIsValidating] = (0, import_react8.useState)(false);
|
|
1075
|
+
const validationRunRef = (0, import_react8.useRef)(0);
|
|
1076
|
+
const validationAbortRef = (0, import_react8.useRef)(void 0);
|
|
1077
|
+
const submitRunRef = (0, import_react8.useRef)(0);
|
|
1078
|
+
const submitAbortRef = (0, import_react8.useRef)(void 0);
|
|
1079
|
+
const dataRef = (0, import_react8.useRef)(data);
|
|
1080
|
+
dataRef.current = data;
|
|
1081
|
+
const commitSyncResult = (0, import_react8.useCallback)((res) => {
|
|
1082
|
+
setValidationResult(
|
|
1083
|
+
(previous) => sameValidationResult(previous, res) ? previous : res
|
|
1084
|
+
);
|
|
1085
|
+
return res.valid;
|
|
1086
|
+
}, []);
|
|
1087
|
+
(0, import_react8.useEffect)(() => {
|
|
1088
|
+
commitSyncResult((0, import_core5.validateFields)(fields, data));
|
|
1089
|
+
}, [fields, data, commitSyncResult]);
|
|
1090
|
+
(0, import_react8.useEffect)(
|
|
1091
|
+
() => () => {
|
|
1092
|
+
validationAbortRef.current?.abort();
|
|
1093
|
+
submitAbortRef.current?.abort();
|
|
1094
|
+
},
|
|
1095
|
+
[]
|
|
1096
|
+
);
|
|
1029
1097
|
const validate = (0, import_react8.useCallback)(() => {
|
|
1030
1098
|
const res = (0, import_core5.validateFields)(fields, data);
|
|
1031
1099
|
setErrors(res.errors);
|
|
1032
|
-
return res
|
|
1100
|
+
return commitSyncResult(res);
|
|
1101
|
+
}, [fields, data, commitSyncResult]);
|
|
1102
|
+
const validateAsync = (0, import_react8.useCallback)(async () => {
|
|
1103
|
+
const run = ++validationRunRef.current;
|
|
1104
|
+
validationAbortRef.current?.abort();
|
|
1105
|
+
const controller = new AbortController();
|
|
1106
|
+
validationAbortRef.current = controller;
|
|
1107
|
+
const snapshot = data;
|
|
1108
|
+
setIsValidating(true);
|
|
1109
|
+
try {
|
|
1110
|
+
const res = await (0, import_core5.validateFieldsAsync)(fields, snapshot, snapshot, {
|
|
1111
|
+
signal: controller.signal
|
|
1112
|
+
});
|
|
1113
|
+
if (run !== validationRunRef.current || dataRef.current !== snapshot) {
|
|
1114
|
+
return res.valid;
|
|
1115
|
+
}
|
|
1116
|
+
setErrors(res.errors);
|
|
1117
|
+
setValidationResult(res);
|
|
1118
|
+
return res.valid;
|
|
1119
|
+
} finally {
|
|
1120
|
+
if (run === validationRunRef.current) {
|
|
1121
|
+
setIsValidating(false);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1033
1124
|
}, [fields, data]);
|
|
1034
1125
|
const handleChange = (0, import_react8.useCallback)(
|
|
1035
1126
|
(newData) => {
|
|
1036
1127
|
const next = (0, import_core5.applyComputedValues)(fields, newData);
|
|
1037
1128
|
setData(next);
|
|
1129
|
+
dataRef.current = next;
|
|
1038
1130
|
setIsDirty(true);
|
|
1131
|
+
validationAbortRef.current?.abort();
|
|
1132
|
+
validationRunRef.current += 1;
|
|
1133
|
+
setIsValidating(false);
|
|
1134
|
+
const res = (0, import_core5.validateFields)(fields, next);
|
|
1135
|
+
commitSyncResult(res);
|
|
1039
1136
|
if (validateOnChange) {
|
|
1040
|
-
const res = (0, import_core5.validateFields)(fields, next);
|
|
1041
1137
|
setErrors(res.errors);
|
|
1042
1138
|
}
|
|
1043
1139
|
},
|
|
1044
|
-
[fields, validateOnChange]
|
|
1140
|
+
[fields, validateOnChange, commitSyncResult]
|
|
1045
1141
|
);
|
|
1046
1142
|
const setFieldValue = (0, import_react8.useCallback)(
|
|
1047
1143
|
(name, value) => {
|
|
@@ -1052,26 +1148,39 @@ function useDynamicForm({
|
|
|
1052
1148
|
const setFieldTouched = (0, import_react8.useCallback)((name, isTouched = true) => {
|
|
1053
1149
|
setTouched((prev) => ({ ...prev, [name]: isTouched }));
|
|
1054
1150
|
}, []);
|
|
1151
|
+
const touchAll = (0, import_react8.useCallback)(() => {
|
|
1152
|
+
setTouched(
|
|
1153
|
+
Object.fromEntries(
|
|
1154
|
+
(0, import_core5.collectFieldPaths)(fields, data).map((path) => [path, true])
|
|
1155
|
+
)
|
|
1156
|
+
);
|
|
1157
|
+
}, [fields, data]);
|
|
1158
|
+
const resetTouched = (0, import_react8.useCallback)(() => setTouched({}), []);
|
|
1055
1159
|
const handleBlur = (0, import_react8.useCallback)(
|
|
1056
1160
|
(fieldName) => {
|
|
1057
1161
|
setFieldTouched(fieldName, true);
|
|
1058
1162
|
if (validateOnBlur) {
|
|
1059
1163
|
const res = (0, import_core5.validateFields)(fields, data);
|
|
1060
1164
|
setErrors(res.errors);
|
|
1165
|
+
commitSyncResult(res);
|
|
1061
1166
|
}
|
|
1062
1167
|
},
|
|
1063
|
-
[fields, data, validateOnBlur, setFieldTouched]
|
|
1168
|
+
[fields, data, validateOnBlur, setFieldTouched, commitSyncResult]
|
|
1064
1169
|
);
|
|
1065
1170
|
const reset = (0, import_react8.useCallback)(
|
|
1066
1171
|
(newValues) => {
|
|
1067
1172
|
const seed = newValues ?? initialValues;
|
|
1068
1173
|
const next = (0, import_core5.applyComputedValues)(fields, seed);
|
|
1069
1174
|
setData(next);
|
|
1175
|
+
dataRef.current = next;
|
|
1070
1176
|
setErrors({});
|
|
1071
1177
|
setIsDirty(false);
|
|
1072
1178
|
setTouched({});
|
|
1073
1179
|
setIsSubmitting(false);
|
|
1074
1180
|
setIsSubmitted(false);
|
|
1181
|
+
validationAbortRef.current?.abort();
|
|
1182
|
+
validationRunRef.current += 1;
|
|
1183
|
+
setIsValidating(false);
|
|
1075
1184
|
},
|
|
1076
1185
|
[fields, initialValues]
|
|
1077
1186
|
);
|
|
@@ -1081,26 +1190,54 @@ function useDynamicForm({
|
|
|
1081
1190
|
e.preventDefault();
|
|
1082
1191
|
}
|
|
1083
1192
|
setIsSubmitting(true);
|
|
1193
|
+
const submitRun = ++submitRunRef.current;
|
|
1084
1194
|
try {
|
|
1085
|
-
|
|
1086
|
-
|
|
1195
|
+
touchAll();
|
|
1196
|
+
const run = ++validationRunRef.current;
|
|
1197
|
+
validationAbortRef.current?.abort();
|
|
1198
|
+
submitAbortRef.current?.abort();
|
|
1199
|
+
const controller = new AbortController();
|
|
1200
|
+
submitAbortRef.current = controller;
|
|
1201
|
+
const snapshot = data;
|
|
1202
|
+
setIsValidating(true);
|
|
1203
|
+
const res = await (0, import_core5.validateFieldsAsync)(fields, snapshot, snapshot, {
|
|
1204
|
+
signal: controller.signal
|
|
1205
|
+
});
|
|
1206
|
+
if (submitRun !== submitRunRef.current) {
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
if (dataRef.current === snapshot && run === validationRunRef.current) {
|
|
1210
|
+
setErrors(res.errors);
|
|
1211
|
+
setValidationResult(res);
|
|
1212
|
+
} else {
|
|
1213
|
+
const live = (0, import_core5.validateFields)(fields, dataRef.current);
|
|
1214
|
+
setErrors(live.errors);
|
|
1215
|
+
commitSyncResult(live);
|
|
1216
|
+
}
|
|
1087
1217
|
setIsSubmitted(true);
|
|
1088
1218
|
if (res.valid) {
|
|
1089
|
-
await onValid(
|
|
1219
|
+
await onValid(snapshot);
|
|
1090
1220
|
} else if (onInvalid) {
|
|
1091
1221
|
onInvalid(res.errors);
|
|
1092
1222
|
}
|
|
1093
1223
|
} finally {
|
|
1224
|
+
if (submitRun === submitRunRef.current) {
|
|
1225
|
+
setIsValidating(false);
|
|
1226
|
+
}
|
|
1094
1227
|
setIsSubmitting(false);
|
|
1095
1228
|
}
|
|
1096
1229
|
},
|
|
1097
|
-
[fields, data]
|
|
1230
|
+
[fields, data, touchAll, commitSyncResult]
|
|
1098
1231
|
);
|
|
1099
|
-
const isValid = Object.keys(errors).length === 0;
|
|
1100
1232
|
return {
|
|
1101
1233
|
data,
|
|
1102
1234
|
errors,
|
|
1103
|
-
isValid,
|
|
1235
|
+
isValid: validationResult.valid,
|
|
1236
|
+
isValidating,
|
|
1237
|
+
// Matches Vue and Angular: a run still in flight is not complete, whatever
|
|
1238
|
+
// the last finished pass concluded.
|
|
1239
|
+
isValidationComplete: validationResult.complete && !isValidating,
|
|
1240
|
+
validationStatus: isValidating ? "pending" : validationResult.status,
|
|
1104
1241
|
isDirty,
|
|
1105
1242
|
isSubmitting,
|
|
1106
1243
|
isSubmitted,
|
|
@@ -1108,10 +1245,14 @@ function useDynamicForm({
|
|
|
1108
1245
|
setData,
|
|
1109
1246
|
setFieldValue,
|
|
1110
1247
|
setFieldTouched,
|
|
1248
|
+
setTouched,
|
|
1249
|
+
touchAll,
|
|
1250
|
+
resetTouched,
|
|
1111
1251
|
handleChange,
|
|
1112
1252
|
handleBlur,
|
|
1113
1253
|
reset,
|
|
1114
1254
|
validate,
|
|
1255
|
+
validateAsync,
|
|
1115
1256
|
handleSubmit
|
|
1116
1257
|
};
|
|
1117
1258
|
}
|
|
@@ -1119,18 +1260,24 @@ function useDynamicForm({
|
|
|
1119
1260
|
// src/index.ts
|
|
1120
1261
|
var import_core6 = require("@dynamic-field-kit/core");
|
|
1121
1262
|
var import_core7 = require("@dynamic-field-kit/core");
|
|
1263
|
+
var import_core8 = require("@dynamic-field-kit/core");
|
|
1122
1264
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1123
1265
|
0 && (module.exports = {
|
|
1124
1266
|
DynamicFormDevTools,
|
|
1125
1267
|
DynamicInput,
|
|
1268
|
+
FIELD_RENDERER_PROP_KEYS,
|
|
1126
1269
|
FieldInput,
|
|
1127
1270
|
FieldRegistry,
|
|
1128
1271
|
FieldRegistryProvider,
|
|
1129
1272
|
MultiFieldInput,
|
|
1273
|
+
buildFieldRendererProps,
|
|
1274
|
+
collectFieldPaths,
|
|
1130
1275
|
defaultRenderersMap,
|
|
1131
1276
|
fieldRegistry,
|
|
1132
1277
|
getDefaultRenderer,
|
|
1278
|
+
indexGroupPathMap,
|
|
1133
1279
|
layoutRegistry,
|
|
1280
|
+
makeFieldId,
|
|
1134
1281
|
resolveDisabled,
|
|
1135
1282
|
resolveOptions,
|
|
1136
1283
|
resolveReadOnly,
|