@m4l/components 9.4.27 → 9.4.28-BE20260210-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/DynamicForm/DynamicForm.d.ts +28 -0
- package/components/DynamicForm/DynamicForm.js +126 -0
- package/components/DynamicForm/DynamicForm.styles.d.ts +2 -0
- package/components/DynamicForm/DynamicForm.styles.js +93 -0
- package/components/DynamicForm/constants.d.ts +14 -0
- package/components/DynamicForm/constants.js +18 -0
- package/components/DynamicForm/helpers/normalizeValidations.d.ts +15 -0
- package/components/DynamicForm/helpers/normalizeValidations.js +192 -0
- package/components/DynamicForm/helpers/resolveFieldComponent.d.ts +16 -0
- package/components/DynamicForm/helpers/resolveFieldComponent.js +118 -0
- package/components/DynamicForm/helpers/resolveInitialValue.d.ts +14 -0
- package/components/DynamicForm/helpers/resolveInitialValue.js +99 -0
- package/components/DynamicForm/hooks/useDynamicForm/useDynamicForm.d.ts +14 -0
- package/components/DynamicForm/hooks/useDynamicForm/useDynamicForm.js +38 -0
- package/components/DynamicForm/hooks/useDynamicFormConditionals/useDynamicFormConditionals.d.ts +12 -0
- package/components/DynamicForm/hooks/useDynamicFormConditionals/useDynamicFormConditionals.js +69 -0
- package/components/DynamicForm/hooks/useDynamicFormData/types.d.ts +18 -0
- package/components/DynamicForm/hooks/useDynamicFormData/useDynamicFormData.d.ts +12 -0
- package/components/DynamicForm/hooks/useDynamicFormData/useDynamicFormData.js +128 -0
- package/components/DynamicForm/index.d.ts +4 -0
- package/components/DynamicForm/index.js +1 -0
- package/components/DynamicForm/slots/DynamicFormEnum.d.ts +14 -0
- package/components/DynamicForm/slots/DynamicFormEnum.js +15 -0
- package/components/DynamicForm/slots/DynamicFormSlots.d.ts +14 -0
- package/components/DynamicForm/slots/DynamicFormSlots.js +51 -0
- package/components/DynamicForm/slots/index.d.ts +3 -0
- package/components/DynamicForm/slots/index.js +1 -0
- package/components/DynamicForm/subcomponents/DynamicFormContent/DynamicFormContent.d.ts +2 -0
- package/components/DynamicForm/subcomponents/DynamicFormContent/DynamicFormContent.js +71 -0
- package/components/DynamicForm/subcomponents/DynamicFormCoupled/DynamicFormCoupled.d.ts +32 -0
- package/components/DynamicForm/subcomponents/DynamicFormCoupled/DynamicFormCoupled.js +45 -0
- package/components/DynamicForm/subcomponents/DynamicFormCoupledInternal/DynamicFormCoupledInternal.d.ts +7 -0
- package/components/DynamicForm/subcomponents/DynamicFormCoupledInternal/DynamicFormCoupledInternal.js +86 -0
- package/components/DynamicForm/subcomponents/DynamicFormCoupledInternalContent/DynamicFormCoupledInternalContent.d.ts +9 -0
- package/components/DynamicForm/subcomponents/DynamicFormCoupledInternalContent/DynamicFormCoupledInternalContent.js +62 -0
- package/components/DynamicForm/subcomponents/index.d.ts +4 -0
- package/components/DynamicForm/subcomponents/index.js +1 -0
- package/components/DynamicForm/types.d.ts +313 -0
- package/components/extended/mui/Autocomplete/Autocomplete.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/constants.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/dictionary.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/hooks/useAutocomplete/useAutocomplete.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/hooks/useEndAdornments.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/hooks/useMultipleChips.js +13 -10
- package/components/extended/mui/Autocomplete/hooks/useMultipleChips.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/hooks/useValuesAndHandlers.js +0 -5
- package/components/extended/mui/Autocomplete/hooks/useValuesAndHandlers.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/icons.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/subcomponents/LoadingText/LoadingText.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/subcomponents/NoOptionsText/NoOptionsText.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/subcomponents/PopperComponent/PopperComponent.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/subcomponents/RenderAdornment/RenderAdornment.js +2 -1
- package/components/extended/mui/Autocomplete/subcomponents/RenderAdornment/RenderAdornment.test.d.ts +1 -0
- package/components/extended/mui/Autocomplete/subcomponents/renderOptions/renderOptions.test.d.ts +1 -0
- package/components/hook-form/RHFCheckbox/index.js +1 -0
- package/components/hook-form/RHFCheckbox/types.js +1 -0
- package/components/index.d.ts +1 -0
- package/index.js +66 -60
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { DynamicFormProps, DynamicFormRef } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Componente DynamicForm que renderiza y edita formularios dinámicos
|
|
5
|
+
* a partir del JSON real enviado por la API, usando React Hook Form.
|
|
6
|
+
*
|
|
7
|
+
* Este componente interpreta el contrato API y renderiza los campos
|
|
8
|
+
* según las especificaciones del backend, incluyendo:
|
|
9
|
+
* - Validaciones dinámicas
|
|
10
|
+
* - Reglas condicionales
|
|
11
|
+
* - Valores iniciales
|
|
12
|
+
* - Tipos de campos diversos
|
|
13
|
+
*
|
|
14
|
+
* ### Dependencias:
|
|
15
|
+
* - **`useDynamicForm:`** Hook que gestiona la lógica del formulario dinámico.
|
|
16
|
+
* - **`resolveFieldComponent:`** Helper que resuelve el componente RHF correspondiente.
|
|
17
|
+
* @param {DynamicFormProps} props - Propiedades del componente.
|
|
18
|
+
* @returns {JSX.Element} Elemento renderizado.
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <DynamicForm
|
|
22
|
+
* data={formData}
|
|
23
|
+
* onSubmit={(data) => console.log(data)}
|
|
24
|
+
* size="medium"
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const DynamicForm: React.ForwardRefExoticComponent<DynamicFormProps & React.RefAttributes<DynamicFormRef>>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { forwardRef, useMemo, useImperativeHandle } from "react";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { useModuleSkeleton } from "@m4l/core";
|
|
5
|
+
import { T as TEST_PROP_ID } from "../../test/constants_no_mock.js";
|
|
6
|
+
import { L as LinearProgress } from "../extended/mui/LinearProgress/index.js";
|
|
7
|
+
import { D as DynamicFormRootStyled, a as DynamicFormFormProviderStyled } from "./slots/DynamicFormSlots.js";
|
|
8
|
+
import { D as DYNAMIC_FORM_CLASSES, E as EMPTY_DATA } from "./constants.js";
|
|
9
|
+
import { u as useDynamicForm } from "./hooks/useDynamicForm/useDynamicForm.js";
|
|
10
|
+
import { u as useDynamicFormData } from "./hooks/useDynamicFormData/useDynamicFormData.js";
|
|
11
|
+
import { u as useComponentSize } from "../../hooks/useComponentSize/useComponentSize.js";
|
|
12
|
+
import { D as DynamicFormCoupled } from "./subcomponents/DynamicFormCoupled/DynamicFormCoupled.js";
|
|
13
|
+
import { D as DynamicFormContent } from "./subcomponents/DynamicFormContent/DynamicFormContent.js";
|
|
14
|
+
const DynamicForm = forwardRef((props, ref) => {
|
|
15
|
+
const {
|
|
16
|
+
idForm,
|
|
17
|
+
idFormInstance,
|
|
18
|
+
inputEndpoint,
|
|
19
|
+
outputEndpoint,
|
|
20
|
+
data,
|
|
21
|
+
formatInputData,
|
|
22
|
+
formatOutputData,
|
|
23
|
+
onSubmit,
|
|
24
|
+
onSubmitError,
|
|
25
|
+
size,
|
|
26
|
+
className,
|
|
27
|
+
dataTestId,
|
|
28
|
+
fieldWrapperClassName,
|
|
29
|
+
coupled = false,
|
|
30
|
+
name,
|
|
31
|
+
actionFormIntroProps,
|
|
32
|
+
renderActions,
|
|
33
|
+
...others
|
|
34
|
+
} = props;
|
|
35
|
+
const { currentSize } = useComponentSize(size);
|
|
36
|
+
const isSkeleton = useModuleSkeleton();
|
|
37
|
+
const {
|
|
38
|
+
formData,
|
|
39
|
+
statusLoad,
|
|
40
|
+
handleSubmit
|
|
41
|
+
} = useDynamicFormData({
|
|
42
|
+
idForm,
|
|
43
|
+
idFormInstance,
|
|
44
|
+
inputEndpoint,
|
|
45
|
+
outputEndpoint,
|
|
46
|
+
initialData: data,
|
|
47
|
+
formatInputData,
|
|
48
|
+
formatOutputData,
|
|
49
|
+
onSubmit
|
|
50
|
+
});
|
|
51
|
+
const formDataToUse = formData || data;
|
|
52
|
+
const { initialValues, validationSchema } = useDynamicForm(formDataToUse || EMPTY_DATA);
|
|
53
|
+
const ownerState = useMemo(() => ({
|
|
54
|
+
size: currentSize
|
|
55
|
+
}), [currentSize]);
|
|
56
|
+
const formRef = React.useRef(null);
|
|
57
|
+
useImperativeHandle(ref, () => {
|
|
58
|
+
return formRef.current;
|
|
59
|
+
}, []);
|
|
60
|
+
if (!formDataToUse) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (isSkeleton) {
|
|
64
|
+
return /* @__PURE__ */ jsx(
|
|
65
|
+
DynamicFormRootStyled,
|
|
66
|
+
{
|
|
67
|
+
ownerState,
|
|
68
|
+
className: clsx(className, DYNAMIC_FORM_CLASSES.root),
|
|
69
|
+
...process.env.NODE_ENV !== "production" ? { [TEST_PROP_ID]: dataTestId } : {},
|
|
70
|
+
...others,
|
|
71
|
+
children: /* @__PURE__ */ jsx(LinearProgress, {})
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (coupled) {
|
|
76
|
+
if (!name) {
|
|
77
|
+
console.warn('DynamicForm: La prop "name" es requerida cuando el formulario está acoplado (coupled=true)');
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return /* @__PURE__ */ jsx(
|
|
81
|
+
DynamicFormCoupled,
|
|
82
|
+
{
|
|
83
|
+
name,
|
|
84
|
+
value: formDataToUse,
|
|
85
|
+
initialValues,
|
|
86
|
+
validationSchema,
|
|
87
|
+
ownerState,
|
|
88
|
+
currentSize,
|
|
89
|
+
fieldWrapperClassName,
|
|
90
|
+
dataTestId,
|
|
91
|
+
className,
|
|
92
|
+
...others
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return /* @__PURE__ */ jsx(
|
|
97
|
+
DynamicFormFormProviderStyled,
|
|
98
|
+
{
|
|
99
|
+
ref: formRef,
|
|
100
|
+
values: initialValues,
|
|
101
|
+
validationSchema,
|
|
102
|
+
onSubmit: outputEndpoint ? handleSubmit : onSubmit,
|
|
103
|
+
onSubmitError,
|
|
104
|
+
statusLoad,
|
|
105
|
+
mode: "onChange",
|
|
106
|
+
className: clsx(className, DYNAMIC_FORM_CLASSES.root, DYNAMIC_FORM_CLASSES.formProvider),
|
|
107
|
+
children: formDataToUse && /* @__PURE__ */ jsx(
|
|
108
|
+
DynamicFormContent,
|
|
109
|
+
{
|
|
110
|
+
data: formDataToUse,
|
|
111
|
+
ownerState,
|
|
112
|
+
currentSize,
|
|
113
|
+
fieldWrapperClassName,
|
|
114
|
+
dataTestId,
|
|
115
|
+
coupled,
|
|
116
|
+
actionFormIntroProps,
|
|
117
|
+
renderActions
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
DynamicForm.displayName = "DynamicForm";
|
|
124
|
+
export {
|
|
125
|
+
DynamicForm as D
|
|
126
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const componentStyles = {
|
|
2
|
+
/**
|
|
3
|
+
* Estilos para el elemento raíz del formulario dinámico.
|
|
4
|
+
*/
|
|
5
|
+
root: ({ theme }) => ({
|
|
6
|
+
display: "flex",
|
|
7
|
+
flexDirection: "column",
|
|
8
|
+
gap: theme.vars.size.baseSpacings.sp2,
|
|
9
|
+
width: "100%",
|
|
10
|
+
"& .MuiLinearProgress-root": {
|
|
11
|
+
backgroundColor: theme.vars.palette.primary.opacity
|
|
12
|
+
}
|
|
13
|
+
}),
|
|
14
|
+
/**
|
|
15
|
+
* Estilos para el wrapper del RHFormProvider.
|
|
16
|
+
*/
|
|
17
|
+
formProvider: () => ({
|
|
18
|
+
width: "100%"
|
|
19
|
+
}),
|
|
20
|
+
/**
|
|
21
|
+
* Estilos para el contenedor del contenido del formulario (div interno).
|
|
22
|
+
*/
|
|
23
|
+
contentRoot: ({ theme }) => ({
|
|
24
|
+
display: "flex",
|
|
25
|
+
flexDirection: "column",
|
|
26
|
+
gap: theme.vars.size.baseSpacings.sp2,
|
|
27
|
+
width: "100%",
|
|
28
|
+
padding: theme.vars.size.baseSpacings.sp2,
|
|
29
|
+
border: `1px solid ${theme.vars.palette.border.default}`,
|
|
30
|
+
borderRadius: theme.vars.size.borderRadius.r1
|
|
31
|
+
}),
|
|
32
|
+
/**
|
|
33
|
+
* Estilos para el wrapper de cada campo del formulario.
|
|
34
|
+
*/
|
|
35
|
+
fieldWrapper: ({ theme, ownerState }) => {
|
|
36
|
+
const ownerStateTyped = ownerState;
|
|
37
|
+
return {
|
|
38
|
+
display: "flex",
|
|
39
|
+
flexDirection: "column",
|
|
40
|
+
gap: theme.vars.size.baseSpacings.sp1,
|
|
41
|
+
width: "100%",
|
|
42
|
+
...ownerStateTyped?.readOnly && {
|
|
43
|
+
opacity: 0.7
|
|
44
|
+
},
|
|
45
|
+
...ownerStateTyped?.disabled && {
|
|
46
|
+
pointerEvents: "none",
|
|
47
|
+
opacity: 0.5
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* Estilos para el label de cada campo.
|
|
53
|
+
*/
|
|
54
|
+
label: () => ({
|
|
55
|
+
width: "100%"
|
|
56
|
+
}),
|
|
57
|
+
/**
|
|
58
|
+
* Estilos para el control (input) de cada campo.
|
|
59
|
+
*/
|
|
60
|
+
control: () => ({
|
|
61
|
+
width: "100%"
|
|
62
|
+
}),
|
|
63
|
+
/**
|
|
64
|
+
* Estilos para el helper text de cada campo.
|
|
65
|
+
*/
|
|
66
|
+
helperText: () => ({
|
|
67
|
+
width: "100%"
|
|
68
|
+
}),
|
|
69
|
+
/**
|
|
70
|
+
* Estilos para el divisor de sección (si se necesita en el futuro).
|
|
71
|
+
*/
|
|
72
|
+
sectionDivider: ({ theme }) => ({
|
|
73
|
+
width: "100%",
|
|
74
|
+
height: theme.vars.size.borderStroke.container,
|
|
75
|
+
backgroundColor: theme.vars.palette.border.main,
|
|
76
|
+
marginTop: theme.vars.size.baseSpacings.sp2,
|
|
77
|
+
marginBottom: theme.vars.size.baseSpacings.sp2
|
|
78
|
+
}),
|
|
79
|
+
/**
|
|
80
|
+
* Estilos para el wrapper del CheckableList que proporciona altura definida.
|
|
81
|
+
*/
|
|
82
|
+
checkableListWrapper: () => ({
|
|
83
|
+
width: "100%",
|
|
84
|
+
height: "400px",
|
|
85
|
+
// Altura fija para que el CheckableList pueda calcular su contenedor
|
|
86
|
+
minHeight: "400px",
|
|
87
|
+
display: "flex",
|
|
88
|
+
flexDirection: "column"
|
|
89
|
+
})
|
|
90
|
+
};
|
|
91
|
+
export {
|
|
92
|
+
componentStyles as c
|
|
93
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DynamicFormApiResponse } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Clave de identificación del componente dentro del sistema.
|
|
4
|
+
*
|
|
5
|
+
* Esta constante se utiliza como identificador único para asociar y personalizar estilos
|
|
6
|
+
* y configuraciones relacionadas con el componente dentro del sistema de temas y estilos.
|
|
7
|
+
* @default 'M4LDynamicForm'
|
|
8
|
+
*/
|
|
9
|
+
export declare const DYNAMIC_FORM_KEY_COMPONENT = "M4LDynamicForm";
|
|
10
|
+
/**
|
|
11
|
+
* Inventario de clases CSS para el componente
|
|
12
|
+
*/
|
|
13
|
+
export declare const DYNAMIC_FORM_CLASSES: Record<"label" | "root" | "control" | "helperText" | "formProvider" | "contentRoot" | "fieldWrapper" | "sectionDivider" | "checkableListWrapper", string>;
|
|
14
|
+
export declare const EMPTY_DATA: DynamicFormApiResponse;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { g as getComponentClasses } from "../../utils/getComponentSlotRoot.js";
|
|
2
|
+
import { D as DynamicFormSlots } from "./slots/DynamicFormEnum.js";
|
|
3
|
+
const DYNAMIC_FORM_KEY_COMPONENT = "M4LDynamicForm";
|
|
4
|
+
const DYNAMIC_FORM_CLASSES = getComponentClasses(
|
|
5
|
+
DYNAMIC_FORM_KEY_COMPONENT,
|
|
6
|
+
DynamicFormSlots
|
|
7
|
+
);
|
|
8
|
+
const EMPTY_DATA = {
|
|
9
|
+
form: { id: 0, name: "", version: 0, nameDictionaryId: 0, descriptionDictionaryId: 0 },
|
|
10
|
+
fields: [],
|
|
11
|
+
instance: { id: 0, name: null, createdAt: "", updatedAt: "", resourceTypeId: "", resourceSerialId: "" },
|
|
12
|
+
conditionalRules: []
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
DYNAMIC_FORM_CLASSES as D,
|
|
16
|
+
EMPTY_DATA as E,
|
|
17
|
+
DYNAMIC_FORM_KEY_COMPONENT as a
|
|
18
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Field, FieldValidation } from '../types';
|
|
2
|
+
import * as Yup from 'yup';
|
|
3
|
+
/**
|
|
4
|
+
* Normaliza una validación para soportar tanto `validationType` como `type`.
|
|
5
|
+
* El JSON puede venir inconsistente, por lo que este helper unifica el formato.
|
|
6
|
+
* @param validation - Validación del campo
|
|
7
|
+
* @returns Validación normalizada con `validationType` siempre presente
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeValidation(validation: FieldValidation): FieldValidation;
|
|
10
|
+
/**
|
|
11
|
+
* Crea las reglas de validación de Yup para un campo específico.
|
|
12
|
+
* @param field - Campo del formulario
|
|
13
|
+
* @returns Schema de Yup para el campo o undefined si no hay validaciones ni es requerido
|
|
14
|
+
*/
|
|
15
|
+
export declare function createFieldValidationSchema(field: Field): Yup.StringSchema | Yup.NumberSchema | Yup.BooleanSchema | Yup.ArraySchema<any, any, any, any> | undefined;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import * as Yup from "yup";
|
|
2
|
+
function normalizeValidation(validation) {
|
|
3
|
+
if (validation.validationType) {
|
|
4
|
+
return validation;
|
|
5
|
+
}
|
|
6
|
+
if (validation.type) {
|
|
7
|
+
return {
|
|
8
|
+
...validation,
|
|
9
|
+
validationType: validation.type
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return validation;
|
|
13
|
+
}
|
|
14
|
+
function createFieldValidationSchema(field) {
|
|
15
|
+
if ((!field.validations || field.validations.length === 0) && !field.isRequired) {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
let baseSchema;
|
|
19
|
+
const fieldType = field.fieldType.id;
|
|
20
|
+
const rhfComponent = field.fieldType.rhfComponent;
|
|
21
|
+
if (rhfComponent === "RHFCheckableList" || rhfComponent === "RHFDaysOfWeekPicker") {
|
|
22
|
+
baseSchema = Yup.array().of(Yup.mixed());
|
|
23
|
+
} else if (fieldType === "fft_number" || fieldType === "fft_integer" || fieldType === "fft_double") {
|
|
24
|
+
baseSchema = Yup.number();
|
|
25
|
+
} else if (fieldType === "fft_boolean" || fieldType === "fft_checkbox") {
|
|
26
|
+
baseSchema = Yup.boolean();
|
|
27
|
+
} else {
|
|
28
|
+
baseSchema = Yup.string();
|
|
29
|
+
}
|
|
30
|
+
const activeValidations = field.validations ? field.validations.filter((v) => v.isActive).map(normalizeValidation).sort((a, b) => a.displayOrder - b.displayOrder) : [];
|
|
31
|
+
let schema = baseSchema;
|
|
32
|
+
activeValidations.forEach((validation) => {
|
|
33
|
+
const validationType = validation.validationType || validation.type;
|
|
34
|
+
if (!validationType) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
switch (validationType.id) {
|
|
38
|
+
case "fvl_required":
|
|
39
|
+
if (field.isRequired) {
|
|
40
|
+
if (baseSchema instanceof Yup.StringSchema) {
|
|
41
|
+
schema = schema.nullable().required(validation.errorMessage || "Este campo es requerido");
|
|
42
|
+
} else if (baseSchema instanceof Yup.NumberSchema) {
|
|
43
|
+
schema = schema.required(validation.errorMessage || "Este campo es requerido");
|
|
44
|
+
} else if (baseSchema instanceof Yup.ArraySchema) {
|
|
45
|
+
schema = schema.min(1, validation.errorMessage || "Este campo es requerido");
|
|
46
|
+
} else {
|
|
47
|
+
schema = schema.required(validation.errorMessage || "Este campo es requerido");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
break;
|
|
51
|
+
/**
|
|
52
|
+
* Validación de longitud máxima.
|
|
53
|
+
*/
|
|
54
|
+
case "fvl_max_length":
|
|
55
|
+
if (validationType.requiresValue && validation.value && baseSchema instanceof Yup.StringSchema) {
|
|
56
|
+
const maxLength = parseInt(validation.value, 10);
|
|
57
|
+
if (!isNaN(maxLength)) {
|
|
58
|
+
schema = schema.max(maxLength, validation.errorMessage || `Máximo ${maxLength} caracteres`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
case "fvl_min_length":
|
|
63
|
+
if (validationType.requiresValue && validation.value && baseSchema instanceof Yup.StringSchema) {
|
|
64
|
+
const minLength = parseInt(validation.value, 10);
|
|
65
|
+
if (!isNaN(minLength)) {
|
|
66
|
+
schema = schema.min(minLength, validation.errorMessage || `Mínimo ${minLength} caracteres`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
case "fvl_max":
|
|
71
|
+
if (validationType.requiresValue && validation.value && baseSchema instanceof Yup.NumberSchema) {
|
|
72
|
+
const maxValue = parseFloat(validation.value);
|
|
73
|
+
if (!isNaN(maxValue)) {
|
|
74
|
+
schema = schema.max(maxValue, validation.errorMessage || `El valor máximo es ${maxValue}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
case "fvl_min":
|
|
79
|
+
if (validationType.requiresValue && validation.value && baseSchema instanceof Yup.NumberSchema) {
|
|
80
|
+
const minValue = parseFloat(validation.value);
|
|
81
|
+
if (!isNaN(minValue)) {
|
|
82
|
+
schema = schema.min(minValue, validation.errorMessage || `El valor mínimo es ${minValue}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
case "fvl_email":
|
|
87
|
+
if (baseSchema instanceof Yup.StringSchema) {
|
|
88
|
+
schema = schema.email(validation.errorMessage || "Debe ser un email válido");
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
case "fvl_url":
|
|
92
|
+
if (baseSchema instanceof Yup.StringSchema) {
|
|
93
|
+
schema = schema.url(validation.errorMessage || "Debe ser una URL válida");
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
case "fvl_pattern":
|
|
97
|
+
if (validationType.requiresValue && validation.value && baseSchema instanceof Yup.StringSchema) {
|
|
98
|
+
try {
|
|
99
|
+
const regex = new RegExp(validation.value);
|
|
100
|
+
schema = schema.matches(regex, validation.errorMessage || "El formato no es válido");
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.warn(`Patrón regex inválido: ${validation.value}`, e);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
/**
|
|
107
|
+
* Validación personalizada (custom).
|
|
108
|
+
* Permite implementar validaciones específicas usando una función de test.
|
|
109
|
+
* El valor de la validación puede contener:
|
|
110
|
+
* - Una expresión regular para validar strings
|
|
111
|
+
* - Un valor de comparación para números
|
|
112
|
+
* - Una función de validación personalizada (si se extiende el sistema)
|
|
113
|
+
*/
|
|
114
|
+
case "fvl_custom":
|
|
115
|
+
if (validationType.requiresValue && validation.value) {
|
|
116
|
+
if (baseSchema instanceof Yup.StringSchema) {
|
|
117
|
+
schema = schema.test(
|
|
118
|
+
"custom",
|
|
119
|
+
validation.errorMessage || "El valor no cumple con la validación personalizada",
|
|
120
|
+
(value) => {
|
|
121
|
+
if (!value) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const regex = new RegExp(validation.value);
|
|
126
|
+
return regex.test(value);
|
|
127
|
+
} catch {
|
|
128
|
+
return value === validation.value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
} else if (baseSchema instanceof Yup.NumberSchema) {
|
|
133
|
+
schema = schema.test(
|
|
134
|
+
"custom",
|
|
135
|
+
validation.errorMessage || "El valor no cumple con la validación personalizada",
|
|
136
|
+
(value) => {
|
|
137
|
+
if (value === void 0 || value === null) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
const customValue = parseFloat(validation.value);
|
|
141
|
+
if (!isNaN(customValue)) {
|
|
142
|
+
return value === customValue;
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
if (baseSchema instanceof Yup.StringSchema) {
|
|
150
|
+
schema = schema.test(
|
|
151
|
+
"custom",
|
|
152
|
+
validation.errorMessage || "El valor no cumple con la validación personalizada",
|
|
153
|
+
(value) => {
|
|
154
|
+
if (!value) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
console.warn(`Tipo de validación no soportado: ${validationType.id}`);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
if (field.isRequired && !activeValidations.some((v) => {
|
|
168
|
+
const vt = v.validationType || v.type;
|
|
169
|
+
return vt?.id === "fvl_required";
|
|
170
|
+
})) {
|
|
171
|
+
if (baseSchema instanceof Yup.StringSchema) {
|
|
172
|
+
schema = schema.nullable().required("Este campo es requerido");
|
|
173
|
+
} else if (baseSchema instanceof Yup.NumberSchema) {
|
|
174
|
+
schema = schema.required("Este campo es requerido");
|
|
175
|
+
} else if (baseSchema instanceof Yup.ArraySchema) {
|
|
176
|
+
schema = schema.min(1, "Este campo es requerido");
|
|
177
|
+
} else {
|
|
178
|
+
schema = schema.required("Este campo es requerido");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (!field.isRequired) {
|
|
182
|
+
if (baseSchema instanceof Yup.ArraySchema) {
|
|
183
|
+
schema = schema.default([]);
|
|
184
|
+
} else {
|
|
185
|
+
schema = schema.nullable();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return schema;
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
createFieldValidationSchema as c
|
|
192
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { Field } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Resuelve el componente RHF correspondiente según el tipo de campo.
|
|
5
|
+
* @param field - Campo del formulario dinámico
|
|
6
|
+
* @param props - Props adicionales para el componente (size, disabled, etc.)
|
|
7
|
+
* @returns Componente RHF renderizado o null si no se encuentra el componente
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveFieldComponent(field: Field, props: {
|
|
10
|
+
name: string;
|
|
11
|
+
size?: 'small' | 'medium';
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
readOnly?: boolean;
|
|
14
|
+
placeholder?: string | null;
|
|
15
|
+
rhfComponentProps?: Record<string, unknown>;
|
|
16
|
+
}): ReactElement | null;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { R as RHFDaysOfWeekPicker } from "../../hook-form/RHFDaysOfWeekPicker/RHFDaysOfWeekPicker.js";
|
|
3
|
+
import { b as DynamicFormCheckableListWrapperStyled } from "../slots/DynamicFormSlots.js";
|
|
4
|
+
import { R as RHFCheckableList } from "../../hook-form/RHFCheckableList/RHFCheckableList.js";
|
|
5
|
+
import { R as RHFCheckbox } from "../../hook-form/RHFCheckbox/RHFCheckbox.js";
|
|
6
|
+
import { R as RHFSelect } from "../../hook-form/RHFSelect/RHFSelect.js";
|
|
7
|
+
import { R as RHFNumberInput } from "../../hook-form/RHFNumberInput/RHFNumberInput.js";
|
|
8
|
+
import { R as RHFTextField } from "../../hook-form/RHFTextField/RHFTextField.js";
|
|
9
|
+
function resolveFieldComponent(field, props) {
|
|
10
|
+
const { name, size = "medium", disabled, readOnly, placeholder, rhfComponentProps = {} } = props;
|
|
11
|
+
const componentName = field.fieldType.rhfComponent;
|
|
12
|
+
const selectOptions = field.options.map((option) => ({
|
|
13
|
+
id: option.value,
|
|
14
|
+
label: option.name || option.value
|
|
15
|
+
}));
|
|
16
|
+
const checkableListItems = field.options.map((option) => ({
|
|
17
|
+
id: option.id,
|
|
18
|
+
label: option.name || option.value,
|
|
19
|
+
value: option.value
|
|
20
|
+
}));
|
|
21
|
+
switch (componentName) {
|
|
22
|
+
case "RHFTextField":
|
|
23
|
+
return /* @__PURE__ */ jsx(
|
|
24
|
+
RHFTextField,
|
|
25
|
+
{
|
|
26
|
+
name,
|
|
27
|
+
size,
|
|
28
|
+
label: field.name,
|
|
29
|
+
mandatory: field.isRequired,
|
|
30
|
+
helperMessage: field.helperText || "",
|
|
31
|
+
disabled: disabled || readOnly,
|
|
32
|
+
placeholder: placeholder || void 0,
|
|
33
|
+
...rhfComponentProps
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
case "RHFSelect":
|
|
37
|
+
return /* @__PURE__ */ jsx(
|
|
38
|
+
RHFSelect,
|
|
39
|
+
{
|
|
40
|
+
name,
|
|
41
|
+
size,
|
|
42
|
+
label: field.name,
|
|
43
|
+
mandatory: field.isRequired,
|
|
44
|
+
options: selectOptions,
|
|
45
|
+
helperMessage: field.helperText || "",
|
|
46
|
+
disabled: disabled || readOnly,
|
|
47
|
+
placeholder: placeholder || void 0,
|
|
48
|
+
...rhfComponentProps
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
case "RHFNumberInput":
|
|
52
|
+
return /* @__PURE__ */ jsx(
|
|
53
|
+
RHFNumberInput,
|
|
54
|
+
{
|
|
55
|
+
name,
|
|
56
|
+
size,
|
|
57
|
+
label: field.name,
|
|
58
|
+
mandatory: field.isRequired,
|
|
59
|
+
helperMessage: field.helperText || "",
|
|
60
|
+
disabled: disabled || readOnly,
|
|
61
|
+
...rhfComponentProps
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
case "RHFCheckbox":
|
|
65
|
+
return /* @__PURE__ */ jsx(
|
|
66
|
+
RHFCheckbox,
|
|
67
|
+
{
|
|
68
|
+
name,
|
|
69
|
+
size,
|
|
70
|
+
label: field.name,
|
|
71
|
+
mandatory: field.isRequired,
|
|
72
|
+
helperMessage: field.helperText || "",
|
|
73
|
+
disabled: disabled || readOnly,
|
|
74
|
+
...rhfComponentProps
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
case "RHFCheckableList":
|
|
78
|
+
const hasGroups = rhfComponentProps?.groups !== void 0 && Array.isArray(rhfComponentProps.groups) && rhfComponentProps.groups.length > 0;
|
|
79
|
+
const { groups: groupsFromProps, searchable: searchableFromProps, ...otherRHFProps } = rhfComponentProps || {};
|
|
80
|
+
const searchable = searchableFromProps !== void 0 && searchableFromProps !== null ? Boolean(searchableFromProps) : hasGroups;
|
|
81
|
+
return /* @__PURE__ */ jsx(DynamicFormCheckableListWrapperStyled, { children: /* @__PURE__ */ jsx(
|
|
82
|
+
RHFCheckableList,
|
|
83
|
+
{
|
|
84
|
+
name,
|
|
85
|
+
size,
|
|
86
|
+
label: field.name,
|
|
87
|
+
mandatory: field.isRequired,
|
|
88
|
+
items: hasGroups ? void 0 : checkableListItems,
|
|
89
|
+
groups: hasGroups ? groupsFromProps : void 0,
|
|
90
|
+
helperMessage: field.helperText || "",
|
|
91
|
+
disabled: disabled || readOnly,
|
|
92
|
+
searchable,
|
|
93
|
+
...otherRHFProps
|
|
94
|
+
}
|
|
95
|
+
) });
|
|
96
|
+
case "RHFDaysOfWeekPicker":
|
|
97
|
+
return (
|
|
98
|
+
// @ts-expect-error - value es proporcionado automáticamente por withRHFController
|
|
99
|
+
/* @__PURE__ */ jsx(
|
|
100
|
+
RHFDaysOfWeekPicker,
|
|
101
|
+
{
|
|
102
|
+
name,
|
|
103
|
+
size,
|
|
104
|
+
label: field.name,
|
|
105
|
+
mandatory: field.isRequired,
|
|
106
|
+
helperMessage: field.helperText || "",
|
|
107
|
+
disabled: disabled || readOnly,
|
|
108
|
+
...rhfComponentProps
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
default:
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
resolveFieldComponent as r
|
|
118
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Field } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Resuelve el valor inicial de un campo a partir de current_value o default_value.
|
|
4
|
+
*
|
|
5
|
+
* El componente debe resolver dinámicamente el valor según:
|
|
6
|
+
* - value_string
|
|
7
|
+
* - value_integer
|
|
8
|
+
* - value_double
|
|
9
|
+
* - value_boolean
|
|
10
|
+
* - value_timestamp
|
|
11
|
+
* @param field - Campo del formulario
|
|
12
|
+
* @returns Valor inicial resuelto o undefined (puede ser array para RHFCheckableList)
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveInitialValue(field: Field): string | number | boolean | (string | number)[] | null | undefined;
|