@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 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
- // Sincronizar estado interno cuando cambien las props
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 && (!value || value.value !== defaultValue.value)) {
58
- setValue(defaultValue);
59
- setInputValue(defaultValue.label);
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
- }, [defaultValue]);
84
+ lastSyncedDefaultValue.current = defaultValue.value;
85
+ }, [defaultValue?.value, isMulti, name, setFormValue]);
62
86
  useEffect(() => {
63
- if (!areOptionArraysEqual(defaultValueMulti, valueMulti)) {
64
- setValueMulti(defaultValueMulti);
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
- }, [defaultValueMulti]);
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.1",
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": "99cc7bef84a3d3c919d336a95eebe82cce2e8c08"
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 interno cuando cambien las props
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 && (!value || value.value !== defaultValue.value)) {
126
- setValue(defaultValue);
127
- setInputValue(defaultValue.label);
140
+ if (!defaultValue || isMulti || hasManualSingleValue.current) {
141
+ return;
128
142
  }
129
- }, [defaultValue]);
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 (!areOptionArraysEqual(defaultValueMulti, valueMulti)) {
133
- setValueMulti(defaultValueMulti);
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
- }, [defaultValueMulti]);
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(