@dynamic-field-kit/react 1.5.1 → 1.7.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 +224 -0
- package/README.md +170 -29
- package/dist/index.d.mts +114 -7
- package/dist/index.d.ts +114 -7
- package/dist/index.js +394 -124
- package/dist/index.mjs +434 -154
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -32,24 +32,30 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
DynamicFormDevTools: () => DynamicFormDevTools,
|
|
34
34
|
DynamicInput: () => DynamicInput_default,
|
|
35
|
+
FIELD_RENDERER_PROP_KEYS: () => import_core8.FIELD_RENDERER_PROP_KEYS,
|
|
35
36
|
FieldInput: () => FieldInput_default,
|
|
36
|
-
FieldRegistry: () =>
|
|
37
|
+
FieldRegistry: () => import_core7.FieldRegistry,
|
|
37
38
|
FieldRegistryProvider: () => FieldRegistryProvider,
|
|
38
39
|
MultiFieldInput: () => MultiFieldInput_default,
|
|
40
|
+
buildFieldRendererProps: () => import_core8.buildFieldRendererProps,
|
|
41
|
+
collectFieldPaths: () => import_core9.collectFieldPaths,
|
|
39
42
|
defaultRenderersMap: () => defaultRenderersMap,
|
|
40
43
|
fieldRegistry: () => fieldRegistry,
|
|
41
44
|
getDefaultRenderer: () => getDefaultRenderer,
|
|
45
|
+
indexGroupPathMap: () => import_core9.indexGroupPathMap,
|
|
42
46
|
layoutRegistry: () => layoutRegistry,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
makeErrorId: () => import_core8.makeErrorId,
|
|
48
|
+
makeFieldId: () => import_core8.makeFieldId,
|
|
49
|
+
resolveDisabled: () => import_core9.resolveDisabled,
|
|
50
|
+
resolveOptions: () => import_core9.resolveOptions,
|
|
51
|
+
resolveReadOnly: () => import_core9.resolveReadOnly,
|
|
46
52
|
useDynamicForm: () => useDynamicForm,
|
|
47
53
|
useFieldRegistry: () => useFieldRegistry,
|
|
48
|
-
validateField: () =>
|
|
49
|
-
validateFieldAsync: () =>
|
|
50
|
-
validateFields: () =>
|
|
51
|
-
validateFieldsAsync: () =>
|
|
52
|
-
validators: () =>
|
|
54
|
+
validateField: () => import_core9.validateField,
|
|
55
|
+
validateFieldAsync: () => import_core9.validateFieldAsync,
|
|
56
|
+
validateFields: () => import_core9.validateFields,
|
|
57
|
+
validateFieldsAsync: () => import_core9.validateFieldsAsync,
|
|
58
|
+
validators: () => import_core9.validators
|
|
53
59
|
});
|
|
54
60
|
module.exports = __toCommonJS(index_exports);
|
|
55
61
|
|
|
@@ -166,6 +172,7 @@ layoutRegistry.register("responsive", ({ children, config }) => {
|
|
|
166
172
|
});
|
|
167
173
|
|
|
168
174
|
// src/components/DynamicInput.tsx
|
|
175
|
+
var import_core2 = require("@dynamic-field-kit/core");
|
|
169
176
|
var import_react3 = __toESM(require("react"));
|
|
170
177
|
|
|
171
178
|
// src/defaultRenderers.tsx
|
|
@@ -491,56 +498,42 @@ function useFieldRegistry() {
|
|
|
491
498
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
492
499
|
var DynamicInputInner = ({
|
|
493
500
|
type,
|
|
494
|
-
value,
|
|
495
501
|
onChange,
|
|
496
502
|
onBlur,
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
description,
|
|
501
|
-
disabled,
|
|
502
|
-
readOnly,
|
|
503
|
-
required,
|
|
504
|
-
touched,
|
|
505
|
-
dirty,
|
|
506
|
-
error,
|
|
507
|
-
id,
|
|
508
|
-
ariaInvalid,
|
|
509
|
-
ariaDescribedBy,
|
|
510
|
-
ariaRequired,
|
|
511
|
-
extraProps
|
|
503
|
+
extraProps,
|
|
504
|
+
onOptionsQuery,
|
|
505
|
+
...rendererProps
|
|
512
506
|
}) => {
|
|
513
507
|
const registry = useFieldRegistry();
|
|
514
|
-
const Renderer = (0, import_react3.useMemo)(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
508
|
+
const { Renderer, isDefault } = (0, import_react3.useMemo)(() => {
|
|
509
|
+
const registered = registry.get(type);
|
|
510
|
+
return {
|
|
511
|
+
Renderer: registered ?? getDefaultRenderer(type),
|
|
512
|
+
isDefault: !registered
|
|
513
|
+
};
|
|
514
|
+
}, [registry, type]);
|
|
518
515
|
if (!Renderer) {
|
|
519
516
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
520
517
|
"Unknown field type: ",
|
|
521
518
|
type
|
|
522
519
|
] });
|
|
523
520
|
}
|
|
524
|
-
|
|
521
|
+
const { error, id } = rendererProps;
|
|
522
|
+
const control = import_react3.default.createElement(Renderer, {
|
|
525
523
|
...extraProps,
|
|
526
|
-
|
|
524
|
+
...rendererProps,
|
|
527
525
|
onValueChange: onChange,
|
|
528
526
|
onBlur,
|
|
529
|
-
|
|
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
|
|
527
|
+
onOptionsQuery
|
|
543
528
|
});
|
|
529
|
+
const firstError = Array.isArray(error) ? error[0] : error;
|
|
530
|
+
if (!isDefault || !firstError || !id) {
|
|
531
|
+
return control;
|
|
532
|
+
}
|
|
533
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
534
|
+
control,
|
|
535
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { id: (0, import_core2.makeErrorId)(id), className: "dfk-field-error", role: "alert", children: firstError })
|
|
536
|
+
] });
|
|
544
537
|
};
|
|
545
538
|
var DynamicInput = /* @__PURE__ */ import_react3.default.memo(
|
|
546
539
|
DynamicInputInner
|
|
@@ -548,16 +541,16 @@ var DynamicInput = /* @__PURE__ */ import_react3.default.memo(
|
|
|
548
541
|
var DynamicInput_default = DynamicInput;
|
|
549
542
|
|
|
550
543
|
// src/components/FieldInput.tsx
|
|
551
|
-
var
|
|
544
|
+
var import_core5 = require("@dynamic-field-kit/core");
|
|
552
545
|
var import_react6 = __toESM(require("react"));
|
|
553
546
|
|
|
554
547
|
// src/components/FieldGroupInput.tsx
|
|
555
|
-
var
|
|
548
|
+
var import_core4 = require("@dynamic-field-kit/core");
|
|
556
549
|
var import_react5 = require("react");
|
|
557
550
|
|
|
558
551
|
// src/components/MultiFieldInput.tsx
|
|
559
|
-
var
|
|
560
|
-
var import_react4 = require("react");
|
|
552
|
+
var import_core3 = require("@dynamic-field-kit/core");
|
|
553
|
+
var import_react4 = __toESM(require("react"));
|
|
561
554
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
562
555
|
function resolveLayout(layout) {
|
|
563
556
|
if (!layout) {
|
|
@@ -568,25 +561,46 @@ function resolveLayout(layout) {
|
|
|
568
561
|
}
|
|
569
562
|
return { type: layout.type, config: layout };
|
|
570
563
|
}
|
|
571
|
-
var
|
|
564
|
+
var MultiFieldInputInner = ({
|
|
572
565
|
fieldDescriptions,
|
|
573
566
|
properties,
|
|
574
567
|
onChange,
|
|
575
568
|
layout,
|
|
569
|
+
idPrefix,
|
|
570
|
+
initialProperties,
|
|
576
571
|
rootData,
|
|
577
572
|
onValidityChange,
|
|
578
|
-
onBlurField
|
|
579
|
-
|
|
573
|
+
onBlurField,
|
|
574
|
+
touched: touchedProp,
|
|
575
|
+
errors: errorsProp,
|
|
576
|
+
onTouchedChange,
|
|
577
|
+
form
|
|
578
|
+
}, ref) => {
|
|
579
|
+
const effectiveProperties = properties ?? form?.data;
|
|
580
|
+
const effectiveOnChange = onChange ?? form?.handleChange;
|
|
581
|
+
const effectiveOnBlurField = onBlurField ?? form?.handleBlur;
|
|
582
|
+
const controlledTouched = touchedProp ?? form?.touched;
|
|
583
|
+
const effectiveErrors = errorsProp ?? form?.errors;
|
|
580
584
|
const [data, setData] = (0, import_react4.useState)({});
|
|
581
|
-
const [
|
|
582
|
-
|
|
585
|
+
const [internalTouched, setInternalTouched] = (0, import_react4.useState)({});
|
|
586
|
+
const fallbackBaselineRef = (0, import_react4.useRef)(
|
|
587
|
+
effectiveProperties
|
|
583
588
|
);
|
|
584
|
-
|
|
589
|
+
if (fallbackBaselineRef.current === void 0) {
|
|
590
|
+
fallbackBaselineRef.current = effectiveProperties;
|
|
591
|
+
}
|
|
592
|
+
const baseline = initialProperties ?? form?.baselineValues ?? fallbackBaselineRef.current ?? {};
|
|
593
|
+
const autoId = (0, import_react4.useId)().replace(/[^a-zA-Z0-9]/g, "");
|
|
594
|
+
const effectiveIdPrefix = idPrefix ?? `dfk-${autoId}`;
|
|
595
|
+
const isTouchedControlled = controlledTouched !== void 0;
|
|
596
|
+
const effectiveTouched = controlledTouched ?? internalTouched;
|
|
585
597
|
(0, import_react4.useEffect)(() => {
|
|
586
|
-
if (
|
|
587
|
-
setData(
|
|
598
|
+
if (effectiveProperties) {
|
|
599
|
+
setData(
|
|
600
|
+
(0, import_core3.applyComputedValues)(fieldDescriptions, effectiveProperties, rootData)
|
|
601
|
+
);
|
|
588
602
|
}
|
|
589
|
-
}, [
|
|
603
|
+
}, [effectiveProperties]);
|
|
590
604
|
const effectiveRoot = rootData ?? data;
|
|
591
605
|
const visibleFields = (0, import_react4.useMemo)(
|
|
592
606
|
() => fieldDescriptions.filter(
|
|
@@ -596,24 +610,30 @@ var MultiFieldInput = ({
|
|
|
596
610
|
);
|
|
597
611
|
const dataRef = (0, import_react4.useRef)(data);
|
|
598
612
|
dataRef.current = data;
|
|
599
|
-
const onChangeRef = (0, import_react4.useRef)(
|
|
600
|
-
onChangeRef.current =
|
|
613
|
+
const onChangeRef = (0, import_react4.useRef)(effectiveOnChange);
|
|
614
|
+
onChangeRef.current = effectiveOnChange;
|
|
601
615
|
const fieldDescriptionsRef = (0, import_react4.useRef)(fieldDescriptions);
|
|
602
616
|
fieldDescriptionsRef.current = fieldDescriptions;
|
|
603
617
|
const rootDataRef = (0, import_react4.useRef)(rootData);
|
|
604
618
|
rootDataRef.current = rootData;
|
|
605
619
|
const onValidityChangeRef = (0, import_react4.useRef)(onValidityChange);
|
|
606
620
|
onValidityChangeRef.current = onValidityChange;
|
|
607
|
-
const onBlurFieldRef = (0, import_react4.useRef)(
|
|
608
|
-
onBlurFieldRef.current =
|
|
621
|
+
const onBlurFieldRef = (0, import_react4.useRef)(effectiveOnBlurField);
|
|
622
|
+
onBlurFieldRef.current = effectiveOnBlurField;
|
|
623
|
+
const onTouchedChangeRef = (0, import_react4.useRef)(onTouchedChange);
|
|
624
|
+
onTouchedChangeRef.current = onTouchedChange;
|
|
625
|
+
const isTouchedControlledRef = (0, import_react4.useRef)(isTouchedControlled);
|
|
626
|
+
isTouchedControlledRef.current = isTouchedControlled;
|
|
627
|
+
const effectiveTouchedRef = (0, import_react4.useRef)(effectiveTouched);
|
|
628
|
+
effectiveTouchedRef.current = effectiveTouched;
|
|
609
629
|
(0, import_react4.useEffect)(() => {
|
|
610
630
|
onValidityChangeRef.current?.(
|
|
611
|
-
(0,
|
|
631
|
+
(0, import_core3.validateFields)(fieldDescriptions, data, rootData)
|
|
612
632
|
);
|
|
613
633
|
}, [data, fieldDescriptions, rootData]);
|
|
614
634
|
const handleValueChangeField = (0, import_react4.useCallback)((value, key) => {
|
|
615
635
|
const merged = { ...dataRef.current, [key]: value };
|
|
616
|
-
const next = (0,
|
|
636
|
+
const next = (0, import_core3.applyComputedValues)(
|
|
617
637
|
fieldDescriptionsRef.current,
|
|
618
638
|
merged,
|
|
619
639
|
rootDataRef.current
|
|
@@ -622,10 +642,39 @@ var MultiFieldInput = ({
|
|
|
622
642
|
setData(next);
|
|
623
643
|
onChangeRef.current?.(next);
|
|
624
644
|
}, []);
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
645
|
+
const markTouched = (0, import_react4.useCallback)((key, isTouched) => {
|
|
646
|
+
const current = effectiveTouchedRef.current;
|
|
647
|
+
if (Boolean(current[key]) === isTouched) {
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const next = { ...current, [key]: isTouched };
|
|
651
|
+
if (!isTouchedControlledRef.current) {
|
|
652
|
+
effectiveTouchedRef.current = next;
|
|
653
|
+
setInternalTouched(next);
|
|
654
|
+
}
|
|
655
|
+
onTouchedChangeRef.current?.(next);
|
|
628
656
|
}, []);
|
|
657
|
+
const handleBlurField = (0, import_react4.useCallback)(
|
|
658
|
+
(key) => {
|
|
659
|
+
markTouched(key, true);
|
|
660
|
+
onBlurFieldRef.current?.(key);
|
|
661
|
+
},
|
|
662
|
+
[markTouched]
|
|
663
|
+
);
|
|
664
|
+
(0, import_react4.useImperativeHandle)(
|
|
665
|
+
ref,
|
|
666
|
+
() => ({
|
|
667
|
+
resetTouched: () => {
|
|
668
|
+
if (!isTouchedControlledRef.current) {
|
|
669
|
+
effectiveTouchedRef.current = {};
|
|
670
|
+
}
|
|
671
|
+
setInternalTouched({});
|
|
672
|
+
},
|
|
673
|
+
setFieldTouched: (fieldName, isTouched = true) => markTouched(fieldName, isTouched),
|
|
674
|
+
getTouched: () => effectiveTouchedRef.current
|
|
675
|
+
}),
|
|
676
|
+
[markTouched]
|
|
677
|
+
);
|
|
629
678
|
const { type, config } = resolveLayout(layout);
|
|
630
679
|
const Layout = layoutRegistry.get(type);
|
|
631
680
|
if (!Layout) {
|
|
@@ -637,22 +686,31 @@ var MultiFieldInput = ({
|
|
|
637
686
|
fieldDescription: f,
|
|
638
687
|
renderInfos: data,
|
|
639
688
|
rootData: effectiveRoot,
|
|
640
|
-
|
|
641
|
-
|
|
689
|
+
idPrefix: effectiveIdPrefix,
|
|
690
|
+
touched: Boolean(effectiveTouched[f.name]),
|
|
691
|
+
touchedMap: (0, import_core3.isFieldGroup)(f) ? effectiveTouched : void 0,
|
|
692
|
+
errors: effectiveErrors,
|
|
693
|
+
dirty: !Object.is(data[f.name], baseline[f.name]),
|
|
642
694
|
onBlurField: handleBlurField,
|
|
643
695
|
onValueChangeField: handleValueChangeField
|
|
644
696
|
},
|
|
645
697
|
f.name
|
|
646
698
|
)) });
|
|
647
699
|
};
|
|
700
|
+
var MultiFieldInput = /* @__PURE__ */ import_react4.default.forwardRef(MultiFieldInputInner);
|
|
701
|
+
MultiFieldInput.displayName = "MultiFieldInput";
|
|
648
702
|
var MultiFieldInput_default = MultiFieldInput;
|
|
649
703
|
|
|
650
704
|
// src/components/FieldGroupInput.tsx
|
|
651
705
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
706
|
+
var EMPTY_TOUCHED = Object.freeze({});
|
|
652
707
|
var FieldGroupInput = ({
|
|
653
708
|
fieldDescription,
|
|
654
709
|
items,
|
|
655
710
|
rootData,
|
|
711
|
+
errors,
|
|
712
|
+
touched,
|
|
713
|
+
onBlurField,
|
|
656
714
|
onChange
|
|
657
715
|
}) => {
|
|
658
716
|
const {
|
|
@@ -666,6 +724,14 @@ var FieldGroupInput = ({
|
|
|
666
724
|
const addText = addLabel ?? "Add";
|
|
667
725
|
const removeText = removeLabel ?? "Remove";
|
|
668
726
|
const groupName = label ?? fieldDescription.name;
|
|
727
|
+
const errorsByItem = (0, import_react5.useMemo)(
|
|
728
|
+
() => (0, import_core4.indexGroupPathMap)(errors, fieldDescription.name),
|
|
729
|
+
[errors, fieldDescription.name]
|
|
730
|
+
);
|
|
731
|
+
const touchedByItem = (0, import_react5.useMemo)(
|
|
732
|
+
() => (0, import_core4.indexGroupPathMap)(touched, fieldDescription.name),
|
|
733
|
+
[touched, fieldDescription.name]
|
|
734
|
+
);
|
|
669
735
|
const handleItemChange = (0, import_react5.useCallback)(
|
|
670
736
|
(index, next) => {
|
|
671
737
|
const nextItems = items.slice();
|
|
@@ -675,14 +741,14 @@ var FieldGroupInput = ({
|
|
|
675
741
|
[items, onChange]
|
|
676
742
|
);
|
|
677
743
|
const handleAdd = (0, import_react5.useCallback)(() => {
|
|
678
|
-
if (!(0,
|
|
744
|
+
if (!(0, import_core4.canAddGroupItem)(fieldDescription, items)) {
|
|
679
745
|
return;
|
|
680
746
|
}
|
|
681
|
-
onChange([...items, (0,
|
|
747
|
+
onChange([...items, (0, import_core4.createGroupItem)(fieldDescription)]);
|
|
682
748
|
}, [fieldDescription, items, onChange]);
|
|
683
749
|
const handleRemove = (0, import_react5.useCallback)(
|
|
684
750
|
(index) => {
|
|
685
|
-
if (!(0,
|
|
751
|
+
if (!(0, import_core4.canRemoveGroupItem)(fieldDescription, items)) {
|
|
686
752
|
return;
|
|
687
753
|
}
|
|
688
754
|
onChange(items.filter((_, i) => i !== index));
|
|
@@ -702,6 +768,9 @@ var FieldGroupInput = ({
|
|
|
702
768
|
fieldDescriptions: fields,
|
|
703
769
|
properties: item,
|
|
704
770
|
rootData,
|
|
771
|
+
errors: errorsByItem?.[index],
|
|
772
|
+
touched: touched === void 0 ? void 0 : touchedByItem?.[index] ?? EMPTY_TOUCHED,
|
|
773
|
+
onBlurField: (key) => onBlurField?.(`${fieldDescription.name}[${index}].${key}`),
|
|
705
774
|
onChange: (next) => handleItemChange(index, next)
|
|
706
775
|
}
|
|
707
776
|
) }),
|
|
@@ -711,7 +780,7 @@ var FieldGroupInput = ({
|
|
|
711
780
|
type: "button",
|
|
712
781
|
"aria-label": `${removeText} ${groupName} ${index + 1}`,
|
|
713
782
|
onClick: () => handleRemove(index),
|
|
714
|
-
disabled: !(0,
|
|
783
|
+
disabled: !(0, import_core4.canRemoveGroupItem)(fieldDescription, items),
|
|
715
784
|
children: removeText
|
|
716
785
|
}
|
|
717
786
|
)
|
|
@@ -725,7 +794,7 @@ var FieldGroupInput = ({
|
|
|
725
794
|
type: "button",
|
|
726
795
|
"aria-label": `${addText} ${groupName}`,
|
|
727
796
|
onClick: handleAdd,
|
|
728
|
-
disabled: !(0,
|
|
797
|
+
disabled: !(0, import_core4.canAddGroupItem)(fieldDescription, items),
|
|
729
798
|
children: addText
|
|
730
799
|
}
|
|
731
800
|
)
|
|
@@ -739,12 +808,50 @@ var FieldInputInner = ({
|
|
|
739
808
|
fieldDescription,
|
|
740
809
|
renderInfos,
|
|
741
810
|
rootData,
|
|
811
|
+
idPrefix = "dfk-field",
|
|
742
812
|
touched,
|
|
813
|
+
touchedMap,
|
|
743
814
|
dirty,
|
|
815
|
+
errors,
|
|
744
816
|
onBlurField,
|
|
745
817
|
onValueChangeField
|
|
746
818
|
}) => {
|
|
747
|
-
const { name,
|
|
819
|
+
const { name, fields } = fieldDescription;
|
|
820
|
+
const isAsync = (0, import_core5.isAsyncOptions)(fieldDescription);
|
|
821
|
+
const [optionsState, setOptionsState] = (0, import_react6.useState)(
|
|
822
|
+
isAsync ? { status: "idle" } : void 0
|
|
823
|
+
);
|
|
824
|
+
const loaderRef = (0, import_react6.useRef)(void 0);
|
|
825
|
+
const fieldRef = (0, import_react6.useRef)(fieldDescription);
|
|
826
|
+
fieldRef.current = fieldDescription;
|
|
827
|
+
const ensureLoader = (0, import_react6.useCallback)(() => {
|
|
828
|
+
if (!(0, import_core5.isAsyncOptions)(fieldRef.current)) {
|
|
829
|
+
return void 0;
|
|
830
|
+
}
|
|
831
|
+
if (!loaderRef.current) {
|
|
832
|
+
loaderRef.current = (0, import_core5.createOptionsLoader)(
|
|
833
|
+
fieldRef.current,
|
|
834
|
+
setOptionsState
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
return loaderRef.current;
|
|
838
|
+
}, []);
|
|
839
|
+
(0, import_react6.useEffect)(
|
|
840
|
+
() => () => {
|
|
841
|
+
loaderRef.current?.dispose();
|
|
842
|
+
loaderRef.current = void 0;
|
|
843
|
+
},
|
|
844
|
+
[]
|
|
845
|
+
);
|
|
846
|
+
(0, import_react6.useEffect)(() => {
|
|
847
|
+
ensureLoader()?.update(renderInfos, rootData);
|
|
848
|
+
});
|
|
849
|
+
const handleOptionsQuery = (0, import_react6.useCallback)(
|
|
850
|
+
(query) => {
|
|
851
|
+
ensureLoader()?.setQuery(query);
|
|
852
|
+
},
|
|
853
|
+
[ensureLoader]
|
|
854
|
+
);
|
|
748
855
|
const handleChange = (0, import_react6.useCallback)(
|
|
749
856
|
(v) => onValueChangeField(v, name),
|
|
750
857
|
[onValueChangeField, name]
|
|
@@ -761,51 +868,40 @@ var FieldInputInner = ({
|
|
|
761
868
|
fieldDescription,
|
|
762
869
|
items,
|
|
763
870
|
rootData,
|
|
871
|
+
errors,
|
|
872
|
+
touched: touchedMap,
|
|
873
|
+
onBlurField,
|
|
764
874
|
onChange: handleChange
|
|
765
875
|
}
|
|
766
876
|
);
|
|
767
877
|
}
|
|
768
|
-
const
|
|
878
|
+
const rendererProps = (0, import_core5.buildFieldRendererProps)({
|
|
769
879
|
fieldDescription,
|
|
770
|
-
renderInfos,
|
|
771
|
-
rootData
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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}`;
|
|
880
|
+
data: renderInfos,
|
|
881
|
+
rootData,
|
|
882
|
+
id: (0, import_core5.makeFieldId)(fieldDescription, idPrefix),
|
|
883
|
+
touched,
|
|
884
|
+
dirty,
|
|
885
|
+
validationErrors: errors === void 0 ? void 0 : errors[name] ?? [],
|
|
886
|
+
optionsState
|
|
887
|
+
});
|
|
782
888
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
783
889
|
DynamicInput_default,
|
|
784
890
|
{
|
|
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,
|
|
891
|
+
...rendererProps,
|
|
892
|
+
description: rendererProps.description,
|
|
801
893
|
onChange: handleChange,
|
|
802
|
-
onBlur: handleBlur
|
|
894
|
+
onBlur: handleBlur,
|
|
895
|
+
onOptionsQuery: isAsync ? handleOptionsQuery : void 0
|
|
803
896
|
}
|
|
804
897
|
);
|
|
805
898
|
};
|
|
806
899
|
var FieldInput = /* @__PURE__ */ import_react6.default.memo(FieldInputInner, (prev, next) => {
|
|
807
900
|
const name = prev.fieldDescription.name;
|
|
808
|
-
|
|
901
|
+
if ((0, import_core5.isAsyncOptions)(prev.fieldDescription) && prev.renderInfos !== next.renderInfos) {
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
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
905
|
});
|
|
810
906
|
var FieldInput_default = FieldInput;
|
|
811
907
|
|
|
@@ -1010,38 +1106,134 @@ var DynamicFormDevTools = ({
|
|
|
1010
1106
|
};
|
|
1011
1107
|
|
|
1012
1108
|
// src/useDynamicForm.ts
|
|
1013
|
-
var
|
|
1109
|
+
var import_core6 = require("@dynamic-field-kit/core");
|
|
1014
1110
|
var import_react8 = require("react");
|
|
1111
|
+
function sameStringMap(left, right) {
|
|
1112
|
+
const keys = Object.keys(left);
|
|
1113
|
+
return keys.length === Object.keys(right).length && keys.every(
|
|
1114
|
+
(key) => left[key]?.length === right[key]?.length && left[key]?.every((value, index) => value === right[key]?.[index])
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
function sameValidationResult(left, right) {
|
|
1118
|
+
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);
|
|
1119
|
+
}
|
|
1015
1120
|
function useDynamicForm({
|
|
1016
1121
|
fields,
|
|
1017
1122
|
initialValues = {},
|
|
1018
1123
|
validateOnBlur = true,
|
|
1019
|
-
validateOnChange = false
|
|
1124
|
+
validateOnChange = false,
|
|
1125
|
+
messages
|
|
1020
1126
|
}) {
|
|
1127
|
+
const validationContextRef = (0, import_react8.useRef)({
|
|
1128
|
+
t: (0, import_core6.createMessageResolver)(messages)
|
|
1129
|
+
});
|
|
1130
|
+
validationContextRef.current.t = (0, import_core6.createMessageResolver)(messages);
|
|
1021
1131
|
const [data, setData] = (0, import_react8.useState)(
|
|
1022
|
-
() => (0,
|
|
1132
|
+
() => (0, import_core6.applyComputedValues)(fields, initialValues)
|
|
1023
1133
|
);
|
|
1024
1134
|
const [errors, setErrors] = (0, import_react8.useState)({});
|
|
1025
1135
|
const [isDirty, setIsDirty] = (0, import_react8.useState)(false);
|
|
1026
1136
|
const [touched, setTouched] = (0, import_react8.useState)({});
|
|
1027
1137
|
const [isSubmitting, setIsSubmitting] = (0, import_react8.useState)(false);
|
|
1028
1138
|
const [isSubmitted, setIsSubmitted] = (0, import_react8.useState)(false);
|
|
1139
|
+
const [validationResult, setValidationResult] = (0, import_react8.useState)(
|
|
1140
|
+
() => (0, import_core6.validateFields)(fields, data, void 0, validationContextRef.current)
|
|
1141
|
+
);
|
|
1142
|
+
const [isValidating, setIsValidating] = (0, import_react8.useState)(false);
|
|
1143
|
+
const validationRunRef = (0, import_react8.useRef)(0);
|
|
1144
|
+
const validationAbortRef = (0, import_react8.useRef)(void 0);
|
|
1145
|
+
const submitRunRef = (0, import_react8.useRef)(0);
|
|
1146
|
+
const submitAbortRef = (0, import_react8.useRef)(void 0);
|
|
1147
|
+
const dataRef = (0, import_react8.useRef)(data);
|
|
1148
|
+
dataRef.current = data;
|
|
1149
|
+
const baselineRef = (0, import_react8.useRef)(dataRef.current);
|
|
1150
|
+
const [baselineValues, setBaselineValues] = (0, import_react8.useState)(
|
|
1151
|
+
() => baselineRef.current
|
|
1152
|
+
);
|
|
1153
|
+
const commitSyncResult = (0, import_react8.useCallback)((res) => {
|
|
1154
|
+
setValidationResult(
|
|
1155
|
+
(previous) => sameValidationResult(previous, res) ? previous : res
|
|
1156
|
+
);
|
|
1157
|
+
return res.valid;
|
|
1158
|
+
}, []);
|
|
1159
|
+
const lastValidatedRef = (0, import_react8.useRef)(void 0);
|
|
1160
|
+
const fieldsRef = (0, import_react8.useRef)(fields);
|
|
1161
|
+
if (fieldsRef.current !== fields) {
|
|
1162
|
+
fieldsRef.current = fields;
|
|
1163
|
+
lastValidatedRef.current = void 0;
|
|
1164
|
+
}
|
|
1165
|
+
(0, import_react8.useEffect)(() => {
|
|
1166
|
+
if (lastValidatedRef.current === data) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
lastValidatedRef.current = data;
|
|
1170
|
+
commitSyncResult(
|
|
1171
|
+
(0, import_core6.validateFields)(fields, data, void 0, validationContextRef.current)
|
|
1172
|
+
);
|
|
1173
|
+
}, [fields, data, commitSyncResult]);
|
|
1174
|
+
(0, import_react8.useEffect)(
|
|
1175
|
+
() => () => {
|
|
1176
|
+
validationAbortRef.current?.abort();
|
|
1177
|
+
submitAbortRef.current?.abort();
|
|
1178
|
+
},
|
|
1179
|
+
[]
|
|
1180
|
+
);
|
|
1029
1181
|
const validate = (0, import_react8.useCallback)(() => {
|
|
1030
|
-
const res = (0,
|
|
1182
|
+
const res = (0, import_core6.validateFields)(
|
|
1183
|
+
fields,
|
|
1184
|
+
data,
|
|
1185
|
+
void 0,
|
|
1186
|
+
validationContextRef.current
|
|
1187
|
+
);
|
|
1031
1188
|
setErrors(res.errors);
|
|
1032
|
-
return res
|
|
1189
|
+
return commitSyncResult(res);
|
|
1190
|
+
}, [fields, data, commitSyncResult]);
|
|
1191
|
+
const validateAsync = (0, import_react8.useCallback)(async () => {
|
|
1192
|
+
const run = ++validationRunRef.current;
|
|
1193
|
+
validationAbortRef.current?.abort();
|
|
1194
|
+
const controller = new AbortController();
|
|
1195
|
+
validationAbortRef.current = controller;
|
|
1196
|
+
const snapshot = data;
|
|
1197
|
+
setIsValidating(true);
|
|
1198
|
+
try {
|
|
1199
|
+
const res = await (0, import_core6.validateFieldsAsync)(fields, snapshot, snapshot, {
|
|
1200
|
+
...validationContextRef.current,
|
|
1201
|
+
signal: controller.signal
|
|
1202
|
+
});
|
|
1203
|
+
if (run !== validationRunRef.current || dataRef.current !== snapshot) {
|
|
1204
|
+
return res.valid;
|
|
1205
|
+
}
|
|
1206
|
+
setErrors(res.errors);
|
|
1207
|
+
setValidationResult(res);
|
|
1208
|
+
return res.valid;
|
|
1209
|
+
} finally {
|
|
1210
|
+
if (run === validationRunRef.current) {
|
|
1211
|
+
setIsValidating(false);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1033
1214
|
}, [fields, data]);
|
|
1034
1215
|
const handleChange = (0, import_react8.useCallback)(
|
|
1035
1216
|
(newData) => {
|
|
1036
|
-
const next = (0,
|
|
1217
|
+
const next = (0, import_core6.applyComputedValues)(fields, newData);
|
|
1037
1218
|
setData(next);
|
|
1219
|
+
dataRef.current = next;
|
|
1038
1220
|
setIsDirty(true);
|
|
1221
|
+
validationAbortRef.current?.abort();
|
|
1222
|
+
validationRunRef.current += 1;
|
|
1223
|
+
setIsValidating(false);
|
|
1224
|
+
lastValidatedRef.current = next;
|
|
1225
|
+
const res = (0, import_core6.validateFields)(
|
|
1226
|
+
fields,
|
|
1227
|
+
next,
|
|
1228
|
+
void 0,
|
|
1229
|
+
validationContextRef.current
|
|
1230
|
+
);
|
|
1231
|
+
commitSyncResult(res);
|
|
1039
1232
|
if (validateOnChange) {
|
|
1040
|
-
const res = (0, import_core5.validateFields)(fields, next);
|
|
1041
1233
|
setErrors(res.errors);
|
|
1042
1234
|
}
|
|
1043
1235
|
},
|
|
1044
|
-
[fields, validateOnChange]
|
|
1236
|
+
[fields, validateOnChange, commitSyncResult]
|
|
1045
1237
|
);
|
|
1046
1238
|
const setFieldValue = (0, import_react8.useCallback)(
|
|
1047
1239
|
(name, value) => {
|
|
@@ -1052,26 +1244,57 @@ function useDynamicForm({
|
|
|
1052
1244
|
const setFieldTouched = (0, import_react8.useCallback)((name, isTouched = true) => {
|
|
1053
1245
|
setTouched((prev) => ({ ...prev, [name]: isTouched }));
|
|
1054
1246
|
}, []);
|
|
1247
|
+
const touchAll = (0, import_react8.useCallback)(() => {
|
|
1248
|
+
setTouched(
|
|
1249
|
+
Object.fromEntries(
|
|
1250
|
+
(0, import_core6.collectFieldPaths)(fields, data).map((path) => [path, true])
|
|
1251
|
+
)
|
|
1252
|
+
);
|
|
1253
|
+
}, [fields, data]);
|
|
1254
|
+
const resetTouched = (0, import_react8.useCallback)(() => setTouched({}), []);
|
|
1255
|
+
const getDirtyValues = (0, import_react8.useCallback)(() => {
|
|
1256
|
+
const baseline = baselineRef.current;
|
|
1257
|
+
const current = dataRef.current;
|
|
1258
|
+
const dirty = {};
|
|
1259
|
+
for (const key of Object.keys(current)) {
|
|
1260
|
+
if (!Object.is(current[key], baseline[key])) {
|
|
1261
|
+
dirty[key] = current[key];
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
return dirty;
|
|
1265
|
+
}, []);
|
|
1055
1266
|
const handleBlur = (0, import_react8.useCallback)(
|
|
1056
1267
|
(fieldName) => {
|
|
1057
1268
|
setFieldTouched(fieldName, true);
|
|
1058
1269
|
if (validateOnBlur) {
|
|
1059
|
-
const res = (0,
|
|
1270
|
+
const res = (0, import_core6.validateFields)(
|
|
1271
|
+
fields,
|
|
1272
|
+
data,
|
|
1273
|
+
void 0,
|
|
1274
|
+
validationContextRef.current
|
|
1275
|
+
);
|
|
1060
1276
|
setErrors(res.errors);
|
|
1277
|
+
commitSyncResult(res);
|
|
1061
1278
|
}
|
|
1062
1279
|
},
|
|
1063
|
-
[fields, data, validateOnBlur, setFieldTouched]
|
|
1280
|
+
[fields, data, validateOnBlur, setFieldTouched, commitSyncResult]
|
|
1064
1281
|
);
|
|
1065
1282
|
const reset = (0, import_react8.useCallback)(
|
|
1066
1283
|
(newValues) => {
|
|
1067
1284
|
const seed = newValues ?? initialValues;
|
|
1068
|
-
const next = (0,
|
|
1285
|
+
const next = (0, import_core6.applyComputedValues)(fields, seed);
|
|
1069
1286
|
setData(next);
|
|
1287
|
+
dataRef.current = next;
|
|
1288
|
+
baselineRef.current = next;
|
|
1289
|
+
setBaselineValues(next);
|
|
1070
1290
|
setErrors({});
|
|
1071
1291
|
setIsDirty(false);
|
|
1072
1292
|
setTouched({});
|
|
1073
1293
|
setIsSubmitting(false);
|
|
1074
1294
|
setIsSubmitted(false);
|
|
1295
|
+
validationAbortRef.current?.abort();
|
|
1296
|
+
validationRunRef.current += 1;
|
|
1297
|
+
setIsValidating(false);
|
|
1075
1298
|
},
|
|
1076
1299
|
[fields, initialValues]
|
|
1077
1300
|
);
|
|
@@ -1081,56 +1304,103 @@ function useDynamicForm({
|
|
|
1081
1304
|
e.preventDefault();
|
|
1082
1305
|
}
|
|
1083
1306
|
setIsSubmitting(true);
|
|
1307
|
+
const submitRun = ++submitRunRef.current;
|
|
1084
1308
|
try {
|
|
1085
|
-
|
|
1086
|
-
|
|
1309
|
+
touchAll();
|
|
1310
|
+
const run = ++validationRunRef.current;
|
|
1311
|
+
validationAbortRef.current?.abort();
|
|
1312
|
+
submitAbortRef.current?.abort();
|
|
1313
|
+
const controller = new AbortController();
|
|
1314
|
+
submitAbortRef.current = controller;
|
|
1315
|
+
const snapshot = data;
|
|
1316
|
+
setIsValidating(true);
|
|
1317
|
+
const res = await (0, import_core6.validateFieldsAsync)(fields, snapshot, snapshot, {
|
|
1318
|
+
...validationContextRef.current,
|
|
1319
|
+
signal: controller.signal
|
|
1320
|
+
});
|
|
1321
|
+
if (submitRun !== submitRunRef.current) {
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
if (dataRef.current === snapshot && run === validationRunRef.current) {
|
|
1325
|
+
setErrors(res.errors);
|
|
1326
|
+
setValidationResult(res);
|
|
1327
|
+
} else {
|
|
1328
|
+
const live = (0, import_core6.validateFields)(
|
|
1329
|
+
fields,
|
|
1330
|
+
dataRef.current,
|
|
1331
|
+
void 0,
|
|
1332
|
+
validationContextRef.current
|
|
1333
|
+
);
|
|
1334
|
+
setErrors(live.errors);
|
|
1335
|
+
commitSyncResult(live);
|
|
1336
|
+
}
|
|
1087
1337
|
setIsSubmitted(true);
|
|
1088
1338
|
if (res.valid) {
|
|
1089
|
-
await onValid(
|
|
1339
|
+
await onValid(snapshot);
|
|
1090
1340
|
} else if (onInvalid) {
|
|
1091
1341
|
onInvalid(res.errors);
|
|
1092
1342
|
}
|
|
1093
1343
|
} finally {
|
|
1344
|
+
if (submitRun === submitRunRef.current) {
|
|
1345
|
+
setIsValidating(false);
|
|
1346
|
+
}
|
|
1094
1347
|
setIsSubmitting(false);
|
|
1095
1348
|
}
|
|
1096
1349
|
},
|
|
1097
|
-
[fields, data]
|
|
1350
|
+
[fields, data, touchAll, commitSyncResult]
|
|
1098
1351
|
);
|
|
1099
|
-
const isValid = Object.keys(errors).length === 0;
|
|
1100
1352
|
return {
|
|
1101
1353
|
data,
|
|
1102
1354
|
errors,
|
|
1103
|
-
isValid,
|
|
1355
|
+
isValid: validationResult.valid,
|
|
1356
|
+
isValidating,
|
|
1357
|
+
// Matches Vue and Angular: a run still in flight is not complete, whatever
|
|
1358
|
+
// the last finished pass concluded.
|
|
1359
|
+
isValidationComplete: validationResult.complete && !isValidating,
|
|
1360
|
+
validationStatus: isValidating ? "pending" : validationResult.status,
|
|
1104
1361
|
isDirty,
|
|
1362
|
+
baselineValues,
|
|
1363
|
+
getDirtyValues,
|
|
1105
1364
|
isSubmitting,
|
|
1106
1365
|
isSubmitted,
|
|
1107
1366
|
touched,
|
|
1108
1367
|
setData,
|
|
1109
1368
|
setFieldValue,
|
|
1110
1369
|
setFieldTouched,
|
|
1370
|
+
setTouched,
|
|
1371
|
+
touchAll,
|
|
1372
|
+
resetTouched,
|
|
1111
1373
|
handleChange,
|
|
1112
1374
|
handleBlur,
|
|
1113
1375
|
reset,
|
|
1114
1376
|
validate,
|
|
1377
|
+
validateAsync,
|
|
1115
1378
|
handleSubmit
|
|
1116
1379
|
};
|
|
1117
1380
|
}
|
|
1118
1381
|
|
|
1119
1382
|
// src/index.ts
|
|
1120
|
-
var import_core6 = require("@dynamic-field-kit/core");
|
|
1121
1383
|
var import_core7 = require("@dynamic-field-kit/core");
|
|
1384
|
+
var import_core8 = require("@dynamic-field-kit/core");
|
|
1385
|
+
var import_core9 = require("@dynamic-field-kit/core");
|
|
1122
1386
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1123
1387
|
0 && (module.exports = {
|
|
1124
1388
|
DynamicFormDevTools,
|
|
1125
1389
|
DynamicInput,
|
|
1390
|
+
FIELD_RENDERER_PROP_KEYS,
|
|
1126
1391
|
FieldInput,
|
|
1127
1392
|
FieldRegistry,
|
|
1128
1393
|
FieldRegistryProvider,
|
|
1129
1394
|
MultiFieldInput,
|
|
1395
|
+
buildFieldRendererProps,
|
|
1396
|
+
collectFieldPaths,
|
|
1130
1397
|
defaultRenderersMap,
|
|
1131
1398
|
fieldRegistry,
|
|
1132
1399
|
getDefaultRenderer,
|
|
1400
|
+
indexGroupPathMap,
|
|
1133
1401
|
layoutRegistry,
|
|
1402
|
+
makeErrorId,
|
|
1403
|
+
makeFieldId,
|
|
1134
1404
|
resolveDisabled,
|
|
1135
1405
|
resolveOptions,
|
|
1136
1406
|
resolveReadOnly,
|