@m4l/components 9.3.19 → 9.3.20-JT10102025.beta.2
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/DynamicFilter/helpers/formatToInitialFilters.js +3 -3
- package/components/DynamicFilter/subcomponents/FieldTypes/BooleanFilter/helpers.js +11 -3
- package/components/DynamicFilter/subcomponents/FieldTypes/DateTimeFilter/helpers.js +16 -4
- package/components/DynamicFilter/subcomponents/FieldTypes/NumberFilter/helpers.js +11 -8
- package/components/DynamicFilter/subcomponents/FieldTypes/SelectAsyncFilter/helpers.js +21 -8
- package/components/DynamicFilter/subcomponents/FieldTypes/SelectFilter/helpers.js +18 -8
- package/components/DynamicFilter/subcomponents/FieldTypes/StringFilter/helpers.js +5 -3
- package/components/Stepper/Stepper.styles.js +17 -14
- package/components/Stepper/helpers/getInitialFieldValues/index.d.ts +12 -0
- package/components/Stepper/helpers/getInitialFieldValues/index.js +14 -0
- package/components/Stepper/helpers/index.d.ts +2 -0
- package/components/Stepper/helpers/parseWatchedValues/index.d.ts +17 -0
- package/components/Stepper/helpers/parseWatchedValues/index.js +12 -0
- package/components/Stepper/hooks/useDynamicValidation/index.d.ts +11 -0
- package/components/Stepper/hooks/useDynamicValidation/index.js +69 -0
- package/components/Stepper/hooks/useStepperActions/index.js +19 -3
- package/components/Stepper/store/StepperStore/index.js +20 -1
- package/components/Stepper/subcomponents/StepArea/index.js +45 -25
- package/components/Stepper/subcomponents/StepArea/subcomponents/Inidicator/index.js +18 -10
- package/components/Stepper/subcomponents/StepperButtons/StepperCancelButton/index.d.ts +2 -1
- package/components/Stepper/subcomponents/StepperButtons/StepperCancelButton/index.js +38 -5
- package/components/Stepper/subcomponents/StepperButtons/StepperNextButton/index.d.ts +2 -1
- package/components/Stepper/subcomponents/StepperButtons/StepperNextButton/index.js +5 -3
- package/components/Stepper/subcomponents/StepperButtons/StepperPrevButton/index.d.ts +2 -1
- package/components/Stepper/subcomponents/StepperButtons/StepperPrevButton/index.js +5 -3
- package/components/Stepper/subcomponents/StepperButtons/StepperSubmitButton/index.d.ts +2 -1
- package/components/Stepper/subcomponents/StepperButtons/StepperSubmitButton/index.js +5 -3
- package/components/Stepper/subcomponents/StepperContent/subcomponents/Step/index.js +30 -11
- package/components/Stepper/subcomponents/StepperFooter/subcomponents/StepperFooterRightActions/index.js +20 -10
- package/components/Stepper/types.d.ts +7 -0
- package/components/hook-form/RHFormContext/index.d.ts +1 -1
- package/components/hook-form/RHFormContext/index.js +29 -4
- package/package.json +1 -1
|
@@ -30,9 +30,9 @@ function formatToInitialFilters(appliedFilters) {
|
|
|
30
30
|
return {
|
|
31
31
|
name: af.field.name,
|
|
32
32
|
operator: af.operator,
|
|
33
|
-
...operand1 ? { operand1 } : {},
|
|
34
|
-
...operand2 ? { operand2 } : {},
|
|
35
|
-
...operandArray ? { operandArray } : {}
|
|
33
|
+
...operand1 !== void 0 ? { operand1 } : {},
|
|
34
|
+
...operand2 !== void 0 ? { operand2 } : {},
|
|
35
|
+
...operandArray !== void 0 ? { operandArray } : {}
|
|
36
36
|
};
|
|
37
37
|
});
|
|
38
38
|
}
|
|
@@ -11,12 +11,16 @@ class BooleanFilterHelpers {
|
|
|
11
11
|
*/
|
|
12
12
|
getDefaultFilter(field, fixed) {
|
|
13
13
|
const defaultOperand1 = field.defaultOperand1;
|
|
14
|
+
let isSet = false;
|
|
15
|
+
if (defaultOperand1 !== void 0 && typeof defaultOperand1 === "boolean") {
|
|
16
|
+
isSet = true;
|
|
17
|
+
}
|
|
14
18
|
const defaultFilter = {
|
|
15
19
|
id: 0,
|
|
16
20
|
fieldType: "boolean",
|
|
17
21
|
field,
|
|
18
22
|
fixed,
|
|
19
|
-
isSet
|
|
23
|
+
isSet,
|
|
20
24
|
operator: "e",
|
|
21
25
|
operand1: true
|
|
22
26
|
};
|
|
@@ -45,7 +49,9 @@ class BooleanFilterHelpers {
|
|
|
45
49
|
const operator = filterValueBoolean ? filterValueBoolean.operator : field.defaultOperator ?? "e";
|
|
46
50
|
const formValueOperand1 = filterValueBoolean ? {
|
|
47
51
|
value: filterValueBoolean.operand1,
|
|
48
|
-
label: getLabel(
|
|
52
|
+
label: getLabel(
|
|
53
|
+
`${DYNAMIC_FILTER_DICTIONARY_ID}.operand_${filterValueBoolean.operand1}`
|
|
54
|
+
)
|
|
49
55
|
} : null;
|
|
50
56
|
return {
|
|
51
57
|
fieldType: "boolean",
|
|
@@ -63,7 +69,9 @@ class BooleanFilterHelpers {
|
|
|
63
69
|
const labelOperands = filter.operand1 ? getLabel(DICCTIONARY.operand_true) : getLabel(DICCTIONARY.operand_false);
|
|
64
70
|
return {
|
|
65
71
|
labelField: filter.field.label ?? getLabel(filter.field.dictionaryId),
|
|
66
|
-
labelOperator: getLabel(
|
|
72
|
+
labelOperator: getLabel(
|
|
73
|
+
`${DYNAMIC_FILTER_DICTIONARY_ID}.operator_${filter.operator}`
|
|
74
|
+
),
|
|
67
75
|
labelOperands
|
|
68
76
|
};
|
|
69
77
|
}
|
|
@@ -18,12 +18,16 @@ class DateTimeFilterHelpers {
|
|
|
18
18
|
defaultEndDate.setHours(23, 59, 59, 999);
|
|
19
19
|
const defaultOperand1 = field.defaultOperand1 && field.defaultOperand1 instanceof Date ? field.defaultOperand1 : defaultStartDate;
|
|
20
20
|
const defaultOperand2 = field.defaultOperand2 && field.defaultOperand2 instanceof Date ? field.defaultOperand2 : defaultEndDate;
|
|
21
|
+
let isSet = false;
|
|
22
|
+
if (defaultOperand1 !== void 0) {
|
|
23
|
+
isSet = true;
|
|
24
|
+
}
|
|
21
25
|
return {
|
|
22
26
|
id: 0,
|
|
23
27
|
fieldType: "datetime",
|
|
24
28
|
field,
|
|
25
29
|
fixed,
|
|
26
|
-
isSet
|
|
30
|
+
isSet,
|
|
27
31
|
operator: "b",
|
|
28
32
|
operand1: defaultOperand1,
|
|
29
33
|
operand2: defaultOperand2
|
|
@@ -83,13 +87,21 @@ class DateTimeFilterHelpers {
|
|
|
83
87
|
labelOperands = filter.operand1 && filter.operand2 ? `${dateFormatter.formatDate(
|
|
84
88
|
filter.operand1,
|
|
85
89
|
dateFormatter.datetimeFormat
|
|
86
|
-
)} - ${dateFormatter.formatDate(
|
|
90
|
+
)} - ${dateFormatter.formatDate(
|
|
91
|
+
filter.operand2,
|
|
92
|
+
dateFormatter.datetimeFormat
|
|
93
|
+
)}` : "";
|
|
87
94
|
} else {
|
|
88
|
-
labelOperands = filter.operand1 ? dateFormatter.formatDate(
|
|
95
|
+
labelOperands = filter.operand1 ? dateFormatter.formatDate(
|
|
96
|
+
filter.operand1,
|
|
97
|
+
dateFormatter.datetimeFormat
|
|
98
|
+
) : "";
|
|
89
99
|
}
|
|
90
100
|
return {
|
|
91
101
|
labelField: filter.field.label ?? getLabel(filter.field.dictionaryId),
|
|
92
|
-
labelOperator: getLabel(
|
|
102
|
+
labelOperator: getLabel(
|
|
103
|
+
`${DYNAMIC_FILTER_DICTIONARY_ID}.operator_${filter.operator}`
|
|
104
|
+
),
|
|
93
105
|
labelOperands
|
|
94
106
|
};
|
|
95
107
|
}
|
|
@@ -15,12 +15,10 @@ class NumberFilterHelpers {
|
|
|
15
15
|
const defaultOperand1 = field.defaultOperand1 ?? null;
|
|
16
16
|
const defaultOperand2 = field.defaultOperand2 ?? null;
|
|
17
17
|
let isSet = false;
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
isSet = defaultOperand1 !== null ? true : false;
|
|
23
|
-
}
|
|
18
|
+
if (["b"].includes(dOperator)) {
|
|
19
|
+
isSet = defaultOperand1 !== null && defaultOperand2 !== null ? true : false;
|
|
20
|
+
} else {
|
|
21
|
+
isSet = defaultOperand1 !== null ? true : false;
|
|
24
22
|
}
|
|
25
23
|
return {
|
|
26
24
|
id: 0,
|
|
@@ -85,7 +83,9 @@ class NumberFilterHelpers {
|
|
|
85
83
|
}
|
|
86
84
|
return {
|
|
87
85
|
labelField: filter.field.label ?? getLabel(filter.field.dictionaryId),
|
|
88
|
-
labelOperator: getLabel(
|
|
86
|
+
labelOperator: getLabel(
|
|
87
|
+
`${DYNAMIC_FILTER_DICTIONARY_ID}.operator_${filter.operator}`
|
|
88
|
+
),
|
|
89
89
|
labelOperands
|
|
90
90
|
};
|
|
91
91
|
}
|
|
@@ -100,7 +100,10 @@ class NumberFilterHelpers {
|
|
|
100
100
|
),
|
|
101
101
|
formValueOperand2: Yup.mixed().when(["formValueOperator.id"], {
|
|
102
102
|
is: "b",
|
|
103
|
-
then: Yup.number().typeError(getLabel(DICCTIONARY.error_operand_mustbe_number)).min(
|
|
103
|
+
then: Yup.number().typeError(getLabel(DICCTIONARY.error_operand_mustbe_number)).min(
|
|
104
|
+
Yup.ref("formValueOperand1"),
|
|
105
|
+
getLabel(DICCTIONARY.error_less_than_start)
|
|
106
|
+
)
|
|
104
107
|
})
|
|
105
108
|
});
|
|
106
109
|
}
|
|
@@ -27,13 +27,16 @@ class SelectAsyncFilterHelpers {
|
|
|
27
27
|
let operandsArray = [];
|
|
28
28
|
if (isMultiple) {
|
|
29
29
|
dOperator = field.defaultOperator ?? "in";
|
|
30
|
-
isSet =
|
|
30
|
+
isSet = defaultOperandsArray !== null && defaultOperandsArray.length > 0;
|
|
31
31
|
if (field.defaultOperandsArray !== void 0 && Array.isArray(field.defaultOperandsArray)) {
|
|
32
|
-
operandsArray = filterValidOperandsArraySelectAsync(
|
|
32
|
+
operandsArray = filterValidOperandsArraySelectAsync(
|
|
33
|
+
field.defaultOperandsArray,
|
|
34
|
+
field
|
|
35
|
+
);
|
|
33
36
|
}
|
|
34
37
|
} else {
|
|
35
38
|
dOperator = field.defaultOperator ?? "e";
|
|
36
|
-
isSet =
|
|
39
|
+
isSet = defaultOperand1 ? true : false;
|
|
37
40
|
}
|
|
38
41
|
const defaultCommonFilter = {
|
|
39
42
|
id: 0,
|
|
@@ -122,15 +125,21 @@ class SelectAsyncFilterHelpers {
|
|
|
122
125
|
let labelOperands = "";
|
|
123
126
|
if (isMultiple) {
|
|
124
127
|
labelOperands = operandsArray?.reduce?.((label, operand) => {
|
|
125
|
-
const textLabel = field.selectAsyncOptions?.getOptionLabel?.(
|
|
128
|
+
const textLabel = field.selectAsyncOptions?.getOptionLabel?.(
|
|
129
|
+
operand
|
|
130
|
+
) || "";
|
|
126
131
|
return label ? `${label} | ${textLabel}` : textLabel;
|
|
127
132
|
}, "")?.toString() || "?";
|
|
128
133
|
} else {
|
|
129
|
-
labelOperands = (field.selectAsyncOptions?.getOptionLabel?.(
|
|
134
|
+
labelOperands = (field.selectAsyncOptions?.getOptionLabel?.(
|
|
135
|
+
filter.operand1
|
|
136
|
+
) ?? "?") + "";
|
|
130
137
|
}
|
|
131
138
|
return {
|
|
132
139
|
labelField: filter.field.label ?? getLabel(filter.field.dictionaryId),
|
|
133
|
-
labelOperator: getLabel(
|
|
140
|
+
labelOperator: getLabel(
|
|
141
|
+
`${DYNAMIC_FILTER_DICTIONARY_ID}.operator_${filter.operator}`
|
|
142
|
+
),
|
|
134
143
|
labelOperands
|
|
135
144
|
};
|
|
136
145
|
}
|
|
@@ -163,7 +172,9 @@ class SelectAsyncFilterHelpers {
|
|
|
163
172
|
return false;
|
|
164
173
|
}
|
|
165
174
|
if (isMultiple) {
|
|
166
|
-
if (SELECT_ASYNC_OPERATORS_MULTIPLE.findIndex(
|
|
175
|
+
if (SELECT_ASYNC_OPERATORS_MULTIPLE.findIndex(
|
|
176
|
+
(f) => f === filter.operator
|
|
177
|
+
) === -1) {
|
|
167
178
|
return false;
|
|
168
179
|
}
|
|
169
180
|
if (!filter.operandsArray || typeof filter.operandsArray !== "object") {
|
|
@@ -180,7 +191,9 @@ class SelectAsyncFilterHelpers {
|
|
|
180
191
|
return false;
|
|
181
192
|
}
|
|
182
193
|
} else {
|
|
183
|
-
if (SELECT_ASYNC_OPERATORS_SINGLE.findIndex(
|
|
194
|
+
if (SELECT_ASYNC_OPERATORS_SINGLE.findIndex(
|
|
195
|
+
(f) => f === filter.operator
|
|
196
|
+
) === -1) {
|
|
184
197
|
return false;
|
|
185
198
|
}
|
|
186
199
|
if (!filter.operand1 || typeof filter.operand1 !== "object") {
|
|
@@ -26,13 +26,15 @@ class SelectFilterHelpers {
|
|
|
26
26
|
let operandsArray = [];
|
|
27
27
|
if (isMultiple) {
|
|
28
28
|
dOperator = field.defaultOperator ?? "in";
|
|
29
|
-
isSet =
|
|
29
|
+
isSet = defaultOperandsArray !== null && defaultOperandsArray.length > 0;
|
|
30
30
|
if (field.defaultOperandsArray !== void 0 && Array.isArray(field.defaultOperandsArray)) {
|
|
31
|
-
operandsArray = filterValidOperandsArraySelect(
|
|
31
|
+
operandsArray = filterValidOperandsArraySelect(
|
|
32
|
+
field.defaultOperandsArray
|
|
33
|
+
);
|
|
32
34
|
}
|
|
33
35
|
} else {
|
|
34
36
|
dOperator = field.defaultOperator ?? "e";
|
|
35
|
-
isSet =
|
|
37
|
+
isSet = defaultOperand1 !== null;
|
|
36
38
|
}
|
|
37
39
|
const defaultCommonFilter = {
|
|
38
40
|
id: 0,
|
|
@@ -123,7 +125,9 @@ class SelectFilterHelpers {
|
|
|
123
125
|
let labelOperands = "";
|
|
124
126
|
if (isMultiple) {
|
|
125
127
|
labelOperands = operandsArray?.reduce?.((label, operand) => {
|
|
126
|
-
const textLabel = field.selectOptions?.options.find(
|
|
128
|
+
const textLabel = field.selectOptions?.options.find(
|
|
129
|
+
(option) => option.id === operand
|
|
130
|
+
)?.label || "";
|
|
127
131
|
return label ? `${label} | ${textLabel}` : textLabel;
|
|
128
132
|
}, "")?.toString() || "?";
|
|
129
133
|
} else {
|
|
@@ -131,7 +135,9 @@ class SelectFilterHelpers {
|
|
|
131
135
|
}
|
|
132
136
|
return {
|
|
133
137
|
labelField: filter.field.label ?? getLabel(filter.field.dictionaryId),
|
|
134
|
-
labelOperator: getLabel(
|
|
138
|
+
labelOperator: getLabel(
|
|
139
|
+
`${DYNAMIC_FILTER_DICTIONARY_ID}.operator_${filter.operator}`
|
|
140
|
+
),
|
|
135
141
|
labelOperands
|
|
136
142
|
};
|
|
137
143
|
}
|
|
@@ -147,9 +153,13 @@ class SelectFilterHelpers {
|
|
|
147
153
|
}),
|
|
148
154
|
formValueOperand1: Yup.mixed().when(["multiple"], {
|
|
149
155
|
is: false,
|
|
150
|
-
then: Yup.mixed().test(
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
then: Yup.mixed().test(
|
|
157
|
+
"is-string-or-number",
|
|
158
|
+
getLabel(DICCTIONARY.error_operand_required),
|
|
159
|
+
(value) => {
|
|
160
|
+
return typeof value === "string" || typeof value === "number";
|
|
161
|
+
}
|
|
162
|
+
).required(getLabel(DICCTIONARY.error_operand_required))
|
|
153
163
|
})
|
|
154
164
|
});
|
|
155
165
|
}
|
|
@@ -12,8 +12,8 @@ class StringFilterHelpers {
|
|
|
12
12
|
getDefaultFilter(field, fixed) {
|
|
13
13
|
const defaultOperand1 = field.defaultOperand1;
|
|
14
14
|
let isSet = false;
|
|
15
|
-
if (
|
|
16
|
-
isSet =
|
|
15
|
+
if (defaultOperand1 !== null && typeof defaultOperand1 === "string") {
|
|
16
|
+
isSet = true;
|
|
17
17
|
}
|
|
18
18
|
const defaultFilter = {
|
|
19
19
|
id: 0,
|
|
@@ -64,7 +64,9 @@ class StringFilterHelpers {
|
|
|
64
64
|
const labelOperands = filter.operand1 !== void 0 ? filter.operand1 + "" : "";
|
|
65
65
|
return {
|
|
66
66
|
labelField: filter.field.label ?? getLabel(filter.field.dictionaryId),
|
|
67
|
-
labelOperator: getLabel(
|
|
67
|
+
labelOperator: getLabel(
|
|
68
|
+
`${DYNAMIC_FILTER_DICTIONARY_ID}.operator_${filter.operator}`
|
|
69
|
+
),
|
|
68
70
|
labelOperands
|
|
69
71
|
};
|
|
70
72
|
}
|
|
@@ -24,8 +24,13 @@ const stepperStyles = {
|
|
|
24
24
|
flexDirection: ownerState?.orientation === "vertical" || theme.generalSettings.isMobile ? "column" : "row",
|
|
25
25
|
gap: theme.vars.size.baseSpacings.sp8
|
|
26
26
|
}),
|
|
27
|
+
/**
|
|
28
|
+
* Estilos para el contenido del paso dentro del Stepper.
|
|
29
|
+
*/
|
|
27
30
|
stepContent: ({ ownerState }) => ({
|
|
28
|
-
|
|
31
|
+
height: "100%",
|
|
32
|
+
display: ownerState?.isStepVisible ? "flex" : "none",
|
|
33
|
+
flexDirection: "column"
|
|
29
34
|
}),
|
|
30
35
|
/**
|
|
31
36
|
* Estilos para la sección que contiene los botones de acción del Stepper.
|
|
@@ -71,8 +76,7 @@ const stepperStyles = {
|
|
|
71
76
|
const isLastStep = step === totalSteps - 1;
|
|
72
77
|
return {
|
|
73
78
|
cursor: "pointer",
|
|
74
|
-
display: "flex",
|
|
75
|
-
visibility: ownerState?.isStepVisible ? "visible" : "hidden",
|
|
79
|
+
display: ownerState?.isStepVisible ? "flex" : "none",
|
|
76
80
|
flexDirection: ownerState?.orientation === "vertical" || theme.generalSettings.isMobile ? "column" : "row",
|
|
77
81
|
alignItems: ownerState?.orientation === "vertical" || theme.generalSettings.isMobile ? "center" : "start",
|
|
78
82
|
justifyContent: ownerState?.visibleTitle ? "space-between" : "center",
|
|
@@ -126,7 +130,7 @@ const stepperStyles = {
|
|
|
126
130
|
whiteSpace: "normal",
|
|
127
131
|
order: ownerState?.orientation === "vertical" || theme.generalSettings.isMobile ? 1 : 0,
|
|
128
132
|
"&.M4LTypography-root": {
|
|
129
|
-
color:
|
|
133
|
+
color: ownerState?.originalStepIndex !== void 0 && typeof ownerState.originalStepIndex === "number" ? (ownerState.currentStep ?? 0) > ownerState.originalStepIndex ? theme.vars.palette.primary.semanticText : (ownerState.currentStep ?? 0) === ownerState.originalStepIndex ? theme.vars.palette.text.primary : theme.vars.palette.text.secondary : theme.vars.palette.text.secondary,
|
|
130
134
|
textAlign: ownerState?.orientation === "vertical" || theme.generalSettings.isMobile ? "center" : "right"
|
|
131
135
|
}
|
|
132
136
|
}),
|
|
@@ -134,13 +138,12 @@ const stepperStyles = {
|
|
|
134
138
|
* Estilos para el indicador numérico de cada paso del Stepper.
|
|
135
139
|
*/
|
|
136
140
|
indicator: ({ theme, ownerState }) => {
|
|
137
|
-
const currentStep = ownerState?.currentStep ?? 0;
|
|
138
141
|
const step = ownerState?.step ?? 0;
|
|
139
142
|
const totalSteps = ownerState?.totalSteps ?? 0;
|
|
140
|
-
const isCompleted =
|
|
141
|
-
const isCurrent =
|
|
143
|
+
const isCompleted = ownerState?.isCompleted ?? false;
|
|
144
|
+
const isCurrent = ownerState?.isCurrent ?? false;
|
|
145
|
+
const isValidStep = ownerState?.isValidStep ?? true;
|
|
142
146
|
const isLastStep = step === totalSteps - 1;
|
|
143
|
-
const isValidStep = ownerState?.stepValidationStatus?.[step] ?? true;
|
|
144
147
|
return {
|
|
145
148
|
...ownerState?.orientation === "horizontal" ? {
|
|
146
149
|
marginTop: theme.generalSettings.isMobile ? "6px" : theme.vars.size.baseSpacings.sp1
|
|
@@ -150,7 +153,7 @@ const stepperStyles = {
|
|
|
150
153
|
justifyContent: "center",
|
|
151
154
|
flexShrink: 0,
|
|
152
155
|
order: ownerState?.orientation === "vertical" || theme.generalSettings.isMobile ? 0 : 1,
|
|
153
|
-
background: isCompleted ? ownerState?.indicatorType === "dot" || theme.generalSettings.isMobile ? theme.vars.palette.background.default : theme.vars.palette.primary.toneOpacity :
|
|
156
|
+
background: !isValidStep && ownerState?.hasBeenValidated ? theme.vars.palette.error.enabled : isCompleted && isValidStep ? ownerState?.indicatorType === "dot" || theme.generalSettings.isMobile ? theme.vars.palette.background.default : theme.vars.palette.primary.toneOpacity : isCurrent ? ownerState?.indicatorType === "dot" || theme.generalSettings.isMobile ? theme.vars.palette.background.default : theme.vars.palette.primary.enabled : ownerState?.indicatorType === "dot" || theme.generalSettings.isMobile ? theme.vars.palette.background.default : theme.vars.palette.default.enabled,
|
|
154
157
|
borderRadius: theme.vars.size.borderRadius.r2,
|
|
155
158
|
...ownerState?.indicatorType === "number" && !theme.generalSettings.isMobile && {
|
|
156
159
|
boxShadow: isCurrent ? "0 2px 8px 0 rgb(0, 100, 255, 0.16)" : "none"
|
|
@@ -178,13 +181,13 @@ const stepperStyles = {
|
|
|
178
181
|
* Estilos para el número de texto dentro del indicador numérico del Stepper.
|
|
179
182
|
*/
|
|
180
183
|
textNumber: ({ theme, ownerState }) => {
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
184
|
+
const isCompleted = ownerState?.isCompleted ?? false;
|
|
185
|
+
const isCurrent = ownerState?.isCurrent ?? false;
|
|
186
|
+
const isValidStep = ownerState?.isValidStep ?? true;
|
|
187
|
+
const hasBeenValidated = ownerState?.hasBeenValidated ?? false;
|
|
185
188
|
return {
|
|
186
189
|
"&.M4LTypography-root": {
|
|
187
|
-
color: isCompleted || isCurrent ? theme.vars.palette.primary.contrastText : theme.vars.palette.text.primary
|
|
190
|
+
color: !isValidStep && hasBeenValidated ? theme.vars.palette.error.contrastText : isCompleted || isCurrent ? theme.vars.palette.primary.contrastText : theme.vars.palette.text.primary
|
|
188
191
|
}
|
|
189
192
|
};
|
|
190
193
|
},
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Obtiene los valores iniciales de campos específicos del formulario.
|
|
3
|
+
* Se usa para inicializar el ref que trackea cambios en los campos.
|
|
4
|
+
* @param allValues - Todos los valores del formulario (de getValues())
|
|
5
|
+
* @param fields - Lista de nombres de campos de los que queremos obtener valores
|
|
6
|
+
* @returns Objeto con solo los valores de los campos especificados
|
|
7
|
+
* @example
|
|
8
|
+
* const formValues = { name: 'John', age: 25, city: 'NY', country: 'USA' };
|
|
9
|
+
* getInitialFieldValues(formValues, ['name', 'age'])
|
|
10
|
+
* // Retorna: { name: 'John', age: 25 }
|
|
11
|
+
*/
|
|
12
|
+
export declare function getInitialFieldValues(allValues: Record<string, any>, fields?: string[]): Record<string, any>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function getInitialFieldValues(allValues, fields = []) {
|
|
2
|
+
if (fields.length === 0) {
|
|
3
|
+
return {};
|
|
4
|
+
}
|
|
5
|
+
return fields.reduce((acc, field) => {
|
|
6
|
+
if (field in allValues) {
|
|
7
|
+
acc[field] = allValues[field];
|
|
8
|
+
}
|
|
9
|
+
return acc;
|
|
10
|
+
}, {});
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
getInitialFieldValues as g
|
|
14
|
+
};
|
|
@@ -2,3 +2,5 @@ export { evaluateVisibilityStepCondition } from './evaluateVisibilityStepConditi
|
|
|
2
2
|
export { findNextVisibleValidStep } from './findNextVisibleValidStep';
|
|
3
3
|
export { findPrevVisibleValidStep } from './findPrevVisibleValidStep';
|
|
4
4
|
export { isLastVisibleValidStep } from './isLastVisibleValidStep';
|
|
5
|
+
export { parseWatchedValues } from './parseWatchedValues';
|
|
6
|
+
export { getInitialFieldValues } from './getInitialFieldValues';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convierte los valores observados de useWatch en un objeto estructurado con los nombres de campos como keys.
|
|
3
|
+
*
|
|
4
|
+
* useWatch de react-hook-form retorna diferentes estructuras según la cantidad de campos observados:
|
|
5
|
+
* - Un solo campo: retorna el valor directamente
|
|
6
|
+
* - Múltiples campos: retorna un array con los valores en el mismo orden de los campos
|
|
7
|
+
* @param watchValues - Valores observados de useWatch (puede ser un valor único o un array)
|
|
8
|
+
* @param fields - Lista de nombres de campos que se están observando
|
|
9
|
+
* @returns Objeto con los valores actuales mapeados a sus respectivos nombres de campo
|
|
10
|
+
* @example
|
|
11
|
+
* // Para un solo campo
|
|
12
|
+
* parseWatchedValues('John', ['name']) // { name: 'John' }
|
|
13
|
+
*
|
|
14
|
+
* // Para múltiples campos
|
|
15
|
+
* parseWatchedValues(['John', 25], ['name', 'age']) // { name: 'John', age: 25 }
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseWatchedValues(watchValues: any, fields: string[]): Record<string, any>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function parseWatchedValues(watchValues, fields) {
|
|
2
|
+
if (fields.length === 1) {
|
|
3
|
+
return { [fields[0]]: watchValues };
|
|
4
|
+
}
|
|
5
|
+
return fields.reduce((acc, field, index) => {
|
|
6
|
+
acc[field] = Array.isArray(watchValues) ? watchValues[index] : watchValues;
|
|
7
|
+
return acc;
|
|
8
|
+
}, {});
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
parseWatchedValues as p
|
|
12
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
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[], skipIfValidating?: boolean) => void;
|
|
7
|
+
clearAllValidation: () => void;
|
|
8
|
+
activeFields: string[];
|
|
9
|
+
startExternalValidation: () => void;
|
|
10
|
+
endExternalValidation: () => void;
|
|
11
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useState, useRef, useEffect, useCallback } from "react";
|
|
2
|
+
import { useFormContext, useWatch } from "react-hook-form";
|
|
3
|
+
import { p as parseWatchedValues } from "../../helpers/parseWatchedValues/index.js";
|
|
4
|
+
import { g as getInitialFieldValues } from "../../helpers/getInitialFieldValues/index.js";
|
|
5
|
+
function useDynamicValidation() {
|
|
6
|
+
const { trigger, getValues } = useFormContext();
|
|
7
|
+
const [activeFields, setActiveFields] = useState([]);
|
|
8
|
+
const [isValidating, setIsValidating] = useState(false);
|
|
9
|
+
const statusLoad = useWatch({
|
|
10
|
+
name: "statusLoad"
|
|
11
|
+
});
|
|
12
|
+
const watchValues = useWatch({
|
|
13
|
+
name: activeFields.length > 0 ? activeFields : ["__dummy__"]
|
|
14
|
+
});
|
|
15
|
+
const lastValuesRef = useRef({});
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (activeFields.length === 0 || statusLoad !== "ready" || isValidating) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const currentValues = parseWatchedValues(watchValues, activeFields);
|
|
21
|
+
const changedFields = activeFields.filter((field) => {
|
|
22
|
+
const currentValue = currentValues[field];
|
|
23
|
+
const lastValue = lastValuesRef.current[field];
|
|
24
|
+
const hasChanged = JSON.stringify(currentValue) !== JSON.stringify(lastValue);
|
|
25
|
+
if (hasChanged) {
|
|
26
|
+
lastValuesRef.current[field] = currentValue;
|
|
27
|
+
}
|
|
28
|
+
return hasChanged;
|
|
29
|
+
});
|
|
30
|
+
if (changedFields.length > 0) {
|
|
31
|
+
const timeoutId = setTimeout(() => {
|
|
32
|
+
trigger(changedFields);
|
|
33
|
+
}, 100);
|
|
34
|
+
return () => clearTimeout(timeoutId);
|
|
35
|
+
}
|
|
36
|
+
}, [activeFields, trigger, watchValues, statusLoad, isValidating]);
|
|
37
|
+
const startExternalValidation = useCallback(() => {
|
|
38
|
+
setIsValidating(true);
|
|
39
|
+
}, []);
|
|
40
|
+
const endExternalValidation = useCallback(() => {
|
|
41
|
+
setIsValidating(false);
|
|
42
|
+
}, []);
|
|
43
|
+
const activateFieldsValidation = useCallback(
|
|
44
|
+
(fields, skipIfValidating = true) => {
|
|
45
|
+
if (skipIfValidating && isValidating) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const formValues = getValues();
|
|
49
|
+
setActiveFields(fields);
|
|
50
|
+
const initialValues = getInitialFieldValues(formValues, fields);
|
|
51
|
+
lastValuesRef.current = initialValues;
|
|
52
|
+
},
|
|
53
|
+
[getValues, isValidating]
|
|
54
|
+
);
|
|
55
|
+
const clearAllValidation = useCallback(() => {
|
|
56
|
+
setActiveFields([]);
|
|
57
|
+
lastValuesRef.current = {};
|
|
58
|
+
}, []);
|
|
59
|
+
return {
|
|
60
|
+
activateFieldsValidation,
|
|
61
|
+
clearAllValidation,
|
|
62
|
+
activeFields,
|
|
63
|
+
startExternalValidation,
|
|
64
|
+
endExternalValidation
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
useDynamicValidation as u
|
|
69
|
+
};
|
|
@@ -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,20 @@ 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
|
+
clearAllValidation();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
47
60
|
if (success && futureFields.length > 0) {
|
|
48
61
|
clearErrors(futureFields);
|
|
49
62
|
setTimeout(() => {
|
|
@@ -62,13 +75,16 @@ function useStepperActions() {
|
|
|
62
75
|
steps,
|
|
63
76
|
currentStep,
|
|
64
77
|
trigger,
|
|
65
|
-
getValues
|
|
78
|
+
getValues,
|
|
79
|
+
activateFieldsValidation,
|
|
80
|
+
clearAllValidation
|
|
66
81
|
]);
|
|
67
82
|
const cancelAction = useCallback(() => {
|
|
68
83
|
reset();
|
|
69
84
|
clearErrors();
|
|
85
|
+
clearAllValidation();
|
|
70
86
|
resetStepper();
|
|
71
|
-
}, [reset, clearErrors, resetStepper]);
|
|
87
|
+
}, [reset, clearErrors, clearAllValidation, resetStepper]);
|
|
72
88
|
return { prevStepAction, nextStepAction, cancelAction };
|
|
73
89
|
}
|
|
74
90
|
export {
|
|
@@ -35,6 +35,18 @@ const createStepperStore = (initProps, storeDevtoolsEnabled = false) => {
|
|
|
35
35
|
if (!preserveStepValidationStatus) {
|
|
36
36
|
state.stepValidationStatus = {};
|
|
37
37
|
}
|
|
38
|
+
if (!preserveStepValidationStatus) {
|
|
39
|
+
const firstVisibleStepIndex = findNextVisibleValidStep(
|
|
40
|
+
-1,
|
|
41
|
+
// Comenzar desde -1 para encontrar el primer step visible
|
|
42
|
+
steps,
|
|
43
|
+
{},
|
|
44
|
+
state.visibilityData
|
|
45
|
+
);
|
|
46
|
+
if (firstVisibleStepIndex !== 0 && firstVisibleStepIndex < steps.length) {
|
|
47
|
+
state.currentStep = firstVisibleStepIndex;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
38
50
|
});
|
|
39
51
|
},
|
|
40
52
|
/**
|
|
@@ -114,9 +126,16 @@ const createStepperStore = (initProps, storeDevtoolsEnabled = false) => {
|
|
|
114
126
|
*/
|
|
115
127
|
resetStepper: () => {
|
|
116
128
|
set((state) => {
|
|
117
|
-
state.currentStep = 0;
|
|
118
129
|
state.stepValidationStatus = {};
|
|
119
130
|
state.isValidating = false;
|
|
131
|
+
const firstVisibleStepIndex = findNextVisibleValidStep(
|
|
132
|
+
-1,
|
|
133
|
+
// Comenzar desde -1 para encontrar el primer step visible
|
|
134
|
+
state.steps,
|
|
135
|
+
{},
|
|
136
|
+
state.visibilityData
|
|
137
|
+
);
|
|
138
|
+
state.currentStep = firstVisibleStepIndex < state.steps.length ? firstVisibleStepIndex : 0;
|
|
120
139
|
});
|
|
121
140
|
}
|
|
122
141
|
}
|
|
@@ -5,10 +5,16 @@ 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 {
|
|
14
|
+
activateFieldsValidation,
|
|
15
|
+
startExternalValidation,
|
|
16
|
+
endExternalValidation
|
|
17
|
+
} = useDynamicValidation();
|
|
12
18
|
const {
|
|
13
19
|
currentStep,
|
|
14
20
|
steps,
|
|
@@ -32,16 +38,19 @@ function StepArea() {
|
|
|
32
38
|
}),
|
|
33
39
|
shallow
|
|
34
40
|
);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
const allSteps = steps;
|
|
42
|
+
const visibleSteps = useMemo(() => {
|
|
43
|
+
return steps.filter(
|
|
44
|
+
(step) => evaluateVisibilityStepCondition(step, formValues || {}, visibilityData)
|
|
45
|
+
);
|
|
46
|
+
}, [steps, formValues, visibilityData]);
|
|
38
47
|
const ownerState = {
|
|
39
48
|
visibleTitle,
|
|
40
49
|
orientation,
|
|
41
|
-
totalSteps:
|
|
50
|
+
totalSteps: visibleSteps.length
|
|
42
51
|
};
|
|
43
52
|
const handleStepClick = async (targetIndex) => {
|
|
44
|
-
const currentIndex =
|
|
53
|
+
const currentIndex = visibleSteps.findIndex(
|
|
45
54
|
(step) => step.key === steps[currentStep].key
|
|
46
55
|
);
|
|
47
56
|
if (targetIndex === currentIndex) {
|
|
@@ -49,7 +58,7 @@ function StepArea() {
|
|
|
49
58
|
}
|
|
50
59
|
const isNavigatingForward = targetIndex > currentIndex;
|
|
51
60
|
const targetOriginalIndex = steps.findIndex(
|
|
52
|
-
(s) => s.key ===
|
|
61
|
+
(s) => s.key === visibleSteps[targetIndex].key
|
|
53
62
|
);
|
|
54
63
|
if (!isNavigatingForward) {
|
|
55
64
|
setCurrentStep(targetOriginalIndex);
|
|
@@ -58,37 +67,43 @@ function StepArea() {
|
|
|
58
67
|
const isSkippingSteps = targetIndex > currentIndex + 1;
|
|
59
68
|
if (isSkippingSteps) {
|
|
60
69
|
for (let i = currentIndex; i < targetIndex; i++) {
|
|
61
|
-
const step =
|
|
70
|
+
const step = visibleSteps[i];
|
|
62
71
|
const stepOriginalIndex = steps.findIndex((s) => s.key === step.key);
|
|
72
|
+
startExternalValidation();
|
|
63
73
|
const isValid = await trigger(step.validationFields);
|
|
74
|
+
endExternalValidation();
|
|
64
75
|
if (!isValid) {
|
|
65
76
|
setCurrentStep(stepOriginalIndex);
|
|
66
77
|
setStepValidationStatus(stepOriginalIndex, false);
|
|
78
|
+
activateFieldsValidation(step.validationFields || []);
|
|
67
79
|
return;
|
|
68
80
|
}
|
|
69
81
|
setStepValidationStatus(stepOriginalIndex, true);
|
|
70
82
|
}
|
|
71
83
|
} else {
|
|
72
|
-
const currentStepData =
|
|
84
|
+
const currentStepData = visibleSteps[currentIndex];
|
|
73
85
|
const currentStepOriginalIndex = steps.findIndex(
|
|
74
86
|
(s) => s.key === currentStepData.key
|
|
75
87
|
);
|
|
88
|
+
startExternalValidation();
|
|
76
89
|
const isCurrentValid = await trigger(currentStepData.validationFields);
|
|
90
|
+
endExternalValidation();
|
|
77
91
|
if (!isCurrentValid) {
|
|
78
92
|
setStepValidationStatus(currentStepOriginalIndex, false);
|
|
93
|
+
activateFieldsValidation(currentStepData.validationFields || []);
|
|
79
94
|
return;
|
|
80
95
|
}
|
|
81
96
|
setStepValidationStatus(currentStepOriginalIndex, true);
|
|
82
97
|
}
|
|
83
98
|
setCurrentStep(targetOriginalIndex);
|
|
84
99
|
if (isNavigatingForward) {
|
|
85
|
-
const currentStepData =
|
|
100
|
+
const currentStepData = visibleSteps[currentIndex];
|
|
86
101
|
const currentStepOriginalIndex = steps.findIndex(
|
|
87
102
|
(s) => s.key === currentStepData.key
|
|
88
103
|
);
|
|
89
104
|
const isCurrentStepValid = stepValidationStatus[currentStepOriginalIndex] !== false;
|
|
90
105
|
if (isCurrentStepValid) {
|
|
91
|
-
const targetStep =
|
|
106
|
+
const targetStep = visibleSteps[targetIndex];
|
|
92
107
|
const targetStepFields = targetStep.validationFields || [];
|
|
93
108
|
if (targetStepFields.length > 0) {
|
|
94
109
|
clearErrors(targetStepFields);
|
|
@@ -100,25 +115,24 @@ function StepArea() {
|
|
|
100
115
|
}
|
|
101
116
|
}
|
|
102
117
|
};
|
|
103
|
-
return /* @__PURE__ */ jsx(StepAreaStyled, { ownerState,
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
return /* @__PURE__ */ jsx(StepAreaStyled, { ownerState, children: allSteps.map((step, originalIndex) => {
|
|
119
|
+
const isStepVisible = evaluateVisibilityStepCondition(
|
|
120
|
+
step,
|
|
121
|
+
formValues || {},
|
|
122
|
+
visibilityData
|
|
123
|
+
);
|
|
124
|
+
const visibleStepIndex = isStepVisible ? visibleSteps.findIndex((visStep) => visStep.key === step.key) : -1;
|
|
108
125
|
return /* @__PURE__ */ jsxs(
|
|
109
126
|
StepStyled,
|
|
110
127
|
{
|
|
111
128
|
role: "button",
|
|
112
|
-
onClick: () => handleStepClick(
|
|
129
|
+
onClick: () => isStepVisible ? handleStepClick(visibleStepIndex) : void 0,
|
|
113
130
|
ownerState: {
|
|
114
131
|
...ownerState,
|
|
115
132
|
currentStep,
|
|
116
|
-
step:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
formValues || {},
|
|
120
|
-
visibilityData
|
|
121
|
-
)
|
|
133
|
+
step: visibleStepIndex,
|
|
134
|
+
// Usa el índice en la secuencia visible
|
|
135
|
+
isStepVisible
|
|
122
136
|
},
|
|
123
137
|
"data-testid": `stepper-step-${step.key}`,
|
|
124
138
|
children: [
|
|
@@ -126,7 +140,13 @@ function StepArea() {
|
|
|
126
140
|
StepNameStyled,
|
|
127
141
|
{
|
|
128
142
|
variant: "body",
|
|
129
|
-
ownerState: {
|
|
143
|
+
ownerState: {
|
|
144
|
+
...ownerState,
|
|
145
|
+
currentStep,
|
|
146
|
+
step: visibleStepIndex,
|
|
147
|
+
originalStepIndex: originalIndex,
|
|
148
|
+
isStepVisible
|
|
149
|
+
},
|
|
130
150
|
children: step.title
|
|
131
151
|
}
|
|
132
152
|
),
|
|
@@ -134,8 +154,8 @@ function StepArea() {
|
|
|
134
154
|
Indicator,
|
|
135
155
|
{
|
|
136
156
|
currentStep,
|
|
137
|
-
step:
|
|
138
|
-
totalSteps:
|
|
157
|
+
step: visibleStepIndex,
|
|
158
|
+
totalSteps: visibleSteps.length,
|
|
139
159
|
originalStepIndex: originalIndex
|
|
140
160
|
}
|
|
141
161
|
)
|
|
@@ -21,15 +21,6 @@ function Indicator(props) {
|
|
|
21
21
|
);
|
|
22
22
|
const { host_static_assets, environment_assets } = useEnvironment();
|
|
23
23
|
const { currentSize } = useComponentSize(size);
|
|
24
|
-
const ownerState = {
|
|
25
|
-
size: currentSize,
|
|
26
|
-
currentStep,
|
|
27
|
-
step,
|
|
28
|
-
totalSteps,
|
|
29
|
-
orientation,
|
|
30
|
-
indicatorType,
|
|
31
|
-
stepValidationStatus
|
|
32
|
-
};
|
|
33
24
|
const isMobile = useIsMobile();
|
|
34
25
|
const effectiveIndicatorType = useMemo(() => {
|
|
35
26
|
return isMobile ? "dot" : indicatorType;
|
|
@@ -45,6 +36,20 @@ function Indicator(props) {
|
|
|
45
36
|
hasBeenValidated: validated
|
|
46
37
|
};
|
|
47
38
|
}, [currentStep, originalStepIndex, stepValidationStatus]);
|
|
39
|
+
const ownerState = {
|
|
40
|
+
size: currentSize,
|
|
41
|
+
currentStep,
|
|
42
|
+
step,
|
|
43
|
+
totalSteps,
|
|
44
|
+
orientation,
|
|
45
|
+
indicatorType,
|
|
46
|
+
stepValidationStatus,
|
|
47
|
+
originalStepIndex,
|
|
48
|
+
isCompleted,
|
|
49
|
+
isCurrent: currentStep === originalStepIndex,
|
|
50
|
+
isValidStep,
|
|
51
|
+
hasBeenValidated
|
|
52
|
+
};
|
|
48
53
|
return /* @__PURE__ */ jsx(IndicatorStyled, { ownerState, children: hasBeenValidated && !isValidStep ? (
|
|
49
54
|
// Mostrar error solo si ha sido validado y es inválido
|
|
50
55
|
/* @__PURE__ */ jsx(
|
|
@@ -67,7 +72,10 @@ function Indicator(props) {
|
|
|
67
72
|
)
|
|
68
73
|
) : (
|
|
69
74
|
// Mostrar estado normal (número o dot) para pasos no validados o actuales
|
|
70
|
-
/* @__PURE__ */ jsx(TextNumberStyled, { variant: "body", ownerState
|
|
75
|
+
/* @__PURE__ */ jsx(TextNumberStyled, { variant: "body", ownerState, children: effectiveIndicatorType === "number" ? (
|
|
76
|
+
// Solo mostrar número si el step es visible (step >= 0)
|
|
77
|
+
step >= 0 ? step + 1 : ""
|
|
78
|
+
) : /* @__PURE__ */ jsx(
|
|
71
79
|
Icon,
|
|
72
80
|
{
|
|
73
81
|
src: `${host_static_assets}/${environment_assets}/${currentStep === originalStepIndex ? pathIcons.dotSelected : pathIcons.dotOutline}`,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { StepperButtonProps } from '../../../types';
|
|
1
2
|
/**
|
|
2
3
|
* Botón modular para cancelar el proceso del Stepper
|
|
3
4
|
*/
|
|
4
|
-
export declare function StepperCancelButton(): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function StepperCancelButton(props: StepperButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,22 +1,55 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { useFormContext, useFormState } from "react-hook-form";
|
|
2
4
|
import { u as useStepperActions } from "../../../hooks/useStepperActions/index.js";
|
|
3
|
-
import { D as DICTIONARY } from "../../../dictionary.js";
|
|
5
|
+
import { D as DICTIONARY$1 } from "../../../dictionary.js";
|
|
6
|
+
import { D as DICTIONARY } from "../../../../CommonActions/dictionary.js";
|
|
4
7
|
import { useModuleDictionary } from "@m4l/core";
|
|
8
|
+
import { u as useModal } from "../../../../../hooks/useModal/index.js";
|
|
9
|
+
import { u as useWindowToolsMF } from "../../../../WindowBase/hooks/useWindowToolsMF/index.js";
|
|
10
|
+
import { W as WindowConfirm } from "../../../../WindowConfirm/WindowConfirm.js";
|
|
5
11
|
import { B as Button } from "../../../../mui_extended/Button/Button.js";
|
|
6
|
-
function StepperCancelButton() {
|
|
12
|
+
function StepperCancelButton(props) {
|
|
13
|
+
const { label, ...rest } = props;
|
|
7
14
|
const { getLabel } = useModuleDictionary();
|
|
8
15
|
const { cancelAction } = useStepperActions();
|
|
9
|
-
const
|
|
16
|
+
const { openModal } = useModal();
|
|
17
|
+
const { close: closeWindow } = useWindowToolsMF();
|
|
18
|
+
const { control } = useFormContext();
|
|
19
|
+
const { isDirty } = useFormState({
|
|
20
|
+
control
|
|
21
|
+
});
|
|
22
|
+
const onConfirmQuit = useCallback(() => {
|
|
10
23
|
cancelAction();
|
|
11
|
-
|
|
24
|
+
closeWindow();
|
|
25
|
+
}, [cancelAction, closeWindow]);
|
|
26
|
+
const handleCancel = useCallback(() => {
|
|
27
|
+
if (isDirty) {
|
|
28
|
+
openModal({
|
|
29
|
+
window: /* @__PURE__ */ jsx(
|
|
30
|
+
WindowConfirm,
|
|
31
|
+
{
|
|
32
|
+
variant: "warning",
|
|
33
|
+
title: getLabel(DICTIONARY.CONFIRM_QUIT_TITLE),
|
|
34
|
+
msg: getLabel(DICTIONARY.CONFIRM_QUIT_MSG),
|
|
35
|
+
onClickIntro: onConfirmQuit
|
|
36
|
+
}
|
|
37
|
+
),
|
|
38
|
+
variant: "warning"
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
onConfirmQuit();
|
|
42
|
+
}
|
|
43
|
+
}, [getLabel, isDirty, openModal, onConfirmQuit]);
|
|
12
44
|
return /* @__PURE__ */ jsx(
|
|
13
45
|
Button,
|
|
14
46
|
{
|
|
15
47
|
type: "button",
|
|
16
|
-
label: getLabel(DICTIONARY.LABEL_CANCEL_BUTTON),
|
|
48
|
+
label: label || getLabel(DICTIONARY$1.LABEL_CANCEL_BUTTON),
|
|
17
49
|
variant: "outlined",
|
|
18
50
|
color: "default",
|
|
19
51
|
onClick: handleCancel,
|
|
52
|
+
...rest,
|
|
20
53
|
"data-testid": "stepper-cancel-button"
|
|
21
54
|
}
|
|
22
55
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { StepperButtonProps } from '../../../types';
|
|
1
2
|
/**
|
|
2
3
|
* Botón modular para avanzar al siguiente step del Stepper
|
|
3
4
|
*/
|
|
4
|
-
export declare function StepperNextButton(): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function StepperNextButton(props: StepperButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -6,7 +6,8 @@ import { D as DICTIONARY } from "../../../dictionary.js";
|
|
|
6
6
|
import { useModuleDictionary, useEnvironment } from "@m4l/core";
|
|
7
7
|
import { I as IconButton } from "../../../../mui_extended/IconButton/IconButton.js";
|
|
8
8
|
import { B as Button } from "../../../../mui_extended/Button/Button.js";
|
|
9
|
-
function StepperNextButton() {
|
|
9
|
+
function StepperNextButton(props) {
|
|
10
|
+
const { label, ...rest } = props;
|
|
10
11
|
const { nextStepAction } = useStepperActions();
|
|
11
12
|
const isMobile = useIsMobile();
|
|
12
13
|
const { getLabel } = useModuleDictionary();
|
|
@@ -28,12 +29,13 @@ function StepperNextButton() {
|
|
|
28
29
|
Button,
|
|
29
30
|
{
|
|
30
31
|
type: "button",
|
|
31
|
-
label: getLabel(DICTIONARY.LABEL_NEXT_BUTTON),
|
|
32
|
+
label: label || getLabel(DICTIONARY.LABEL_NEXT_BUTTON),
|
|
32
33
|
variant: "contained",
|
|
33
34
|
color: "primary",
|
|
34
35
|
onClick: handleNext,
|
|
35
36
|
endIcon: `${host_static_assets}/${environment_assets}/${pathIcons.arrowRight}`,
|
|
36
|
-
"data-testid": "stepper-next-button"
|
|
37
|
+
"data-testid": "stepper-next-button",
|
|
38
|
+
...rest
|
|
37
39
|
}
|
|
38
40
|
);
|
|
39
41
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { StepperButtonProps } from '../../../types';
|
|
1
2
|
/**
|
|
2
3
|
* Botón modular para ir al step anterior del Stepper
|
|
3
4
|
*/
|
|
4
|
-
export declare function StepperPrevButton(): import("react/jsx-runtime").JSX.Element | null;
|
|
5
|
+
export declare function StepperPrevButton(props: StepperButtonProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -7,7 +7,8 @@ import { useIsMobile } from "@m4l/graphics";
|
|
|
7
7
|
import { D as DICTIONARY } from "../../../dictionary.js";
|
|
8
8
|
import { I as IconButton } from "../../../../mui_extended/IconButton/IconButton.js";
|
|
9
9
|
import { B as Button } from "../../../../mui_extended/Button/Button.js";
|
|
10
|
-
function StepperPrevButton() {
|
|
10
|
+
function StepperPrevButton(props) {
|
|
11
|
+
const { label, ...rest } = props;
|
|
11
12
|
const { currentStep } = useStepper((state) => ({
|
|
12
13
|
currentStep: state.currentStep
|
|
13
14
|
}));
|
|
@@ -35,12 +36,13 @@ function StepperPrevButton() {
|
|
|
35
36
|
Button,
|
|
36
37
|
{
|
|
37
38
|
type: "button",
|
|
38
|
-
label:
|
|
39
|
+
label: label || getLabel(DICTIONARY.LABEL_PREV_BUTTON),
|
|
39
40
|
variant: "outlined",
|
|
40
41
|
color: "default",
|
|
41
42
|
onClick: handlePrev,
|
|
42
43
|
startIcon: `${host_static_assets}/${environment_assets}/${pathIcons.arrowLeft}`,
|
|
43
|
-
"data-testid": "stepper-prev-button"
|
|
44
|
+
"data-testid": "stepper-prev-button",
|
|
45
|
+
...rest
|
|
44
46
|
}
|
|
45
47
|
);
|
|
46
48
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { StepperButtonProps } from '../../../types';
|
|
1
2
|
/**
|
|
2
3
|
* Botón modular para finalizar el Stepper y ejecutar onFinalSubmit
|
|
3
4
|
*/
|
|
4
|
-
export declare function StepperSubmitButton(): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function StepperSubmitButton(props: StepperButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,16 +2,18 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { D as DICTIONARY } from "../../../dictionary.js";
|
|
3
3
|
import { useModuleDictionary } from "@m4l/core";
|
|
4
4
|
import { B as Button } from "../../../../mui_extended/Button/Button.js";
|
|
5
|
-
function StepperSubmitButton() {
|
|
5
|
+
function StepperSubmitButton(props) {
|
|
6
|
+
const { label, ...rest } = props;
|
|
6
7
|
const { getLabel } = useModuleDictionary();
|
|
7
8
|
return /* @__PURE__ */ jsx(
|
|
8
9
|
Button,
|
|
9
10
|
{
|
|
10
11
|
type: "submit",
|
|
11
|
-
label: getLabel(DICTIONARY.LABEL_SUBMIT_BUTTON),
|
|
12
|
+
label: label || getLabel(DICTIONARY.LABEL_SUBMIT_BUTTON),
|
|
12
13
|
variant: "contained",
|
|
13
14
|
color: "primary",
|
|
14
|
-
"data-testid": "stepper-submit-button"
|
|
15
|
+
"data-testid": "stepper-submit-button",
|
|
16
|
+
...rest
|
|
15
17
|
}
|
|
16
18
|
);
|
|
17
19
|
}
|
|
@@ -12,28 +12,47 @@ function Step(props) {
|
|
|
12
12
|
steps: state.steps,
|
|
13
13
|
visibilityData: state.visibilityData
|
|
14
14
|
}));
|
|
15
|
-
const currentStepConfig = useMemo(() => {
|
|
16
|
-
return steps[currentStep];
|
|
17
|
-
}, [steps, currentStep]);
|
|
18
15
|
const stepConfig = useMemo(() => {
|
|
19
16
|
return steps.find((step) => step.key === stepKey);
|
|
20
17
|
}, [steps, stepKey]);
|
|
21
18
|
const isStepVisible = useMemo(() => {
|
|
22
|
-
if (!
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
if (stepKey !== currentStepConfig.key) {
|
|
19
|
+
if (!stepConfig) {
|
|
26
20
|
return false;
|
|
27
21
|
}
|
|
28
|
-
|
|
22
|
+
const meetsVisibilityCondition = evaluateVisibilityStepCondition(
|
|
29
23
|
stepConfig,
|
|
30
24
|
formValues || {},
|
|
31
25
|
visibilityData
|
|
32
|
-
)
|
|
26
|
+
);
|
|
27
|
+
if (!meetsVisibilityCondition) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const currentStepConfig = steps[currentStep];
|
|
31
|
+
if (!currentStepConfig) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (evaluateVisibilityStepCondition(currentStepConfig, formValues || {}, visibilityData)) {
|
|
35
|
+
return stepKey === currentStepConfig.key;
|
|
36
|
+
}
|
|
37
|
+
const visibleStepsUpToCurrent = [];
|
|
38
|
+
for (let i = 0; i <= currentStep; i++) {
|
|
39
|
+
const step = steps[i];
|
|
40
|
+
if (evaluateVisibilityStepCondition(step, formValues || {}, visibilityData)) {
|
|
41
|
+
visibleStepsUpToCurrent.push(step);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (visibleStepsUpToCurrent.length === 0) {
|
|
45
|
+
for (let i = currentStep + 1; i < steps.length; i++) {
|
|
46
|
+
const step = steps[i];
|
|
47
|
+
if (evaluateVisibilityStepCondition(step, formValues || {}, visibilityData)) {
|
|
48
|
+
return stepKey === step.key;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
33
51
|
return false;
|
|
34
52
|
}
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
const activeVisibleStep = visibleStepsUpToCurrent[visibleStepsUpToCurrent.length - 1];
|
|
54
|
+
return stepKey === activeVisibleStep.key;
|
|
55
|
+
}, [currentStep, stepKey, stepConfig, steps, formValues, visibilityData]);
|
|
37
56
|
return /* @__PURE__ */ jsx(StepContentStyled, { ownerState: { isStepVisible }, children });
|
|
38
57
|
}
|
|
39
58
|
export {
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useMemo } from "react";
|
|
2
|
+
import React, { useState, useEffect, useMemo } from "react";
|
|
3
3
|
import { u as useIsLastVisibleValidStep } from "../../../../hooks/useIsLastVisibleValidStep/index.js";
|
|
4
4
|
import { m as StepperFooterRightActionsStyled } from "../../../../slots/StepperSlot.js";
|
|
5
|
-
import { S as StepperNextButton } from "../../../StepperButtons/StepperNextButton/index.js";
|
|
6
5
|
import { S as StepperSubmitButton } from "../../../StepperButtons/StepperSubmitButton/index.js";
|
|
6
|
+
import { S as StepperNextButton } from "../../../StepperButtons/StepperNextButton/index.js";
|
|
7
7
|
function StepperFooterRightActions(props) {
|
|
8
8
|
const { children } = props;
|
|
9
9
|
const isLastVisibleValidStep = useIsLastVisibleValidStep();
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const [hasCustomSubmitButton, setHasCustomSubmitButton] = useState(false);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const hasCustomButton = React.Children.toArray(children).some((child) => {
|
|
13
|
+
if (React.isValidElement(child)) {
|
|
14
|
+
return child.type === StepperSubmitButton;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
});
|
|
18
|
+
setHasCustomSubmitButton(hasCustomButton);
|
|
19
|
+
}, [children]);
|
|
20
|
+
const filteredChildren = useMemo(() => {
|
|
14
21
|
return React.Children.toArray(children).filter((child) => {
|
|
15
22
|
if (React.isValidElement(child)) {
|
|
16
|
-
if (child.type ===
|
|
23
|
+
if (!isLastVisibleValidStep && child.type === StepperSubmitButton) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (isLastVisibleValidStep && child.type === StepperNextButton) {
|
|
17
27
|
return false;
|
|
18
28
|
}
|
|
19
29
|
}
|
|
@@ -21,10 +31,10 @@ function StepperFooterRightActions(props) {
|
|
|
21
31
|
});
|
|
22
32
|
}, [children, isLastVisibleValidStep]);
|
|
23
33
|
const submitButton = useMemo(() => {
|
|
24
|
-
return isLastVisibleValidStep ? /* @__PURE__ */ jsx(StepperSubmitButton, {}) : null;
|
|
25
|
-
}, [isLastVisibleValidStep]);
|
|
34
|
+
return isLastVisibleValidStep && !hasCustomSubmitButton ? /* @__PURE__ */ jsx(StepperSubmitButton, {}) : null;
|
|
35
|
+
}, [isLastVisibleValidStep, hasCustomSubmitButton]);
|
|
26
36
|
return /* @__PURE__ */ jsxs(StepperFooterRightActionsStyled, { children: [
|
|
27
|
-
|
|
37
|
+
filteredChildren,
|
|
28
38
|
submitButton
|
|
29
39
|
] });
|
|
30
40
|
}
|
|
@@ -5,6 +5,7 @@ import { ContentAreaSlots, ContentSlots, StepperFooterSlots, StepperSlots } from
|
|
|
5
5
|
import { STEPPER_PREFIX_NAME } from './constants';
|
|
6
6
|
import { M4LOverridesStyleRules } from '../../@types/augmentations';
|
|
7
7
|
import { Step as StepComponent } from './subcomponents/StepperContent/subcomponents/Step';
|
|
8
|
+
import { ButtonProps } from '../mui_extended/Button';
|
|
8
9
|
export type Orientation = 'horizontal' | 'vertical';
|
|
9
10
|
export type IndicatorType = 'number' | 'dot';
|
|
10
11
|
export type FormData = Record<string, string | number | boolean | null | undefined>;
|
|
@@ -145,6 +146,12 @@ export interface StepperFooterProps {
|
|
|
145
146
|
export interface StepperFooterLeftActionsProps {
|
|
146
147
|
children?: ReactNode;
|
|
147
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Props para los botones del Stepper que extienden las props de Button de mui_extended
|
|
151
|
+
*/
|
|
152
|
+
export interface StepperButtonProps extends Omit<ButtonProps, 'label'> {
|
|
153
|
+
label?: string;
|
|
154
|
+
}
|
|
148
155
|
/**
|
|
149
156
|
* Props del StepperFooterRightActions
|
|
150
157
|
*/
|
|
@@ -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
|
{
|