@dynamic-field-kit/react 1.6.0 → 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 +58 -0
- package/README.md +101 -22
- package/dist/index.d.mts +37 -4
- package/dist/index.d.ts +37 -4
- package/dist/index.js +178 -55
- package/dist/index.mjs +191 -63
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -111,6 +111,9 @@ layoutRegistry.register("responsive", ({ children, config }) => {
|
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
// src/components/DynamicInput.tsx
|
|
114
|
+
import {
|
|
115
|
+
makeErrorId
|
|
116
|
+
} from "@dynamic-field-kit/core";
|
|
114
117
|
import React3, { useMemo } from "react";
|
|
115
118
|
|
|
116
119
|
// src/defaultRenderers.tsx
|
|
@@ -435,31 +438,45 @@ function useFieldRegistry() {
|
|
|
435
438
|
}
|
|
436
439
|
|
|
437
440
|
// src/components/DynamicInput.tsx
|
|
438
|
-
import { jsxs as jsxs3 } from "react/jsx-runtime";
|
|
441
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
439
442
|
var DynamicInputInner = ({
|
|
440
443
|
type,
|
|
441
444
|
onChange,
|
|
442
445
|
onBlur,
|
|
443
446
|
extraProps,
|
|
447
|
+
onOptionsQuery,
|
|
444
448
|
...rendererProps
|
|
445
449
|
}) => {
|
|
446
450
|
const registry = useFieldRegistry();
|
|
447
|
-
const Renderer = useMemo(
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
+
const { Renderer, isDefault } = useMemo(() => {
|
|
452
|
+
const registered = registry.get(type);
|
|
453
|
+
return {
|
|
454
|
+
Renderer: registered ?? getDefaultRenderer(type),
|
|
455
|
+
isDefault: !registered
|
|
456
|
+
};
|
|
457
|
+
}, [registry, type]);
|
|
451
458
|
if (!Renderer) {
|
|
452
459
|
return /* @__PURE__ */ jsxs3("div", { children: [
|
|
453
460
|
"Unknown field type: ",
|
|
454
461
|
type
|
|
455
462
|
] });
|
|
456
463
|
}
|
|
457
|
-
|
|
464
|
+
const { error, id } = rendererProps;
|
|
465
|
+
const control = React3.createElement(Renderer, {
|
|
458
466
|
...extraProps,
|
|
459
467
|
...rendererProps,
|
|
460
468
|
onValueChange: onChange,
|
|
461
|
-
onBlur
|
|
469
|
+
onBlur,
|
|
470
|
+
onOptionsQuery
|
|
462
471
|
});
|
|
472
|
+
const firstError = Array.isArray(error) ? error[0] : error;
|
|
473
|
+
if (!isDefault || !firstError || !id) {
|
|
474
|
+
return control;
|
|
475
|
+
}
|
|
476
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
477
|
+
control,
|
|
478
|
+
/* @__PURE__ */ jsx5("div", { id: makeErrorId(id), className: "dfk-field-error", role: "alert", children: firstError })
|
|
479
|
+
] });
|
|
463
480
|
};
|
|
464
481
|
var DynamicInput = /* @__PURE__ */ React3.memo(
|
|
465
482
|
DynamicInputInner
|
|
@@ -469,9 +486,11 @@ var DynamicInput_default = DynamicInput;
|
|
|
469
486
|
// src/components/FieldInput.tsx
|
|
470
487
|
import {
|
|
471
488
|
buildFieldRendererProps,
|
|
489
|
+
createOptionsLoader,
|
|
490
|
+
isAsyncOptions,
|
|
472
491
|
makeFieldId
|
|
473
492
|
} from "@dynamic-field-kit/core";
|
|
474
|
-
import React6, { useCallback as useCallback3 } from "react";
|
|
493
|
+
import React6, { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
|
|
475
494
|
|
|
476
495
|
// src/components/FieldGroupInput.tsx
|
|
477
496
|
import {
|
|
@@ -497,7 +516,7 @@ import React4, {
|
|
|
497
516
|
useRef,
|
|
498
517
|
useState as useState2
|
|
499
518
|
} from "react";
|
|
500
|
-
import { jsx as
|
|
519
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
501
520
|
function resolveLayout(layout) {
|
|
502
521
|
if (!layout) {
|
|
503
522
|
return { type: "column", config: {} };
|
|
@@ -513,6 +532,7 @@ var MultiFieldInputInner = ({
|
|
|
513
532
|
onChange,
|
|
514
533
|
layout,
|
|
515
534
|
idPrefix,
|
|
535
|
+
initialProperties,
|
|
516
536
|
rootData,
|
|
517
537
|
onValidityChange,
|
|
518
538
|
onBlurField,
|
|
@@ -528,7 +548,13 @@ var MultiFieldInputInner = ({
|
|
|
528
548
|
const effectiveErrors = errorsProp ?? form?.errors;
|
|
529
549
|
const [data, setData] = useState2({});
|
|
530
550
|
const [internalTouched, setInternalTouched] = useState2({});
|
|
531
|
-
const
|
|
551
|
+
const fallbackBaselineRef = useRef(
|
|
552
|
+
effectiveProperties
|
|
553
|
+
);
|
|
554
|
+
if (fallbackBaselineRef.current === void 0) {
|
|
555
|
+
fallbackBaselineRef.current = effectiveProperties;
|
|
556
|
+
}
|
|
557
|
+
const baseline = initialProperties ?? form?.baselineValues ?? fallbackBaselineRef.current ?? {};
|
|
532
558
|
const autoId = useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
533
559
|
const effectiveIdPrefix = idPrefix ?? `dfk-${autoId}`;
|
|
534
560
|
const isTouchedControlled = controlledTouched !== void 0;
|
|
@@ -619,7 +645,7 @@ var MultiFieldInputInner = ({
|
|
|
619
645
|
if (!Layout) {
|
|
620
646
|
throw new Error(`Unknown layout: ${type}`);
|
|
621
647
|
}
|
|
622
|
-
return /* @__PURE__ */
|
|
648
|
+
return /* @__PURE__ */ jsx6(Layout, { config, children: visibleFields.map((f) => /* @__PURE__ */ jsx6(
|
|
623
649
|
FieldInput_default,
|
|
624
650
|
{
|
|
625
651
|
fieldDescription: f,
|
|
@@ -629,7 +655,7 @@ var MultiFieldInputInner = ({
|
|
|
629
655
|
touched: Boolean(effectiveTouched[f.name]),
|
|
630
656
|
touchedMap: isFieldGroup(f) ? effectiveTouched : void 0,
|
|
631
657
|
errors: effectiveErrors,
|
|
632
|
-
dirty: data[f.name]
|
|
658
|
+
dirty: !Object.is(data[f.name], baseline[f.name]),
|
|
633
659
|
onBlurField: handleBlurField,
|
|
634
660
|
onValueChangeField: handleValueChangeField
|
|
635
661
|
},
|
|
@@ -641,7 +667,7 @@ MultiFieldInput.displayName = "MultiFieldInput";
|
|
|
641
667
|
var MultiFieldInput_default = MultiFieldInput;
|
|
642
668
|
|
|
643
669
|
// src/components/FieldGroupInput.tsx
|
|
644
|
-
import { jsx as
|
|
670
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
645
671
|
var EMPTY_TOUCHED = Object.freeze({});
|
|
646
672
|
var FieldGroupInput = ({
|
|
647
673
|
fieldDescription,
|
|
@@ -695,13 +721,13 @@ var FieldGroupInput = ({
|
|
|
695
721
|
[fieldDescription, items, onChange]
|
|
696
722
|
);
|
|
697
723
|
return /* @__PURE__ */ jsxs4("div", { className: fieldDescription.className, children: [
|
|
698
|
-
label && /* @__PURE__ */
|
|
724
|
+
label && /* @__PURE__ */ jsx7("div", { children: label }),
|
|
699
725
|
items.map((item, index) => /* @__PURE__ */ jsxs4(
|
|
700
726
|
"div",
|
|
701
727
|
{
|
|
702
728
|
style: { display: "flex", alignItems: "flex-start", gap: 8 },
|
|
703
729
|
children: [
|
|
704
|
-
/* @__PURE__ */
|
|
730
|
+
/* @__PURE__ */ jsx7("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx7(
|
|
705
731
|
MultiFieldInput_default,
|
|
706
732
|
{
|
|
707
733
|
fieldDescriptions: fields,
|
|
@@ -713,7 +739,7 @@ var FieldGroupInput = ({
|
|
|
713
739
|
onChange: (next) => handleItemChange(index, next)
|
|
714
740
|
}
|
|
715
741
|
) }),
|
|
716
|
-
/* @__PURE__ */
|
|
742
|
+
/* @__PURE__ */ jsx7(
|
|
717
743
|
"button",
|
|
718
744
|
{
|
|
719
745
|
type: "button",
|
|
@@ -727,7 +753,7 @@ var FieldGroupInput = ({
|
|
|
727
753
|
},
|
|
728
754
|
itemKey(item, index)
|
|
729
755
|
)),
|
|
730
|
-
/* @__PURE__ */
|
|
756
|
+
/* @__PURE__ */ jsx7(
|
|
731
757
|
"button",
|
|
732
758
|
{
|
|
733
759
|
type: "button",
|
|
@@ -742,7 +768,7 @@ var FieldGroupInput = ({
|
|
|
742
768
|
var FieldGroupInput_default = FieldGroupInput;
|
|
743
769
|
|
|
744
770
|
// src/components/FieldInput.tsx
|
|
745
|
-
import { jsx as
|
|
771
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
746
772
|
var FieldInputInner = ({
|
|
747
773
|
fieldDescription,
|
|
748
774
|
renderInfos,
|
|
@@ -756,6 +782,41 @@ var FieldInputInner = ({
|
|
|
756
782
|
onValueChangeField
|
|
757
783
|
}) => {
|
|
758
784
|
const { name, fields } = fieldDescription;
|
|
785
|
+
const isAsync = isAsyncOptions(fieldDescription);
|
|
786
|
+
const [optionsState, setOptionsState] = useState3(
|
|
787
|
+
isAsync ? { status: "idle" } : void 0
|
|
788
|
+
);
|
|
789
|
+
const loaderRef = useRef2(void 0);
|
|
790
|
+
const fieldRef = useRef2(fieldDescription);
|
|
791
|
+
fieldRef.current = fieldDescription;
|
|
792
|
+
const ensureLoader = useCallback3(() => {
|
|
793
|
+
if (!isAsyncOptions(fieldRef.current)) {
|
|
794
|
+
return void 0;
|
|
795
|
+
}
|
|
796
|
+
if (!loaderRef.current) {
|
|
797
|
+
loaderRef.current = createOptionsLoader(
|
|
798
|
+
fieldRef.current,
|
|
799
|
+
setOptionsState
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
return loaderRef.current;
|
|
803
|
+
}, []);
|
|
804
|
+
useEffect3(
|
|
805
|
+
() => () => {
|
|
806
|
+
loaderRef.current?.dispose();
|
|
807
|
+
loaderRef.current = void 0;
|
|
808
|
+
},
|
|
809
|
+
[]
|
|
810
|
+
);
|
|
811
|
+
useEffect3(() => {
|
|
812
|
+
ensureLoader()?.update(renderInfos, rootData);
|
|
813
|
+
});
|
|
814
|
+
const handleOptionsQuery = useCallback3(
|
|
815
|
+
(query) => {
|
|
816
|
+
ensureLoader()?.setQuery(query);
|
|
817
|
+
},
|
|
818
|
+
[ensureLoader]
|
|
819
|
+
);
|
|
759
820
|
const handleChange = useCallback3(
|
|
760
821
|
(v) => onValueChangeField(v, name),
|
|
761
822
|
[onValueChangeField, name]
|
|
@@ -766,7 +827,7 @@ var FieldInputInner = ({
|
|
|
766
827
|
);
|
|
767
828
|
if (fields) {
|
|
768
829
|
const items = Array.isArray(renderInfos[name]) ? renderInfos[name] : [];
|
|
769
|
-
return /* @__PURE__ */
|
|
830
|
+
return /* @__PURE__ */ jsx8(
|
|
770
831
|
FieldGroupInput_default,
|
|
771
832
|
{
|
|
772
833
|
fieldDescription,
|
|
@@ -786,27 +847,32 @@ var FieldInputInner = ({
|
|
|
786
847
|
id: makeFieldId(fieldDescription, idPrefix),
|
|
787
848
|
touched,
|
|
788
849
|
dirty,
|
|
789
|
-
validationErrors: errors === void 0 ? void 0 : errors[name] ?? []
|
|
850
|
+
validationErrors: errors === void 0 ? void 0 : errors[name] ?? [],
|
|
851
|
+
optionsState
|
|
790
852
|
});
|
|
791
|
-
return /* @__PURE__ */
|
|
853
|
+
return /* @__PURE__ */ jsx8(
|
|
792
854
|
DynamicInput_default,
|
|
793
855
|
{
|
|
794
856
|
...rendererProps,
|
|
795
857
|
description: rendererProps.description,
|
|
796
858
|
onChange: handleChange,
|
|
797
|
-
onBlur: handleBlur
|
|
859
|
+
onBlur: handleBlur,
|
|
860
|
+
onOptionsQuery: isAsync ? handleOptionsQuery : void 0
|
|
798
861
|
}
|
|
799
862
|
);
|
|
800
863
|
};
|
|
801
864
|
var FieldInput = /* @__PURE__ */ React6.memo(FieldInputInner, (prev, next) => {
|
|
802
865
|
const name = prev.fieldDescription.name;
|
|
866
|
+
if (isAsyncOptions(prev.fieldDescription) && prev.renderInfos !== next.renderInfos) {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
803
869
|
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];
|
|
804
870
|
});
|
|
805
871
|
var FieldInput_default = FieldInput;
|
|
806
872
|
|
|
807
873
|
// src/components/DynamicFormDevTools.tsx
|
|
808
|
-
import { useState as
|
|
809
|
-
import { jsx as
|
|
874
|
+
import { useState as useState4 } from "react";
|
|
875
|
+
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
810
876
|
var DynamicFormDevTools = ({
|
|
811
877
|
data,
|
|
812
878
|
errors = {},
|
|
@@ -815,8 +881,8 @@ var DynamicFormDevTools = ({
|
|
|
815
881
|
fields = [],
|
|
816
882
|
position = "bottom-right"
|
|
817
883
|
}) => {
|
|
818
|
-
const [isOpen, setIsOpen] =
|
|
819
|
-
const [activeTab, setActiveTab] =
|
|
884
|
+
const [isOpen, setIsOpen] = useState4(false);
|
|
885
|
+
const [activeTab, setActiveTab] = useState4("data");
|
|
820
886
|
const errorCount = Object.keys(errors).length;
|
|
821
887
|
const posStyle = position === "bottom-left" ? { left: "16px", bottom: "16px" } : { right: "16px", bottom: "16px" };
|
|
822
888
|
if (!isOpen) {
|
|
@@ -843,8 +909,8 @@ var DynamicFormDevTools = ({
|
|
|
843
909
|
gap: "6px"
|
|
844
910
|
},
|
|
845
911
|
children: [
|
|
846
|
-
/* @__PURE__ */
|
|
847
|
-
errorCount > 0 && /* @__PURE__ */
|
|
912
|
+
/* @__PURE__ */ jsx9("span", { children: "\u{1F50D} DevTools" }),
|
|
913
|
+
errorCount > 0 && /* @__PURE__ */ jsx9(
|
|
848
914
|
"span",
|
|
849
915
|
{
|
|
850
916
|
style: {
|
|
@@ -894,8 +960,8 @@ var DynamicFormDevTools = ({
|
|
|
894
960
|
borderBottom: "1px solid #334155"
|
|
895
961
|
},
|
|
896
962
|
children: [
|
|
897
|
-
/* @__PURE__ */
|
|
898
|
-
/* @__PURE__ */
|
|
963
|
+
/* @__PURE__ */ jsx9("span", { style: { fontWeight: "bold", color: "#38bdf8" }, children: "\u{1F6E0}\uFE0F Form DevTools" }),
|
|
964
|
+
/* @__PURE__ */ jsx9(
|
|
899
965
|
"button",
|
|
900
966
|
{
|
|
901
967
|
type: "button",
|
|
@@ -913,7 +979,7 @@ var DynamicFormDevTools = ({
|
|
|
913
979
|
]
|
|
914
980
|
}
|
|
915
981
|
),
|
|
916
|
-
/* @__PURE__ */
|
|
982
|
+
/* @__PURE__ */ jsx9(
|
|
917
983
|
"div",
|
|
918
984
|
{
|
|
919
985
|
style: {
|
|
@@ -948,7 +1014,7 @@ var DynamicFormDevTools = ({
|
|
|
948
1014
|
}
|
|
949
1015
|
),
|
|
950
1016
|
/* @__PURE__ */ jsxs5("div", { style: { padding: "12px", overflowY: "auto", flex: 1 }, children: [
|
|
951
|
-
activeTab === "data" && /* @__PURE__ */
|
|
1017
|
+
activeTab === "data" && /* @__PURE__ */ jsx9(
|
|
952
1018
|
"pre",
|
|
953
1019
|
{
|
|
954
1020
|
style: {
|
|
@@ -960,24 +1026,24 @@ var DynamicFormDevTools = ({
|
|
|
960
1026
|
children: JSON.stringify(data, null, 2)
|
|
961
1027
|
}
|
|
962
1028
|
),
|
|
963
|
-
activeTab === "errors" && /* @__PURE__ */
|
|
1029
|
+
activeTab === "errors" && /* @__PURE__ */ jsx9("div", { children: Object.keys(errors).length === 0 ? /* @__PURE__ */ jsx9("span", { style: { color: "#4ade80" }, children: "\u2713 No validation errors" }) : Object.entries(errors).map(([field, msgs]) => /* @__PURE__ */ jsxs5("div", { style: { marginBottom: "8px" }, children: [
|
|
964
1030
|
/* @__PURE__ */ jsxs5("span", { style: { color: "#f87171", fontWeight: "bold" }, children: [
|
|
965
1031
|
field,
|
|
966
1032
|
":"
|
|
967
1033
|
] }),
|
|
968
|
-
/* @__PURE__ */
|
|
1034
|
+
/* @__PURE__ */ jsx9("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: msgs.map((m, i) => /* @__PURE__ */ jsx9("li", { style: { color: "#fca5a5" }, children: m }, i)) })
|
|
969
1035
|
] }, field)) }),
|
|
970
1036
|
activeTab === "meta" && /* @__PURE__ */ jsxs5("div", { children: [
|
|
971
1037
|
/* @__PURE__ */ jsxs5("div", { style: { marginBottom: "6px" }, children: [
|
|
972
|
-
/* @__PURE__ */
|
|
973
|
-
/* @__PURE__ */
|
|
1038
|
+
/* @__PURE__ */ jsx9("span", { style: { color: "#94a3b8" }, children: "isDirty: " }),
|
|
1039
|
+
/* @__PURE__ */ jsx9("span", { style: { color: isDirty ? "#facc15" : "#4ade80" }, children: String(isDirty) })
|
|
974
1040
|
] }),
|
|
975
1041
|
/* @__PURE__ */ jsxs5("div", { children: [
|
|
976
|
-
/* @__PURE__ */
|
|
977
|
-
/* @__PURE__ */
|
|
1042
|
+
/* @__PURE__ */ jsx9("span", { style: { color: "#94a3b8" }, children: "Touched Fields:" }),
|
|
1043
|
+
/* @__PURE__ */ jsx9("pre", { style: { margin: "4px 0 0 0", color: "#cbd5e1" }, children: JSON.stringify(touched, null, 2) })
|
|
978
1044
|
] })
|
|
979
1045
|
] }),
|
|
980
|
-
activeTab === "fields" && /* @__PURE__ */
|
|
1046
|
+
activeTab === "fields" && /* @__PURE__ */ jsx9("div", { children: fields.length === 0 ? /* @__PURE__ */ jsx9("span", { style: { color: "#94a3b8" }, children: "No field descriptions passed" }) : fields.map((f) => /* @__PURE__ */ jsxs5(
|
|
981
1047
|
"div",
|
|
982
1048
|
{
|
|
983
1049
|
style: {
|
|
@@ -987,7 +1053,7 @@ var DynamicFormDevTools = ({
|
|
|
987
1053
|
borderRadius: "4px"
|
|
988
1054
|
},
|
|
989
1055
|
children: [
|
|
990
|
-
/* @__PURE__ */
|
|
1056
|
+
/* @__PURE__ */ jsx9("div", { style: { color: "#38bdf8", fontWeight: "bold" }, children: f.name }),
|
|
991
1057
|
/* @__PURE__ */ jsxs5("div", { style: { color: "#94a3b8", fontSize: "10px" }, children: [
|
|
992
1058
|
"type: ",
|
|
993
1059
|
f.type,
|
|
@@ -1008,10 +1074,11 @@ var DynamicFormDevTools = ({
|
|
|
1008
1074
|
import {
|
|
1009
1075
|
applyComputedValues as applyComputedValues2,
|
|
1010
1076
|
collectFieldPaths,
|
|
1077
|
+
createMessageResolver,
|
|
1011
1078
|
validateFields as validateFields2,
|
|
1012
1079
|
validateFieldsAsync
|
|
1013
1080
|
} from "@dynamic-field-kit/core";
|
|
1014
|
-
import { useCallback as useCallback4, useEffect as
|
|
1081
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useRef as useRef3, useState as useState5 } from "react";
|
|
1015
1082
|
function sameStringMap(left, right) {
|
|
1016
1083
|
const keys = Object.keys(left);
|
|
1017
1084
|
return keys.length === Object.keys(right).length && keys.every(
|
|
@@ -1025,36 +1092,57 @@ function useDynamicForm({
|
|
|
1025
1092
|
fields,
|
|
1026
1093
|
initialValues = {},
|
|
1027
1094
|
validateOnBlur = true,
|
|
1028
|
-
validateOnChange = false
|
|
1095
|
+
validateOnChange = false,
|
|
1096
|
+
messages
|
|
1029
1097
|
}) {
|
|
1030
|
-
const
|
|
1098
|
+
const validationContextRef = useRef3({
|
|
1099
|
+
t: createMessageResolver(messages)
|
|
1100
|
+
});
|
|
1101
|
+
validationContextRef.current.t = createMessageResolver(messages);
|
|
1102
|
+
const [data, setData] = useState5(
|
|
1031
1103
|
() => applyComputedValues2(fields, initialValues)
|
|
1032
1104
|
);
|
|
1033
|
-
const [errors, setErrors] =
|
|
1034
|
-
const [isDirty, setIsDirty] =
|
|
1035
|
-
const [touched, setTouched] =
|
|
1036
|
-
const [isSubmitting, setIsSubmitting] =
|
|
1037
|
-
const [isSubmitted, setIsSubmitted] =
|
|
1038
|
-
const [validationResult, setValidationResult] =
|
|
1039
|
-
() => validateFields2(fields, data)
|
|
1105
|
+
const [errors, setErrors] = useState5({});
|
|
1106
|
+
const [isDirty, setIsDirty] = useState5(false);
|
|
1107
|
+
const [touched, setTouched] = useState5({});
|
|
1108
|
+
const [isSubmitting, setIsSubmitting] = useState5(false);
|
|
1109
|
+
const [isSubmitted, setIsSubmitted] = useState5(false);
|
|
1110
|
+
const [validationResult, setValidationResult] = useState5(
|
|
1111
|
+
() => validateFields2(fields, data, void 0, validationContextRef.current)
|
|
1040
1112
|
);
|
|
1041
|
-
const [isValidating, setIsValidating] =
|
|
1042
|
-
const validationRunRef =
|
|
1043
|
-
const validationAbortRef =
|
|
1044
|
-
const submitRunRef =
|
|
1045
|
-
const submitAbortRef =
|
|
1046
|
-
const dataRef =
|
|
1113
|
+
const [isValidating, setIsValidating] = useState5(false);
|
|
1114
|
+
const validationRunRef = useRef3(0);
|
|
1115
|
+
const validationAbortRef = useRef3(void 0);
|
|
1116
|
+
const submitRunRef = useRef3(0);
|
|
1117
|
+
const submitAbortRef = useRef3(void 0);
|
|
1118
|
+
const dataRef = useRef3(data);
|
|
1047
1119
|
dataRef.current = data;
|
|
1120
|
+
const baselineRef = useRef3(dataRef.current);
|
|
1121
|
+
const [baselineValues, setBaselineValues] = useState5(
|
|
1122
|
+
() => baselineRef.current
|
|
1123
|
+
);
|
|
1048
1124
|
const commitSyncResult = useCallback4((res) => {
|
|
1049
1125
|
setValidationResult(
|
|
1050
1126
|
(previous) => sameValidationResult(previous, res) ? previous : res
|
|
1051
1127
|
);
|
|
1052
1128
|
return res.valid;
|
|
1053
1129
|
}, []);
|
|
1054
|
-
|
|
1055
|
-
|
|
1130
|
+
const lastValidatedRef = useRef3(void 0);
|
|
1131
|
+
const fieldsRef = useRef3(fields);
|
|
1132
|
+
if (fieldsRef.current !== fields) {
|
|
1133
|
+
fieldsRef.current = fields;
|
|
1134
|
+
lastValidatedRef.current = void 0;
|
|
1135
|
+
}
|
|
1136
|
+
useEffect4(() => {
|
|
1137
|
+
if (lastValidatedRef.current === data) {
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
lastValidatedRef.current = data;
|
|
1141
|
+
commitSyncResult(
|
|
1142
|
+
validateFields2(fields, data, void 0, validationContextRef.current)
|
|
1143
|
+
);
|
|
1056
1144
|
}, [fields, data, commitSyncResult]);
|
|
1057
|
-
|
|
1145
|
+
useEffect4(
|
|
1058
1146
|
() => () => {
|
|
1059
1147
|
validationAbortRef.current?.abort();
|
|
1060
1148
|
submitAbortRef.current?.abort();
|
|
@@ -1062,7 +1150,12 @@ function useDynamicForm({
|
|
|
1062
1150
|
[]
|
|
1063
1151
|
);
|
|
1064
1152
|
const validate = useCallback4(() => {
|
|
1065
|
-
const res = validateFields2(
|
|
1153
|
+
const res = validateFields2(
|
|
1154
|
+
fields,
|
|
1155
|
+
data,
|
|
1156
|
+
void 0,
|
|
1157
|
+
validationContextRef.current
|
|
1158
|
+
);
|
|
1066
1159
|
setErrors(res.errors);
|
|
1067
1160
|
return commitSyncResult(res);
|
|
1068
1161
|
}, [fields, data, commitSyncResult]);
|
|
@@ -1075,6 +1168,7 @@ function useDynamicForm({
|
|
|
1075
1168
|
setIsValidating(true);
|
|
1076
1169
|
try {
|
|
1077
1170
|
const res = await validateFieldsAsync(fields, snapshot, snapshot, {
|
|
1171
|
+
...validationContextRef.current,
|
|
1078
1172
|
signal: controller.signal
|
|
1079
1173
|
});
|
|
1080
1174
|
if (run !== validationRunRef.current || dataRef.current !== snapshot) {
|
|
@@ -1098,7 +1192,13 @@ function useDynamicForm({
|
|
|
1098
1192
|
validationAbortRef.current?.abort();
|
|
1099
1193
|
validationRunRef.current += 1;
|
|
1100
1194
|
setIsValidating(false);
|
|
1101
|
-
|
|
1195
|
+
lastValidatedRef.current = next;
|
|
1196
|
+
const res = validateFields2(
|
|
1197
|
+
fields,
|
|
1198
|
+
next,
|
|
1199
|
+
void 0,
|
|
1200
|
+
validationContextRef.current
|
|
1201
|
+
);
|
|
1102
1202
|
commitSyncResult(res);
|
|
1103
1203
|
if (validateOnChange) {
|
|
1104
1204
|
setErrors(res.errors);
|
|
@@ -1123,11 +1223,27 @@ function useDynamicForm({
|
|
|
1123
1223
|
);
|
|
1124
1224
|
}, [fields, data]);
|
|
1125
1225
|
const resetTouched = useCallback4(() => setTouched({}), []);
|
|
1226
|
+
const getDirtyValues = useCallback4(() => {
|
|
1227
|
+
const baseline = baselineRef.current;
|
|
1228
|
+
const current = dataRef.current;
|
|
1229
|
+
const dirty = {};
|
|
1230
|
+
for (const key of Object.keys(current)) {
|
|
1231
|
+
if (!Object.is(current[key], baseline[key])) {
|
|
1232
|
+
dirty[key] = current[key];
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return dirty;
|
|
1236
|
+
}, []);
|
|
1126
1237
|
const handleBlur = useCallback4(
|
|
1127
1238
|
(fieldName) => {
|
|
1128
1239
|
setFieldTouched(fieldName, true);
|
|
1129
1240
|
if (validateOnBlur) {
|
|
1130
|
-
const res = validateFields2(
|
|
1241
|
+
const res = validateFields2(
|
|
1242
|
+
fields,
|
|
1243
|
+
data,
|
|
1244
|
+
void 0,
|
|
1245
|
+
validationContextRef.current
|
|
1246
|
+
);
|
|
1131
1247
|
setErrors(res.errors);
|
|
1132
1248
|
commitSyncResult(res);
|
|
1133
1249
|
}
|
|
@@ -1140,6 +1256,8 @@ function useDynamicForm({
|
|
|
1140
1256
|
const next = applyComputedValues2(fields, seed);
|
|
1141
1257
|
setData(next);
|
|
1142
1258
|
dataRef.current = next;
|
|
1259
|
+
baselineRef.current = next;
|
|
1260
|
+
setBaselineValues(next);
|
|
1143
1261
|
setErrors({});
|
|
1144
1262
|
setIsDirty(false);
|
|
1145
1263
|
setTouched({});
|
|
@@ -1168,6 +1286,7 @@ function useDynamicForm({
|
|
|
1168
1286
|
const snapshot = data;
|
|
1169
1287
|
setIsValidating(true);
|
|
1170
1288
|
const res = await validateFieldsAsync(fields, snapshot, snapshot, {
|
|
1289
|
+
...validationContextRef.current,
|
|
1171
1290
|
signal: controller.signal
|
|
1172
1291
|
});
|
|
1173
1292
|
if (submitRun !== submitRunRef.current) {
|
|
@@ -1177,7 +1296,12 @@ function useDynamicForm({
|
|
|
1177
1296
|
setErrors(res.errors);
|
|
1178
1297
|
setValidationResult(res);
|
|
1179
1298
|
} else {
|
|
1180
|
-
const live = validateFields2(
|
|
1299
|
+
const live = validateFields2(
|
|
1300
|
+
fields,
|
|
1301
|
+
dataRef.current,
|
|
1302
|
+
void 0,
|
|
1303
|
+
validationContextRef.current
|
|
1304
|
+
);
|
|
1181
1305
|
setErrors(live.errors);
|
|
1182
1306
|
commitSyncResult(live);
|
|
1183
1307
|
}
|
|
@@ -1206,6 +1330,8 @@ function useDynamicForm({
|
|
|
1206
1330
|
isValidationComplete: validationResult.complete && !isValidating,
|
|
1207
1331
|
validationStatus: isValidating ? "pending" : validationResult.status,
|
|
1208
1332
|
isDirty,
|
|
1333
|
+
baselineValues,
|
|
1334
|
+
getDirtyValues,
|
|
1209
1335
|
isSubmitting,
|
|
1210
1336
|
isSubmitted,
|
|
1211
1337
|
touched,
|
|
@@ -1229,6 +1355,7 @@ import { FieldRegistry } from "@dynamic-field-kit/core";
|
|
|
1229
1355
|
import {
|
|
1230
1356
|
buildFieldRendererProps as buildFieldRendererProps2,
|
|
1231
1357
|
makeFieldId as makeFieldId2,
|
|
1358
|
+
makeErrorId as makeErrorId2,
|
|
1232
1359
|
FIELD_RENDERER_PROP_KEYS
|
|
1233
1360
|
} from "@dynamic-field-kit/core";
|
|
1234
1361
|
import {
|
|
@@ -1258,6 +1385,7 @@ export {
|
|
|
1258
1385
|
getDefaultRenderer,
|
|
1259
1386
|
indexGroupPathMap2 as indexGroupPathMap,
|
|
1260
1387
|
layoutRegistry,
|
|
1388
|
+
makeErrorId2 as makeErrorId,
|
|
1261
1389
|
makeFieldId2 as makeFieldId,
|
|
1262
1390
|
resolveDisabled,
|
|
1263
1391
|
resolveOptions,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-field-kit/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "React renderer for dynamic-field-kit",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@dynamic-field-kit/core": "^1.
|
|
29
|
+
"@dynamic-field-kit/core": "^1.7.0",
|
|
30
30
|
"@testing-library/dom": "^10.0.0",
|
|
31
31
|
"@testing-library/jest-dom": "^7.0.1",
|
|
32
32
|
"@testing-library/react": "^16.0.0",
|