@zauru-sdk/components 6.5.1 → 6.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/esm/Form/SelectField/index.js +45 -8
- package/package.json +2 -2
- package/src/Form/SelectField/index.tsx +63 -8
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [6.5.3](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.5.1...@zauru-sdk/components@6.5.3) (2026-09-16)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* conservar limpieza manual en SelectField multi ([311f4cb](https://github.com/intuitiva/zauru-typescript-sdk/commit/311f4cb58f4767d0263ac0fd73dd9eaff111edc6))
|
|
12
|
+
* sincronizar defaults async de SelectField ([5a06089](https://github.com/intuitiva/zauru-typescript-sdk/commit/5a06089bd2bbda775418123ed983ab98bf8cbef0))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## [6.5.2](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.5.1...@zauru-sdk/components@6.5.2) (2026-09-16)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* sincronizar defaults async de SelectField ([5a06089](https://github.com/intuitiva/zauru-typescript-sdk/commit/5a06089bd2bbda775418123ed983ab98bf8cbef0))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
6
29
|
## [6.5.1](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.5.0...@zauru-sdk/components@6.5.1) (2026-09-16)
|
|
7
30
|
|
|
8
31
|
**Note:** Version bump only for package @zauru-sdk/components
|
|
@@ -24,6 +24,10 @@ export const SelectField = (props) => {
|
|
|
24
24
|
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
25
25
|
const selectRef = useRef(null);
|
|
26
26
|
const optionsRef = useRef(null);
|
|
27
|
+
const hasManualSingleValue = useRef(false);
|
|
28
|
+
const hasManualMultiValue = useRef(false);
|
|
29
|
+
const lastSyncedDefaultValue = useRef(defaultValue?.value);
|
|
30
|
+
const lastSyncedDefaultValues = useRef(defaultValueMulti.map((option) => option.value));
|
|
27
31
|
const [isTabPressed, setIsTabPressed] = useState(false);
|
|
28
32
|
const [isSearching, setIsSearching] = useState(false);
|
|
29
33
|
// Use refs to store stable references to callbacks
|
|
@@ -52,18 +56,49 @@ export const SelectField = (props) => {
|
|
|
52
56
|
const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
|
|
53
57
|
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
|
|
54
58
|
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-200`;
|
|
55
|
-
|
|
59
|
+
const defaultValueMultiKey = defaultValueMulti
|
|
60
|
+
.map((option) => String(option.value))
|
|
61
|
+
.join("\u0000");
|
|
62
|
+
// Sincronizar defaults asíncronos con el estado visual y React Hook Form.
|
|
63
|
+
//
|
|
64
|
+
// Es importante depender del valor escalar y no del objeto completo: muchas
|
|
65
|
+
// pantallas construyen el option con `find()` en cada render. También solo
|
|
66
|
+
// reemplazamos el valor actual si todavía está vacío o conserva el último
|
|
67
|
+
// default sincronizado; así una selección manual no se pierde cuando el
|
|
68
|
+
// catálogo termina de cargar o se recrea el objeto de opciones.
|
|
56
69
|
useEffect(() => {
|
|
57
|
-
if (defaultValue
|
|
58
|
-
|
|
59
|
-
|
|
70
|
+
if (!defaultValue || isMulti || hasManualSingleValue.current) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const currentValue = value?.value;
|
|
74
|
+
const canSyncDefault = currentValue === undefined ||
|
|
75
|
+
currentValue === null ||
|
|
76
|
+
currentValue === lastSyncedDefaultValue.current;
|
|
77
|
+
if (canSyncDefault) {
|
|
78
|
+
if (currentValue !== defaultValue.value) {
|
|
79
|
+
setValue(defaultValue);
|
|
80
|
+
setInputValue(defaultValue.label);
|
|
81
|
+
}
|
|
82
|
+
setFormValue?.(name || "", defaultValue.value);
|
|
60
83
|
}
|
|
61
|
-
|
|
84
|
+
lastSyncedDefaultValue.current = defaultValue.value;
|
|
85
|
+
}, [defaultValue?.value, isMulti, name, setFormValue]);
|
|
62
86
|
useEffect(() => {
|
|
63
|
-
if (!
|
|
64
|
-
|
|
87
|
+
if (!isMulti || hasManualMultiValue.current) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const currentValues = valueMulti.map((option) => option.value);
|
|
91
|
+
const previousDefaultValues = lastSyncedDefaultValues.current;
|
|
92
|
+
const canSyncDefault = currentValues.length === 0 ||
|
|
93
|
+
areOptionArraysEqual(valueMulti, previousDefaultValues.map((value) => ({ value, label: "" })));
|
|
94
|
+
if (canSyncDefault) {
|
|
95
|
+
if (!areOptionArraysEqual(defaultValueMulti, valueMulti)) {
|
|
96
|
+
setValueMulti(defaultValueMulti);
|
|
97
|
+
}
|
|
98
|
+
setFormValue?.(name || "", defaultValueMulti.map((option) => option.value));
|
|
65
99
|
}
|
|
66
|
-
|
|
100
|
+
lastSyncedDefaultValues.current = defaultValueMulti.map((option) => option.value);
|
|
101
|
+
}, [defaultValueMultiKey, isMulti, name, setFormValue]);
|
|
67
102
|
useEffect(() => {
|
|
68
103
|
setFilteredOptions(options);
|
|
69
104
|
}, [options]);
|
|
@@ -80,6 +115,7 @@ export const SelectField = (props) => {
|
|
|
80
115
|
};
|
|
81
116
|
}, []);
|
|
82
117
|
const handleSetSingleValue = useCallback((value) => {
|
|
118
|
+
hasManualSingleValue.current = true;
|
|
83
119
|
if (value) {
|
|
84
120
|
setValue(value);
|
|
85
121
|
setInputValue(value.label);
|
|
@@ -97,6 +133,7 @@ export const SelectField = (props) => {
|
|
|
97
133
|
onChangeRef.current && onChangeRef.current(value ?? null);
|
|
98
134
|
}, [name, setFormValue]);
|
|
99
135
|
const handleAddMultiValue = useCallback((value) => {
|
|
136
|
+
hasManualMultiValue.current = true;
|
|
100
137
|
if (value) {
|
|
101
138
|
const existValue = options.some((v) => v.value === value.value);
|
|
102
139
|
const noEstaYaSeleccionado = !valueMulti.some((v) => v.value === value.value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zauru-sdk/components",
|
|
3
|
-
"version": "6.5.
|
|
3
|
+
"version": "6.5.3",
|
|
4
4
|
"description": "Componentes reutilizables en las WebApps de Zauru.",
|
|
5
5
|
"main": "./dist/esm/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"styled-components": "^5.3.5",
|
|
63
63
|
"zod": "^3.23.8"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "7fd754da02ac48ceb661b39ac86f64a64fe4f749"
|
|
66
66
|
}
|
|
@@ -81,6 +81,12 @@ export const SelectField = (props: Props) => {
|
|
|
81
81
|
const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);
|
|
82
82
|
const selectRef = useRef<HTMLDivElement>(null);
|
|
83
83
|
const optionsRef = useRef<HTMLUListElement>(null);
|
|
84
|
+
const hasManualSingleValue = useRef(false);
|
|
85
|
+
const hasManualMultiValue = useRef(false);
|
|
86
|
+
const lastSyncedDefaultValue = useRef(defaultValue?.value);
|
|
87
|
+
const lastSyncedDefaultValues = useRef(
|
|
88
|
+
defaultValueMulti.map((option) => option.value),
|
|
89
|
+
);
|
|
84
90
|
const [isTabPressed, setIsTabPressed] = useState<boolean>(false);
|
|
85
91
|
const [isSearching, setIsSearching] = useState<boolean>(false);
|
|
86
92
|
|
|
@@ -119,20 +125,67 @@ export const SelectField = (props: Props) => {
|
|
|
119
125
|
const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
|
|
120
126
|
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
|
|
121
127
|
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-200`;
|
|
128
|
+
const defaultValueMultiKey = defaultValueMulti
|
|
129
|
+
.map((option) => String(option.value))
|
|
130
|
+
.join("\u0000");
|
|
122
131
|
|
|
123
|
-
// Sincronizar estado
|
|
132
|
+
// Sincronizar defaults asíncronos con el estado visual y React Hook Form.
|
|
133
|
+
//
|
|
134
|
+
// Es importante depender del valor escalar y no del objeto completo: muchas
|
|
135
|
+
// pantallas construyen el option con `find()` en cada render. También solo
|
|
136
|
+
// reemplazamos el valor actual si todavía está vacío o conserva el último
|
|
137
|
+
// default sincronizado; así una selección manual no se pierde cuando el
|
|
138
|
+
// catálogo termina de cargar o se recrea el objeto de opciones.
|
|
124
139
|
useEffect(() => {
|
|
125
|
-
if (defaultValue
|
|
126
|
-
|
|
127
|
-
setInputValue(defaultValue.label);
|
|
140
|
+
if (!defaultValue || isMulti || hasManualSingleValue.current) {
|
|
141
|
+
return;
|
|
128
142
|
}
|
|
129
|
-
|
|
143
|
+
|
|
144
|
+
const currentValue = value?.value;
|
|
145
|
+
const canSyncDefault =
|
|
146
|
+
currentValue === undefined ||
|
|
147
|
+
currentValue === null ||
|
|
148
|
+
currentValue === lastSyncedDefaultValue.current;
|
|
149
|
+
|
|
150
|
+
if (canSyncDefault) {
|
|
151
|
+
if (currentValue !== defaultValue.value) {
|
|
152
|
+
setValue(defaultValue);
|
|
153
|
+
setInputValue(defaultValue.label);
|
|
154
|
+
}
|
|
155
|
+
setFormValue?.(name || "", defaultValue.value);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
lastSyncedDefaultValue.current = defaultValue.value;
|
|
159
|
+
}, [defaultValue?.value, isMulti, name, setFormValue]);
|
|
130
160
|
|
|
131
161
|
useEffect(() => {
|
|
132
|
-
if (!
|
|
133
|
-
|
|
162
|
+
if (!isMulti || hasManualMultiValue.current) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const currentValues = valueMulti.map((option) => option.value);
|
|
167
|
+
const previousDefaultValues = lastSyncedDefaultValues.current;
|
|
168
|
+
const canSyncDefault =
|
|
169
|
+
currentValues.length === 0 ||
|
|
170
|
+
areOptionArraysEqual(
|
|
171
|
+
valueMulti,
|
|
172
|
+
previousDefaultValues.map((value) => ({ value, label: "" })),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (canSyncDefault) {
|
|
176
|
+
if (!areOptionArraysEqual(defaultValueMulti, valueMulti)) {
|
|
177
|
+
setValueMulti(defaultValueMulti);
|
|
178
|
+
}
|
|
179
|
+
setFormValue?.(
|
|
180
|
+
name || "",
|
|
181
|
+
defaultValueMulti.map((option) => option.value),
|
|
182
|
+
);
|
|
134
183
|
}
|
|
135
|
-
|
|
184
|
+
|
|
185
|
+
lastSyncedDefaultValues.current = defaultValueMulti.map(
|
|
186
|
+
(option) => option.value,
|
|
187
|
+
);
|
|
188
|
+
}, [defaultValueMultiKey, isMulti, name, setFormValue]);
|
|
136
189
|
|
|
137
190
|
useEffect(() => {
|
|
138
191
|
setFilteredOptions(options);
|
|
@@ -156,6 +209,7 @@ export const SelectField = (props: Props) => {
|
|
|
156
209
|
|
|
157
210
|
const handleSetSingleValue = useCallback(
|
|
158
211
|
(value?: SelectFieldOption) => {
|
|
212
|
+
hasManualSingleValue.current = true;
|
|
159
213
|
if (value) {
|
|
160
214
|
setValue(value);
|
|
161
215
|
setInputValue(value.label);
|
|
@@ -176,6 +230,7 @@ export const SelectField = (props: Props) => {
|
|
|
176
230
|
|
|
177
231
|
const handleAddMultiValue = useCallback(
|
|
178
232
|
(value?: SelectFieldOption) => {
|
|
233
|
+
hasManualMultiValue.current = true;
|
|
179
234
|
if (value) {
|
|
180
235
|
const existValue = options.some((v) => v.value === value.value);
|
|
181
236
|
const noEstaYaSeleccionado = !valueMulti.some(
|