@juantroconisf/lib 11.7.0 → 11.9.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/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +193 -93
- package/dist/index.mjs +193 -93
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -238,6 +238,11 @@ interface HelpersFunc<O extends StateType> {
|
|
|
238
238
|
moveById: <K extends ArrayPaths<O>>(arrayKey: K, fromId: string | number, toId: string | number) => void;
|
|
239
239
|
/** Gets an item from an array by its unique identifier (O(1) via indexMap). */
|
|
240
240
|
getItemById: <K extends ArrayPaths<O>>(arrayKey: K, itemId: string | number) => (NestedFieldValue<O, K> extends (infer E)[] ? E : never) | undefined;
|
|
241
|
+
/** Validates a single item in an array by its unique identifier. */
|
|
242
|
+
validateItem: <K extends ArrayPaths<O>>(arrayKey: K, itemId: string | number) => Promise<{
|
|
243
|
+
isValid: boolean;
|
|
244
|
+
errors: string[];
|
|
245
|
+
}>;
|
|
241
246
|
}
|
|
242
247
|
/**
|
|
243
248
|
* The response object from the useForm hook.
|
|
@@ -285,7 +290,7 @@ interface UseFormResponse<O extends StateType> {
|
|
|
285
290
|
* If validation fails, identifying errors and touching fields.
|
|
286
291
|
* If validation succeeds, calls the provided handler with the current state.
|
|
287
292
|
*/
|
|
288
|
-
onFormSubmit: (fn: FormSubmitHandler<O>) => (e: React.FormEvent) => void
|
|
293
|
+
onFormSubmit: (fn: FormSubmitHandler<O>) => (e: React.FormEvent) => Promise<void>;
|
|
289
294
|
/**
|
|
290
295
|
* A controlled form component that handles submission and validation.
|
|
291
296
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -238,6 +238,11 @@ interface HelpersFunc<O extends StateType> {
|
|
|
238
238
|
moveById: <K extends ArrayPaths<O>>(arrayKey: K, fromId: string | number, toId: string | number) => void;
|
|
239
239
|
/** Gets an item from an array by its unique identifier (O(1) via indexMap). */
|
|
240
240
|
getItemById: <K extends ArrayPaths<O>>(arrayKey: K, itemId: string | number) => (NestedFieldValue<O, K> extends (infer E)[] ? E : never) | undefined;
|
|
241
|
+
/** Validates a single item in an array by its unique identifier. */
|
|
242
|
+
validateItem: <K extends ArrayPaths<O>>(arrayKey: K, itemId: string | number) => Promise<{
|
|
243
|
+
isValid: boolean;
|
|
244
|
+
errors: string[];
|
|
245
|
+
}>;
|
|
241
246
|
}
|
|
242
247
|
/**
|
|
243
248
|
* The response object from the useForm hook.
|
|
@@ -285,7 +290,7 @@ interface UseFormResponse<O extends StateType> {
|
|
|
285
290
|
* If validation fails, identifying errors and touching fields.
|
|
286
291
|
* If validation succeeds, calls the provided handler with the current state.
|
|
287
292
|
*/
|
|
288
|
-
onFormSubmit: (fn: FormSubmitHandler<O>) => (e: React.FormEvent) => void
|
|
293
|
+
onFormSubmit: (fn: FormSubmitHandler<O>) => (e: React.FormEvent) => Promise<void>;
|
|
289
294
|
/**
|
|
290
295
|
* A controlled form component that handles submission and validation.
|
|
291
296
|
*/
|
package/dist/index.js
CHANGED
|
@@ -428,7 +428,17 @@ function useForm(schema, {
|
|
|
428
428
|
const indexMapRef = (0, import_react2.useRef)(indexMap);
|
|
429
429
|
indexMapRef.current = indexMap;
|
|
430
430
|
const getIndex = (0, import_react2.useCallback)((arrayKey, itemId) => {
|
|
431
|
-
|
|
431
|
+
const map = indexMapRef.current.get(arrayKey);
|
|
432
|
+
if (!map) return void 0;
|
|
433
|
+
const index = map.get(itemId);
|
|
434
|
+
if (index !== void 0) return index;
|
|
435
|
+
if (typeof itemId === "string") {
|
|
436
|
+
const num = Number(itemId);
|
|
437
|
+
if (!isNaN(num)) return map.get(num);
|
|
438
|
+
} else if (typeof itemId === "number") {
|
|
439
|
+
return map.get(String(itemId));
|
|
440
|
+
}
|
|
441
|
+
return void 0;
|
|
432
442
|
}, []);
|
|
433
443
|
const ruleCache = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
434
444
|
(0, import_react2.useMemo)(() => {
|
|
@@ -543,9 +553,21 @@ function useForm(schema, {
|
|
|
543
553
|
[getRule, runValidation, validationSchema]
|
|
544
554
|
);
|
|
545
555
|
const validateAll = (0, import_react2.useCallback)(() => {
|
|
546
|
-
if (!validationSchema)
|
|
547
|
-
|
|
556
|
+
if (!validationSchema)
|
|
557
|
+
return Promise.resolve({ isValid: true, errors: [] });
|
|
558
|
+
const cleanMetadata = new Map(metadataRef.current);
|
|
559
|
+
cleanMetadata.forEach((value, key) => {
|
|
560
|
+
if (value.isInvalid) {
|
|
561
|
+
cleanMetadata.set(key, {
|
|
562
|
+
...value,
|
|
563
|
+
isInvalid: false,
|
|
564
|
+
errorMessage: ""
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
});
|
|
548
568
|
const handleErrors = (err) => {
|
|
569
|
+
const newMetadata = new Map(cleanMetadata);
|
|
570
|
+
const errors = [];
|
|
549
571
|
if (err.inner) {
|
|
550
572
|
err.inner.forEach((validationError) => {
|
|
551
573
|
const yupPath = validationError.path;
|
|
@@ -573,6 +595,7 @@ function useForm(schema, {
|
|
|
573
595
|
}
|
|
574
596
|
}
|
|
575
597
|
const compositeKey = compositeParts.join(".");
|
|
598
|
+
errors.push(compositeKey);
|
|
576
599
|
const currentMeta = newMetadata.get(compositeKey) || {
|
|
577
600
|
isTouched: false,
|
|
578
601
|
isInvalid: false,
|
|
@@ -587,25 +610,26 @@ function useForm(schema, {
|
|
|
587
610
|
});
|
|
588
611
|
}
|
|
589
612
|
setMetadata(newMetadata);
|
|
590
|
-
return
|
|
613
|
+
return { isValid: false, errors };
|
|
591
614
|
};
|
|
592
615
|
try {
|
|
593
616
|
validationSchema.validateSync(state, { abortEarly: false });
|
|
594
617
|
} catch (err) {
|
|
595
618
|
if (err.name === "ValidationError") {
|
|
596
|
-
return handleErrors(err);
|
|
619
|
+
return Promise.resolve(handleErrors(err));
|
|
597
620
|
}
|
|
598
621
|
return (async () => {
|
|
599
622
|
try {
|
|
600
623
|
await validationSchema.validate(state, { abortEarly: false });
|
|
601
|
-
|
|
624
|
+
setMetadata(cleanMetadata);
|
|
625
|
+
return { isValid: true, errors: [] };
|
|
602
626
|
} catch (asyncErr) {
|
|
603
627
|
return handleErrors(asyncErr);
|
|
604
628
|
}
|
|
605
629
|
})();
|
|
606
630
|
}
|
|
607
|
-
setMetadata(
|
|
608
|
-
return
|
|
631
|
+
setMetadata(cleanMetadata);
|
|
632
|
+
return Promise.resolve({ isValid: true, errors: [] });
|
|
609
633
|
}, [validationSchema, state, arrayIdentifiers]);
|
|
610
634
|
const handleFieldChange = (0, import_react2.useCallback)(
|
|
611
635
|
(resolution, newValue) => {
|
|
@@ -750,91 +774,88 @@ function useForm(schema, {
|
|
|
750
774
|
},
|
|
751
775
|
[validateField, getRule, validationSchema]
|
|
752
776
|
);
|
|
753
|
-
const on = (0, import_react2.useMemo)(
|
|
754
|
-
() => {
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
return {
|
|
790
|
-
...createHandlers(data),
|
|
791
|
-
selectedKeys: data.value === null || data.value === void 0 ? [] : isArray ? data.value.map(String) : [String(data.value)],
|
|
792
|
-
onSelectionChange: (v) => {
|
|
793
|
-
const fixed = typeof v === "string" || v === null ? v : isArray ? Array.from(v) : Array.from(v)[0] ?? null;
|
|
794
|
-
handleFieldChange(data, fixed);
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
}),
|
|
798
|
-
autocomplete: (...args) => getCachedProps("autocomplete", args, (data) => ({
|
|
777
|
+
const on = (0, import_react2.useMemo)(() => {
|
|
778
|
+
const getCachedProps = (method, args, factory) => {
|
|
779
|
+
const data = resolveFieldData(
|
|
780
|
+
args,
|
|
781
|
+
stateRef.current,
|
|
782
|
+
getIndex,
|
|
783
|
+
getNestedValue,
|
|
784
|
+
getRule,
|
|
785
|
+
validationSchema
|
|
786
|
+
);
|
|
787
|
+
if (!data) return {};
|
|
788
|
+
const meta = metadataRef.current.get(data.compositeKey);
|
|
789
|
+
const deps = JSON.stringify([
|
|
790
|
+
data.value,
|
|
791
|
+
meta?.isInvalid,
|
|
792
|
+
meta?.errorMessage,
|
|
793
|
+
meta?.isTouched
|
|
794
|
+
]);
|
|
795
|
+
const cacheKey = `${method}:${JSON.stringify(args)}`;
|
|
796
|
+
const cached = propsCache.current.get(cacheKey);
|
|
797
|
+
if (cached && cached.deps === deps) {
|
|
798
|
+
return cached.props;
|
|
799
|
+
}
|
|
800
|
+
const props = factory(data);
|
|
801
|
+
propsCache.current.set(cacheKey, { props, deps });
|
|
802
|
+
return props;
|
|
803
|
+
};
|
|
804
|
+
return {
|
|
805
|
+
input: (...args) => getCachedProps("input", args, (data) => ({
|
|
806
|
+
...createHandlers(data),
|
|
807
|
+
value: data.value === null || data.value === void 0 ? "" : typeof data.value === "boolean" ? data.value : String(data.value),
|
|
808
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
809
|
+
})),
|
|
810
|
+
select: (...args) => getCachedProps("select", args, (data) => {
|
|
811
|
+
const isArray = Array.isArray(data.value);
|
|
812
|
+
return {
|
|
799
813
|
...createHandlers(data),
|
|
800
|
-
|
|
814
|
+
selectedKeys: data.value === null || data.value === void 0 ? [] : isArray ? data.value.map(String) : [String(data.value)],
|
|
801
815
|
onSelectionChange: (v) => {
|
|
802
|
-
const fixed = typeof v === "string" || v === null
|
|
816
|
+
const fixed = typeof v === "string" || v === null ? v : isArray ? Array.from(v) : Array.from(v)[0] ?? null;
|
|
803
817
|
handleFieldChange(data, fixed);
|
|
804
818
|
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
819
|
+
};
|
|
820
|
+
}),
|
|
821
|
+
autocomplete: (...args) => getCachedProps("autocomplete", args, (data) => ({
|
|
822
|
+
...createHandlers(data),
|
|
823
|
+
selectedKey: data.value === null || data.value === void 0 ? null : String(data.value),
|
|
824
|
+
onSelectionChange: (v) => {
|
|
825
|
+
const fixed = typeof v === "string" || v === null || v === void 0 ? v : String(v);
|
|
826
|
+
handleFieldChange(data, fixed);
|
|
827
|
+
}
|
|
828
|
+
})),
|
|
829
|
+
numberInput: (...args) => getCachedProps("numberInput", args, (data) => ({
|
|
830
|
+
...createHandlers(data),
|
|
831
|
+
value: data.value === null || data.value === void 0 ? "" : String(data.value),
|
|
832
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
833
|
+
})),
|
|
834
|
+
checkbox: (...args) => getCachedProps("checkbox", args, (data) => ({
|
|
835
|
+
...createHandlers(data),
|
|
836
|
+
isSelected: Boolean(data.value),
|
|
837
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
838
|
+
})),
|
|
839
|
+
switch: (...args) => getCachedProps("switch", args, (data) => ({
|
|
840
|
+
...createHandlers(data),
|
|
841
|
+
isSelected: Boolean(data.value),
|
|
842
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
843
|
+
})),
|
|
844
|
+
radio: (...args) => getCachedProps("radio", args, (data) => ({
|
|
845
|
+
...createHandlers(data),
|
|
846
|
+
value: data.value === null || data.value === void 0 ? "" : String(data.value),
|
|
847
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
848
|
+
}))
|
|
849
|
+
};
|
|
850
|
+
}, [
|
|
851
|
+
createHandlers,
|
|
852
|
+
getIndex,
|
|
853
|
+
handleFieldChange,
|
|
854
|
+
getRule,
|
|
855
|
+
validationSchema,
|
|
856
|
+
state,
|
|
857
|
+
metadata
|
|
858
|
+
]);
|
|
838
859
|
const helpers = (0, import_react2.useMemo)(
|
|
839
860
|
() => ({
|
|
840
861
|
addItem: (arrayKey, item, index) => {
|
|
@@ -948,6 +969,85 @@ function useForm(schema, {
|
|
|
948
969
|
const index = getIndex(arrayKey, itemId);
|
|
949
970
|
if (index === void 0) return void 0;
|
|
950
971
|
return getNestedValue(stateRef.current, arrayKey)[index];
|
|
972
|
+
},
|
|
973
|
+
validateItem: async (arrayKey, itemId) => {
|
|
974
|
+
if (!validationSchema) return { isValid: true, errors: [] };
|
|
975
|
+
const index = getIndex(arrayKey, itemId);
|
|
976
|
+
if (index === void 0) return { isValid: true, errors: [] };
|
|
977
|
+
const yupPath = `${String(arrayKey)}[${index}]`;
|
|
978
|
+
const prefix = `${String(arrayKey)}.${itemId}.`;
|
|
979
|
+
const cleanMetadata = new Map(metadataRef.current);
|
|
980
|
+
for (const key of cleanMetadata.keys()) {
|
|
981
|
+
if (key.startsWith(prefix) || key === `${String(arrayKey)}.${itemId}`) {
|
|
982
|
+
const meta = cleanMetadata.get(key);
|
|
983
|
+
cleanMetadata.set(key, {
|
|
984
|
+
...meta,
|
|
985
|
+
isInvalid: false,
|
|
986
|
+
errorMessage: ""
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
const handleErrors = (err) => {
|
|
991
|
+
const newMetadata = new Map(cleanMetadata);
|
|
992
|
+
const errors = [];
|
|
993
|
+
if (err.inner) {
|
|
994
|
+
err.inner.forEach((validationError) => {
|
|
995
|
+
const localDotPath = validationError.path.replace(
|
|
996
|
+
/\[(\d+)\]/g,
|
|
997
|
+
".$1"
|
|
998
|
+
);
|
|
999
|
+
const parts = localDotPath.split(".");
|
|
1000
|
+
let current = stateRef.current;
|
|
1001
|
+
const compositeParts = [];
|
|
1002
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1003
|
+
const part = parts[i];
|
|
1004
|
+
if (Array.isArray(current)) {
|
|
1005
|
+
const idx = parseInt(part, 10);
|
|
1006
|
+
const item = current[idx];
|
|
1007
|
+
if (item && typeof item === "object") {
|
|
1008
|
+
const genericPath = compositeParts.join(".").replace(/\.\d+/g, "");
|
|
1009
|
+
const idKey = arrayIdentifiers?.[genericPath] || "id";
|
|
1010
|
+
const id = item[idKey];
|
|
1011
|
+
compositeParts.push(String(id !== void 0 ? id : idx));
|
|
1012
|
+
} else {
|
|
1013
|
+
compositeParts.push(part);
|
|
1014
|
+
}
|
|
1015
|
+
current = item;
|
|
1016
|
+
} else {
|
|
1017
|
+
compositeParts.push(part);
|
|
1018
|
+
current = current?.[part];
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
const compositeKey = compositeParts.join(".");
|
|
1022
|
+
errors.push(compositeKey);
|
|
1023
|
+
const currentMeta = newMetadata.get(compositeKey) || {
|
|
1024
|
+
isTouched: false,
|
|
1025
|
+
isInvalid: false,
|
|
1026
|
+
errorMessage: ""
|
|
1027
|
+
};
|
|
1028
|
+
newMetadata.set(compositeKey, {
|
|
1029
|
+
...currentMeta,
|
|
1030
|
+
isTouched: true,
|
|
1031
|
+
isInvalid: true,
|
|
1032
|
+
errorMessage: validationError.message
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
setMetadata(newMetadata);
|
|
1037
|
+
return { isValid: false, errors };
|
|
1038
|
+
};
|
|
1039
|
+
try {
|
|
1040
|
+
await validationSchema.validateAt(yupPath, stateRef.current, {
|
|
1041
|
+
abortEarly: false
|
|
1042
|
+
});
|
|
1043
|
+
setMetadata(cleanMetadata);
|
|
1044
|
+
return { isValid: true, errors: [] };
|
|
1045
|
+
} catch (err) {
|
|
1046
|
+
if (err.name === "ValidationError") {
|
|
1047
|
+
return handleErrors(err);
|
|
1048
|
+
}
|
|
1049
|
+
return { isValid: true, errors: [] };
|
|
1050
|
+
}
|
|
951
1051
|
}
|
|
952
1052
|
}),
|
|
953
1053
|
[getIndex, arrayIdentifiers, state, metadata]
|
|
@@ -1036,7 +1136,8 @@ function useForm(schema, {
|
|
|
1036
1136
|
const onFormSubmit = (0, import_react2.useCallback)(
|
|
1037
1137
|
(fn) => async (e) => {
|
|
1038
1138
|
e.preventDefault();
|
|
1039
|
-
|
|
1139
|
+
const { isValid } = await validateAll();
|
|
1140
|
+
if (!isValid) return;
|
|
1040
1141
|
fn(stateRef.current, e);
|
|
1041
1142
|
},
|
|
1042
1143
|
[validateAll]
|
|
@@ -1071,9 +1172,8 @@ function useForm(schema, {
|
|
|
1071
1172
|
const { onSubmit, ...rest } = props;
|
|
1072
1173
|
const handleSubmit = async (e) => {
|
|
1073
1174
|
e.preventDefault();
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
}
|
|
1175
|
+
const { isValid } = await validateAllRef.current();
|
|
1176
|
+
if (!isValid) return;
|
|
1077
1177
|
onFormSubmitPropRef.current?.(stateRef.current, e);
|
|
1078
1178
|
if (resetOnSubmitRef.current) {
|
|
1079
1179
|
handleReset({ keepValues: keepValuesPropRef.current });
|
package/dist/index.mjs
CHANGED
|
@@ -402,7 +402,17 @@ function useForm(schema, {
|
|
|
402
402
|
const indexMapRef = useRef(indexMap);
|
|
403
403
|
indexMapRef.current = indexMap;
|
|
404
404
|
const getIndex = useCallback((arrayKey, itemId) => {
|
|
405
|
-
|
|
405
|
+
const map = indexMapRef.current.get(arrayKey);
|
|
406
|
+
if (!map) return void 0;
|
|
407
|
+
const index = map.get(itemId);
|
|
408
|
+
if (index !== void 0) return index;
|
|
409
|
+
if (typeof itemId === "string") {
|
|
410
|
+
const num = Number(itemId);
|
|
411
|
+
if (!isNaN(num)) return map.get(num);
|
|
412
|
+
} else if (typeof itemId === "number") {
|
|
413
|
+
return map.get(String(itemId));
|
|
414
|
+
}
|
|
415
|
+
return void 0;
|
|
406
416
|
}, []);
|
|
407
417
|
const ruleCache = useRef(/* @__PURE__ */ new Map());
|
|
408
418
|
useMemo(() => {
|
|
@@ -517,9 +527,21 @@ function useForm(schema, {
|
|
|
517
527
|
[getRule, runValidation, validationSchema]
|
|
518
528
|
);
|
|
519
529
|
const validateAll = useCallback(() => {
|
|
520
|
-
if (!validationSchema)
|
|
521
|
-
|
|
530
|
+
if (!validationSchema)
|
|
531
|
+
return Promise.resolve({ isValid: true, errors: [] });
|
|
532
|
+
const cleanMetadata = new Map(metadataRef.current);
|
|
533
|
+
cleanMetadata.forEach((value, key) => {
|
|
534
|
+
if (value.isInvalid) {
|
|
535
|
+
cleanMetadata.set(key, {
|
|
536
|
+
...value,
|
|
537
|
+
isInvalid: false,
|
|
538
|
+
errorMessage: ""
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
});
|
|
522
542
|
const handleErrors = (err) => {
|
|
543
|
+
const newMetadata = new Map(cleanMetadata);
|
|
544
|
+
const errors = [];
|
|
523
545
|
if (err.inner) {
|
|
524
546
|
err.inner.forEach((validationError) => {
|
|
525
547
|
const yupPath = validationError.path;
|
|
@@ -547,6 +569,7 @@ function useForm(schema, {
|
|
|
547
569
|
}
|
|
548
570
|
}
|
|
549
571
|
const compositeKey = compositeParts.join(".");
|
|
572
|
+
errors.push(compositeKey);
|
|
550
573
|
const currentMeta = newMetadata.get(compositeKey) || {
|
|
551
574
|
isTouched: false,
|
|
552
575
|
isInvalid: false,
|
|
@@ -561,25 +584,26 @@ function useForm(schema, {
|
|
|
561
584
|
});
|
|
562
585
|
}
|
|
563
586
|
setMetadata(newMetadata);
|
|
564
|
-
return
|
|
587
|
+
return { isValid: false, errors };
|
|
565
588
|
};
|
|
566
589
|
try {
|
|
567
590
|
validationSchema.validateSync(state, { abortEarly: false });
|
|
568
591
|
} catch (err) {
|
|
569
592
|
if (err.name === "ValidationError") {
|
|
570
|
-
return handleErrors(err);
|
|
593
|
+
return Promise.resolve(handleErrors(err));
|
|
571
594
|
}
|
|
572
595
|
return (async () => {
|
|
573
596
|
try {
|
|
574
597
|
await validationSchema.validate(state, { abortEarly: false });
|
|
575
|
-
|
|
598
|
+
setMetadata(cleanMetadata);
|
|
599
|
+
return { isValid: true, errors: [] };
|
|
576
600
|
} catch (asyncErr) {
|
|
577
601
|
return handleErrors(asyncErr);
|
|
578
602
|
}
|
|
579
603
|
})();
|
|
580
604
|
}
|
|
581
|
-
setMetadata(
|
|
582
|
-
return
|
|
605
|
+
setMetadata(cleanMetadata);
|
|
606
|
+
return Promise.resolve({ isValid: true, errors: [] });
|
|
583
607
|
}, [validationSchema, state, arrayIdentifiers]);
|
|
584
608
|
const handleFieldChange = useCallback(
|
|
585
609
|
(resolution, newValue) => {
|
|
@@ -724,91 +748,88 @@ function useForm(schema, {
|
|
|
724
748
|
},
|
|
725
749
|
[validateField, getRule, validationSchema]
|
|
726
750
|
);
|
|
727
|
-
const on = useMemo(
|
|
728
|
-
() => {
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
return {
|
|
764
|
-
...createHandlers(data),
|
|
765
|
-
selectedKeys: data.value === null || data.value === void 0 ? [] : isArray ? data.value.map(String) : [String(data.value)],
|
|
766
|
-
onSelectionChange: (v) => {
|
|
767
|
-
const fixed = typeof v === "string" || v === null ? v : isArray ? Array.from(v) : Array.from(v)[0] ?? null;
|
|
768
|
-
handleFieldChange(data, fixed);
|
|
769
|
-
}
|
|
770
|
-
};
|
|
771
|
-
}),
|
|
772
|
-
autocomplete: (...args) => getCachedProps("autocomplete", args, (data) => ({
|
|
751
|
+
const on = useMemo(() => {
|
|
752
|
+
const getCachedProps = (method, args, factory) => {
|
|
753
|
+
const data = resolveFieldData(
|
|
754
|
+
args,
|
|
755
|
+
stateRef.current,
|
|
756
|
+
getIndex,
|
|
757
|
+
getNestedValue,
|
|
758
|
+
getRule,
|
|
759
|
+
validationSchema
|
|
760
|
+
);
|
|
761
|
+
if (!data) return {};
|
|
762
|
+
const meta = metadataRef.current.get(data.compositeKey);
|
|
763
|
+
const deps = JSON.stringify([
|
|
764
|
+
data.value,
|
|
765
|
+
meta?.isInvalid,
|
|
766
|
+
meta?.errorMessage,
|
|
767
|
+
meta?.isTouched
|
|
768
|
+
]);
|
|
769
|
+
const cacheKey = `${method}:${JSON.stringify(args)}`;
|
|
770
|
+
const cached = propsCache.current.get(cacheKey);
|
|
771
|
+
if (cached && cached.deps === deps) {
|
|
772
|
+
return cached.props;
|
|
773
|
+
}
|
|
774
|
+
const props = factory(data);
|
|
775
|
+
propsCache.current.set(cacheKey, { props, deps });
|
|
776
|
+
return props;
|
|
777
|
+
};
|
|
778
|
+
return {
|
|
779
|
+
input: (...args) => getCachedProps("input", args, (data) => ({
|
|
780
|
+
...createHandlers(data),
|
|
781
|
+
value: data.value === null || data.value === void 0 ? "" : typeof data.value === "boolean" ? data.value : String(data.value),
|
|
782
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
783
|
+
})),
|
|
784
|
+
select: (...args) => getCachedProps("select", args, (data) => {
|
|
785
|
+
const isArray = Array.isArray(data.value);
|
|
786
|
+
return {
|
|
773
787
|
...createHandlers(data),
|
|
774
|
-
|
|
788
|
+
selectedKeys: data.value === null || data.value === void 0 ? [] : isArray ? data.value.map(String) : [String(data.value)],
|
|
775
789
|
onSelectionChange: (v) => {
|
|
776
|
-
const fixed = typeof v === "string" || v === null
|
|
790
|
+
const fixed = typeof v === "string" || v === null ? v : isArray ? Array.from(v) : Array.from(v)[0] ?? null;
|
|
777
791
|
handleFieldChange(data, fixed);
|
|
778
792
|
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
793
|
+
};
|
|
794
|
+
}),
|
|
795
|
+
autocomplete: (...args) => getCachedProps("autocomplete", args, (data) => ({
|
|
796
|
+
...createHandlers(data),
|
|
797
|
+
selectedKey: data.value === null || data.value === void 0 ? null : String(data.value),
|
|
798
|
+
onSelectionChange: (v) => {
|
|
799
|
+
const fixed = typeof v === "string" || v === null || v === void 0 ? v : String(v);
|
|
800
|
+
handleFieldChange(data, fixed);
|
|
801
|
+
}
|
|
802
|
+
})),
|
|
803
|
+
numberInput: (...args) => getCachedProps("numberInput", args, (data) => ({
|
|
804
|
+
...createHandlers(data),
|
|
805
|
+
value: data.value === null || data.value === void 0 ? "" : String(data.value),
|
|
806
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
807
|
+
})),
|
|
808
|
+
checkbox: (...args) => getCachedProps("checkbox", args, (data) => ({
|
|
809
|
+
...createHandlers(data),
|
|
810
|
+
isSelected: Boolean(data.value),
|
|
811
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
812
|
+
})),
|
|
813
|
+
switch: (...args) => getCachedProps("switch", args, (data) => ({
|
|
814
|
+
...createHandlers(data),
|
|
815
|
+
isSelected: Boolean(data.value),
|
|
816
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
817
|
+
})),
|
|
818
|
+
radio: (...args) => getCachedProps("radio", args, (data) => ({
|
|
819
|
+
...createHandlers(data),
|
|
820
|
+
value: data.value === null || data.value === void 0 ? "" : String(data.value),
|
|
821
|
+
onValueChange: (v) => handleFieldChange(data, v)
|
|
822
|
+
}))
|
|
823
|
+
};
|
|
824
|
+
}, [
|
|
825
|
+
createHandlers,
|
|
826
|
+
getIndex,
|
|
827
|
+
handleFieldChange,
|
|
828
|
+
getRule,
|
|
829
|
+
validationSchema,
|
|
830
|
+
state,
|
|
831
|
+
metadata
|
|
832
|
+
]);
|
|
812
833
|
const helpers = useMemo(
|
|
813
834
|
() => ({
|
|
814
835
|
addItem: (arrayKey, item, index) => {
|
|
@@ -922,6 +943,85 @@ function useForm(schema, {
|
|
|
922
943
|
const index = getIndex(arrayKey, itemId);
|
|
923
944
|
if (index === void 0) return void 0;
|
|
924
945
|
return getNestedValue(stateRef.current, arrayKey)[index];
|
|
946
|
+
},
|
|
947
|
+
validateItem: async (arrayKey, itemId) => {
|
|
948
|
+
if (!validationSchema) return { isValid: true, errors: [] };
|
|
949
|
+
const index = getIndex(arrayKey, itemId);
|
|
950
|
+
if (index === void 0) return { isValid: true, errors: [] };
|
|
951
|
+
const yupPath = `${String(arrayKey)}[${index}]`;
|
|
952
|
+
const prefix = `${String(arrayKey)}.${itemId}.`;
|
|
953
|
+
const cleanMetadata = new Map(metadataRef.current);
|
|
954
|
+
for (const key of cleanMetadata.keys()) {
|
|
955
|
+
if (key.startsWith(prefix) || key === `${String(arrayKey)}.${itemId}`) {
|
|
956
|
+
const meta = cleanMetadata.get(key);
|
|
957
|
+
cleanMetadata.set(key, {
|
|
958
|
+
...meta,
|
|
959
|
+
isInvalid: false,
|
|
960
|
+
errorMessage: ""
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
const handleErrors = (err) => {
|
|
965
|
+
const newMetadata = new Map(cleanMetadata);
|
|
966
|
+
const errors = [];
|
|
967
|
+
if (err.inner) {
|
|
968
|
+
err.inner.forEach((validationError) => {
|
|
969
|
+
const localDotPath = validationError.path.replace(
|
|
970
|
+
/\[(\d+)\]/g,
|
|
971
|
+
".$1"
|
|
972
|
+
);
|
|
973
|
+
const parts = localDotPath.split(".");
|
|
974
|
+
let current = stateRef.current;
|
|
975
|
+
const compositeParts = [];
|
|
976
|
+
for (let i = 0; i < parts.length; i++) {
|
|
977
|
+
const part = parts[i];
|
|
978
|
+
if (Array.isArray(current)) {
|
|
979
|
+
const idx = parseInt(part, 10);
|
|
980
|
+
const item = current[idx];
|
|
981
|
+
if (item && typeof item === "object") {
|
|
982
|
+
const genericPath = compositeParts.join(".").replace(/\.\d+/g, "");
|
|
983
|
+
const idKey = arrayIdentifiers?.[genericPath] || "id";
|
|
984
|
+
const id = item[idKey];
|
|
985
|
+
compositeParts.push(String(id !== void 0 ? id : idx));
|
|
986
|
+
} else {
|
|
987
|
+
compositeParts.push(part);
|
|
988
|
+
}
|
|
989
|
+
current = item;
|
|
990
|
+
} else {
|
|
991
|
+
compositeParts.push(part);
|
|
992
|
+
current = current?.[part];
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
const compositeKey = compositeParts.join(".");
|
|
996
|
+
errors.push(compositeKey);
|
|
997
|
+
const currentMeta = newMetadata.get(compositeKey) || {
|
|
998
|
+
isTouched: false,
|
|
999
|
+
isInvalid: false,
|
|
1000
|
+
errorMessage: ""
|
|
1001
|
+
};
|
|
1002
|
+
newMetadata.set(compositeKey, {
|
|
1003
|
+
...currentMeta,
|
|
1004
|
+
isTouched: true,
|
|
1005
|
+
isInvalid: true,
|
|
1006
|
+
errorMessage: validationError.message
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
setMetadata(newMetadata);
|
|
1011
|
+
return { isValid: false, errors };
|
|
1012
|
+
};
|
|
1013
|
+
try {
|
|
1014
|
+
await validationSchema.validateAt(yupPath, stateRef.current, {
|
|
1015
|
+
abortEarly: false
|
|
1016
|
+
});
|
|
1017
|
+
setMetadata(cleanMetadata);
|
|
1018
|
+
return { isValid: true, errors: [] };
|
|
1019
|
+
} catch (err) {
|
|
1020
|
+
if (err.name === "ValidationError") {
|
|
1021
|
+
return handleErrors(err);
|
|
1022
|
+
}
|
|
1023
|
+
return { isValid: true, errors: [] };
|
|
1024
|
+
}
|
|
925
1025
|
}
|
|
926
1026
|
}),
|
|
927
1027
|
[getIndex, arrayIdentifiers, state, metadata]
|
|
@@ -1010,7 +1110,8 @@ function useForm(schema, {
|
|
|
1010
1110
|
const onFormSubmit = useCallback(
|
|
1011
1111
|
(fn) => async (e) => {
|
|
1012
1112
|
e.preventDefault();
|
|
1013
|
-
|
|
1113
|
+
const { isValid } = await validateAll();
|
|
1114
|
+
if (!isValid) return;
|
|
1014
1115
|
fn(stateRef.current, e);
|
|
1015
1116
|
},
|
|
1016
1117
|
[validateAll]
|
|
@@ -1045,9 +1146,8 @@ function useForm(schema, {
|
|
|
1045
1146
|
const { onSubmit, ...rest } = props;
|
|
1046
1147
|
const handleSubmit = async (e) => {
|
|
1047
1148
|
e.preventDefault();
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
}
|
|
1149
|
+
const { isValid } = await validateAllRef.current();
|
|
1150
|
+
if (!isValid) return;
|
|
1051
1151
|
onFormSubmitPropRef.current?.(stateRef.current, e);
|
|
1052
1152
|
if (resetOnSubmitRef.current) {
|
|
1053
1153
|
handleReset({ keepValues: keepValuesPropRef.current });
|