@vuehookform/core 0.1.2 → 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/README.md +15 -13
- package/dist/core/formContext.d.ts +10 -1
- 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/vuehookform.cjs +432 -118
- package/dist/vuehookform.js +433 -120
- package/package.json +8 -1
package/dist/vuehookform.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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
3
|
if (!path || obj === null || obj === void 0) return obj;
|
|
4
4
|
const keys = path.split(".");
|
|
@@ -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,27 +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
|
-
|
|
80
|
-
|
|
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,
|
|
81
143
|
options
|
|
82
144
|
};
|
|
83
145
|
}
|
|
@@ -86,6 +148,17 @@ function clearFieldErrors(errors, fieldPath) {
|
|
|
86
148
|
for (const key of Object.keys(newErrors)) if (key === fieldPath || key.startsWith(`${fieldPath}.`)) delete newErrors[key];
|
|
87
149
|
return newErrors;
|
|
88
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
|
+
}
|
|
89
162
|
function groupErrorsByPath(issues) {
|
|
90
163
|
const grouped = /* @__PURE__ */ new Map();
|
|
91
164
|
for (const issue of issues) {
|
|
@@ -99,9 +172,10 @@ function groupErrorsByPath(issues) {
|
|
|
99
172
|
}
|
|
100
173
|
return grouped;
|
|
101
174
|
}
|
|
102
|
-
function createFieldError(errors) {
|
|
175
|
+
function createFieldError(errors, criteriaMode = "firstError") {
|
|
103
176
|
const firstError = errors[0];
|
|
104
177
|
if (!firstError) return "";
|
|
178
|
+
if (criteriaMode === "firstError") return firstError.message;
|
|
105
179
|
if (errors.length === 1) return firstError.message;
|
|
106
180
|
const types = {};
|
|
107
181
|
for (const err of errors) {
|
|
@@ -116,37 +190,89 @@ function createFieldError(errors) {
|
|
|
116
190
|
};
|
|
117
191
|
}
|
|
118
192
|
function createValidation(ctx) {
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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;
|
|
125
200
|
}
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
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
|
+
}
|
|
134
244
|
return true;
|
|
135
245
|
}
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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));
|
|
140
265
|
return false;
|
|
266
|
+
} finally {
|
|
267
|
+
setValidating(ctx, validatingKey, false);
|
|
141
268
|
}
|
|
142
|
-
const newErrors = {};
|
|
143
|
-
const grouped = groupErrorsByPath(zodErrors);
|
|
144
|
-
for (const [path, errors] of grouped) set(newErrors, path, createFieldError(errors));
|
|
145
|
-
ctx.errors.value = newErrors;
|
|
146
|
-
return false;
|
|
147
269
|
}
|
|
148
|
-
return {
|
|
270
|
+
return {
|
|
271
|
+
validate,
|
|
272
|
+
clearAllPendingErrors
|
|
273
|
+
};
|
|
149
274
|
}
|
|
275
|
+
var validationRequestCounter = 0;
|
|
150
276
|
function createFieldRegistration(ctx, validate) {
|
|
151
277
|
function register(name, registerOptions) {
|
|
152
278
|
let fieldRef = ctx.fieldRefs.get(name);
|
|
@@ -161,11 +287,12 @@ function createFieldRegistration(ctx, validate) {
|
|
|
161
287
|
if (registerOptions) ctx.fieldOptions.set(name, registerOptions);
|
|
162
288
|
let handlers = ctx.fieldHandlers.get(name);
|
|
163
289
|
if (!handlers) {
|
|
164
|
-
const runCustomValidation = async (fieldName, value, requestId) => {
|
|
290
|
+
const runCustomValidation = async (fieldName, value, requestId, resetGenAtStart) => {
|
|
165
291
|
const fieldOpts = ctx.fieldOptions.get(fieldName);
|
|
166
292
|
if (!fieldOpts?.validate || fieldOpts.disabled) return;
|
|
167
293
|
const error = await fieldOpts.validate(value);
|
|
168
294
|
if (requestId !== ctx.validationRequestIds.get(fieldName)) return;
|
|
295
|
+
if (ctx.resetGeneration.value !== resetGenAtStart) return;
|
|
169
296
|
if (error) ctx.errors.value = {
|
|
170
297
|
...ctx.errors.value,
|
|
171
298
|
[fieldName]: error
|
|
@@ -184,21 +311,26 @@ function createFieldRegistration(ctx, validate) {
|
|
|
184
311
|
...ctx.dirtyFields.value,
|
|
185
312
|
[name]: true
|
|
186
313
|
};
|
|
187
|
-
if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange")
|
|
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
|
+
}
|
|
188
319
|
const fieldOpts = ctx.fieldOptions.get(name);
|
|
189
320
|
if (fieldOpts?.validate && !fieldOpts.disabled) {
|
|
190
|
-
const requestId =
|
|
321
|
+
const requestId = ++validationRequestCounter;
|
|
191
322
|
ctx.validationRequestIds.set(name, requestId);
|
|
323
|
+
const resetGenAtStart = ctx.resetGeneration.value;
|
|
192
324
|
const debounceMs = fieldOpts.validateDebounce || 0;
|
|
193
325
|
if (debounceMs > 0) {
|
|
194
326
|
const existingTimer = ctx.debounceTimers.get(name);
|
|
195
327
|
if (existingTimer) clearTimeout(existingTimer);
|
|
196
328
|
const timer = setTimeout(() => {
|
|
197
329
|
ctx.debounceTimers.delete(name);
|
|
198
|
-
runCustomValidation(name, value, requestId);
|
|
330
|
+
runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
199
331
|
}, debounceMs);
|
|
200
332
|
ctx.debounceTimers.set(name, timer);
|
|
201
|
-
} else await runCustomValidation(name, value, requestId);
|
|
333
|
+
} else await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
202
334
|
}
|
|
203
335
|
};
|
|
204
336
|
const onBlur = async (_e) => {
|
|
@@ -206,12 +338,18 @@ function createFieldRegistration(ctx, validate) {
|
|
|
206
338
|
...ctx.touchedFields.value,
|
|
207
339
|
[name]: true
|
|
208
340
|
};
|
|
209
|
-
if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur")
|
|
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
|
+
}
|
|
210
346
|
};
|
|
211
347
|
const refCallback = (el) => {
|
|
212
348
|
const currentFieldRef = ctx.fieldRefs.get(name);
|
|
213
349
|
if (!currentFieldRef) return;
|
|
214
350
|
const previousEl = currentFieldRef.value;
|
|
351
|
+
if (previousEl === el) return;
|
|
352
|
+
if (previousEl && el) return;
|
|
215
353
|
currentFieldRef.value = el;
|
|
216
354
|
const opts = ctx.fieldOptions.get(name);
|
|
217
355
|
if (el && !opts?.controlled && el instanceof HTMLInputElement) {
|
|
@@ -220,6 +358,12 @@ function createFieldRegistration(ctx, validate) {
|
|
|
220
358
|
else el.value = value;
|
|
221
359
|
}
|
|
222
360
|
if (previousEl && !el) {
|
|
361
|
+
const timer = ctx.debounceTimers.get(name);
|
|
362
|
+
if (timer) {
|
|
363
|
+
clearTimeout(timer);
|
|
364
|
+
ctx.debounceTimers.delete(name);
|
|
365
|
+
}
|
|
366
|
+
ctx.validationRequestIds.delete(name);
|
|
223
367
|
if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
|
|
224
368
|
unset(ctx.formData, name);
|
|
225
369
|
const newErrors = { ...ctx.errors.value };
|
|
@@ -234,12 +378,6 @@ function createFieldRegistration(ctx, validate) {
|
|
|
234
378
|
ctx.fieldRefs.delete(name);
|
|
235
379
|
ctx.fieldOptions.delete(name);
|
|
236
380
|
ctx.fieldHandlers.delete(name);
|
|
237
|
-
const timer = ctx.debounceTimers.get(name);
|
|
238
|
-
if (timer) {
|
|
239
|
-
clearTimeout(timer);
|
|
240
|
-
ctx.debounceTimers.delete(name);
|
|
241
|
-
}
|
|
242
|
-
ctx.validationRequestIds.delete(name);
|
|
243
381
|
}
|
|
244
382
|
}
|
|
245
383
|
};
|
|
@@ -267,7 +405,24 @@ function createFieldRegistration(ctx, validate) {
|
|
|
267
405
|
}) }
|
|
268
406
|
};
|
|
269
407
|
}
|
|
270
|
-
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
|
+
}
|
|
271
426
|
ctx.fieldRefs.delete(name);
|
|
272
427
|
ctx.fieldOptions.delete(name);
|
|
273
428
|
ctx.fieldHandlers.delete(name);
|
|
@@ -283,42 +438,89 @@ function createFieldRegistration(ctx, validate) {
|
|
|
283
438
|
unregister
|
|
284
439
|
};
|
|
285
440
|
}
|
|
286
|
-
function createFieldArrayManager(ctx, validate) {
|
|
287
|
-
function fields(name) {
|
|
441
|
+
function createFieldArrayManager(ctx, validate, setFocus) {
|
|
442
|
+
function fields(name, options) {
|
|
288
443
|
let fieldArray = ctx.fieldArrays.get(name);
|
|
289
444
|
if (!fieldArray) {
|
|
290
445
|
const existingValues = get(ctx.formData, name) || [];
|
|
291
446
|
fieldArray = {
|
|
292
447
|
items: ref([]),
|
|
293
|
-
values: existingValues
|
|
448
|
+
values: existingValues,
|
|
449
|
+
indexCache: /* @__PURE__ */ new Map(),
|
|
450
|
+
rules: options?.rules
|
|
294
451
|
};
|
|
295
452
|
ctx.fieldArrays.set(name, fieldArray);
|
|
296
453
|
if (!get(ctx.formData, name)) set(ctx.formData, name, []);
|
|
297
|
-
}
|
|
454
|
+
} else if (options?.rules) fieldArray.rules = options.rules;
|
|
298
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
|
+
};
|
|
299
463
|
const createItem = (key) => ({
|
|
300
464
|
key,
|
|
301
465
|
get index() {
|
|
302
|
-
return
|
|
466
|
+
return indexCache.get(key) ?? -1;
|
|
303
467
|
},
|
|
304
468
|
remove() {
|
|
305
|
-
const currentIndex =
|
|
469
|
+
const currentIndex = indexCache.get(key) ?? -1;
|
|
306
470
|
if (currentIndex !== -1) removeAt(currentIndex);
|
|
307
471
|
}
|
|
308
472
|
});
|
|
309
|
-
if (fa.items.value.length === 0 && fa.values.length > 0)
|
|
310
|
-
|
|
311
|
-
|
|
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];
|
|
312
496
|
set(ctx.formData, name, newValues);
|
|
313
|
-
|
|
497
|
+
const newItems = values.map(() => createItem(generateId()));
|
|
498
|
+
fa.items.value = [...fa.items.value, ...newItems];
|
|
499
|
+
rebuildIndexCache();
|
|
314
500
|
ctx.dirtyFields.value = {
|
|
315
501
|
...ctx.dirtyFields.value,
|
|
316
502
|
[name]: true
|
|
317
503
|
};
|
|
318
504
|
if (ctx.options.mode === "onChange") validate(name);
|
|
505
|
+
handleFocus(insertIndex, values.length, focusOptions);
|
|
319
506
|
};
|
|
320
|
-
const prepend = (value) => {
|
|
321
|
-
|
|
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);
|
|
322
524
|
};
|
|
323
525
|
const update = (index, value) => {
|
|
324
526
|
const currentValues = get(ctx.formData, name) || [];
|
|
@@ -333,38 +535,52 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
333
535
|
if (ctx.options.mode === "onChange") validate(name);
|
|
334
536
|
};
|
|
335
537
|
const removeAt = (index) => {
|
|
336
|
-
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);
|
|
337
543
|
set(ctx.formData, name, newValues);
|
|
338
544
|
const keyToRemove = fa.items.value[index]?.key;
|
|
339
545
|
fa.items.value = fa.items.value.filter((item) => item.key !== keyToRemove);
|
|
546
|
+
rebuildIndexCache();
|
|
340
547
|
ctx.dirtyFields.value = {
|
|
341
548
|
...ctx.dirtyFields.value,
|
|
342
549
|
[name]: true
|
|
343
550
|
};
|
|
344
551
|
if (ctx.options.mode === "onChange") validate(name);
|
|
345
552
|
};
|
|
346
|
-
const insert = (index, value) => {
|
|
553
|
+
const insert = (index, value, focusOptions) => {
|
|
554
|
+
const values = normalizeToArray(value);
|
|
555
|
+
if (values.length === 0) return;
|
|
347
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));
|
|
348
560
|
const newValues = [
|
|
349
|
-
...currentValues.slice(0,
|
|
350
|
-
|
|
351
|
-
...currentValues.slice(
|
|
561
|
+
...currentValues.slice(0, clampedIndex),
|
|
562
|
+
...values,
|
|
563
|
+
...currentValues.slice(clampedIndex)
|
|
352
564
|
];
|
|
353
565
|
set(ctx.formData, name, newValues);
|
|
354
|
-
const
|
|
566
|
+
const newItems = values.map(() => createItem(generateId()));
|
|
355
567
|
fa.items.value = [
|
|
356
|
-
...fa.items.value.slice(0,
|
|
357
|
-
|
|
358
|
-
...fa.items.value.slice(
|
|
568
|
+
...fa.items.value.slice(0, clampedIndex),
|
|
569
|
+
...newItems,
|
|
570
|
+
...fa.items.value.slice(clampedIndex)
|
|
359
571
|
];
|
|
572
|
+
rebuildIndexCache();
|
|
360
573
|
ctx.dirtyFields.value = {
|
|
361
574
|
...ctx.dirtyFields.value,
|
|
362
575
|
[name]: true
|
|
363
576
|
};
|
|
364
577
|
if (ctx.options.mode === "onChange") validate(name);
|
|
578
|
+
handleFocus(clampedIndex, values.length, focusOptions);
|
|
365
579
|
};
|
|
366
580
|
const swap = (indexA, indexB) => {
|
|
367
|
-
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];
|
|
368
584
|
[newValues[indexA], newValues[indexB]] = [newValues[indexB], newValues[indexA]];
|
|
369
585
|
set(ctx.formData, name, newValues);
|
|
370
586
|
const newItems = [...fa.items.value];
|
|
@@ -374,6 +590,7 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
374
590
|
newItems[indexA] = itemB;
|
|
375
591
|
newItems[indexB] = itemA;
|
|
376
592
|
fa.items.value = newItems;
|
|
593
|
+
rebuildIndexCache();
|
|
377
594
|
}
|
|
378
595
|
ctx.dirtyFields.value = {
|
|
379
596
|
...ctx.dirtyFields.value,
|
|
@@ -382,17 +599,22 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
382
599
|
if (ctx.options.mode === "onChange") validate(name);
|
|
383
600
|
};
|
|
384
601
|
const move = (from, to) => {
|
|
385
|
-
const
|
|
602
|
+
const currentValues = get(ctx.formData, name) || [];
|
|
603
|
+
if (from < 0 || from >= currentValues.length || to < 0) return;
|
|
604
|
+
const newValues = [...currentValues];
|
|
386
605
|
const [removed] = newValues.splice(from, 1);
|
|
387
606
|
if (removed !== void 0) {
|
|
388
|
-
|
|
607
|
+
const clampedTo = Math.min(to, newValues.length);
|
|
608
|
+
newValues.splice(clampedTo, 0, removed);
|
|
389
609
|
set(ctx.formData, name, newValues);
|
|
390
610
|
}
|
|
391
611
|
const newItems = [...fa.items.value];
|
|
392
612
|
const [removedItem] = newItems.splice(from, 1);
|
|
393
613
|
if (removedItem) {
|
|
394
|
-
|
|
614
|
+
const clampedTo = Math.min(to, newItems.length);
|
|
615
|
+
newItems.splice(clampedTo, 0, removedItem);
|
|
395
616
|
fa.items.value = newItems;
|
|
617
|
+
rebuildIndexCache();
|
|
396
618
|
}
|
|
397
619
|
ctx.dirtyFields.value = {
|
|
398
620
|
...ctx.dirtyFields.value,
|
|
@@ -400,6 +622,17 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
400
622
|
};
|
|
401
623
|
if (ctx.options.mode === "onChange") validate(name);
|
|
402
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
|
+
};
|
|
403
636
|
return {
|
|
404
637
|
value: fa.items.value,
|
|
405
638
|
append,
|
|
@@ -408,31 +641,56 @@ function createFieldArrayManager(ctx, validate) {
|
|
|
408
641
|
insert,
|
|
409
642
|
swap,
|
|
410
643
|
move,
|
|
411
|
-
update
|
|
644
|
+
update,
|
|
645
|
+
replace
|
|
412
646
|
};
|
|
413
647
|
}
|
|
414
648
|
return { fields };
|
|
415
649
|
}
|
|
416
650
|
function useForm(options) {
|
|
417
651
|
const ctx = createFormContext(options);
|
|
418
|
-
const { validate } = createValidation(ctx);
|
|
652
|
+
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
419
653
|
const { register, unregister } = createFieldRegistration(ctx, validate);
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
})
|
|
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
|
+
});
|
|
431
687
|
function handleSubmit(onValid, onInvalid) {
|
|
432
688
|
return async (e) => {
|
|
433
689
|
e.preventDefault();
|
|
690
|
+
if (ctx.isSubmitting.value) return;
|
|
434
691
|
ctx.isSubmitting.value = true;
|
|
435
692
|
ctx.submitCount.value++;
|
|
693
|
+
ctx.isSubmitSuccessful.value = false;
|
|
436
694
|
try {
|
|
437
695
|
for (const [name, fieldRef] of ctx.fieldRefs.entries()) {
|
|
438
696
|
const el = fieldRef.value;
|
|
@@ -443,41 +701,60 @@ function useForm(options) {
|
|
|
443
701
|
}
|
|
444
702
|
}
|
|
445
703
|
}
|
|
446
|
-
if (await validate())
|
|
447
|
-
|
|
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
|
+
}
|
|
448
714
|
} finally {
|
|
449
715
|
ctx.isSubmitting.value = false;
|
|
450
716
|
}
|
|
451
717
|
};
|
|
452
718
|
}
|
|
453
|
-
function setValue(name, value) {
|
|
719
|
+
function setValue(name, value, setValueOptions) {
|
|
454
720
|
set(ctx.formData, name, value);
|
|
455
|
-
ctx.dirtyFields.value = {
|
|
721
|
+
if (setValueOptions?.shouldDirty !== false) ctx.dirtyFields.value = {
|
|
456
722
|
...ctx.dirtyFields.value,
|
|
457
723
|
[name]: true
|
|
458
724
|
};
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
+
}
|
|
464
736
|
}
|
|
465
|
-
if (
|
|
737
|
+
if (setValueOptions?.shouldValidate) validate(name);
|
|
466
738
|
}
|
|
467
739
|
function getValue(name) {
|
|
468
740
|
return get(ctx.formData, name);
|
|
469
741
|
}
|
|
470
742
|
function reset(values, resetOptions) {
|
|
471
743
|
const opts = resetOptions || {};
|
|
744
|
+
ctx.resetGeneration.value++;
|
|
745
|
+
clearAllPendingErrors();
|
|
746
|
+
ctx.validatingFields.value = {};
|
|
472
747
|
if (!opts.keepDefaultValues && values) Object.assign(ctx.defaultValues, values);
|
|
473
748
|
Object.keys(ctx.formData).forEach((key) => delete ctx.formData[key]);
|
|
474
|
-
const
|
|
749
|
+
const sourceValues = values || ctx.defaultValues;
|
|
750
|
+
const newValues = JSON.parse(JSON.stringify(sourceValues));
|
|
475
751
|
Object.assign(ctx.formData, newValues);
|
|
476
752
|
if (!opts.keepErrors) ctx.errors.value = {};
|
|
477
753
|
if (!opts.keepTouched) ctx.touchedFields.value = {};
|
|
478
754
|
if (!opts.keepDirty) ctx.dirtyFields.value = {};
|
|
479
755
|
if (!opts.keepSubmitCount) ctx.submitCount.value = 0;
|
|
480
756
|
if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
|
|
757
|
+
if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
|
|
481
758
|
ctx.fieldArrays.clear();
|
|
482
759
|
for (const [name, fieldRef] of ctx.fieldRefs.entries()) {
|
|
483
760
|
const el = fieldRef.value;
|
|
@@ -488,13 +765,54 @@ function useForm(options) {
|
|
|
488
765
|
}
|
|
489
766
|
}
|
|
490
767
|
}
|
|
491
|
-
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) {
|
|
492
809
|
return computed(() => {
|
|
493
810
|
if (!name) return ctx.formData;
|
|
494
|
-
if (Array.isArray(name))
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
811
|
+
if (Array.isArray(name)) {
|
|
812
|
+
const result = {};
|
|
813
|
+
for (const n of name) result[n] = get(ctx.formData, n);
|
|
814
|
+
return result;
|
|
815
|
+
}
|
|
498
816
|
return get(ctx.formData, name);
|
|
499
817
|
});
|
|
500
818
|
}
|
|
@@ -552,15 +870,6 @@ function useForm(options) {
|
|
|
552
870
|
}
|
|
553
871
|
return await validate(name);
|
|
554
872
|
}
|
|
555
|
-
function setFocus(name, focusOptions) {
|
|
556
|
-
const fieldRef = ctx.fieldRefs.get(name);
|
|
557
|
-
if (!fieldRef?.value) return;
|
|
558
|
-
const el = fieldRef.value;
|
|
559
|
-
if (typeof el.focus === "function") {
|
|
560
|
-
el.focus();
|
|
561
|
-
if (focusOptions?.shouldSelect && el instanceof HTMLInputElement && typeof el.select === "function") el.select();
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
873
|
return {
|
|
565
874
|
register,
|
|
566
875
|
unregister,
|
|
@@ -570,7 +879,8 @@ function useForm(options) {
|
|
|
570
879
|
setValue,
|
|
571
880
|
getValue,
|
|
572
881
|
reset,
|
|
573
|
-
|
|
882
|
+
resetField,
|
|
883
|
+
watch: watch$1,
|
|
574
884
|
validate,
|
|
575
885
|
clearErrors,
|
|
576
886
|
setError,
|
|
@@ -652,4 +962,7 @@ function useFormState(options = {}) {
|
|
|
652
962
|
return { [name]: fullState[name] };
|
|
653
963
|
});
|
|
654
964
|
}
|
|
655
|
-
|
|
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 };
|