@m4l/components 9.3.15-BE100925-beta.1 → 9.3.15-JT19092025.beta.1
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/components/Stepper/hooks/useDynamicValidation/index.d.ts +9 -0
- package/components/Stepper/hooks/useDynamicValidation/index.js +57 -0
- package/components/Stepper/hooks/useStepperActions/index.js +21 -3
- package/components/Stepper/subcomponents/StepArea/index.js +4 -0
- package/components/hook-form/RHFAutocomplete/RHFAutocomplete.js +35 -3
- package/components/hook-form/RHFAutocomplete/types.d.ts +6 -1
- package/components/hook-form/RHFormContext/index.d.ts +1 -1
- package/components/hook-form/RHFormContext/index.js +29 -4
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook que simula validación onChange de campos específicos del Stepper, manteniendo el formulario en modo onSubmit para preservar el rendimiento.
|
|
3
|
+
* @returns Objeto con funciones para manejar la validación dinámica
|
|
4
|
+
*/
|
|
5
|
+
export declare function useDynamicValidation(): {
|
|
6
|
+
activateFieldsValidation: (fields: string[]) => void;
|
|
7
|
+
clearAllValidation: () => void;
|
|
8
|
+
activeFields: string[];
|
|
9
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useState, useRef, useEffect, useCallback } from "react";
|
|
2
|
+
import { useFormContext, useWatch } from "react-hook-form";
|
|
3
|
+
function useDynamicValidation() {
|
|
4
|
+
const { trigger } = useFormContext();
|
|
5
|
+
const [activeFields, setActiveFields] = useState([]);
|
|
6
|
+
const watchValues = useWatch({
|
|
7
|
+
name: activeFields.length > 0 ? activeFields : ["__dummy__"]
|
|
8
|
+
});
|
|
9
|
+
const lastValuesRef = useRef({});
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (activeFields.length === 0) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const currentValues = activeFields.length === 1 ? { [activeFields[0]]: watchValues } : activeFields.reduce((acc, field, index) => {
|
|
15
|
+
acc[field] = Array.isArray(watchValues) ? watchValues[index] : watchValues;
|
|
16
|
+
return acc;
|
|
17
|
+
}, {});
|
|
18
|
+
const changedFields = activeFields.filter((field) => {
|
|
19
|
+
const currentValue = currentValues[field];
|
|
20
|
+
const lastValue = lastValuesRef.current[field];
|
|
21
|
+
const hasChanged = JSON.stringify(currentValue) !== JSON.stringify(lastValue);
|
|
22
|
+
if (hasChanged) {
|
|
23
|
+
lastValuesRef.current[field] = currentValue;
|
|
24
|
+
}
|
|
25
|
+
return hasChanged;
|
|
26
|
+
});
|
|
27
|
+
if (changedFields.length > 0) {
|
|
28
|
+
const timeoutId = setTimeout(() => {
|
|
29
|
+
trigger(changedFields);
|
|
30
|
+
}, 100);
|
|
31
|
+
return () => clearTimeout(timeoutId);
|
|
32
|
+
}
|
|
33
|
+
}, [activeFields, trigger, watchValues]);
|
|
34
|
+
const activateFieldsValidation = useCallback((fields) => {
|
|
35
|
+
setActiveFields((prev) => {
|
|
36
|
+
const newFields = fields.filter((field) => !prev.includes(field));
|
|
37
|
+
return [...prev, ...newFields];
|
|
38
|
+
});
|
|
39
|
+
fields.forEach((field) => {
|
|
40
|
+
if (!lastValuesRef.current.hasOwnProperty(field)) {
|
|
41
|
+
lastValuesRef.current[field] = void 0;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}, []);
|
|
45
|
+
const clearAllValidation = useCallback(() => {
|
|
46
|
+
setActiveFields([]);
|
|
47
|
+
lastValuesRef.current = {};
|
|
48
|
+
}, []);
|
|
49
|
+
return {
|
|
50
|
+
activateFieldsValidation,
|
|
51
|
+
clearAllValidation,
|
|
52
|
+
activeFields
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
useDynamicValidation as u
|
|
57
|
+
};
|
|
@@ -2,8 +2,10 @@ import { useCallback } from "react";
|
|
|
2
2
|
import { u as useStepper } from "../useStepper/index.js";
|
|
3
3
|
import { useFormContext } from "react-hook-form";
|
|
4
4
|
import { shallow } from "zustand/shallow";
|
|
5
|
+
import { u as useDynamicValidation } from "../useDynamicValidation/index.js";
|
|
5
6
|
function useStepperActions() {
|
|
6
7
|
const { trigger, clearErrors, getValues, reset } = useFormContext();
|
|
8
|
+
const { activateFieldsValidation, clearAllValidation } = useDynamicValidation();
|
|
7
9
|
const {
|
|
8
10
|
nextStep,
|
|
9
11
|
prevStep,
|
|
@@ -41,9 +43,22 @@ function useStepperActions() {
|
|
|
41
43
|
if (fieldsToValidate.length === 0) {
|
|
42
44
|
return true;
|
|
43
45
|
}
|
|
44
|
-
|
|
46
|
+
const result = await trigger(fieldsToValidate);
|
|
47
|
+
if (!result) {
|
|
48
|
+
activateFieldsValidation(fieldsToValidate);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
45
51
|
};
|
|
46
52
|
const success = await nextStep(validateFn, formData);
|
|
53
|
+
if (success) {
|
|
54
|
+
const currentStepData = steps[currentStep - 1];
|
|
55
|
+
const fieldsJustValidated = currentStepData?.validationFields || [];
|
|
56
|
+
if (fieldsJustValidated.length > 0) {
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
clearAllValidation();
|
|
59
|
+
}, 100);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
47
62
|
if (success && futureFields.length > 0) {
|
|
48
63
|
clearErrors(futureFields);
|
|
49
64
|
setTimeout(() => {
|
|
@@ -62,13 +77,16 @@ function useStepperActions() {
|
|
|
62
77
|
steps,
|
|
63
78
|
currentStep,
|
|
64
79
|
trigger,
|
|
65
|
-
getValues
|
|
80
|
+
getValues,
|
|
81
|
+
activateFieldsValidation,
|
|
82
|
+
clearAllValidation
|
|
66
83
|
]);
|
|
67
84
|
const cancelAction = useCallback(() => {
|
|
68
85
|
reset();
|
|
69
86
|
clearErrors();
|
|
87
|
+
clearAllValidation();
|
|
70
88
|
resetStepper();
|
|
71
|
-
}, [reset, clearErrors, resetStepper]);
|
|
89
|
+
}, [reset, clearErrors, clearAllValidation, resetStepper]);
|
|
72
90
|
return { prevStepAction, nextStepAction, cancelAction };
|
|
73
91
|
}
|
|
74
92
|
export {
|
|
@@ -5,10 +5,12 @@ import { u as useStepper } from "../../hooks/useStepper/index.js";
|
|
|
5
5
|
import { e as StepAreaStyled, f as StepStyled, g as StepNameStyled } from "../../slots/StepperSlot.js";
|
|
6
6
|
import { I as Indicator } from "./subcomponents/Inidicator/index.js";
|
|
7
7
|
import { shallow } from "zustand/shallow";
|
|
8
|
+
import { u as useDynamicValidation } from "../../hooks/useDynamicValidation/index.js";
|
|
8
9
|
import { e as evaluateVisibilityStepCondition } from "../../helpers/evaluateVisibilityStepCondition/index.js";
|
|
9
10
|
function StepArea() {
|
|
10
11
|
const { trigger, clearErrors } = useFormContext();
|
|
11
12
|
const formValues = useWatch();
|
|
13
|
+
const { activateFieldsValidation } = useDynamicValidation();
|
|
12
14
|
const {
|
|
13
15
|
currentStep,
|
|
14
16
|
steps,
|
|
@@ -64,6 +66,7 @@ function StepArea() {
|
|
|
64
66
|
if (!isValid) {
|
|
65
67
|
setCurrentStep(stepOriginalIndex);
|
|
66
68
|
setStepValidationStatus(stepOriginalIndex, false);
|
|
69
|
+
activateFieldsValidation(step.validationFields || []);
|
|
67
70
|
return;
|
|
68
71
|
}
|
|
69
72
|
setStepValidationStatus(stepOriginalIndex, true);
|
|
@@ -76,6 +79,7 @@ function StepArea() {
|
|
|
76
79
|
const isCurrentValid = await trigger(currentStepData.validationFields);
|
|
77
80
|
if (!isCurrentValid) {
|
|
78
81
|
setStepValidationStatus(currentStepOriginalIndex, false);
|
|
82
|
+
activateFieldsValidation(currentStepData.validationFields || []);
|
|
79
83
|
return;
|
|
80
84
|
}
|
|
81
85
|
setStepValidationStatus(currentStepOriginalIndex, true);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { getPropertyByString } from "@m4l/core";
|
|
3
|
+
import { useIsMobile } from "@m4l/graphics";
|
|
4
|
+
import { useTheme } from "@mui/material";
|
|
2
5
|
import { useId, useState, useCallback, useEffect } from "react";
|
|
3
6
|
import { useFormContext, Controller } from "react-hook-form";
|
|
4
7
|
import { A as AutocompleteRootStyled, L as LabelStyled } from "./slots/RHFAutocompleteSlots.js";
|
|
@@ -10,22 +13,28 @@ function RHFAutocomplete(props) {
|
|
|
10
13
|
getOptionLabel,
|
|
11
14
|
isOptionEqualToValue,
|
|
12
15
|
label,
|
|
16
|
+
color,
|
|
13
17
|
options,
|
|
14
18
|
disabled,
|
|
15
19
|
onOpen,
|
|
16
20
|
onClose,
|
|
17
21
|
loading,
|
|
22
|
+
variant,
|
|
18
23
|
helperMessage,
|
|
19
24
|
size,
|
|
20
25
|
onChangeFilterParmsLocal,
|
|
21
26
|
mandatory,
|
|
22
27
|
mandatoryMessage,
|
|
23
28
|
multiple,
|
|
29
|
+
imageScale = true,
|
|
30
|
+
imageRepeat,
|
|
24
31
|
refresh
|
|
25
32
|
// onChange: onChangeRHF,
|
|
26
33
|
} = props;
|
|
27
34
|
const htmlForId = useId();
|
|
35
|
+
const theme = useTheme();
|
|
28
36
|
const [open, setOpen] = useState(false);
|
|
37
|
+
const isDesktop = !useIsMobile();
|
|
29
38
|
const onCloseLocal = useCallback((event, reason) => {
|
|
30
39
|
setOpen(false);
|
|
31
40
|
if (onClose) {
|
|
@@ -50,11 +59,34 @@ function RHFAutocomplete(props) {
|
|
|
50
59
|
},
|
|
51
60
|
[getOptionLabel]
|
|
52
61
|
);
|
|
62
|
+
const paletteColor = getPropertyByString(
|
|
63
|
+
theme.vars.palette,
|
|
64
|
+
disabled ? "default" : color || "default",
|
|
65
|
+
theme.vars.palette.default
|
|
66
|
+
);
|
|
53
67
|
const {
|
|
54
|
-
control
|
|
68
|
+
control,
|
|
69
|
+
formState: { errors }
|
|
55
70
|
} = useFormContext();
|
|
71
|
+
const [currentVariant, setCurrentVariant] = useState(null);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const hasError = errors[nameRHF];
|
|
74
|
+
if (hasError) {
|
|
75
|
+
setCurrentVariant("error");
|
|
76
|
+
} else if (variant) {
|
|
77
|
+
setCurrentVariant(variant);
|
|
78
|
+
} else {
|
|
79
|
+
setCurrentVariant(null);
|
|
80
|
+
}
|
|
81
|
+
}, [errors, nameRHF, variant, control]);
|
|
56
82
|
const ownerState = {
|
|
57
|
-
|
|
83
|
+
size: !isDesktop ? "medium" : size,
|
|
84
|
+
semantics: currentVariant,
|
|
85
|
+
disabled,
|
|
86
|
+
multiple: Boolean(multiple),
|
|
87
|
+
imageScale: Boolean(imageScale),
|
|
88
|
+
imageRepeat: Boolean(imageRepeat),
|
|
89
|
+
paletteColor
|
|
58
90
|
};
|
|
59
91
|
return /* @__PURE__ */ jsx(
|
|
60
92
|
AutocompleteRootStyled,
|
|
@@ -115,7 +147,7 @@ function RHFAutocomplete(props) {
|
|
|
115
147
|
htmlFor: htmlForId
|
|
116
148
|
}
|
|
117
149
|
),
|
|
118
|
-
error
|
|
150
|
+
currentVariant === "error" ? /* @__PURE__ */ jsx(HelperError, { message: error?.message }) : null
|
|
119
151
|
] });
|
|
120
152
|
}
|
|
121
153
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AutocompleteCloseReason, AutocompleteFreeSoloValueMapping, AutocompleteInputChangeReason, AutocompleteProps as MUIAutocompleteProps, Theme, PopperProps } from '@mui/material';
|
|
1
|
+
import { AutocompleteCloseReason, AutocompleteFreeSoloValueMapping, AutocompleteInputChangeReason, AutocompleteProps as MUIAutocompleteProps, Theme, PaletteColor, PopperProps } from '@mui/material';
|
|
2
2
|
import { ComponentPalletColor, Sizes } from '@m4l/styles';
|
|
3
3
|
import { TextFieldProps } from '../../mui_extended/TextField/types';
|
|
4
4
|
import { RFHAUTOCOMPLETE_KEY_COMPONENT } from './constants';
|
|
@@ -52,6 +52,11 @@ export interface RHFAutocompleteProps<T = any, Multiple extends boolean | undefi
|
|
|
52
52
|
*/
|
|
53
53
|
export interface RHFAutocompleteOwnerState extends Pick<RHFAutocompleteProps<any>, 'size' | 'disabled' | 'variant'> {
|
|
54
54
|
disabled?: boolean;
|
|
55
|
+
semantics: RHFAutocompleteVariants | 'error' | null;
|
|
56
|
+
multiple: boolean;
|
|
57
|
+
imageScale?: boolean;
|
|
58
|
+
imageRepeat?: boolean;
|
|
59
|
+
paletteColor: PaletteColor;
|
|
55
60
|
}
|
|
56
61
|
/**
|
|
57
62
|
* Defines the types of Slots available for the Autocomplete.
|
|
@@ -3,7 +3,7 @@ import { CustomFormArguments, FormProviderCustomProps, FormProviderProps } from
|
|
|
3
3
|
/**
|
|
4
4
|
* TODO: Documentar
|
|
5
5
|
*/
|
|
6
|
-
export declare function useCustomForm({ validationSchema, values, statusLoad, mode }: CustomFormArguments): import('react-hook-form').UseFormReturn<FieldValues, any, FieldValues>;
|
|
6
|
+
export declare function useCustomForm({ validationSchema, values, statusLoad, mode, }: CustomFormArguments): import('react-hook-form').UseFormReturn<FieldValues, any, FieldValues>;
|
|
7
7
|
/**
|
|
8
8
|
* TODO: Documentar
|
|
9
9
|
*/
|
|
@@ -6,7 +6,12 @@ import { yupResolver } from "@hookform/resolvers/yup";
|
|
|
6
6
|
import { F as FormProviderRoot } from "./styles.js";
|
|
7
7
|
import { R as RHFormProviderUtilityClasses } from "./classes/index.js";
|
|
8
8
|
const classes = RHFormProviderUtilityClasses();
|
|
9
|
-
function useCustomForm({
|
|
9
|
+
function useCustomForm({
|
|
10
|
+
validationSchema,
|
|
11
|
+
values,
|
|
12
|
+
statusLoad,
|
|
13
|
+
mode
|
|
14
|
+
}) {
|
|
10
15
|
const formMethods = useForm({
|
|
11
16
|
resolver: yupResolver(validationSchema),
|
|
12
17
|
defaultValues: values,
|
|
@@ -39,11 +44,31 @@ function useCustomForm({ validationSchema, values, statusLoad, mode }) {
|
|
|
39
44
|
}
|
|
40
45
|
function FormProviderCustom(props) {
|
|
41
46
|
const { children, onSubmit, className, handleSubmit } = props;
|
|
42
|
-
return /* @__PURE__ */ jsx(FormProvider, { ...props, children: /* @__PURE__ */ jsx(
|
|
47
|
+
return /* @__PURE__ */ jsx(FormProvider, { ...props, children: /* @__PURE__ */ jsx(
|
|
48
|
+
FormProviderRoot,
|
|
49
|
+
{
|
|
50
|
+
className: clsx(classes.root, className),
|
|
51
|
+
onSubmit: handleSubmit(onSubmit),
|
|
52
|
+
children
|
|
53
|
+
}
|
|
54
|
+
) });
|
|
43
55
|
}
|
|
44
56
|
function RHFormProvider(props) {
|
|
45
|
-
const {
|
|
46
|
-
|
|
57
|
+
const {
|
|
58
|
+
children,
|
|
59
|
+
onSubmit,
|
|
60
|
+
values,
|
|
61
|
+
validationSchema,
|
|
62
|
+
statusLoad = "ready",
|
|
63
|
+
className,
|
|
64
|
+
mode
|
|
65
|
+
} = props;
|
|
66
|
+
const formMethods = useCustomForm({
|
|
67
|
+
validationSchema,
|
|
68
|
+
statusLoad,
|
|
69
|
+
values,
|
|
70
|
+
mode
|
|
71
|
+
});
|
|
47
72
|
return /* @__PURE__ */ jsx(
|
|
48
73
|
FormProviderCustom,
|
|
49
74
|
{
|