@vuehookform/core 0.2.11 → 0.3.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/README.md +27 -29
- package/dist/core/domSync.d.ts +0 -7
- package/dist/core/formContext.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +324 -35
- package/dist/utils/devWarnings.d.ts +55 -0
- package/dist/vuehookform.cjs +398 -21
- package/dist/vuehookform.js +397 -21
- package/package.json +22 -18
package/dist/vuehookform.cjs
CHANGED
|
@@ -53,6 +53,153 @@ function generateId() {
|
|
|
53
53
|
const random = Math.random().toString(36).substring(2, 11);
|
|
54
54
|
return `field_${Date.now()}_${idCounter++}_${random}`;
|
|
55
55
|
}
|
|
56
|
+
var proc = globalThis.process;
|
|
57
|
+
const __DEV__ = proc?.env?.NODE_ENV !== "production";
|
|
58
|
+
var warnedMessages = /* @__PURE__ */ new Set();
|
|
59
|
+
function warnOnce(message, key) {
|
|
60
|
+
if (!__DEV__) return;
|
|
61
|
+
const cacheKey = key ?? message;
|
|
62
|
+
if (warnedMessages.has(cacheKey)) return;
|
|
63
|
+
warnedMessages.add(cacheKey);
|
|
64
|
+
console.warn(`[vue-hook-form] ${message}`);
|
|
65
|
+
}
|
|
66
|
+
function warn(message) {
|
|
67
|
+
if (!__DEV__) return;
|
|
68
|
+
console.warn(`[vue-hook-form] ${message}`);
|
|
69
|
+
}
|
|
70
|
+
function validatePathSyntax(path) {
|
|
71
|
+
if (!__DEV__) return null;
|
|
72
|
+
if (!path || path.trim() === "") return "Path cannot be empty";
|
|
73
|
+
if (path.startsWith(".") || path.endsWith(".") || path.includes("..")) return `Invalid path "${path}": contains empty segments`;
|
|
74
|
+
if (path.includes("[")) return `Invalid path "${path}": use dot notation (e.g., "items.0") instead of bracket notation (e.g., "items[0]")`;
|
|
75
|
+
if (/\s/.test(path)) return `Invalid path "${path}": paths cannot contain whitespace`;
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function traverseSchemaPath(schema, path) {
|
|
79
|
+
const segments = path.split(".");
|
|
80
|
+
let currentSchema = schema;
|
|
81
|
+
for (let i = 0; i < segments.length; i++) {
|
|
82
|
+
const segment = segments[i];
|
|
83
|
+
if (!segment) continue;
|
|
84
|
+
currentSchema = unwrapSchema(currentSchema);
|
|
85
|
+
if (isZodObject(currentSchema)) {
|
|
86
|
+
const shape = currentSchema.shape;
|
|
87
|
+
if (segment in shape) {
|
|
88
|
+
const nextSchema = shape[segment];
|
|
89
|
+
if (nextSchema) {
|
|
90
|
+
currentSchema = nextSchema;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
error: `Field "${segments.slice(0, i + 1).join(".")}" does not exist in schema`,
|
|
96
|
+
availableFields: Object.keys(shape),
|
|
97
|
+
segmentIndex: i
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (isZodArray(currentSchema) && /^\d+$/.test(segment)) {
|
|
101
|
+
currentSchema = currentSchema.element;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
error: `Cannot navigate path "${path}" at segment "${segment}"`,
|
|
106
|
+
segmentIndex: i
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return { schema: currentSchema };
|
|
110
|
+
}
|
|
111
|
+
function validatePathAgainstSchema(schema, path) {
|
|
112
|
+
if (!__DEV__) return { valid: true };
|
|
113
|
+
try {
|
|
114
|
+
const result = traverseSchemaPath(schema, path);
|
|
115
|
+
if ("error" in result) return {
|
|
116
|
+
valid: false,
|
|
117
|
+
reason: result.error,
|
|
118
|
+
availableFields: result.availableFields
|
|
119
|
+
};
|
|
120
|
+
return { valid: true };
|
|
121
|
+
} catch {
|
|
122
|
+
return { valid: true };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function isArrayFieldInSchema(schema, path) {
|
|
126
|
+
if (!__DEV__) return null;
|
|
127
|
+
try {
|
|
128
|
+
const result = traverseSchemaPath(schema, path);
|
|
129
|
+
if ("error" in result) return null;
|
|
130
|
+
return isZodArray(unwrapSchema(result.schema));
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function warnInvalidPath(fnName, path, reason) {
|
|
136
|
+
if (!__DEV__) return;
|
|
137
|
+
let message = `${fnName}("${path}"): ${reason}`;
|
|
138
|
+
if (reason.includes("bracket notation")) {
|
|
139
|
+
const fixedPath = path.replace(/\[(\d+)\]/g, ".$1");
|
|
140
|
+
message += `\n FIX: Use dot notation for array indices`;
|
|
141
|
+
message += `\n EXAMPLE: ${fnName}("${fixedPath}")`;
|
|
142
|
+
} else if (reason.includes("empty")) {
|
|
143
|
+
message += `\n FIX: Provide a non-empty field path`;
|
|
144
|
+
message += `\n EXAMPLE: ${fnName}("email") or ${fnName}("user.address.city")`;
|
|
145
|
+
} else if (reason.includes("whitespace")) {
|
|
146
|
+
const fixedPath = path.replace(/\s/g, "");
|
|
147
|
+
message += `\n FIX: Remove spaces from the field path`;
|
|
148
|
+
message += `\n EXAMPLE: ${fnName}("${fixedPath}")`;
|
|
149
|
+
} else if (reason.includes("empty segments")) {
|
|
150
|
+
const fixedPath = path.replace(/\.{2,}/g, ".").replace(/^\./, "").replace(/\.$/, "");
|
|
151
|
+
message += `\n FIX: Remove extra dots from the path`;
|
|
152
|
+
message += `\n EXAMPLE: ${fnName}("${fixedPath}")`;
|
|
153
|
+
}
|
|
154
|
+
warnOnce(message, `invalid-path:${fnName}:${path}`);
|
|
155
|
+
}
|
|
156
|
+
function warnPathNotInSchema(fnName, path, availableFields) {
|
|
157
|
+
if (!__DEV__) return;
|
|
158
|
+
let message = `${fnName}("${path}"): Path does not exist in your Zod schema.`;
|
|
159
|
+
message += `\n FIX: Check that the path matches your schema definition exactly (case-sensitive)`;
|
|
160
|
+
if (availableFields && availableFields.length > 0) {
|
|
161
|
+
const pathLower = path.toLowerCase();
|
|
162
|
+
const suggestions = availableFields.filter((f) => f.toLowerCase().includes(pathLower) || pathLower.includes(f.toLowerCase()));
|
|
163
|
+
if (suggestions.length > 0) message += `\n DID YOU MEAN: ${suggestions.slice(0, 3).map((s) => `"${s}"`).join(", ")}`;
|
|
164
|
+
message += `\n AVAILABLE: ${availableFields.slice(0, 8).join(", ")}${availableFields.length > 8 ? "..." : ""}`;
|
|
165
|
+
}
|
|
166
|
+
warnOnce(message, `path-not-in-schema:${fnName}:${path}`);
|
|
167
|
+
}
|
|
168
|
+
function warnFieldsOnNonArray(path) {
|
|
169
|
+
if (!__DEV__) return;
|
|
170
|
+
warnOnce(`fields("${path}"): Expected an array field, but this path does not point to an array in your schema. The fields() method is only for array fields. Use register() for non-array fields.`, `fields-non-array:${path}`);
|
|
171
|
+
}
|
|
172
|
+
function warnArrayOperationRejected(operation, path, reason, details) {
|
|
173
|
+
if (!__DEV__) return;
|
|
174
|
+
warn(`${operation}() on "${path}": ${{
|
|
175
|
+
maxLength: details ? `Would exceed maxLength (current: ${details.current}, max: ${details.limit})` : "Would exceed maxLength rule",
|
|
176
|
+
minLength: details ? `Would violate minLength (current: ${details.current}, min: ${details.limit})` : "Would violate minLength rule"
|
|
177
|
+
}[reason]}. Operation was silently ignored.`);
|
|
178
|
+
}
|
|
179
|
+
function warnArrayIndexOutOfBounds(operation, path, index, length) {
|
|
180
|
+
if (!__DEV__) return;
|
|
181
|
+
warn(`${operation}() on "${path}": Index ${index} is out of bounds (array length: ${length}). Operation was silently ignored.`);
|
|
182
|
+
}
|
|
183
|
+
function getDefProp(schema, prop) {
|
|
184
|
+
return schema.def[prop];
|
|
185
|
+
}
|
|
186
|
+
function getTypeName(schema) {
|
|
187
|
+
return getDefProp(schema, "typeName");
|
|
188
|
+
}
|
|
189
|
+
function isZodObject(schema) {
|
|
190
|
+
return getTypeName(schema) === "ZodObject";
|
|
191
|
+
}
|
|
192
|
+
function isZodArray(schema) {
|
|
193
|
+
return getTypeName(schema) === "ZodArray";
|
|
194
|
+
}
|
|
195
|
+
function unwrapSchema(schema) {
|
|
196
|
+
const typeName = getTypeName(schema);
|
|
197
|
+
const innerType = getDefProp(schema, "innerType");
|
|
198
|
+
const schemaType = getDefProp(schema, "schema");
|
|
199
|
+
if ((typeName === "ZodOptional" || typeName === "ZodNullable" || typeName === "ZodDefault") && innerType) return unwrapSchema(innerType);
|
|
200
|
+
if (typeName === "ZodEffects" && schemaType) return unwrapSchema(schemaType);
|
|
201
|
+
return schema;
|
|
202
|
+
}
|
|
56
203
|
function createFormContext(options) {
|
|
57
204
|
const formData = (0, vue.reactive)({});
|
|
58
205
|
const defaultValues = (0, vue.reactive)({});
|
|
@@ -92,6 +239,13 @@ function createFormContext(options) {
|
|
|
92
239
|
const debounceTimers = /* @__PURE__ */ new Map();
|
|
93
240
|
const validationRequestIds = /* @__PURE__ */ new Map();
|
|
94
241
|
const resetGeneration = (0, vue.ref)(0);
|
|
242
|
+
const isDisabled = (0, vue.ref)(false);
|
|
243
|
+
if (options.disabled !== void 0) {
|
|
244
|
+
isDisabled.value = (0, vue.toValue)(options.disabled) ?? false;
|
|
245
|
+
(0, vue.watch)(() => (0, vue.toValue)(options.disabled), (newDisabled) => {
|
|
246
|
+
isDisabled.value = newDisabled ?? false;
|
|
247
|
+
});
|
|
248
|
+
}
|
|
95
249
|
if (options.values !== void 0) {
|
|
96
250
|
const initialValues = (0, vue.toValue)(options.values);
|
|
97
251
|
if (initialValues && !isAsyncDefaults) {
|
|
@@ -141,6 +295,7 @@ function createFormContext(options) {
|
|
|
141
295
|
debounceTimers,
|
|
142
296
|
validationRequestIds,
|
|
143
297
|
resetGeneration,
|
|
298
|
+
isDisabled,
|
|
144
299
|
options
|
|
145
300
|
};
|
|
146
301
|
}
|
|
@@ -191,12 +346,23 @@ function createFieldError(errors, criteriaMode = "firstError") {
|
|
|
191
346
|
};
|
|
192
347
|
}
|
|
193
348
|
function createValidation(ctx) {
|
|
349
|
+
function applyNativeValidation(fieldPath, errorMessage) {
|
|
350
|
+
if (!ctx.options.shouldUseNativeValidation) return;
|
|
351
|
+
const el = ctx.fieldRefs.get(fieldPath)?.value;
|
|
352
|
+
if (el && "setCustomValidity" in el) el.setCustomValidity(errorMessage || "");
|
|
353
|
+
}
|
|
354
|
+
function clearAllNativeValidation() {
|
|
355
|
+
if (!ctx.options.shouldUseNativeValidation) return;
|
|
356
|
+
for (const [path] of ctx.fieldRefs) applyNativeValidation(path, null);
|
|
357
|
+
}
|
|
194
358
|
function scheduleError(fieldPath, error) {
|
|
195
359
|
const delayMs = ctx.options.delayError || 0;
|
|
360
|
+
const errorMessage = typeof error === "string" ? error : error.message;
|
|
196
361
|
if (delayMs <= 0) {
|
|
197
362
|
const newErrors = { ...ctx.errors.value };
|
|
198
363
|
set(newErrors, fieldPath, error);
|
|
199
364
|
ctx.errors.value = newErrors;
|
|
365
|
+
applyNativeValidation(fieldPath, errorMessage);
|
|
200
366
|
return;
|
|
201
367
|
}
|
|
202
368
|
const existingTimer = ctx.errorDelayTimers.get(fieldPath);
|
|
@@ -210,6 +376,7 @@ function createValidation(ctx) {
|
|
|
210
376
|
const newErrors = { ...ctx.errors.value };
|
|
211
377
|
set(newErrors, fieldPath, pendingError);
|
|
212
378
|
ctx.errors.value = newErrors;
|
|
379
|
+
applyNativeValidation(fieldPath, errorMessage);
|
|
213
380
|
}
|
|
214
381
|
}, delayMs);
|
|
215
382
|
ctx.errorDelayTimers.set(fieldPath, timer);
|
|
@@ -221,6 +388,7 @@ function createValidation(ctx) {
|
|
|
221
388
|
ctx.errorDelayTimers.delete(fieldPath);
|
|
222
389
|
}
|
|
223
390
|
ctx.pendingErrors.delete(fieldPath);
|
|
391
|
+
applyNativeValidation(fieldPath, null);
|
|
224
392
|
return clearFieldErrors$1(ctx.errors.value, fieldPath);
|
|
225
393
|
}
|
|
226
394
|
function clearAllPendingErrors() {
|
|
@@ -241,6 +409,7 @@ function createValidation(ctx) {
|
|
|
241
409
|
else {
|
|
242
410
|
clearAllPendingErrors();
|
|
243
411
|
ctx.errors.value = {};
|
|
412
|
+
clearAllNativeValidation();
|
|
244
413
|
}
|
|
245
414
|
return true;
|
|
246
415
|
}
|
|
@@ -276,6 +445,12 @@ function createValidation(ctx) {
|
|
|
276
445
|
var validationRequestCounter = 0;
|
|
277
446
|
function createFieldRegistration(ctx, validate) {
|
|
278
447
|
function register(name, registerOptions) {
|
|
448
|
+
if (__DEV__) {
|
|
449
|
+
const syntaxError = validatePathSyntax(name);
|
|
450
|
+
if (syntaxError) warnInvalidPath("register", name, syntaxError);
|
|
451
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, name);
|
|
452
|
+
if (!schemaResult.valid) warnPathNotInSchema("register", name, schemaResult.availableFields);
|
|
453
|
+
}
|
|
279
454
|
let fieldRef = ctx.fieldRefs.get(name);
|
|
280
455
|
if (!fieldRef) {
|
|
281
456
|
fieldRef = (0, vue.ref)(null);
|
|
@@ -394,6 +569,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
394
569
|
ref: handlers.refCallback,
|
|
395
570
|
onInput: handlers.onInput,
|
|
396
571
|
onBlur: handlers.onBlur,
|
|
572
|
+
...ctx.isDisabled.value && { disabled: true },
|
|
397
573
|
...registerOptions?.controlled && { value: (0, vue.computed)({
|
|
398
574
|
get: () => get(ctx.formData, name),
|
|
399
575
|
set: (val) => {
|
|
@@ -441,6 +617,13 @@ function createFieldRegistration(ctx, validate) {
|
|
|
441
617
|
}
|
|
442
618
|
function createFieldArrayManager(ctx, validate, setFocus) {
|
|
443
619
|
function fields(name, options) {
|
|
620
|
+
if (__DEV__) {
|
|
621
|
+
const syntaxError = validatePathSyntax(name);
|
|
622
|
+
if (syntaxError) warnInvalidPath("fields", name, syntaxError);
|
|
623
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, name);
|
|
624
|
+
if (!schemaResult.valid) warnPathNotInSchema("fields", name, schemaResult.availableFields);
|
|
625
|
+
if (isArrayFieldInSchema(ctx.options.schema, name) === false) warnFieldsOnNonArray(name);
|
|
626
|
+
}
|
|
444
627
|
let fieldArray = ctx.fieldArrays.get(name);
|
|
445
628
|
if (!fieldArray) {
|
|
446
629
|
const existingValues = get(ctx.formData, name) || [];
|
|
@@ -488,11 +671,17 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
488
671
|
};
|
|
489
672
|
const append = (value, focusOptions) => {
|
|
490
673
|
const values = normalizeToArray(value);
|
|
491
|
-
if (values.length === 0) return;
|
|
674
|
+
if (values.length === 0) return true;
|
|
492
675
|
const currentValues = get(ctx.formData, name) || [];
|
|
493
676
|
const insertIndex = currentValues.length;
|
|
494
677
|
const rules = fa.rules;
|
|
495
|
-
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value)
|
|
678
|
+
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
679
|
+
if (__DEV__) warnArrayOperationRejected("append", name, "maxLength", {
|
|
680
|
+
current: currentValues.length,
|
|
681
|
+
limit: rules.maxLength.value
|
|
682
|
+
});
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
496
685
|
const newValues = [...currentValues, ...values];
|
|
497
686
|
set(ctx.formData, name, newValues);
|
|
498
687
|
const newItems = values.map(() => createItem(generateId()));
|
|
@@ -504,13 +693,20 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
504
693
|
};
|
|
505
694
|
if (ctx.options.mode === "onChange") validate(name);
|
|
506
695
|
handleFocus(insertIndex, values.length, focusOptions);
|
|
696
|
+
return true;
|
|
507
697
|
};
|
|
508
698
|
const prepend = (value, focusOptions) => {
|
|
509
699
|
const values = normalizeToArray(value);
|
|
510
|
-
if (values.length === 0) return;
|
|
700
|
+
if (values.length === 0) return true;
|
|
511
701
|
const currentValues = get(ctx.formData, name) || [];
|
|
512
702
|
const rules = fa.rules;
|
|
513
|
-
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value)
|
|
703
|
+
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
704
|
+
if (__DEV__) warnArrayOperationRejected("prepend", name, "maxLength", {
|
|
705
|
+
current: currentValues.length,
|
|
706
|
+
limit: rules.maxLength.value
|
|
707
|
+
});
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
514
710
|
const newValues = [...values, ...currentValues];
|
|
515
711
|
set(ctx.formData, name, newValues);
|
|
516
712
|
const newItems = values.map(() => createItem(generateId()));
|
|
@@ -522,10 +718,14 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
522
718
|
};
|
|
523
719
|
if (ctx.options.mode === "onChange") validate(name);
|
|
524
720
|
handleFocus(0, values.length, focusOptions);
|
|
721
|
+
return true;
|
|
525
722
|
};
|
|
526
723
|
const update = (index, value) => {
|
|
527
724
|
const currentValues = get(ctx.formData, name) || [];
|
|
528
|
-
if (index < 0 || index >= currentValues.length)
|
|
725
|
+
if (index < 0 || index >= currentValues.length) {
|
|
726
|
+
if (__DEV__) warnArrayIndexOutOfBounds("update", name, index, currentValues.length);
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
529
729
|
const newValues = [...currentValues];
|
|
530
730
|
newValues[index] = value;
|
|
531
731
|
set(ctx.formData, name, newValues);
|
|
@@ -534,12 +734,22 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
534
734
|
[name]: true
|
|
535
735
|
};
|
|
536
736
|
if (ctx.options.mode === "onChange") validate(name);
|
|
737
|
+
return true;
|
|
537
738
|
};
|
|
538
739
|
const removeAt = (index) => {
|
|
539
740
|
const currentValues = get(ctx.formData, name) || [];
|
|
540
|
-
if (index < 0 || index >= currentValues.length)
|
|
741
|
+
if (index < 0 || index >= currentValues.length) {
|
|
742
|
+
if (__DEV__) warnArrayIndexOutOfBounds("remove", name, index, currentValues.length);
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
541
745
|
const rules = fa.rules;
|
|
542
|
-
if (rules?.minLength && currentValues.length - 1 < rules.minLength.value)
|
|
746
|
+
if (rules?.minLength && currentValues.length - 1 < rules.minLength.value) {
|
|
747
|
+
if (__DEV__) warnArrayOperationRejected("remove", name, "minLength", {
|
|
748
|
+
current: currentValues.length,
|
|
749
|
+
limit: rules.minLength.value
|
|
750
|
+
});
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
543
753
|
const newValues = currentValues.filter((_, i) => i !== index);
|
|
544
754
|
set(ctx.formData, name, newValues);
|
|
545
755
|
const keyToRemove = fa.items.value[index]?.key;
|
|
@@ -550,13 +760,20 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
550
760
|
[name]: true
|
|
551
761
|
};
|
|
552
762
|
if (ctx.options.mode === "onChange") validate(name);
|
|
763
|
+
return true;
|
|
553
764
|
};
|
|
554
765
|
const insert = (index, value, focusOptions) => {
|
|
555
766
|
const values = normalizeToArray(value);
|
|
556
|
-
if (values.length === 0) return;
|
|
767
|
+
if (values.length === 0) return true;
|
|
557
768
|
const currentValues = get(ctx.formData, name) || [];
|
|
558
769
|
const rules = fa.rules;
|
|
559
|
-
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value)
|
|
770
|
+
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
771
|
+
if (__DEV__) warnArrayOperationRejected("insert", name, "maxLength", {
|
|
772
|
+
current: currentValues.length,
|
|
773
|
+
limit: rules.maxLength.value
|
|
774
|
+
});
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
560
777
|
const clampedIndex = Math.max(0, Math.min(index, currentValues.length));
|
|
561
778
|
const newValues = [
|
|
562
779
|
...currentValues.slice(0, clampedIndex),
|
|
@@ -577,10 +794,14 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
577
794
|
};
|
|
578
795
|
if (ctx.options.mode === "onChange") validate(name);
|
|
579
796
|
handleFocus(clampedIndex, values.length, focusOptions);
|
|
797
|
+
return true;
|
|
580
798
|
};
|
|
581
799
|
const swap = (indexA, indexB) => {
|
|
582
800
|
const currentValues = get(ctx.formData, name) || [];
|
|
583
|
-
if (indexA < 0 || indexB < 0 || indexA >= currentValues.length || indexB >= currentValues.length)
|
|
801
|
+
if (indexA < 0 || indexB < 0 || indexA >= currentValues.length || indexB >= currentValues.length) {
|
|
802
|
+
if (__DEV__) warnArrayIndexOutOfBounds("swap", name, indexA < 0 || indexA >= currentValues.length ? indexA : indexB, currentValues.length);
|
|
803
|
+
return false;
|
|
804
|
+
}
|
|
584
805
|
const newValues = [...currentValues];
|
|
585
806
|
[newValues[indexA], newValues[indexB]] = [newValues[indexB], newValues[indexA]];
|
|
586
807
|
set(ctx.formData, name, newValues);
|
|
@@ -598,10 +819,14 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
598
819
|
[name]: true
|
|
599
820
|
};
|
|
600
821
|
if (ctx.options.mode === "onChange") validate(name);
|
|
822
|
+
return true;
|
|
601
823
|
};
|
|
602
824
|
const move = (from, to) => {
|
|
603
825
|
const currentValues = get(ctx.formData, name) || [];
|
|
604
|
-
if (from < 0 || from >= currentValues.length || to < 0)
|
|
826
|
+
if (from < 0 || from >= currentValues.length || to < 0) {
|
|
827
|
+
if (__DEV__) warnArrayIndexOutOfBounds("move", name, from < 0 || from >= currentValues.length ? from : to, currentValues.length);
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
605
830
|
const newValues = [...currentValues];
|
|
606
831
|
const [removed] = newValues.splice(from, 1);
|
|
607
832
|
if (removed !== void 0) {
|
|
@@ -622,9 +847,10 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
622
847
|
[name]: true
|
|
623
848
|
};
|
|
624
849
|
if (ctx.options.mode === "onChange") validate(name);
|
|
850
|
+
return true;
|
|
625
851
|
};
|
|
626
852
|
const replace = (newValues) => {
|
|
627
|
-
if (!Array.isArray(newValues)) return;
|
|
853
|
+
if (!Array.isArray(newValues)) return false;
|
|
628
854
|
set(ctx.formData, name, newValues);
|
|
629
855
|
fa.items.value = newValues.map(() => createItem(generateId()));
|
|
630
856
|
rebuildIndexCache();
|
|
@@ -633,12 +859,60 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
633
859
|
[name]: true
|
|
634
860
|
};
|
|
635
861
|
if (ctx.options.mode === "onChange") validate(name);
|
|
862
|
+
return true;
|
|
863
|
+
};
|
|
864
|
+
const removeAll = () => {
|
|
865
|
+
const rules = fa.rules;
|
|
866
|
+
if (rules?.minLength && rules.minLength.value > 0) {
|
|
867
|
+
if (__DEV__) warnArrayOperationRejected("removeAll", name, "minLength", {
|
|
868
|
+
current: fa.items.value.length,
|
|
869
|
+
limit: rules.minLength.value
|
|
870
|
+
});
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
set(ctx.formData, name, []);
|
|
874
|
+
fa.items.value = [];
|
|
875
|
+
rebuildIndexCache();
|
|
876
|
+
ctx.dirtyFields.value = {
|
|
877
|
+
...ctx.dirtyFields.value,
|
|
878
|
+
[name]: true
|
|
879
|
+
};
|
|
880
|
+
if (ctx.options.mode === "onChange") validate(name);
|
|
881
|
+
return true;
|
|
882
|
+
};
|
|
883
|
+
const removeMany = (indices) => {
|
|
884
|
+
const currentValues = get(ctx.formData, name) || [];
|
|
885
|
+
const validIndices = indices.filter((i) => i >= 0 && i < currentValues.length);
|
|
886
|
+
if (validIndices.length === 0) return true;
|
|
887
|
+
const rules = fa.rules;
|
|
888
|
+
const remainingCount = currentValues.length - validIndices.length;
|
|
889
|
+
if (rules?.minLength && remainingCount < rules.minLength.value) {
|
|
890
|
+
if (__DEV__) warnArrayOperationRejected("removeMany", name, "minLength", {
|
|
891
|
+
current: currentValues.length,
|
|
892
|
+
limit: rules.minLength.value
|
|
893
|
+
});
|
|
894
|
+
return false;
|
|
895
|
+
}
|
|
896
|
+
const sortedIndices = [...new Set(validIndices)].sort((a, b) => b - a);
|
|
897
|
+
const indicesToRemove = new Set(sortedIndices);
|
|
898
|
+
const newValues = currentValues.filter((_, i) => !indicesToRemove.has(i));
|
|
899
|
+
set(ctx.formData, name, newValues);
|
|
900
|
+
fa.items.value = fa.items.value.filter((_, i) => !indicesToRemove.has(i));
|
|
901
|
+
rebuildIndexCache();
|
|
902
|
+
ctx.dirtyFields.value = {
|
|
903
|
+
...ctx.dirtyFields.value,
|
|
904
|
+
[name]: true
|
|
905
|
+
};
|
|
906
|
+
if (ctx.options.mode === "onChange") validate(name);
|
|
907
|
+
return true;
|
|
636
908
|
};
|
|
637
909
|
return {
|
|
638
910
|
value: fa.items.value,
|
|
639
911
|
append,
|
|
640
912
|
prepend,
|
|
641
913
|
remove: removeAt,
|
|
914
|
+
removeAll,
|
|
915
|
+
removeMany,
|
|
642
916
|
insert,
|
|
643
917
|
swap,
|
|
644
918
|
move,
|
|
@@ -692,6 +966,14 @@ function useForm(options) {
|
|
|
692
966
|
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
693
967
|
const { register, unregister } = createFieldRegistration(ctx, validate);
|
|
694
968
|
function setFocus(name, focusOptions) {
|
|
969
|
+
if (__DEV__) {
|
|
970
|
+
const syntaxError = validatePathSyntax(name);
|
|
971
|
+
if (syntaxError) warnInvalidPath("setFocus", name, syntaxError);
|
|
972
|
+
else {
|
|
973
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, name);
|
|
974
|
+
if (!schemaResult.valid) warnPathNotInSchema("setFocus", name, schemaResult.availableFields);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
695
977
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
696
978
|
if (!fieldRef?.value) return;
|
|
697
979
|
const el = fieldRef.value;
|
|
@@ -702,11 +984,14 @@ function useForm(options) {
|
|
|
702
984
|
}
|
|
703
985
|
const setFocusWrapper = (name) => setFocus(name);
|
|
704
986
|
const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper);
|
|
705
|
-
|
|
706
|
-
|
|
987
|
+
function getMergedErrors() {
|
|
988
|
+
return {
|
|
707
989
|
...ctx.errors.value,
|
|
708
990
|
...ctx.externalErrors.value
|
|
709
991
|
};
|
|
992
|
+
}
|
|
993
|
+
const formState = (0, vue.computed)(() => {
|
|
994
|
+
const mergedErrors = getMergedErrors();
|
|
710
995
|
return {
|
|
711
996
|
errors: mergedErrors,
|
|
712
997
|
isDirty: Object.keys(ctx.dirtyFields.value).some((k) => ctx.dirtyFields.value[k]),
|
|
@@ -721,12 +1006,14 @@ function useForm(options) {
|
|
|
721
1006
|
submitCount: ctx.submitCount.value,
|
|
722
1007
|
defaultValuesError: ctx.defaultValuesError.value,
|
|
723
1008
|
isSubmitted: ctx.submitCount.value > 0,
|
|
724
|
-
isSubmitSuccessful: ctx.isSubmitSuccessful.value
|
|
1009
|
+
isSubmitSuccessful: ctx.isSubmitSuccessful.value,
|
|
1010
|
+
disabled: ctx.isDisabled.value
|
|
725
1011
|
};
|
|
726
1012
|
});
|
|
727
1013
|
function handleSubmit(onValid, onInvalid) {
|
|
728
1014
|
return async (e) => {
|
|
729
1015
|
e.preventDefault();
|
|
1016
|
+
if (ctx.isDisabled.value) return;
|
|
730
1017
|
if (ctx.isSubmitting.value) return;
|
|
731
1018
|
ctx.isSubmitting.value = true;
|
|
732
1019
|
ctx.submitCount.value++;
|
|
@@ -749,6 +1036,14 @@ function useForm(options) {
|
|
|
749
1036
|
};
|
|
750
1037
|
}
|
|
751
1038
|
function setValue(name, value, setValueOptions) {
|
|
1039
|
+
if (__DEV__) {
|
|
1040
|
+
const syntaxError = validatePathSyntax(name);
|
|
1041
|
+
if (syntaxError) warnInvalidPath("setValue", name, syntaxError);
|
|
1042
|
+
else {
|
|
1043
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, name);
|
|
1044
|
+
if (!schemaResult.valid) warnPathNotInSchema("setValue", name, schemaResult.availableFields);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
752
1047
|
set(ctx.formData, name, value);
|
|
753
1048
|
if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, name);
|
|
754
1049
|
if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, name);
|
|
@@ -758,9 +1053,6 @@ function useForm(options) {
|
|
|
758
1053
|
}
|
|
759
1054
|
if (setValueOptions?.shouldValidate) validate(name);
|
|
760
1055
|
}
|
|
761
|
-
function getValue(name) {
|
|
762
|
-
return get(ctx.formData, name);
|
|
763
|
-
}
|
|
764
1056
|
function reset(values, resetOptions) {
|
|
765
1057
|
const opts = resetOptions || {};
|
|
766
1058
|
ctx.resetGeneration.value++;
|
|
@@ -788,6 +1080,14 @@ function useForm(options) {
|
|
|
788
1080
|
}
|
|
789
1081
|
}
|
|
790
1082
|
function resetField(name, resetFieldOptions) {
|
|
1083
|
+
if (__DEV__) {
|
|
1084
|
+
const syntaxError = validatePathSyntax(name);
|
|
1085
|
+
if (syntaxError) warnInvalidPath("resetField", name, syntaxError);
|
|
1086
|
+
else {
|
|
1087
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, name);
|
|
1088
|
+
if (!schemaResult.valid) warnPathNotInSchema("resetField", name, schemaResult.availableFields);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
791
1091
|
const opts = resetFieldOptions || {};
|
|
792
1092
|
ctx.resetGeneration.value++;
|
|
793
1093
|
const errorTimer = ctx.errorDelayTimers.get(name);
|
|
@@ -810,6 +1110,17 @@ function useForm(options) {
|
|
|
810
1110
|
}
|
|
811
1111
|
}
|
|
812
1112
|
function watch$1(name) {
|
|
1113
|
+
if (__DEV__ && name) {
|
|
1114
|
+
const names = Array.isArray(name) ? name : [name];
|
|
1115
|
+
for (const n of names) {
|
|
1116
|
+
const syntaxError = validatePathSyntax(n);
|
|
1117
|
+
if (syntaxError) warnInvalidPath("watch", n, syntaxError);
|
|
1118
|
+
else {
|
|
1119
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, n);
|
|
1120
|
+
if (!schemaResult.valid) warnPathNotInSchema("watch", n, schemaResult.availableFields);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
813
1124
|
return (0, vue.computed)(() => {
|
|
814
1125
|
if (!name) return ctx.formData;
|
|
815
1126
|
if (Array.isArray(name)) {
|
|
@@ -821,6 +1132,18 @@ function useForm(options) {
|
|
|
821
1132
|
});
|
|
822
1133
|
}
|
|
823
1134
|
function clearErrors(name) {
|
|
1135
|
+
if (__DEV__ && name && !String(name).startsWith("root")) {
|
|
1136
|
+
const names = Array.isArray(name) ? name : [name];
|
|
1137
|
+
for (const n of names) {
|
|
1138
|
+
if (String(n).startsWith("root")) continue;
|
|
1139
|
+
const syntaxError = validatePathSyntax(n);
|
|
1140
|
+
if (syntaxError) warnInvalidPath("clearErrors", n, syntaxError);
|
|
1141
|
+
else {
|
|
1142
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, n);
|
|
1143
|
+
if (!schemaResult.valid) warnPathNotInSchema("clearErrors", n, schemaResult.availableFields);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
824
1147
|
if (name === void 0) {
|
|
825
1148
|
ctx.errors.value = {};
|
|
826
1149
|
return;
|
|
@@ -836,7 +1159,40 @@ function useForm(options) {
|
|
|
836
1159
|
} : error.message);
|
|
837
1160
|
ctx.errors.value = newErrors;
|
|
838
1161
|
}
|
|
1162
|
+
function setErrors(errors, options$1) {
|
|
1163
|
+
const newErrors = options$1?.shouldReplace ? {} : { ...ctx.errors.value };
|
|
1164
|
+
for (const [name, error] of Object.entries(errors)) {
|
|
1165
|
+
if (error === void 0) continue;
|
|
1166
|
+
set(newErrors, name, typeof error === "string" ? error : error.type ? {
|
|
1167
|
+
type: error.type,
|
|
1168
|
+
message: error.message
|
|
1169
|
+
} : error.message);
|
|
1170
|
+
}
|
|
1171
|
+
ctx.errors.value = newErrors;
|
|
1172
|
+
}
|
|
1173
|
+
function hasErrors(fieldPath) {
|
|
1174
|
+
const mergedErrors = getMergedErrors();
|
|
1175
|
+
if (fieldPath === void 0) return Object.keys(mergedErrors).length > 0;
|
|
1176
|
+
const error = get(mergedErrors, fieldPath);
|
|
1177
|
+
return error !== void 0 && error !== null;
|
|
1178
|
+
}
|
|
1179
|
+
function getErrors(fieldPath) {
|
|
1180
|
+
const mergedErrors = getMergedErrors();
|
|
1181
|
+
if (fieldPath === void 0) return mergedErrors;
|
|
1182
|
+
return get(mergedErrors, fieldPath);
|
|
1183
|
+
}
|
|
839
1184
|
function getValues(nameOrNames) {
|
|
1185
|
+
if (__DEV__ && nameOrNames) {
|
|
1186
|
+
const names = Array.isArray(nameOrNames) ? nameOrNames : [nameOrNames];
|
|
1187
|
+
for (const n of names) {
|
|
1188
|
+
const syntaxError = validatePathSyntax(n);
|
|
1189
|
+
if (syntaxError) warnInvalidPath("getValues", n, syntaxError);
|
|
1190
|
+
else {
|
|
1191
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, n);
|
|
1192
|
+
if (!schemaResult.valid) warnPathNotInSchema("getValues", n, schemaResult.availableFields);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
840
1196
|
syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
|
|
841
1197
|
if (nameOrNames === void 0) return { ...ctx.formData };
|
|
842
1198
|
if (Array.isArray(nameOrNames)) {
|
|
@@ -847,6 +1203,14 @@ function useForm(options) {
|
|
|
847
1203
|
return get(ctx.formData, nameOrNames);
|
|
848
1204
|
}
|
|
849
1205
|
function getFieldState(name) {
|
|
1206
|
+
if (__DEV__) {
|
|
1207
|
+
const syntaxError = validatePathSyntax(name);
|
|
1208
|
+
if (syntaxError) warnInvalidPath("getFieldState", name, syntaxError);
|
|
1209
|
+
else {
|
|
1210
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, name);
|
|
1211
|
+
if (!schemaResult.valid) warnPathNotInSchema("getFieldState", name, schemaResult.availableFields);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
850
1214
|
const error = get(ctx.errors.value, name);
|
|
851
1215
|
return {
|
|
852
1216
|
isDirty: ctx.dirtyFields.value[name] === true,
|
|
@@ -856,6 +1220,17 @@ function useForm(options) {
|
|
|
856
1220
|
};
|
|
857
1221
|
}
|
|
858
1222
|
async function trigger(name) {
|
|
1223
|
+
if (__DEV__ && name) {
|
|
1224
|
+
const names = Array.isArray(name) ? name : [name];
|
|
1225
|
+
for (const n of names) {
|
|
1226
|
+
const syntaxError = validatePathSyntax(n);
|
|
1227
|
+
if (syntaxError) warnInvalidPath("trigger", n, syntaxError);
|
|
1228
|
+
else {
|
|
1229
|
+
const schemaResult = validatePathAgainstSchema(ctx.options.schema, n);
|
|
1230
|
+
if (!schemaResult.valid) warnPathNotInSchema("trigger", n, schemaResult.availableFields);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
859
1234
|
if (name === void 0) return await validate();
|
|
860
1235
|
if (Array.isArray(name)) {
|
|
861
1236
|
let allValid = true;
|
|
@@ -871,13 +1246,15 @@ function useForm(options) {
|
|
|
871
1246
|
formState,
|
|
872
1247
|
fields,
|
|
873
1248
|
setValue,
|
|
874
|
-
getValue,
|
|
875
1249
|
reset,
|
|
876
1250
|
resetField,
|
|
877
1251
|
watch: watch$1,
|
|
878
1252
|
validate,
|
|
879
1253
|
clearErrors,
|
|
880
1254
|
setError,
|
|
1255
|
+
setErrors,
|
|
1256
|
+
hasErrors,
|
|
1257
|
+
getErrors,
|
|
881
1258
|
getValues,
|
|
882
1259
|
getFieldState,
|
|
883
1260
|
trigger,
|
|
@@ -910,10 +1287,10 @@ function useController(options) {
|
|
|
910
1287
|
const { name, control, defaultValue } = options;
|
|
911
1288
|
const form = control ?? useFormContext();
|
|
912
1289
|
const elementRef = (0, vue.ref)(null);
|
|
913
|
-
if (defaultValue !== void 0 && form.
|
|
1290
|
+
if (defaultValue !== void 0 && form.getValues(name) === void 0) form.setValue(name, defaultValue);
|
|
914
1291
|
const value = (0, vue.computed)({
|
|
915
1292
|
get: () => {
|
|
916
|
-
return form.
|
|
1293
|
+
return form.getValues(name) ?? defaultValue;
|
|
917
1294
|
},
|
|
918
1295
|
set: (newValue) => {
|
|
919
1296
|
form.setValue(name, newValue);
|