@vuehookform/core 0.1.1 → 0.2.0
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/LICENSE +21 -0
- package/README.md +15 -13
- package/dist/core/formContext.d.ts +21 -3
- package/dist/core/useFieldArray.d.ts +3 -3
- package/dist/core/useFieldRegistration.d.ts +2 -2
- package/dist/core/useValidation.d.ts +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/types.d.ts +301 -33
- package/dist/utils/paths.d.ts +1 -1
- package/dist/vuehookform.cjs +520 -186
- package/dist/vuehookform.js +521 -188
- package/package.json +11 -3
package/dist/vuehookform.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { computed, inject, provide, reactive, ref, shallowRef } from "vue";
|
|
1
|
+
import { computed, inject, nextTick, provide, reactive, ref, shallowRef, toValue, watch } from "vue";
|
|
2
2
|
function get(obj, path) {
|
|
3
|
-
if (!path) return obj;
|
|
3
|
+
if (!path || obj === null || obj === void 0) return obj;
|
|
4
4
|
const keys = path.split(".");
|
|
5
5
|
let result = obj;
|
|
6
6
|
for (const key of keys) {
|
|
@@ -20,9 +20,14 @@ function set(obj, path, value) {
|
|
|
20
20
|
if (keys.some((k) => UNSAFE_KEYS.includes(k))) return;
|
|
21
21
|
const lastKey = keys.pop();
|
|
22
22
|
let current = obj;
|
|
23
|
-
for (
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
for (let i = 0; i < keys.length; i++) {
|
|
24
|
+
const key = keys[i];
|
|
25
|
+
const existing = current[key];
|
|
26
|
+
if (existing !== void 0 && existing !== null && typeof existing !== "object") try {
|
|
27
|
+
if (globalThis.process?.env?.NODE_ENV !== "production") console.warn(`[vue-hook-form] set(): Overwriting primitive value at path "${keys.slice(0, i + 1).join(".")}" with an object. Previous value: ${JSON.stringify(existing)}`);
|
|
28
|
+
} catch {}
|
|
29
|
+
if (!(key in current) || typeof current[key] !== "object" || current[key] === null) {
|
|
30
|
+
const nextKey = keys[i + 1];
|
|
26
31
|
current[key] = nextKey && /^\d+$/.test(nextKey) ? [] : {};
|
|
27
32
|
}
|
|
28
33
|
current = current[key];
|
|
@@ -36,13 +41,16 @@ function unset(obj, path) {
|
|
|
36
41
|
let current = obj;
|
|
37
42
|
for (const key of keys) {
|
|
38
43
|
if (!(key in current)) return;
|
|
39
|
-
|
|
44
|
+
const next = current[key];
|
|
45
|
+
if (next === null || typeof next !== "object") return;
|
|
46
|
+
current = next;
|
|
40
47
|
}
|
|
41
48
|
delete current[lastKey];
|
|
42
49
|
}
|
|
43
50
|
var idCounter = 0;
|
|
44
51
|
function generateId() {
|
|
45
|
-
|
|
52
|
+
const random = Math.random().toString(36).substring(2, 11);
|
|
53
|
+
return `field_${Date.now()}_${idCounter++}_${random}`;
|
|
46
54
|
}
|
|
47
55
|
function createFormContext(options) {
|
|
48
56
|
const formData = reactive({});
|
|
@@ -57,26 +65,81 @@ function createFormContext(options) {
|
|
|
57
65
|
isLoading.value = false;
|
|
58
66
|
}).catch((error) => {
|
|
59
67
|
console.error("Failed to load async default values:", error);
|
|
68
|
+
defaultValuesError.value = error;
|
|
60
69
|
isLoading.value = false;
|
|
70
|
+
options.onDefaultValuesError?.(error);
|
|
61
71
|
});
|
|
62
72
|
} else if (options.defaultValues) {
|
|
63
73
|
Object.assign(defaultValues, options.defaultValues);
|
|
64
74
|
Object.assign(formData, defaultValues);
|
|
65
75
|
}
|
|
76
|
+
const errors = shallowRef({});
|
|
77
|
+
const touchedFields = shallowRef({});
|
|
78
|
+
const dirtyFields = shallowRef({});
|
|
79
|
+
const isSubmitting = ref(false);
|
|
80
|
+
const submitCount = ref(0);
|
|
81
|
+
const defaultValuesError = ref(null);
|
|
82
|
+
const isSubmitSuccessful = ref(false);
|
|
83
|
+
const validatingFields = shallowRef({});
|
|
84
|
+
const externalErrors = shallowRef({});
|
|
85
|
+
const errorDelayTimers = /* @__PURE__ */ new Map();
|
|
86
|
+
const pendingErrors = /* @__PURE__ */ new Map();
|
|
87
|
+
const fieldRefs = /* @__PURE__ */ new Map();
|
|
88
|
+
const fieldOptions = /* @__PURE__ */ new Map();
|
|
89
|
+
const fieldArrays = /* @__PURE__ */ new Map();
|
|
90
|
+
const fieldHandlers = /* @__PURE__ */ new Map();
|
|
91
|
+
const debounceTimers = /* @__PURE__ */ new Map();
|
|
92
|
+
const validationRequestIds = /* @__PURE__ */ new Map();
|
|
93
|
+
const resetGeneration = ref(0);
|
|
94
|
+
if (options.values !== void 0) {
|
|
95
|
+
const initialValues = toValue(options.values);
|
|
96
|
+
if (initialValues && !isAsyncDefaults) {
|
|
97
|
+
for (const [key, value] of Object.entries(initialValues)) if (value !== void 0) set(formData, key, value);
|
|
98
|
+
}
|
|
99
|
+
watch(() => toValue(options.values), (newValues) => {
|
|
100
|
+
if (newValues) {
|
|
101
|
+
for (const [key, value] of Object.entries(newValues)) if (value !== void 0) {
|
|
102
|
+
set(formData, key, value);
|
|
103
|
+
const fieldRef = fieldRefs.get(key);
|
|
104
|
+
const opts = fieldOptions.get(key);
|
|
105
|
+
if (fieldRef?.value && !opts?.controlled) {
|
|
106
|
+
const el = fieldRef.value;
|
|
107
|
+
if (el.type === "checkbox") el.checked = value;
|
|
108
|
+
else el.value = value;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}, { deep: true });
|
|
113
|
+
}
|
|
114
|
+
if (options.errors !== void 0) {
|
|
115
|
+
const initialErrors = toValue(options.errors);
|
|
116
|
+
if (initialErrors) externalErrors.value = initialErrors;
|
|
117
|
+
watch(() => toValue(options.errors), (newErrors) => {
|
|
118
|
+
externalErrors.value = newErrors || {};
|
|
119
|
+
}, { deep: true });
|
|
120
|
+
}
|
|
66
121
|
return {
|
|
67
122
|
formData,
|
|
68
123
|
defaultValues,
|
|
69
|
-
errors
|
|
70
|
-
touchedFields
|
|
71
|
-
dirtyFields
|
|
72
|
-
isSubmitting
|
|
124
|
+
errors,
|
|
125
|
+
touchedFields,
|
|
126
|
+
dirtyFields,
|
|
127
|
+
isSubmitting,
|
|
73
128
|
isLoading,
|
|
74
|
-
submitCount
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
129
|
+
submitCount,
|
|
130
|
+
defaultValuesError,
|
|
131
|
+
isSubmitSuccessful,
|
|
132
|
+
validatingFields,
|
|
133
|
+
externalErrors,
|
|
134
|
+
errorDelayTimers,
|
|
135
|
+
pendingErrors,
|
|
136
|
+
fieldRefs,
|
|
137
|
+
fieldOptions,
|
|
138
|
+
fieldArrays,
|
|
139
|
+
fieldHandlers,
|
|
140
|
+
debounceTimers,
|
|
141
|
+
validationRequestIds,
|
|
142
|
+
resetGeneration,
|
|
80
143
|
options
|
|
81
144
|
};
|
|
82
145
|
}
|
|
@@ -85,6 +148,17 @@ function clearFieldErrors(errors, fieldPath) {
|
|
|
85
148
|
for (const key of Object.keys(newErrors)) if (key === fieldPath || key.startsWith(`${fieldPath}.`)) delete newErrors[key];
|
|
86
149
|
return newErrors;
|
|
87
150
|
}
|
|
151
|
+
function setValidating(ctx, fieldPath, isValidating) {
|
|
152
|
+
if (isValidating) ctx.validatingFields.value = {
|
|
153
|
+
...ctx.validatingFields.value,
|
|
154
|
+
[fieldPath]: true
|
|
155
|
+
};
|
|
156
|
+
else {
|
|
157
|
+
const newValidating = { ...ctx.validatingFields.value };
|
|
158
|
+
delete newValidating[fieldPath];
|
|
159
|
+
ctx.validatingFields.value = newValidating;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
88
162
|
function groupErrorsByPath(issues) {
|
|
89
163
|
const grouped = /* @__PURE__ */ new Map();
|
|
90
164
|
for (const issue of issues) {
|
|
@@ -98,8 +172,11 @@ function groupErrorsByPath(issues) {
|
|
|
98
172
|
}
|
|
99
173
|
return grouped;
|
|
100
174
|
}
|
|
101
|
-
function createFieldError(errors) {
|
|
102
|
-
|
|
175
|
+
function createFieldError(errors, criteriaMode = "firstError") {
|
|
176
|
+
const firstError = errors[0];
|
|
177
|
+
if (!firstError) return "";
|
|
178
|
+
if (criteriaMode === "firstError") return firstError.message;
|
|
179
|
+
if (errors.length === 1) return firstError.message;
|
|
103
180
|
const types = {};
|
|
104
181
|
for (const err of errors) {
|
|
105
182
|
const existing = types[err.type];
|
|
@@ -107,135 +184,215 @@ function createFieldError(errors) {
|
|
|
107
184
|
else types[err.type] = err.message;
|
|
108
185
|
}
|
|
109
186
|
return {
|
|
110
|
-
type:
|
|
111
|
-
message:
|
|
187
|
+
type: firstError.type,
|
|
188
|
+
message: firstError.message,
|
|
112
189
|
types
|
|
113
190
|
};
|
|
114
191
|
}
|
|
115
192
|
function createValidation(ctx) {
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
193
|
+
function scheduleError(fieldPath, error) {
|
|
194
|
+
const delayMs = ctx.options.delayError || 0;
|
|
195
|
+
if (delayMs <= 0) {
|
|
196
|
+
const newErrors = { ...ctx.errors.value };
|
|
197
|
+
set(newErrors, fieldPath, error);
|
|
198
|
+
ctx.errors.value = newErrors;
|
|
199
|
+
return;
|
|
122
200
|
}
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
ctx.
|
|
201
|
+
const existingTimer = ctx.errorDelayTimers.get(fieldPath);
|
|
202
|
+
if (existingTimer) clearTimeout(existingTimer);
|
|
203
|
+
ctx.pendingErrors.set(fieldPath, error);
|
|
204
|
+
const timer = setTimeout(() => {
|
|
205
|
+
ctx.errorDelayTimers.delete(fieldPath);
|
|
206
|
+
const pendingError = ctx.pendingErrors.get(fieldPath);
|
|
207
|
+
if (pendingError !== void 0) {
|
|
208
|
+
ctx.pendingErrors.delete(fieldPath);
|
|
209
|
+
const newErrors = { ...ctx.errors.value };
|
|
210
|
+
set(newErrors, fieldPath, pendingError);
|
|
211
|
+
ctx.errors.value = newErrors;
|
|
212
|
+
}
|
|
213
|
+
}, delayMs);
|
|
214
|
+
ctx.errorDelayTimers.set(fieldPath, timer);
|
|
215
|
+
}
|
|
216
|
+
function cancelError(fieldPath) {
|
|
217
|
+
const timer = ctx.errorDelayTimers.get(fieldPath);
|
|
218
|
+
if (timer) {
|
|
219
|
+
clearTimeout(timer);
|
|
220
|
+
ctx.errorDelayTimers.delete(fieldPath);
|
|
221
|
+
}
|
|
222
|
+
ctx.pendingErrors.delete(fieldPath);
|
|
223
|
+
return clearFieldErrors(ctx.errors.value, fieldPath);
|
|
224
|
+
}
|
|
225
|
+
function clearAllPendingErrors() {
|
|
226
|
+
for (const timer of ctx.errorDelayTimers.values()) clearTimeout(timer);
|
|
227
|
+
ctx.errorDelayTimers.clear();
|
|
228
|
+
ctx.pendingErrors.clear();
|
|
229
|
+
}
|
|
230
|
+
async function validate(fieldPath) {
|
|
231
|
+
const generationAtStart = ctx.resetGeneration.value;
|
|
232
|
+
const criteriaMode = ctx.options.criteriaMode || "firstError";
|
|
233
|
+
const validatingKey = fieldPath || "_form";
|
|
234
|
+
setValidating(ctx, validatingKey, true);
|
|
235
|
+
try {
|
|
236
|
+
const result = await ctx.options.schema.safeParseAsync(ctx.formData);
|
|
237
|
+
if (ctx.resetGeneration.value !== generationAtStart) return true;
|
|
238
|
+
if (result.success) {
|
|
239
|
+
if (fieldPath) ctx.errors.value = cancelError(fieldPath);
|
|
240
|
+
else {
|
|
241
|
+
clearAllPendingErrors();
|
|
242
|
+
ctx.errors.value = {};
|
|
243
|
+
}
|
|
131
244
|
return true;
|
|
132
245
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
246
|
+
const zodErrors = result.error.issues;
|
|
247
|
+
if (fieldPath) {
|
|
248
|
+
const fieldErrors = zodErrors.filter((issue) => {
|
|
249
|
+
const path = issue.path.join(".");
|
|
250
|
+
return path === fieldPath || path.startsWith(`${fieldPath}.`);
|
|
251
|
+
});
|
|
252
|
+
if (fieldErrors.length === 0) {
|
|
253
|
+
ctx.errors.value = cancelError(fieldPath);
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
ctx.errors.value = cancelError(fieldPath);
|
|
257
|
+
const grouped$1 = groupErrorsByPath(fieldErrors);
|
|
258
|
+
for (const [path, errors] of grouped$1) scheduleError(path, createFieldError(errors, criteriaMode));
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
clearAllPendingErrors();
|
|
262
|
+
ctx.errors.value = {};
|
|
263
|
+
const grouped = groupErrorsByPath(zodErrors);
|
|
264
|
+
for (const [path, errors] of grouped) scheduleError(path, createFieldError(errors, criteriaMode));
|
|
137
265
|
return false;
|
|
266
|
+
} finally {
|
|
267
|
+
setValidating(ctx, validatingKey, false);
|
|
138
268
|
}
|
|
139
|
-
const newErrors = {};
|
|
140
|
-
const grouped = groupErrorsByPath(zodErrors);
|
|
141
|
-
for (const [path, errors] of grouped) set(newErrors, path, createFieldError(errors));
|
|
142
|
-
ctx.errors.value = newErrors;
|
|
143
|
-
return false;
|
|
144
269
|
}
|
|
145
|
-
return {
|
|
270
|
+
return {
|
|
271
|
+
validate,
|
|
272
|
+
clearAllPendingErrors
|
|
273
|
+
};
|
|
146
274
|
}
|
|
275
|
+
var validationRequestCounter = 0;
|
|
147
276
|
function createFieldRegistration(ctx, validate) {
|
|
148
277
|
function register(name, registerOptions) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
278
|
+
let fieldRef = ctx.fieldRefs.get(name);
|
|
279
|
+
if (!fieldRef) {
|
|
280
|
+
fieldRef = ref(null);
|
|
281
|
+
ctx.fieldRefs.set(name, fieldRef);
|
|
282
|
+
if (get(ctx.formData, name) === void 0) {
|
|
283
|
+
const defaultValue = get(ctx.defaultValues, name);
|
|
284
|
+
if (defaultValue !== void 0) set(ctx.formData, name, defaultValue);
|
|
285
|
+
}
|
|
155
286
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
287
|
+
if (registerOptions) ctx.fieldOptions.set(name, registerOptions);
|
|
288
|
+
let handlers = ctx.fieldHandlers.get(name);
|
|
289
|
+
if (!handlers) {
|
|
290
|
+
const runCustomValidation = async (fieldName, value, requestId, resetGenAtStart) => {
|
|
291
|
+
const fieldOpts = ctx.fieldOptions.get(fieldName);
|
|
292
|
+
if (!fieldOpts?.validate || fieldOpts.disabled) return;
|
|
293
|
+
const error = await fieldOpts.validate(value);
|
|
294
|
+
if (requestId !== ctx.validationRequestIds.get(fieldName)) return;
|
|
295
|
+
if (ctx.resetGeneration.value !== resetGenAtStart) return;
|
|
296
|
+
if (error) ctx.errors.value = {
|
|
297
|
+
...ctx.errors.value,
|
|
298
|
+
[fieldName]: error
|
|
299
|
+
};
|
|
300
|
+
else {
|
|
301
|
+
const newErrors = { ...ctx.errors.value };
|
|
302
|
+
delete newErrors[fieldName];
|
|
303
|
+
ctx.errors.value = newErrors;
|
|
304
|
+
}
|
|
164
305
|
};
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
ctx.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
306
|
+
const onInput = async (e) => {
|
|
307
|
+
const target = e.target;
|
|
308
|
+
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
309
|
+
set(ctx.formData, name, value);
|
|
310
|
+
ctx.dirtyFields.value = {
|
|
311
|
+
...ctx.dirtyFields.value,
|
|
312
|
+
[name]: true
|
|
313
|
+
};
|
|
314
|
+
if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange") {
|
|
315
|
+
await validate(name);
|
|
316
|
+
const fieldOpts$1 = ctx.fieldOptions.get(name);
|
|
317
|
+
if (fieldOpts$1?.deps && fieldOpts$1.deps.length > 0) for (const depField of fieldOpts$1.deps) validate(depField);
|
|
318
|
+
}
|
|
319
|
+
const fieldOpts = ctx.fieldOptions.get(name);
|
|
320
|
+
if (fieldOpts?.validate && !fieldOpts.disabled) {
|
|
321
|
+
const requestId = ++validationRequestCounter;
|
|
322
|
+
ctx.validationRequestIds.set(name, requestId);
|
|
323
|
+
const resetGenAtStart = ctx.resetGeneration.value;
|
|
324
|
+
const debounceMs = fieldOpts.validateDebounce || 0;
|
|
325
|
+
if (debounceMs > 0) {
|
|
326
|
+
const existingTimer = ctx.debounceTimers.get(name);
|
|
327
|
+
if (existingTimer) clearTimeout(existingTimer);
|
|
328
|
+
const timer = setTimeout(() => {
|
|
329
|
+
ctx.debounceTimers.delete(name);
|
|
330
|
+
runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
331
|
+
}, debounceMs);
|
|
332
|
+
ctx.debounceTimers.set(name, timer);
|
|
333
|
+
} else await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
334
|
+
}
|
|
178
335
|
};
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
ctx.debounceTimers.delete(name);
|
|
190
|
-
runCustomValidation(name, value, requestId);
|
|
191
|
-
}, debounceMs);
|
|
192
|
-
ctx.debounceTimers.set(name, timer);
|
|
193
|
-
} else await runCustomValidation(name, value, requestId);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
const onBlur = async (_e) => {
|
|
197
|
-
ctx.touchedFields.value = {
|
|
198
|
-
...ctx.touchedFields.value,
|
|
199
|
-
[name]: true
|
|
336
|
+
const onBlur = async (_e) => {
|
|
337
|
+
ctx.touchedFields.value = {
|
|
338
|
+
...ctx.touchedFields.value,
|
|
339
|
+
[name]: true
|
|
340
|
+
};
|
|
341
|
+
if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") {
|
|
342
|
+
await validate(name);
|
|
343
|
+
const fieldOpts = ctx.fieldOptions.get(name);
|
|
344
|
+
if (fieldOpts?.deps && fieldOpts.deps.length > 0) for (const depField of fieldOpts.deps) validate(depField);
|
|
345
|
+
}
|
|
200
346
|
};
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
delete newErrors[name];
|
|
216
|
-
ctx.errors.value = newErrors;
|
|
217
|
-
const newTouched = { ...ctx.touchedFields.value };
|
|
218
|
-
delete newTouched[name];
|
|
219
|
-
ctx.touchedFields.value = newTouched;
|
|
220
|
-
const newDirty = { ...ctx.dirtyFields.value };
|
|
221
|
-
delete newDirty[name];
|
|
222
|
-
ctx.dirtyFields.value = newDirty;
|
|
223
|
-
ctx.fieldRefs.delete(name);
|
|
224
|
-
ctx.fieldOptions.delete(name);
|
|
347
|
+
const refCallback = (el) => {
|
|
348
|
+
const currentFieldRef = ctx.fieldRefs.get(name);
|
|
349
|
+
if (!currentFieldRef) return;
|
|
350
|
+
const previousEl = currentFieldRef.value;
|
|
351
|
+
if (previousEl === el) return;
|
|
352
|
+
if (previousEl && el) return;
|
|
353
|
+
currentFieldRef.value = el;
|
|
354
|
+
const opts = ctx.fieldOptions.get(name);
|
|
355
|
+
if (el && !opts?.controlled && el instanceof HTMLInputElement) {
|
|
356
|
+
const value = get(ctx.formData, name);
|
|
357
|
+
if (value !== void 0) if (el.type === "checkbox") el.checked = value;
|
|
358
|
+
else el.value = value;
|
|
359
|
+
}
|
|
360
|
+
if (previousEl && !el) {
|
|
225
361
|
const timer = ctx.debounceTimers.get(name);
|
|
226
362
|
if (timer) {
|
|
227
363
|
clearTimeout(timer);
|
|
228
364
|
ctx.debounceTimers.delete(name);
|
|
229
365
|
}
|
|
230
366
|
ctx.validationRequestIds.delete(name);
|
|
367
|
+
if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
|
|
368
|
+
unset(ctx.formData, name);
|
|
369
|
+
const newErrors = { ...ctx.errors.value };
|
|
370
|
+
delete newErrors[name];
|
|
371
|
+
ctx.errors.value = newErrors;
|
|
372
|
+
const newTouched = { ...ctx.touchedFields.value };
|
|
373
|
+
delete newTouched[name];
|
|
374
|
+
ctx.touchedFields.value = newTouched;
|
|
375
|
+
const newDirty = { ...ctx.dirtyFields.value };
|
|
376
|
+
delete newDirty[name];
|
|
377
|
+
ctx.dirtyFields.value = newDirty;
|
|
378
|
+
ctx.fieldRefs.delete(name);
|
|
379
|
+
ctx.fieldOptions.delete(name);
|
|
380
|
+
ctx.fieldHandlers.delete(name);
|
|
381
|
+
}
|
|
231
382
|
}
|
|
232
|
-
}
|
|
233
|
-
|
|
383
|
+
};
|
|
384
|
+
handlers = {
|
|
385
|
+
onInput,
|
|
386
|
+
onBlur,
|
|
387
|
+
refCallback
|
|
388
|
+
};
|
|
389
|
+
ctx.fieldHandlers.set(name, handlers);
|
|
390
|
+
}
|
|
234
391
|
return {
|
|
235
392
|
name,
|
|
236
|
-
ref: refCallback,
|
|
237
|
-
onInput,
|
|
238
|
-
onBlur,
|
|
393
|
+
ref: handlers.refCallback,
|
|
394
|
+
onInput: handlers.onInput,
|
|
395
|
+
onBlur: handlers.onBlur,
|
|
239
396
|
...registerOptions?.controlled && { value: computed({
|
|
240
397
|
get: () => get(ctx.formData, name),
|
|
241
398
|
set: (val) => {
|
|
@@ -248,9 +405,27 @@ function createFieldRegistration(ctx, validate) {
|
|
|
248
405
|
}) }
|
|
249
406
|
};
|
|
250
407
|
}
|
|
251
|
-
function unregister(name) {
|
|
408
|
+
function unregister(name, options) {
|
|
409
|
+
const opts = options || {};
|
|
410
|
+
if (!opts.keepValue) unset(ctx.formData, name);
|
|
411
|
+
if (!opts.keepError) {
|
|
412
|
+
const newErrors = { ...ctx.errors.value };
|
|
413
|
+
delete newErrors[name];
|
|
414
|
+
ctx.errors.value = newErrors;
|
|
415
|
+
}
|
|
416
|
+
if (!opts.keepTouched) {
|
|
417
|
+
const newTouched = { ...ctx.touchedFields.value };
|
|
418
|
+
delete newTouched[name];
|
|
419
|
+
ctx.touchedFields.value = newTouched;
|
|
420
|
+
}
|
|
421
|
+
if (!opts.keepDirty) {
|
|
422
|
+
const newDirty = { ...ctx.dirtyFields.value };
|
|
423
|
+
delete newDirty[name];
|
|
424
|
+
ctx.dirtyFields.value = newDirty;
|
|
425
|
+
}
|
|
252
426
|
ctx.fieldRefs.delete(name);
|
|
253
427
|
ctx.fieldOptions.delete(name);
|
|
428
|
+
ctx.fieldHandlers.delete(name);
|
|
254
429
|
const timer = ctx.debounceTimers.get(name);
|
|
255
430
|
if (timer) {
|
|
256
431
|
clearTimeout(timer);
|
|
@@ -263,42 +438,89 @@ function createFieldRegistration(ctx, validate) {
|
|
|
263
438
|
unregister
|
|
264
439
|
};
|
|
265
440
|
}
|
|
266
|
-
function createFieldArrayManager(ctx, validate) {
|
|
267
|
-
function fields(name) {
|
|
441
|
+
function createFieldArrayManager(ctx, validate, setFocus) {
|
|
442
|
+
function fields(name, options) {
|
|
268
443
|
let fieldArray = ctx.fieldArrays.get(name);
|
|
269
444
|
if (!fieldArray) {
|
|
270
445
|
const existingValues = get(ctx.formData, name) || [];
|
|
271
446
|
fieldArray = {
|
|
272
447
|
items: ref([]),
|
|
273
|
-
values: existingValues
|
|
448
|
+
values: existingValues,
|
|
449
|
+
indexCache: /* @__PURE__ */ new Map(),
|
|
450
|
+
rules: options?.rules
|
|
274
451
|
};
|
|
275
452
|
ctx.fieldArrays.set(name, fieldArray);
|
|
276
453
|
if (!get(ctx.formData, name)) set(ctx.formData, name, []);
|
|
277
|
-
}
|
|
454
|
+
} else if (options?.rules) fieldArray.rules = options.rules;
|
|
278
455
|
const fa = fieldArray;
|
|
456
|
+
const indexCache = fa.indexCache;
|
|
457
|
+
const rebuildIndexCache = () => {
|
|
458
|
+
indexCache.clear();
|
|
459
|
+
fa.items.value.forEach((item, idx) => {
|
|
460
|
+
indexCache.set(item.key, idx);
|
|
461
|
+
});
|
|
462
|
+
};
|
|
279
463
|
const createItem = (key) => ({
|
|
280
464
|
key,
|
|
281
465
|
get index() {
|
|
282
|
-
return
|
|
466
|
+
return indexCache.get(key) ?? -1;
|
|
283
467
|
},
|
|
284
468
|
remove() {
|
|
285
|
-
const currentIndex =
|
|
469
|
+
const currentIndex = indexCache.get(key) ?? -1;
|
|
286
470
|
if (currentIndex !== -1) removeAt(currentIndex);
|
|
287
471
|
}
|
|
288
472
|
});
|
|
289
|
-
if (fa.items.value.length === 0 && fa.values.length > 0)
|
|
290
|
-
|
|
291
|
-
|
|
473
|
+
if (fa.items.value.length === 0 && fa.values.length > 0) {
|
|
474
|
+
fa.items.value = fa.values.map(() => createItem(generateId()));
|
|
475
|
+
rebuildIndexCache();
|
|
476
|
+
}
|
|
477
|
+
const handleFocus = async (baseIndex, addedCount, focusOptions) => {
|
|
478
|
+
if (!focusOptions?.shouldFocus) return;
|
|
479
|
+
await nextTick();
|
|
480
|
+
const focusItemOffset = focusOptions?.focusIndex ?? 0;
|
|
481
|
+
let fieldPath = `${name}.${baseIndex + Math.min(focusItemOffset, addedCount - 1)}`;
|
|
482
|
+
if (focusOptions?.focusName) fieldPath = `${fieldPath}.${focusOptions.focusName}`;
|
|
483
|
+
setFocus(fieldPath);
|
|
484
|
+
};
|
|
485
|
+
const normalizeToArray = (value) => {
|
|
486
|
+
return Array.isArray(value) ? value : [value];
|
|
487
|
+
};
|
|
488
|
+
const append = (value, focusOptions) => {
|
|
489
|
+
const values = normalizeToArray(value);
|
|
490
|
+
if (values.length === 0) return;
|
|
491
|
+
const currentValues = get(ctx.formData, name) || [];
|
|
492
|
+
const insertIndex = currentValues.length;
|
|
493
|
+
const rules = fa.rules;
|
|
494
|
+
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) return;
|
|
495
|
+
const newValues = [...currentValues, ...values];
|
|
292
496
|
set(ctx.formData, name, newValues);
|
|
293
|
-
|
|
497
|
+
const newItems = values.map(() => createItem(generateId()));
|
|
498
|
+
fa.items.value = [...fa.items.value, ...newItems];
|
|
499
|
+
rebuildIndexCache();
|
|
294
500
|
ctx.dirtyFields.value = {
|
|
295
501
|
...ctx.dirtyFields.value,
|
|
296
502
|
[name]: true
|
|
297
503
|
};
|
|
298
504
|
if (ctx.options.mode === "onChange") validate(name);
|
|
505
|
+
handleFocus(insertIndex, values.length, focusOptions);
|
|
299
506
|
};
|
|
300
|
-
const prepend = (value) => {
|
|
301
|
-
|
|
507
|
+
const prepend = (value, focusOptions) => {
|
|
508
|
+
const values = normalizeToArray(value);
|
|
509
|
+
if (values.length === 0) return;
|
|
510
|
+
const currentValues = get(ctx.formData, name) || [];
|
|
511
|
+
const rules = fa.rules;
|
|
512
|
+
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) return;
|
|
513
|
+
const newValues = [...values, ...currentValues];
|
|
514
|
+
set(ctx.formData, name, newValues);
|
|
515
|
+
const newItems = values.map(() => createItem(generateId()));
|
|
516
|
+
fa.items.value = [...newItems, ...fa.items.value];
|
|
517
|
+
rebuildIndexCache();
|
|
518
|
+
ctx.dirtyFields.value = {
|
|
519
|
+
...ctx.dirtyFields.value,
|
|
520
|
+
[name]: true
|
|
521
|
+
};
|
|
522
|
+
if (ctx.options.mode === "onChange") validate(name);
|
|
523
|
+
handleFocus(0, values.length, focusOptions);
|
|
302
524
|
};
|
|
303
525
|
const update = (index, value) => {
|
|
304
526
|
const currentValues = get(ctx.formData, name) || [];
|
|
@@ -313,38 +535,52 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
313
535
|
if (ctx.options.mode === "onChange") validate(name);
|
|
314
536
|
};
|
|
315
537
|
const removeAt = (index) => {
|
|
316
|
-
const
|
|
538
|
+
const currentValues = get(ctx.formData, name) || [];
|
|
539
|
+
if (index < 0 || index >= currentValues.length) return;
|
|
540
|
+
const rules = fa.rules;
|
|
541
|
+
if (rules?.minLength && currentValues.length - 1 < rules.minLength.value) return;
|
|
542
|
+
const newValues = currentValues.filter((_, i) => i !== index);
|
|
317
543
|
set(ctx.formData, name, newValues);
|
|
318
544
|
const keyToRemove = fa.items.value[index]?.key;
|
|
319
545
|
fa.items.value = fa.items.value.filter((item) => item.key !== keyToRemove);
|
|
546
|
+
rebuildIndexCache();
|
|
320
547
|
ctx.dirtyFields.value = {
|
|
321
548
|
...ctx.dirtyFields.value,
|
|
322
549
|
[name]: true
|
|
323
550
|
};
|
|
324
551
|
if (ctx.options.mode === "onChange") validate(name);
|
|
325
552
|
};
|
|
326
|
-
const insert = (index, value) => {
|
|
553
|
+
const insert = (index, value, focusOptions) => {
|
|
554
|
+
const values = normalizeToArray(value);
|
|
555
|
+
if (values.length === 0) return;
|
|
327
556
|
const currentValues = get(ctx.formData, name) || [];
|
|
557
|
+
const rules = fa.rules;
|
|
558
|
+
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) return;
|
|
559
|
+
const clampedIndex = Math.max(0, Math.min(index, currentValues.length));
|
|
328
560
|
const newValues = [
|
|
329
|
-
...currentValues.slice(0,
|
|
330
|
-
|
|
331
|
-
...currentValues.slice(
|
|
561
|
+
...currentValues.slice(0, clampedIndex),
|
|
562
|
+
...values,
|
|
563
|
+
...currentValues.slice(clampedIndex)
|
|
332
564
|
];
|
|
333
565
|
set(ctx.formData, name, newValues);
|
|
334
|
-
const
|
|
566
|
+
const newItems = values.map(() => createItem(generateId()));
|
|
335
567
|
fa.items.value = [
|
|
336
|
-
...fa.items.value.slice(0,
|
|
337
|
-
|
|
338
|
-
...fa.items.value.slice(
|
|
568
|
+
...fa.items.value.slice(0, clampedIndex),
|
|
569
|
+
...newItems,
|
|
570
|
+
...fa.items.value.slice(clampedIndex)
|
|
339
571
|
];
|
|
572
|
+
rebuildIndexCache();
|
|
340
573
|
ctx.dirtyFields.value = {
|
|
341
574
|
...ctx.dirtyFields.value,
|
|
342
575
|
[name]: true
|
|
343
576
|
};
|
|
344
577
|
if (ctx.options.mode === "onChange") validate(name);
|
|
578
|
+
handleFocus(clampedIndex, values.length, focusOptions);
|
|
345
579
|
};
|
|
346
580
|
const swap = (indexA, indexB) => {
|
|
347
|
-
const
|
|
581
|
+
const currentValues = get(ctx.formData, name) || [];
|
|
582
|
+
if (indexA < 0 || indexB < 0 || indexA >= currentValues.length || indexB >= currentValues.length) return;
|
|
583
|
+
const newValues = [...currentValues];
|
|
348
584
|
[newValues[indexA], newValues[indexB]] = [newValues[indexB], newValues[indexA]];
|
|
349
585
|
set(ctx.formData, name, newValues);
|
|
350
586
|
const newItems = [...fa.items.value];
|
|
@@ -354,6 +590,7 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
354
590
|
newItems[indexA] = itemB;
|
|
355
591
|
newItems[indexB] = itemA;
|
|
356
592
|
fa.items.value = newItems;
|
|
593
|
+
rebuildIndexCache();
|
|
357
594
|
}
|
|
358
595
|
ctx.dirtyFields.value = {
|
|
359
596
|
...ctx.dirtyFields.value,
|
|
@@ -362,17 +599,22 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
362
599
|
if (ctx.options.mode === "onChange") validate(name);
|
|
363
600
|
};
|
|
364
601
|
const move = (from, to) => {
|
|
365
|
-
const
|
|
602
|
+
const currentValues = get(ctx.formData, name) || [];
|
|
603
|
+
if (from < 0 || from >= currentValues.length || to < 0) return;
|
|
604
|
+
const newValues = [...currentValues];
|
|
366
605
|
const [removed] = newValues.splice(from, 1);
|
|
367
606
|
if (removed !== void 0) {
|
|
368
|
-
|
|
607
|
+
const clampedTo = Math.min(to, newValues.length);
|
|
608
|
+
newValues.splice(clampedTo, 0, removed);
|
|
369
609
|
set(ctx.formData, name, newValues);
|
|
370
610
|
}
|
|
371
611
|
const newItems = [...fa.items.value];
|
|
372
612
|
const [removedItem] = newItems.splice(from, 1);
|
|
373
613
|
if (removedItem) {
|
|
374
|
-
|
|
614
|
+
const clampedTo = Math.min(to, newItems.length);
|
|
615
|
+
newItems.splice(clampedTo, 0, removedItem);
|
|
375
616
|
fa.items.value = newItems;
|
|
617
|
+
rebuildIndexCache();
|
|
376
618
|
}
|
|
377
619
|
ctx.dirtyFields.value = {
|
|
378
620
|
...ctx.dirtyFields.value,
|
|
@@ -380,6 +622,17 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
380
622
|
};
|
|
381
623
|
if (ctx.options.mode === "onChange") validate(name);
|
|
382
624
|
};
|
|
625
|
+
const replace = (newValues) => {
|
|
626
|
+
if (!Array.isArray(newValues)) return;
|
|
627
|
+
set(ctx.formData, name, newValues);
|
|
628
|
+
fa.items.value = newValues.map(() => createItem(generateId()));
|
|
629
|
+
rebuildIndexCache();
|
|
630
|
+
ctx.dirtyFields.value = {
|
|
631
|
+
...ctx.dirtyFields.value,
|
|
632
|
+
[name]: true
|
|
633
|
+
};
|
|
634
|
+
if (ctx.options.mode === "onChange") validate(name);
|
|
635
|
+
};
|
|
383
636
|
return {
|
|
384
637
|
value: fa.items.value,
|
|
385
638
|
append,
|
|
@@ -388,31 +641,56 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
388
641
|
insert,
|
|
389
642
|
swap,
|
|
390
643
|
move,
|
|
391
|
-
update
|
|
644
|
+
update,
|
|
645
|
+
replace
|
|
392
646
|
};
|
|
393
647
|
}
|
|
394
648
|
return { fields };
|
|
395
649
|
}
|
|
396
650
|
function useForm(options) {
|
|
397
651
|
const ctx = createFormContext(options);
|
|
398
|
-
const { validate } = createValidation(ctx);
|
|
652
|
+
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
399
653
|
const { register, unregister } = createFieldRegistration(ctx, validate);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
})
|
|
654
|
+
function setFocus(name, focusOptions) {
|
|
655
|
+
const fieldRef = ctx.fieldRefs.get(name);
|
|
656
|
+
if (!fieldRef?.value) return;
|
|
657
|
+
const el = fieldRef.value;
|
|
658
|
+
if (typeof el.focus === "function") {
|
|
659
|
+
el.focus();
|
|
660
|
+
if (focusOptions?.shouldSelect && el instanceof HTMLInputElement && typeof el.select === "function") el.select();
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
const setFocusWrapper = (name) => setFocus(name);
|
|
664
|
+
const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper);
|
|
665
|
+
const formState = computed(() => {
|
|
666
|
+
const mergedErrors = {
|
|
667
|
+
...ctx.errors.value,
|
|
668
|
+
...ctx.externalErrors.value
|
|
669
|
+
};
|
|
670
|
+
return {
|
|
671
|
+
errors: mergedErrors,
|
|
672
|
+
isDirty: Object.keys(ctx.dirtyFields.value).some((k) => ctx.dirtyFields.value[k]),
|
|
673
|
+
dirtyFields: ctx.dirtyFields.value,
|
|
674
|
+
isValid: (ctx.submitCount.value > 0 || Object.keys(ctx.touchedFields.value).length > 0) && Object.keys(mergedErrors).length === 0,
|
|
675
|
+
isSubmitting: ctx.isSubmitting.value,
|
|
676
|
+
isLoading: ctx.isLoading.value,
|
|
677
|
+
isReady: !ctx.isLoading.value,
|
|
678
|
+
isValidating: Object.keys(ctx.validatingFields.value).some((k) => ctx.validatingFields.value[k]),
|
|
679
|
+
validatingFields: ctx.validatingFields.value,
|
|
680
|
+
touchedFields: ctx.touchedFields.value,
|
|
681
|
+
submitCount: ctx.submitCount.value,
|
|
682
|
+
defaultValuesError: ctx.defaultValuesError.value,
|
|
683
|
+
isSubmitted: ctx.submitCount.value > 0,
|
|
684
|
+
isSubmitSuccessful: ctx.isSubmitSuccessful.value
|
|
685
|
+
};
|
|
686
|
+
});
|
|
411
687
|
function handleSubmit(onValid, onInvalid) {
|
|
412
688
|
return async (e) => {
|
|
413
689
|
e.preventDefault();
|
|
690
|
+
if (ctx.isSubmitting.value) return;
|
|
414
691
|
ctx.isSubmitting.value = true;
|
|
415
692
|
ctx.submitCount.value++;
|
|
693
|
+
ctx.isSubmitSuccessful.value = false;
|
|
416
694
|
try {
|
|
417
695
|
for (const [name, fieldRef] of ctx.fieldRefs.entries()) {
|
|
418
696
|
const el = fieldRef.value;
|
|
@@ -423,41 +701,60 @@ function useForm(options) {
|
|
|
423
701
|
}
|
|
424
702
|
}
|
|
425
703
|
}
|
|
426
|
-
if (await validate())
|
|
427
|
-
|
|
704
|
+
if (await validate()) {
|
|
705
|
+
await onValid(ctx.formData);
|
|
706
|
+
ctx.isSubmitSuccessful.value = true;
|
|
707
|
+
} else {
|
|
708
|
+
onInvalid?.(formState.value.errors);
|
|
709
|
+
if (options.shouldFocusError !== false) {
|
|
710
|
+
const firstErrorField = Object.keys(formState.value.errors)[0];
|
|
711
|
+
if (firstErrorField) setFocus(firstErrorField);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
428
714
|
} finally {
|
|
429
715
|
ctx.isSubmitting.value = false;
|
|
430
716
|
}
|
|
431
717
|
};
|
|
432
718
|
}
|
|
433
|
-
function setValue(name, value) {
|
|
719
|
+
function setValue(name, value, setValueOptions) {
|
|
434
720
|
set(ctx.formData, name, value);
|
|
435
|
-
ctx.dirtyFields.value = {
|
|
721
|
+
if (setValueOptions?.shouldDirty !== false) ctx.dirtyFields.value = {
|
|
436
722
|
...ctx.dirtyFields.value,
|
|
437
723
|
[name]: true
|
|
438
724
|
};
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
725
|
+
if (setValueOptions?.shouldTouch) ctx.touchedFields.value = {
|
|
726
|
+
...ctx.touchedFields.value,
|
|
727
|
+
[name]: true
|
|
728
|
+
};
|
|
729
|
+
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
730
|
+
const fieldRef = ctx.fieldRefs.get(name);
|
|
731
|
+
if (fieldRef?.value) {
|
|
732
|
+
const el = fieldRef.value;
|
|
733
|
+
if (el.type === "checkbox") el.checked = value;
|
|
734
|
+
else el.value = value;
|
|
735
|
+
}
|
|
444
736
|
}
|
|
445
|
-
if (
|
|
737
|
+
if (setValueOptions?.shouldValidate) validate(name);
|
|
446
738
|
}
|
|
447
739
|
function getValue(name) {
|
|
448
740
|
return get(ctx.formData, name);
|
|
449
741
|
}
|
|
450
742
|
function reset(values, resetOptions) {
|
|
451
743
|
const opts = resetOptions || {};
|
|
744
|
+
ctx.resetGeneration.value++;
|
|
745
|
+
clearAllPendingErrors();
|
|
746
|
+
ctx.validatingFields.value = {};
|
|
452
747
|
if (!opts.keepDefaultValues && values) Object.assign(ctx.defaultValues, values);
|
|
453
748
|
Object.keys(ctx.formData).forEach((key) => delete ctx.formData[key]);
|
|
454
|
-
const
|
|
749
|
+
const sourceValues = values || ctx.defaultValues;
|
|
750
|
+
const newValues = JSON.parse(JSON.stringify(sourceValues));
|
|
455
751
|
Object.assign(ctx.formData, newValues);
|
|
456
752
|
if (!opts.keepErrors) ctx.errors.value = {};
|
|
457
753
|
if (!opts.keepTouched) ctx.touchedFields.value = {};
|
|
458
754
|
if (!opts.keepDirty) ctx.dirtyFields.value = {};
|
|
459
755
|
if (!opts.keepSubmitCount) ctx.submitCount.value = 0;
|
|
460
756
|
if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
|
|
757
|
+
if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
|
|
461
758
|
ctx.fieldArrays.clear();
|
|
462
759
|
for (const [name, fieldRef] of ctx.fieldRefs.entries()) {
|
|
463
760
|
const el = fieldRef.value;
|
|
@@ -468,13 +765,54 @@ function useForm(options) {
|
|
|
468
765
|
}
|
|
469
766
|
}
|
|
470
767
|
}
|
|
471
|
-
function
|
|
768
|
+
function resetField(name, resetFieldOptions) {
|
|
769
|
+
const opts = resetFieldOptions || {};
|
|
770
|
+
ctx.resetGeneration.value++;
|
|
771
|
+
const errorTimer = ctx.errorDelayTimers.get(name);
|
|
772
|
+
if (errorTimer) {
|
|
773
|
+
clearTimeout(errorTimer);
|
|
774
|
+
ctx.errorDelayTimers.delete(name);
|
|
775
|
+
}
|
|
776
|
+
ctx.pendingErrors.delete(name);
|
|
777
|
+
let defaultValue = opts.defaultValue;
|
|
778
|
+
if (defaultValue === void 0) defaultValue = get(ctx.defaultValues, name);
|
|
779
|
+
else set(ctx.defaultValues, name, defaultValue);
|
|
780
|
+
const clonedValue = defaultValue !== void 0 ? JSON.parse(JSON.stringify(defaultValue)) : void 0;
|
|
781
|
+
set(ctx.formData, name, clonedValue);
|
|
782
|
+
if (!opts.keepError) {
|
|
783
|
+
const newErrors = { ...ctx.errors.value };
|
|
784
|
+
for (const key of Object.keys(newErrors)) if (key === name || key.startsWith(`${name}.`)) delete newErrors[key];
|
|
785
|
+
ctx.errors.value = newErrors;
|
|
786
|
+
}
|
|
787
|
+
if (!opts.keepDirty) {
|
|
788
|
+
const newDirty = { ...ctx.dirtyFields.value };
|
|
789
|
+
delete newDirty[name];
|
|
790
|
+
ctx.dirtyFields.value = newDirty;
|
|
791
|
+
}
|
|
792
|
+
if (!opts.keepTouched) {
|
|
793
|
+
const newTouched = { ...ctx.touchedFields.value };
|
|
794
|
+
delete newTouched[name];
|
|
795
|
+
ctx.touchedFields.value = newTouched;
|
|
796
|
+
}
|
|
797
|
+
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
798
|
+
const fieldRef = ctx.fieldRefs.get(name);
|
|
799
|
+
if (fieldRef?.value) {
|
|
800
|
+
const el = fieldRef.value;
|
|
801
|
+
if (clonedValue !== void 0) if (el.type === "checkbox") el.checked = clonedValue;
|
|
802
|
+
else el.value = clonedValue;
|
|
803
|
+
else if (el.type === "checkbox") el.checked = false;
|
|
804
|
+
else el.value = "";
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function watch$1(name) {
|
|
472
809
|
return computed(() => {
|
|
473
810
|
if (!name) return ctx.formData;
|
|
474
|
-
if (Array.isArray(name))
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
811
|
+
if (Array.isArray(name)) {
|
|
812
|
+
const result = {};
|
|
813
|
+
for (const n of name) result[n] = get(ctx.formData, n);
|
|
814
|
+
return result;
|
|
815
|
+
}
|
|
478
816
|
return get(ctx.formData, name);
|
|
479
817
|
});
|
|
480
818
|
}
|
|
@@ -532,15 +870,6 @@ function useForm(options) {
|
|
|
532
870
|
}
|
|
533
871
|
return await validate(name);
|
|
534
872
|
}
|
|
535
|
-
function setFocus(name, focusOptions) {
|
|
536
|
-
const fieldRef = ctx.fieldRefs.get(name);
|
|
537
|
-
if (!fieldRef?.value) return;
|
|
538
|
-
const el = fieldRef.value;
|
|
539
|
-
if (typeof el.focus === "function") {
|
|
540
|
-
el.focus();
|
|
541
|
-
if (focusOptions?.shouldSelect && el instanceof HTMLInputElement && typeof el.select === "function") el.select();
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
873
|
return {
|
|
545
874
|
register,
|
|
546
875
|
unregister,
|
|
@@ -550,7 +879,8 @@ function useForm(options) {
|
|
|
550
879
|
setValue,
|
|
551
880
|
getValue,
|
|
552
881
|
reset,
|
|
553
|
-
|
|
882
|
+
resetField,
|
|
883
|
+
watch: watch$1,
|
|
554
884
|
validate,
|
|
555
885
|
clearErrors,
|
|
556
886
|
setError,
|
|
@@ -632,4 +962,7 @@ function useFormState(options = {}) {
|
|
|
632
962
|
return { [name]: fullState[name] };
|
|
633
963
|
});
|
|
634
964
|
}
|
|
635
|
-
|
|
965
|
+
function isFieldError(error) {
|
|
966
|
+
return typeof error === "object" && error !== null && "type" in error && "message" in error && typeof error.type === "string" && typeof error.message === "string";
|
|
967
|
+
}
|
|
968
|
+
export { FormContextKey, isFieldError, provideForm, useController, useForm, useFormContext, useFormState, useWatch };
|