@vuehookform/core 0.4.1 → 0.4.2
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 +1 -1
- package/dist/core/domSync.d.ts +5 -0
- package/dist/core/fieldState.d.ts +19 -13
- package/dist/core/formContext.d.ts +64 -7
- package/dist/index.d.ts +2 -1
- package/dist/types.d.ts +24 -3
- package/dist/utils/clone.d.ts +2 -2
- package/dist/utils/hash.d.ts +3 -0
- package/dist/utils/modeChecks.d.ts +22 -0
- package/dist/utils/paths.d.ts +15 -3
- package/dist/vuehookform.cjs +334 -169
- package/dist/vuehookform.js +331 -171
- package/package.json +1 -1
package/dist/vuehookform.cjs
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
let vue = require("vue");
|
|
3
3
|
var pathCache = /* @__PURE__ */ new Map();
|
|
4
4
|
var PATH_CACHE_MAX_SIZE = 256;
|
|
5
|
+
var MAX_ARRAY_INDEX = 1e4;
|
|
5
6
|
function getPathSegments(path) {
|
|
6
7
|
let segments = pathCache.get(path);
|
|
7
8
|
if (segments) return segments;
|
|
@@ -13,6 +14,9 @@ function getPathSegments(path) {
|
|
|
13
14
|
pathCache.set(path, segments);
|
|
14
15
|
return segments;
|
|
15
16
|
}
|
|
17
|
+
function clearPathCache() {
|
|
18
|
+
pathCache.clear();
|
|
19
|
+
}
|
|
16
20
|
function get(obj, path) {
|
|
17
21
|
if (!path || obj === null || obj === void 0) return obj;
|
|
18
22
|
const keys = getPathSegments(path);
|
|
@@ -32,6 +36,13 @@ function set(obj, path, value) {
|
|
|
32
36
|
"prototype"
|
|
33
37
|
];
|
|
34
38
|
if (keys.some((k) => UNSAFE_KEYS.includes(k))) return;
|
|
39
|
+
for (const key of keys) if (/^\d+$/.test(key)) {
|
|
40
|
+
const index = parseInt(key, 10);
|
|
41
|
+
if (index > MAX_ARRAY_INDEX) {
|
|
42
|
+
if (typeof console !== "undefined" && console.warn) console.warn(`[vue-hook-form] set(): Array index ${index} exceeds maximum allowed (${MAX_ARRAY_INDEX}). Path "${path}" was not set to prevent memory exhaustion.`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
35
46
|
const lastKey = keys.pop();
|
|
36
47
|
let current = obj;
|
|
37
48
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -66,13 +77,22 @@ function generateId() {
|
|
|
66
77
|
const random = Math.random().toString(36).substring(2, 11);
|
|
67
78
|
return `field_${Date.now()}_${idCounter++}_${random}`;
|
|
68
79
|
}
|
|
69
|
-
function deepClone(obj) {
|
|
80
|
+
function deepClone(obj, seen) {
|
|
70
81
|
if (obj === null || obj === void 0) return obj;
|
|
71
82
|
if (typeof obj !== "object") return obj;
|
|
72
83
|
if (obj instanceof Date) return new Date(obj.getTime());
|
|
73
|
-
if (
|
|
84
|
+
if (!seen) seen = /* @__PURE__ */ new Map();
|
|
85
|
+
const existingClone = seen.get(obj);
|
|
86
|
+
if (existingClone !== void 0) return existingClone;
|
|
87
|
+
if (Array.isArray(obj)) {
|
|
88
|
+
const clonedArray = [];
|
|
89
|
+
seen.set(obj, clonedArray);
|
|
90
|
+
for (const item of obj) clonedArray.push(deepClone(item, seen));
|
|
91
|
+
return clonedArray;
|
|
92
|
+
}
|
|
74
93
|
const cloned = {};
|
|
75
|
-
|
|
94
|
+
seen.set(obj, cloned);
|
|
95
|
+
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) cloned[key] = deepClone(obj[key], seen);
|
|
76
96
|
return cloned;
|
|
77
97
|
}
|
|
78
98
|
var proc = globalThis.process;
|
|
@@ -230,8 +250,8 @@ function createFormContext(options) {
|
|
|
230
250
|
if (isAsyncDefaults) {
|
|
231
251
|
const asyncFn = options.defaultValues;
|
|
232
252
|
asyncFn().then((values) => {
|
|
233
|
-
Object.assign(defaultValues, values);
|
|
234
|
-
Object.assign(formData, values);
|
|
253
|
+
Object.assign(defaultValues, deepClone(values));
|
|
254
|
+
Object.assign(formData, deepClone(values));
|
|
235
255
|
isLoading.value = false;
|
|
236
256
|
}).catch((error) => {
|
|
237
257
|
console.error("Failed to load async default values:", error);
|
|
@@ -240,8 +260,8 @@ function createFormContext(options) {
|
|
|
240
260
|
options.onDefaultValuesError?.(error);
|
|
241
261
|
});
|
|
242
262
|
} else if (options.defaultValues) {
|
|
243
|
-
Object.assign(defaultValues, options.defaultValues);
|
|
244
|
-
Object.assign(formData, defaultValues);
|
|
263
|
+
Object.assign(defaultValues, deepClone(options.defaultValues));
|
|
264
|
+
Object.assign(formData, deepClone(options.defaultValues));
|
|
245
265
|
}
|
|
246
266
|
const errors = (0, vue.shallowRef)({});
|
|
247
267
|
const touchedFields = (0, vue.shallowRef)({});
|
|
@@ -261,23 +281,24 @@ function createFormContext(options) {
|
|
|
261
281
|
const debounceTimers = /* @__PURE__ */ new Map();
|
|
262
282
|
const validationRequestIds = /* @__PURE__ */ new Map();
|
|
263
283
|
const resetGeneration = (0, vue.ref)(0);
|
|
264
|
-
const dirtyFieldCount = (0, vue.ref)(0);
|
|
265
|
-
const touchedFieldCount = (0, vue.ref)(0);
|
|
266
284
|
const validationCache = /* @__PURE__ */ new Map();
|
|
267
285
|
const schemaValidationTimers = /* @__PURE__ */ new Map();
|
|
286
|
+
const persistentErrorFields = /* @__PURE__ */ new Set();
|
|
287
|
+
const defaultValueHashes = /* @__PURE__ */ new Map();
|
|
268
288
|
const isDisabled = (0, vue.ref)(false);
|
|
289
|
+
const watchStopHandles = [];
|
|
269
290
|
if (options.disabled !== void 0) {
|
|
270
291
|
isDisabled.value = (0, vue.toValue)(options.disabled) ?? false;
|
|
271
|
-
(0, vue.watch)(() => (0, vue.toValue)(options.disabled), (newDisabled) => {
|
|
292
|
+
watchStopHandles.push((0, vue.watch)(() => (0, vue.toValue)(options.disabled), (newDisabled) => {
|
|
272
293
|
isDisabled.value = newDisabled ?? false;
|
|
273
|
-
});
|
|
294
|
+
}));
|
|
274
295
|
}
|
|
275
296
|
if (options.values !== void 0) {
|
|
276
297
|
const initialValues = (0, vue.toValue)(options.values);
|
|
277
298
|
if (initialValues && !isAsyncDefaults) {
|
|
278
299
|
for (const [key, value] of Object.entries(initialValues)) if (value !== void 0) set(formData, key, value);
|
|
279
300
|
}
|
|
280
|
-
(0, vue.watch)(() => (0, vue.toValue)(options.values), (newValues) => {
|
|
301
|
+
watchStopHandles.push((0, vue.watch)(() => (0, vue.toValue)(options.values), (newValues) => {
|
|
281
302
|
if (newValues) {
|
|
282
303
|
for (const [key, value] of Object.entries(newValues)) if (value !== void 0) {
|
|
283
304
|
set(formData, key, value);
|
|
@@ -290,14 +311,14 @@ function createFormContext(options) {
|
|
|
290
311
|
}
|
|
291
312
|
}
|
|
292
313
|
}
|
|
293
|
-
}, { deep: true });
|
|
314
|
+
}, { deep: true }));
|
|
294
315
|
}
|
|
295
316
|
if (options.errors !== void 0) {
|
|
296
317
|
const initialErrors = (0, vue.toValue)(options.errors);
|
|
297
318
|
if (initialErrors) externalErrors.value = initialErrors;
|
|
298
|
-
(0, vue.watch)(() => (0, vue.toValue)(options.errors), (newErrors) => {
|
|
319
|
+
watchStopHandles.push((0, vue.watch)(() => (0, vue.toValue)(options.errors), (newErrors) => {
|
|
299
320
|
externalErrors.value = newErrors || {};
|
|
300
|
-
}, { deep: true });
|
|
321
|
+
}, { deep: true }));
|
|
301
322
|
}
|
|
302
323
|
return {
|
|
303
324
|
formData,
|
|
@@ -322,25 +343,40 @@ function createFormContext(options) {
|
|
|
322
343
|
validationRequestIds,
|
|
323
344
|
resetGeneration,
|
|
324
345
|
isDisabled,
|
|
325
|
-
dirtyFieldCount,
|
|
326
|
-
touchedFieldCount,
|
|
327
346
|
validationCache,
|
|
328
347
|
schemaValidationTimers,
|
|
329
|
-
|
|
348
|
+
persistentErrorFields,
|
|
349
|
+
defaultValueHashes,
|
|
350
|
+
options,
|
|
351
|
+
cleanup: () => {
|
|
352
|
+
for (const stop of watchStopHandles) stop();
|
|
353
|
+
}
|
|
330
354
|
};
|
|
331
355
|
}
|
|
332
356
|
var uniqueIdCounter = 0;
|
|
357
|
+
var circularRefMap = /* @__PURE__ */ new WeakMap();
|
|
333
358
|
function hashValue(value) {
|
|
334
359
|
if (value === null) return "null";
|
|
335
360
|
if (value === void 0) return "undefined";
|
|
336
361
|
const type = typeof value;
|
|
337
362
|
if (type === "string") return `s:${value}`;
|
|
338
|
-
if (type === "number")
|
|
363
|
+
if (type === "number") {
|
|
364
|
+
const num = value;
|
|
365
|
+
if (Number.isNaN(num)) return "n:NaN";
|
|
366
|
+
if (num === 0) return "n:0";
|
|
367
|
+
if (!Number.isFinite(num)) return `n:${num > 0 ? "Infinity" : "-Infinity"}`;
|
|
368
|
+
return `n:${num}`;
|
|
369
|
+
}
|
|
339
370
|
if (type === "boolean") return `b:${value}`;
|
|
340
371
|
if (type === "object") try {
|
|
341
372
|
return `o:${JSON.stringify(value)}`;
|
|
342
373
|
} catch {
|
|
343
|
-
|
|
374
|
+
let id = circularRefMap.get(value);
|
|
375
|
+
if (!id) {
|
|
376
|
+
id = String(++uniqueIdCounter);
|
|
377
|
+
circularRefMap.set(value, id);
|
|
378
|
+
}
|
|
379
|
+
return `o:_${id}`;
|
|
344
380
|
}
|
|
345
381
|
return `x:_${++uniqueIdCounter}`;
|
|
346
382
|
}
|
|
@@ -375,7 +411,7 @@ function extractSubSchema(schema, path) {
|
|
|
375
411
|
let currentSchema = schema;
|
|
376
412
|
let hasEffects = false;
|
|
377
413
|
for (const segment of segments) {
|
|
378
|
-
if (!segment)
|
|
414
|
+
if (!segment) return null;
|
|
379
415
|
if (hasChecks(currentSchema)) hasEffects = true;
|
|
380
416
|
const unwrapped = unwrapNonEffects(currentSchema);
|
|
381
417
|
if (hasChecks(unwrapped)) hasEffects = true;
|
|
@@ -442,10 +478,54 @@ function analyzeSchemaPath(schema, path) {
|
|
|
442
478
|
cache.set(path, result);
|
|
443
479
|
return result;
|
|
444
480
|
}
|
|
445
|
-
function
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
481
|
+
function markFieldTouched(touchedFields, fieldName) {
|
|
482
|
+
if (touchedFields.value[fieldName]) return;
|
|
483
|
+
touchedFields.value = {
|
|
484
|
+
...touchedFields.value,
|
|
485
|
+
[fieldName]: true
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function clearFieldDirty(dirtyFields, fieldName) {
|
|
489
|
+
if (!(fieldName in dirtyFields.value)) return;
|
|
490
|
+
const newDirty = { ...dirtyFields.value };
|
|
491
|
+
delete newDirty[fieldName];
|
|
492
|
+
dirtyFields.value = newDirty;
|
|
493
|
+
}
|
|
494
|
+
function clearFieldTouched(touchedFields, fieldName) {
|
|
495
|
+
if (!(fieldName in touchedFields.value)) return;
|
|
496
|
+
const newTouched = { ...touchedFields.value };
|
|
497
|
+
delete newTouched[fieldName];
|
|
498
|
+
touchedFields.value = newTouched;
|
|
499
|
+
}
|
|
500
|
+
function clearFieldErrors(errors, fieldName) {
|
|
501
|
+
const currentErrors = errors.value;
|
|
502
|
+
const keys = Object.keys(currentErrors);
|
|
503
|
+
if (keys.length === 0) return;
|
|
504
|
+
const prefix = `${fieldName}.`;
|
|
505
|
+
const keysToDelete = [];
|
|
506
|
+
for (const key of keys) if (key === fieldName || key.startsWith(prefix)) keysToDelete.push(key);
|
|
507
|
+
if (keysToDelete.length === 0) return;
|
|
508
|
+
const newErrors = { ...currentErrors };
|
|
509
|
+
for (const key of keysToDelete) delete newErrors[key];
|
|
510
|
+
errors.value = newErrors;
|
|
511
|
+
}
|
|
512
|
+
function updateFieldDirtyState(dirtyFields, defaultValues, defaultValueHashes, fieldName, currentValue) {
|
|
513
|
+
let defaultHash = defaultValueHashes.get(fieldName);
|
|
514
|
+
if (defaultHash === void 0) {
|
|
515
|
+
defaultHash = hashValue(get(defaultValues, fieldName));
|
|
516
|
+
defaultValueHashes.set(fieldName, defaultHash);
|
|
517
|
+
}
|
|
518
|
+
const isDirty = hashValue(currentValue) !== defaultHash;
|
|
519
|
+
const wasDirty = dirtyFields.value[fieldName] === true;
|
|
520
|
+
if (isDirty && !wasDirty) dirtyFields.value = {
|
|
521
|
+
...dirtyFields.value,
|
|
522
|
+
[fieldName]: true
|
|
523
|
+
};
|
|
524
|
+
else if (!isDirty && wasDirty) {
|
|
525
|
+
const newDirty = { ...dirtyFields.value };
|
|
526
|
+
delete newDirty[fieldName];
|
|
527
|
+
dirtyFields.value = newDirty;
|
|
528
|
+
}
|
|
449
529
|
}
|
|
450
530
|
function setValidating(ctx, fieldPath, isValidating) {
|
|
451
531
|
const newSet = new Set(ctx.validatingFields.value);
|
|
@@ -539,37 +619,43 @@ function createValidation(ctx) {
|
|
|
539
619
|
}
|
|
540
620
|
ctx.pendingErrors.delete(fieldPath);
|
|
541
621
|
applyNativeValidation(fieldPath, null);
|
|
542
|
-
|
|
622
|
+
if (ctx.persistentErrorFields.has(fieldPath)) return;
|
|
623
|
+
clearFieldErrors(ctx.errors, fieldPath);
|
|
543
624
|
}
|
|
544
625
|
function clearAllPendingErrors() {
|
|
545
626
|
for (const timer of ctx.errorDelayTimers.values()) clearTimeout(timer);
|
|
546
627
|
ctx.errorDelayTimers.clear();
|
|
547
628
|
ctx.pendingErrors.clear();
|
|
548
629
|
}
|
|
549
|
-
async function validateFieldPartial(fieldPath, subSchema, valueHash, criteriaMode, generationAtStart) {
|
|
630
|
+
async function validateFieldPartial(fieldPath, subSchema, valueHash, cacheKey, criteriaMode, generationAtStart) {
|
|
550
631
|
const fieldValue = get(ctx.formData, fieldPath);
|
|
551
632
|
setValidating(ctx, fieldPath, true);
|
|
552
633
|
try {
|
|
553
634
|
const result = await subSchema.safeParseAsync(fieldValue);
|
|
554
635
|
if (ctx.resetGeneration.value !== generationAtStart) return true;
|
|
555
636
|
if (result.success) {
|
|
556
|
-
|
|
557
|
-
if (valueHash) ctx.validationCache.set(
|
|
637
|
+
cancelError(fieldPath);
|
|
638
|
+
if (valueHash) ctx.validationCache.set(cacheKey, {
|
|
558
639
|
hash: valueHash,
|
|
559
640
|
isValid: true
|
|
560
641
|
});
|
|
561
642
|
return true;
|
|
562
643
|
}
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
644
|
+
const fieldSegments = fieldPath.split(".");
|
|
645
|
+
const fieldErrors = result.error.issues.map((issue) => {
|
|
646
|
+
const issuePath = issue.path.map(String);
|
|
647
|
+
const alreadyPrefixed = issuePath.length >= fieldSegments.length && fieldSegments.every((seg, i) => issuePath[i] === seg);
|
|
648
|
+
return {
|
|
649
|
+
...issue,
|
|
650
|
+
path: alreadyPrefixed ? issuePath : fieldSegments.concat(issuePath)
|
|
651
|
+
};
|
|
652
|
+
});
|
|
653
|
+
cancelError(fieldPath);
|
|
568
654
|
const grouped = groupErrorsByPath(fieldErrors);
|
|
569
655
|
const errorBatch = [];
|
|
570
656
|
for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
|
|
571
657
|
scheduleErrorsBatch(errorBatch);
|
|
572
|
-
if (valueHash) ctx.validationCache.set(
|
|
658
|
+
if (valueHash) ctx.validationCache.set(cacheKey, {
|
|
573
659
|
hash: valueHash,
|
|
574
660
|
isValid: false
|
|
575
661
|
});
|
|
@@ -578,14 +664,14 @@ function createValidation(ctx) {
|
|
|
578
664
|
setValidating(ctx, fieldPath, false);
|
|
579
665
|
}
|
|
580
666
|
}
|
|
581
|
-
async function validateFieldFull(fieldPath, valueHash, criteriaMode, generationAtStart) {
|
|
667
|
+
async function validateFieldFull(fieldPath, valueHash, cacheKey, criteriaMode, generationAtStart) {
|
|
582
668
|
setValidating(ctx, fieldPath, true);
|
|
583
669
|
try {
|
|
584
670
|
const result = await ctx.options.schema.safeParseAsync(ctx.formData);
|
|
585
671
|
if (ctx.resetGeneration.value !== generationAtStart) return true;
|
|
586
672
|
if (result.success) {
|
|
587
|
-
|
|
588
|
-
if (valueHash) ctx.validationCache.set(
|
|
673
|
+
cancelError(fieldPath);
|
|
674
|
+
if (valueHash) ctx.validationCache.set(cacheKey, {
|
|
589
675
|
hash: valueHash,
|
|
590
676
|
isValid: true
|
|
591
677
|
});
|
|
@@ -596,19 +682,19 @@ function createValidation(ctx) {
|
|
|
596
682
|
return path === fieldPath || path.startsWith(`${fieldPath}.`);
|
|
597
683
|
});
|
|
598
684
|
if (fieldErrors.length === 0) {
|
|
599
|
-
|
|
600
|
-
if (valueHash) ctx.validationCache.set(
|
|
685
|
+
cancelError(fieldPath);
|
|
686
|
+
if (valueHash) ctx.validationCache.set(cacheKey, {
|
|
601
687
|
hash: valueHash,
|
|
602
688
|
isValid: true
|
|
603
689
|
});
|
|
604
690
|
return true;
|
|
605
691
|
}
|
|
606
|
-
|
|
692
|
+
cancelError(fieldPath);
|
|
607
693
|
const grouped = groupErrorsByPath(fieldErrors);
|
|
608
694
|
const errorBatch = [];
|
|
609
695
|
for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
|
|
610
696
|
scheduleErrorsBatch(errorBatch);
|
|
611
|
-
if (valueHash) ctx.validationCache.set(
|
|
697
|
+
if (valueHash) ctx.validationCache.set(cacheKey, {
|
|
612
698
|
hash: valueHash,
|
|
613
699
|
isValid: false
|
|
614
700
|
});
|
|
@@ -623,14 +709,16 @@ function createValidation(ctx) {
|
|
|
623
709
|
let valueHash;
|
|
624
710
|
if (fieldPath) {
|
|
625
711
|
valueHash = hashValue(get(ctx.formData, fieldPath));
|
|
626
|
-
const cached = ctx.validationCache.get(fieldPath);
|
|
627
|
-
if (cached && cached.hash === valueHash) {
|
|
628
|
-
if (cached.isValid) ctx.errors.value = cancelError(fieldPath);
|
|
629
|
-
return cached.isValid;
|
|
630
|
-
}
|
|
631
712
|
const analysis = analyzeSchemaPath(ctx.options.schema, fieldPath);
|
|
632
|
-
|
|
633
|
-
|
|
713
|
+
const usePartial = analysis.canPartialValidate && analysis.subSchema;
|
|
714
|
+
const cacheKey = `${fieldPath}:${usePartial ? "partial" : "full"}`;
|
|
715
|
+
const cached = ctx.validationCache.get(cacheKey);
|
|
716
|
+
if (cached && cached.hash === valueHash && cached.isValid) {
|
|
717
|
+
cancelError(fieldPath);
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
if (usePartial) return validateFieldPartial(fieldPath, analysis.subSchema, valueHash, cacheKey, criteriaMode, generationAtStart);
|
|
721
|
+
return validateFieldFull(fieldPath, valueHash, cacheKey, criteriaMode, generationAtStart);
|
|
634
722
|
}
|
|
635
723
|
const validatingKey = "_form";
|
|
636
724
|
setValidating(ctx, validatingKey, true);
|
|
@@ -661,47 +749,31 @@ function createValidation(ctx) {
|
|
|
661
749
|
clearAllPendingErrors
|
|
662
750
|
};
|
|
663
751
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
}
|
|
672
|
-
function markFieldTouched(touchedFields, touchedFieldCount, fieldName) {
|
|
673
|
-
if (touchedFields.value[fieldName]) return;
|
|
674
|
-
touchedFields.value = {
|
|
675
|
-
...touchedFields.value,
|
|
676
|
-
[fieldName]: true
|
|
677
|
-
};
|
|
678
|
-
touchedFieldCount.value++;
|
|
679
|
-
}
|
|
680
|
-
function clearFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
|
|
681
|
-
if (!(fieldName in dirtyFields.value)) return;
|
|
682
|
-
const newDirty = { ...dirtyFields.value };
|
|
683
|
-
delete newDirty[fieldName];
|
|
684
|
-
dirtyFields.value = newDirty;
|
|
685
|
-
dirtyFieldCount.value--;
|
|
752
|
+
var VALID_MODES = [
|
|
753
|
+
"onSubmit",
|
|
754
|
+
"onBlur",
|
|
755
|
+
"onChange",
|
|
756
|
+
"onTouched"
|
|
757
|
+
];
|
|
758
|
+
function validateMode(mode, paramName) {
|
|
759
|
+
if (__DEV__ && !VALID_MODES.includes(mode)) warnOnce(`Invalid ${paramName}: "${mode}". Expected one of: ${VALID_MODES.join(", ")}`, `invalid-mode-${mode}`);
|
|
686
760
|
}
|
|
687
|
-
function
|
|
688
|
-
if (
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
761
|
+
function shouldValidateOnChange(mode, isTouched, reValidateMode, hasSubmitted) {
|
|
762
|
+
if (__DEV__) {
|
|
763
|
+
validateMode(mode, "validation mode");
|
|
764
|
+
if (reValidateMode) validateMode(reValidateMode, "reValidateMode");
|
|
765
|
+
}
|
|
766
|
+
if (mode === "onChange") return true;
|
|
767
|
+
if (mode === "onTouched" && isTouched) return true;
|
|
768
|
+
if (hasSubmitted === true && reValidateMode === "onChange") return true;
|
|
769
|
+
return false;
|
|
693
770
|
}
|
|
694
|
-
function
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
for (const key of keys) if (key === fieldName || key.startsWith(prefix)) keysToDelete.push(key);
|
|
701
|
-
if (keysToDelete.length === 0) return;
|
|
702
|
-
const newErrors = { ...currentErrors };
|
|
703
|
-
for (const key of keysToDelete) delete newErrors[key];
|
|
704
|
-
errors.value = newErrors;
|
|
771
|
+
function shouldValidateOnBlur(mode, hasSubmitted, reValidateMode) {
|
|
772
|
+
if (__DEV__) {
|
|
773
|
+
validateMode(mode, "validation mode");
|
|
774
|
+
if (reValidateMode) validateMode(reValidateMode, "reValidateMode");
|
|
775
|
+
}
|
|
776
|
+
return mode === "onBlur" || mode === "onTouched" || hasSubmitted && (reValidateMode === "onBlur" || reValidateMode === "onTouched");
|
|
705
777
|
}
|
|
706
778
|
var validationRequestCounter = 0;
|
|
707
779
|
function createFieldRegistration(ctx, validate) {
|
|
@@ -742,11 +814,14 @@ function createFieldRegistration(ctx, validate) {
|
|
|
742
814
|
};
|
|
743
815
|
const onInput = async (e) => {
|
|
744
816
|
const target = e.target;
|
|
745
|
-
|
|
817
|
+
let value;
|
|
818
|
+
if (target.type === "checkbox") value = target.checked;
|
|
819
|
+
else if (target.type === "number" || target.type === "range") value = target.valueAsNumber;
|
|
820
|
+
else value = target.value;
|
|
746
821
|
set(ctx.formData, name, value);
|
|
747
|
-
|
|
822
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, value);
|
|
748
823
|
const fieldOpts = ctx.fieldOptions.get(name);
|
|
749
|
-
if (ctx.options.mode
|
|
824
|
+
if (shouldValidateOnChange(ctx.options.mode ?? "onSubmit", ctx.touchedFields.value[name] === true, ctx.options.reValidateMode, ctx.submitCount.value > 0)) {
|
|
750
825
|
const validationDebounceMs = ctx.options.validationDebounce || 0;
|
|
751
826
|
if (validationDebounceMs > 0) {
|
|
752
827
|
const existingTimer = ctx.schemaValidationTimers.get(name);
|
|
@@ -779,15 +854,18 @@ function createFieldRegistration(ctx, validate) {
|
|
|
779
854
|
const timer = setTimeout(async () => {
|
|
780
855
|
ctx.debounceTimers.delete(name);
|
|
781
856
|
await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
782
|
-
ctx.validationRequestIds.delete(name);
|
|
857
|
+
if (ctx.validationRequestIds.get(name) === requestId) ctx.validationRequestIds.delete(name);
|
|
783
858
|
}, debounceMs);
|
|
784
859
|
ctx.debounceTimers.set(name, timer);
|
|
785
|
-
} else
|
|
860
|
+
} else {
|
|
861
|
+
await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
862
|
+
if (ctx.validationRequestIds.get(name) === requestId) ctx.validationRequestIds.delete(name);
|
|
863
|
+
}
|
|
786
864
|
}
|
|
787
865
|
};
|
|
788
866
|
const onBlur = async (_e) => {
|
|
789
|
-
markFieldTouched(ctx.touchedFields,
|
|
790
|
-
if (ctx.options.mode
|
|
867
|
+
markFieldTouched(ctx.touchedFields, name);
|
|
868
|
+
if (shouldValidateOnBlur(ctx.options.mode ?? "onSubmit", ctx.submitCount.value > 0, ctx.options.reValidateMode)) {
|
|
791
869
|
await validate(name);
|
|
792
870
|
const fieldOpts = ctx.fieldOptions.get(name);
|
|
793
871
|
if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
|
|
@@ -820,15 +898,21 @@ function createFieldRegistration(ctx, validate) {
|
|
|
820
898
|
clearTimeout(schemaTimer);
|
|
821
899
|
ctx.schemaValidationTimers.delete(name);
|
|
822
900
|
}
|
|
901
|
+
const errorTimer = ctx.errorDelayTimers.get(name);
|
|
902
|
+
if (errorTimer) {
|
|
903
|
+
clearTimeout(errorTimer);
|
|
904
|
+
ctx.errorDelayTimers.delete(name);
|
|
905
|
+
}
|
|
906
|
+
ctx.pendingErrors.delete(name);
|
|
823
907
|
ctx.validationRequestIds.delete(name);
|
|
908
|
+
ctx.fieldRefs.delete(name);
|
|
909
|
+
ctx.fieldOptions.delete(name);
|
|
910
|
+
ctx.fieldHandlers.delete(name);
|
|
824
911
|
if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
|
|
825
912
|
unset(ctx.formData, name);
|
|
826
913
|
clearFieldErrors(ctx.errors, name);
|
|
827
|
-
clearFieldTouched(ctx.touchedFields,
|
|
828
|
-
clearFieldDirty(ctx.dirtyFields,
|
|
829
|
-
ctx.fieldRefs.delete(name);
|
|
830
|
-
ctx.fieldOptions.delete(name);
|
|
831
|
-
ctx.fieldHandlers.delete(name);
|
|
914
|
+
clearFieldTouched(ctx.touchedFields, name);
|
|
915
|
+
clearFieldDirty(ctx.dirtyFields, name);
|
|
832
916
|
}
|
|
833
917
|
}
|
|
834
918
|
};
|
|
@@ -849,7 +933,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
849
933
|
get: () => get(ctx.formData, name),
|
|
850
934
|
set: (val) => {
|
|
851
935
|
set(ctx.formData, name, val);
|
|
852
|
-
|
|
936
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, val);
|
|
853
937
|
}
|
|
854
938
|
}) }
|
|
855
939
|
};
|
|
@@ -858,8 +942,8 @@ function createFieldRegistration(ctx, validate) {
|
|
|
858
942
|
const opts = options || {};
|
|
859
943
|
if (!opts.keepValue) unset(ctx.formData, name);
|
|
860
944
|
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
861
|
-
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields,
|
|
862
|
-
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields,
|
|
945
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
|
|
946
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
|
|
863
947
|
ctx.fieldRefs.delete(name);
|
|
864
948
|
ctx.fieldOptions.delete(name);
|
|
865
949
|
ctx.fieldHandlers.delete(name);
|
|
@@ -874,6 +958,8 @@ function createFieldRegistration(ctx, validate) {
|
|
|
874
958
|
ctx.schemaValidationTimers.delete(name);
|
|
875
959
|
}
|
|
876
960
|
ctx.validationRequestIds.delete(name);
|
|
961
|
+
ctx.validationCache.delete(`${name}:partial`);
|
|
962
|
+
ctx.validationCache.delete(`${name}:full`);
|
|
877
963
|
}
|
|
878
964
|
return {
|
|
879
965
|
register,
|
|
@@ -963,10 +1049,32 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
963
1049
|
const normalizeToArray = (value) => {
|
|
964
1050
|
return Array.isArray(value) ? value : [value];
|
|
965
1051
|
};
|
|
1052
|
+
const clearValidationCache = () => {
|
|
1053
|
+
for (const key of ctx.validationCache.keys()) {
|
|
1054
|
+
const colonIndex = key.lastIndexOf(":");
|
|
1055
|
+
const fieldPath = colonIndex > 0 ? key.slice(0, colonIndex) : key;
|
|
1056
|
+
if (fieldPath === name || fieldPath.startsWith(`${name}.`)) ctx.validationCache.delete(key);
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
const validateIfNeeded = () => {
|
|
1060
|
+
const isTouched = ctx.touchedFields.value[name] === true;
|
|
1061
|
+
const hasSubmitted = ctx.submitCount.value > 0;
|
|
1062
|
+
if (shouldValidateOnChange(ctx.options.mode ?? "onSubmit", isTouched, ctx.options.reValidateMode, hasSubmitted)) validate(name);
|
|
1063
|
+
};
|
|
1064
|
+
const ensureSync = () => {
|
|
1065
|
+
const currentValues = get(ctx.formData, name) || [];
|
|
1066
|
+
if (fa.items.value.length !== currentValues.length) {
|
|
1067
|
+
if (__DEV__) console.warn(`[vue-hook-form] Field array out of sync with formData. Rebuilding items array (items: ${fa.items.value.length}, formData: ${currentValues.length})`);
|
|
1068
|
+
fa.items.value = currentValues.map(() => createItem(generateId()));
|
|
1069
|
+
rebuildIndexCache();
|
|
1070
|
+
}
|
|
1071
|
+
return currentValues;
|
|
1072
|
+
};
|
|
966
1073
|
const append = (value, focusOptions) => {
|
|
1074
|
+
clearValidationCache();
|
|
967
1075
|
const values = normalizeToArray(value);
|
|
968
1076
|
if (values.length === 0) return true;
|
|
969
|
-
const currentValues =
|
|
1077
|
+
const currentValues = ensureSync();
|
|
970
1078
|
const insertIndex = currentValues.length;
|
|
971
1079
|
const rules = fa.rules;
|
|
972
1080
|
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
@@ -981,15 +1089,16 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
981
1089
|
const newItems = values.map(() => createItem(generateId()));
|
|
982
1090
|
fa.items.value = [...fa.items.value, ...newItems];
|
|
983
1091
|
appendToCache(insertIndex);
|
|
984
|
-
|
|
985
|
-
|
|
1092
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1093
|
+
validateIfNeeded();
|
|
986
1094
|
handleFocus(insertIndex, values.length, focusOptions);
|
|
987
1095
|
return true;
|
|
988
1096
|
};
|
|
989
1097
|
const prepend = (value, focusOptions) => {
|
|
1098
|
+
clearValidationCache();
|
|
990
1099
|
const values = normalizeToArray(value);
|
|
991
1100
|
if (values.length === 0) return true;
|
|
992
|
-
const currentValues =
|
|
1101
|
+
const currentValues = ensureSync();
|
|
993
1102
|
const rules = fa.rules;
|
|
994
1103
|
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
995
1104
|
if (__DEV__) warnArrayOperationRejected("prepend", name, "maxLength", {
|
|
@@ -1003,13 +1112,14 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1003
1112
|
const newItems = values.map(() => createItem(generateId()));
|
|
1004
1113
|
fa.items.value = [...newItems, ...fa.items.value];
|
|
1005
1114
|
updateCacheAfterInsert(0, values.length);
|
|
1006
|
-
|
|
1007
|
-
|
|
1115
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1116
|
+
validateIfNeeded();
|
|
1008
1117
|
handleFocus(0, values.length, focusOptions);
|
|
1009
1118
|
return true;
|
|
1010
1119
|
};
|
|
1011
1120
|
const update = (index, value) => {
|
|
1012
|
-
|
|
1121
|
+
clearValidationCache();
|
|
1122
|
+
const currentValues = ensureSync();
|
|
1013
1123
|
if (index < 0 || index >= currentValues.length) {
|
|
1014
1124
|
if (__DEV__) warnArrayIndexOutOfBounds("update", name, index, currentValues.length);
|
|
1015
1125
|
return false;
|
|
@@ -1017,12 +1127,13 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1017
1127
|
const newValues = [...currentValues];
|
|
1018
1128
|
newValues[index] = value;
|
|
1019
1129
|
set(ctx.formData, name, newValues);
|
|
1020
|
-
|
|
1021
|
-
|
|
1130
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1131
|
+
validateIfNeeded();
|
|
1022
1132
|
return true;
|
|
1023
1133
|
};
|
|
1024
1134
|
const removeAt = (index) => {
|
|
1025
|
-
|
|
1135
|
+
clearValidationCache();
|
|
1136
|
+
const currentValues = ensureSync();
|
|
1026
1137
|
if (index < 0 || index >= currentValues.length) {
|
|
1027
1138
|
if (__DEV__) warnArrayIndexOutOfBounds("remove", name, index, currentValues.length);
|
|
1028
1139
|
return false;
|
|
@@ -1040,14 +1151,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1040
1151
|
const keyToRemove = fa.items.value[index]?.key;
|
|
1041
1152
|
fa.items.value = fa.items.value.filter((item) => item.key !== keyToRemove);
|
|
1042
1153
|
if (keyToRemove) updateCacheAfterRemove(keyToRemove, index);
|
|
1043
|
-
|
|
1044
|
-
|
|
1154
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1155
|
+
validateIfNeeded();
|
|
1045
1156
|
return true;
|
|
1046
1157
|
};
|
|
1047
1158
|
const insert = (index, value, focusOptions) => {
|
|
1159
|
+
clearValidationCache();
|
|
1048
1160
|
const values = normalizeToArray(value);
|
|
1049
1161
|
if (values.length === 0) return true;
|
|
1050
|
-
const currentValues =
|
|
1162
|
+
const currentValues = ensureSync();
|
|
1051
1163
|
const rules = fa.rules;
|
|
1052
1164
|
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
1053
1165
|
if (__DEV__) warnArrayOperationRejected("insert", name, "maxLength", {
|
|
@@ -1056,27 +1168,31 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1056
1168
|
});
|
|
1057
1169
|
return false;
|
|
1058
1170
|
}
|
|
1059
|
-
|
|
1171
|
+
if (index < 0 || index > currentValues.length) {
|
|
1172
|
+
if (__DEV__) warnArrayIndexOutOfBounds("insert", name, index, currentValues.length);
|
|
1173
|
+
return false;
|
|
1174
|
+
}
|
|
1060
1175
|
const newValues = [
|
|
1061
|
-
...currentValues.slice(0,
|
|
1176
|
+
...currentValues.slice(0, index),
|
|
1062
1177
|
...values,
|
|
1063
|
-
...currentValues.slice(
|
|
1178
|
+
...currentValues.slice(index)
|
|
1064
1179
|
];
|
|
1065
1180
|
set(ctx.formData, name, newValues);
|
|
1066
1181
|
const newItems = values.map(() => createItem(generateId()));
|
|
1067
1182
|
fa.items.value = [
|
|
1068
|
-
...fa.items.value.slice(0,
|
|
1183
|
+
...fa.items.value.slice(0, index),
|
|
1069
1184
|
...newItems,
|
|
1070
|
-
...fa.items.value.slice(
|
|
1185
|
+
...fa.items.value.slice(index)
|
|
1071
1186
|
];
|
|
1072
|
-
updateCacheAfterInsert(
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
handleFocus(
|
|
1187
|
+
updateCacheAfterInsert(index, values.length);
|
|
1188
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1189
|
+
validateIfNeeded();
|
|
1190
|
+
handleFocus(index, values.length, focusOptions);
|
|
1076
1191
|
return true;
|
|
1077
1192
|
};
|
|
1078
1193
|
const swap = (indexA, indexB) => {
|
|
1079
|
-
|
|
1194
|
+
clearValidationCache();
|
|
1195
|
+
const currentValues = ensureSync();
|
|
1080
1196
|
if (indexA < 0 || indexB < 0 || indexA >= currentValues.length || indexB >= currentValues.length) {
|
|
1081
1197
|
if (__DEV__) warnArrayIndexOutOfBounds("swap", name, indexA < 0 || indexA >= currentValues.length ? indexA : indexB, currentValues.length);
|
|
1082
1198
|
return false;
|
|
@@ -1093,51 +1209,52 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1093
1209
|
fa.items.value = newItems;
|
|
1094
1210
|
swapInCache(indexA, indexB);
|
|
1095
1211
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1212
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1213
|
+
validateIfNeeded();
|
|
1098
1214
|
return true;
|
|
1099
1215
|
};
|
|
1100
1216
|
const move = (from, to) => {
|
|
1101
|
-
|
|
1102
|
-
|
|
1217
|
+
clearValidationCache();
|
|
1218
|
+
const currentValues = ensureSync();
|
|
1219
|
+
if (from < 0 || from >= currentValues.length || to < 0 || to >= currentValues.length) {
|
|
1103
1220
|
if (__DEV__) warnArrayIndexOutOfBounds("move", name, from < 0 || from >= currentValues.length ? from : to, currentValues.length);
|
|
1104
1221
|
return false;
|
|
1105
1222
|
}
|
|
1106
1223
|
const newValues = [...currentValues];
|
|
1107
1224
|
const [removed] = newValues.splice(from, 1);
|
|
1108
1225
|
if (removed !== void 0) {
|
|
1109
|
-
|
|
1110
|
-
newValues.splice(clampedTo, 0, removed);
|
|
1226
|
+
newValues.splice(to, 0, removed);
|
|
1111
1227
|
set(ctx.formData, name, newValues);
|
|
1112
1228
|
}
|
|
1113
1229
|
const newItems = [...fa.items.value];
|
|
1114
1230
|
const [removedItem] = newItems.splice(from, 1);
|
|
1115
1231
|
if (removedItem) {
|
|
1116
|
-
|
|
1117
|
-
newItems.splice(clampedTo, 0, removedItem);
|
|
1232
|
+
newItems.splice(to, 0, removedItem);
|
|
1118
1233
|
fa.items.value = newItems;
|
|
1119
|
-
const minIdx = Math.min(from,
|
|
1120
|
-
const maxIdx = Math.max(from,
|
|
1234
|
+
const minIdx = Math.min(from, to);
|
|
1235
|
+
const maxIdx = Math.max(from, to);
|
|
1121
1236
|
const items = fa.items.value;
|
|
1122
1237
|
for (let i = minIdx; i <= maxIdx; i++) {
|
|
1123
1238
|
const item = items[i];
|
|
1124
1239
|
if (item) indexCache.set(item.key, i);
|
|
1125
1240
|
}
|
|
1126
1241
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1242
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1243
|
+
validateIfNeeded();
|
|
1129
1244
|
return true;
|
|
1130
1245
|
};
|
|
1131
1246
|
const replace = (newValues) => {
|
|
1247
|
+
clearValidationCache();
|
|
1132
1248
|
if (!Array.isArray(newValues)) return false;
|
|
1133
1249
|
set(ctx.formData, name, newValues);
|
|
1134
1250
|
fa.items.value = newValues.map(() => createItem(generateId()));
|
|
1135
1251
|
rebuildIndexCache();
|
|
1136
|
-
|
|
1137
|
-
|
|
1252
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1253
|
+
validateIfNeeded();
|
|
1138
1254
|
return true;
|
|
1139
1255
|
};
|
|
1140
1256
|
const removeAll = () => {
|
|
1257
|
+
clearValidationCache();
|
|
1141
1258
|
const rules = fa.rules;
|
|
1142
1259
|
if (rules?.minLength && rules.minLength.value > 0) {
|
|
1143
1260
|
if (__DEV__) warnArrayOperationRejected("removeAll", name, "minLength", {
|
|
@@ -1149,12 +1266,13 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1149
1266
|
set(ctx.formData, name, []);
|
|
1150
1267
|
fa.items.value = [];
|
|
1151
1268
|
indexCache.clear();
|
|
1152
|
-
|
|
1153
|
-
|
|
1269
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1270
|
+
validateIfNeeded();
|
|
1154
1271
|
return true;
|
|
1155
1272
|
};
|
|
1156
1273
|
const removeMany = (indices) => {
|
|
1157
|
-
|
|
1274
|
+
clearValidationCache();
|
|
1275
|
+
const currentValues = ensureSync();
|
|
1158
1276
|
const validIndices = indices.filter((i) => i >= 0 && i < currentValues.length);
|
|
1159
1277
|
if (validIndices.length === 0) return true;
|
|
1160
1278
|
const rules = fa.rules;
|
|
@@ -1176,8 +1294,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1176
1294
|
fa.items.value.forEach((item, idx) => {
|
|
1177
1295
|
indexCache.set(item.key, idx);
|
|
1178
1296
|
});
|
|
1179
|
-
|
|
1180
|
-
|
|
1297
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1298
|
+
validateIfNeeded();
|
|
1181
1299
|
return true;
|
|
1182
1300
|
};
|
|
1183
1301
|
return {
|
|
@@ -1200,7 +1318,13 @@ function syncUncontrolledInputs(fieldRefs, fieldOptions, formData) {
|
|
|
1200
1318
|
for (const [name, fieldRef] of Array.from(fieldRefs.entries())) {
|
|
1201
1319
|
const el = fieldRef.value;
|
|
1202
1320
|
if (el) {
|
|
1203
|
-
if (!fieldOptions.get(name)?.controlled)
|
|
1321
|
+
if (!fieldOptions.get(name)?.controlled) {
|
|
1322
|
+
let value;
|
|
1323
|
+
if (el.type === "checkbox") value = el.checked;
|
|
1324
|
+
else if (el.type === "number" || el.type === "range") value = el.valueAsNumber;
|
|
1325
|
+
else value = el.value;
|
|
1326
|
+
set(formData, name, value);
|
|
1327
|
+
}
|
|
1204
1328
|
}
|
|
1205
1329
|
}
|
|
1206
1330
|
}
|
|
@@ -1210,6 +1334,10 @@ function updateDomElement(el, value) {
|
|
|
1210
1334
|
}
|
|
1211
1335
|
function useForm(options) {
|
|
1212
1336
|
const ctx = createFormContext(options);
|
|
1337
|
+
let isSubmissionLocked = false;
|
|
1338
|
+
(0, vue.onUnmounted)(() => {
|
|
1339
|
+
ctx.cleanup();
|
|
1340
|
+
});
|
|
1213
1341
|
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
1214
1342
|
const { register, unregister } = createFieldRegistration(ctx, validate);
|
|
1215
1343
|
function setFocus(name, focusOptions) {
|
|
@@ -1252,10 +1380,10 @@ function useForm(options) {
|
|
|
1252
1380
|
};
|
|
1253
1381
|
return cachedMergedErrors;
|
|
1254
1382
|
}
|
|
1255
|
-
const isDirtyComputed = (0, vue.computed)(() => ctx.
|
|
1383
|
+
const isDirtyComputed = (0, vue.computed)(() => Object.keys(ctx.dirtyFields.value).length > 0);
|
|
1256
1384
|
const errorsComputed = (0, vue.computed)(() => getMergedErrors());
|
|
1257
1385
|
const isValidComputed = (0, vue.computed)(() => {
|
|
1258
|
-
if (!(ctx.submitCount.value > 0 || ctx.
|
|
1386
|
+
if (!(ctx.submitCount.value > 0 || Object.keys(ctx.touchedFields.value).length > 0)) return false;
|
|
1259
1387
|
return Object.keys(errorsComputed.value).length === 0;
|
|
1260
1388
|
});
|
|
1261
1389
|
const isReadyComputed = (0, vue.computed)(() => !ctx.isLoading.value);
|
|
@@ -1313,7 +1441,8 @@ function useForm(options) {
|
|
|
1313
1441
|
return async (e) => {
|
|
1314
1442
|
e.preventDefault();
|
|
1315
1443
|
if (ctx.isDisabled.value) return;
|
|
1316
|
-
if (
|
|
1444
|
+
if (isSubmissionLocked) return;
|
|
1445
|
+
isSubmissionLocked = true;
|
|
1317
1446
|
ctx.isSubmitting.value = true;
|
|
1318
1447
|
ctx.submitCount.value++;
|
|
1319
1448
|
ctx.isSubmitSuccessful.value = false;
|
|
@@ -1331,6 +1460,7 @@ function useForm(options) {
|
|
|
1331
1460
|
}
|
|
1332
1461
|
} finally {
|
|
1333
1462
|
ctx.isSubmitting.value = false;
|
|
1463
|
+
isSubmissionLocked = false;
|
|
1334
1464
|
}
|
|
1335
1465
|
};
|
|
1336
1466
|
}
|
|
@@ -1344,8 +1474,9 @@ function useForm(options) {
|
|
|
1344
1474
|
}
|
|
1345
1475
|
}
|
|
1346
1476
|
set(ctx.formData, name, value);
|
|
1347
|
-
if (setValueOptions?.shouldDirty
|
|
1348
|
-
|
|
1477
|
+
if (setValueOptions?.shouldDirty === false) clearFieldDirty(ctx.dirtyFields, name);
|
|
1478
|
+
else updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, value);
|
|
1479
|
+
if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, name);
|
|
1349
1480
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
1350
1481
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
1351
1482
|
if (fieldRef?.value) updateDomElement(fieldRef.value, value);
|
|
@@ -1354,25 +1485,28 @@ function useForm(options) {
|
|
|
1354
1485
|
}
|
|
1355
1486
|
function reset(values, resetOptions) {
|
|
1356
1487
|
const opts = resetOptions || {};
|
|
1488
|
+
ctx.validationCache.clear();
|
|
1357
1489
|
ctx.resetGeneration.value++;
|
|
1358
1490
|
clearAllPendingErrors();
|
|
1359
1491
|
ctx.validatingFields.value = /* @__PURE__ */ new Set();
|
|
1360
|
-
ctx.validationCache.clear();
|
|
1361
1492
|
for (const timer of ctx.schemaValidationTimers.values()) clearTimeout(timer);
|
|
1362
1493
|
ctx.schemaValidationTimers.clear();
|
|
1363
|
-
|
|
1494
|
+
for (const timer of ctx.debounceTimers.values()) clearTimeout(timer);
|
|
1495
|
+
ctx.debounceTimers.clear();
|
|
1496
|
+
ctx.validationRequestIds.clear();
|
|
1497
|
+
if (!opts.keepDefaultValues && values) {
|
|
1498
|
+
Object.assign(ctx.defaultValues, values);
|
|
1499
|
+
ctx.defaultValueHashes.clear();
|
|
1500
|
+
}
|
|
1364
1501
|
Object.keys(ctx.formData).forEach((key) => delete ctx.formData[key]);
|
|
1365
1502
|
const newValues = deepClone(values || ctx.defaultValues);
|
|
1366
1503
|
Object.assign(ctx.formData, newValues);
|
|
1367
|
-
if (!opts.keepErrors)
|
|
1368
|
-
|
|
1369
|
-
ctx.
|
|
1370
|
-
ctx.touchedFieldCount.value = 0;
|
|
1371
|
-
}
|
|
1372
|
-
if (!opts.keepDirty) {
|
|
1373
|
-
ctx.dirtyFields.value = {};
|
|
1374
|
-
ctx.dirtyFieldCount.value = 0;
|
|
1504
|
+
if (!opts.keepErrors) {
|
|
1505
|
+
ctx.errors.value = {};
|
|
1506
|
+
ctx.persistentErrorFields.clear();
|
|
1375
1507
|
}
|
|
1508
|
+
if (!opts.keepTouched) ctx.touchedFields.value = {};
|
|
1509
|
+
if (!opts.keepDirty) ctx.dirtyFields.value = {};
|
|
1376
1510
|
if (!opts.keepSubmitCount) ctx.submitCount.value = 0;
|
|
1377
1511
|
if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
|
|
1378
1512
|
if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
|
|
@@ -1397,6 +1531,8 @@ function useForm(options) {
|
|
|
1397
1531
|
}
|
|
1398
1532
|
const opts = resetFieldOptions || {};
|
|
1399
1533
|
ctx.resetGeneration.value++;
|
|
1534
|
+
ctx.validationCache.delete(`${name}:partial`);
|
|
1535
|
+
ctx.validationCache.delete(`${name}:full`);
|
|
1400
1536
|
const errorTimer = ctx.errorDelayTimers.get(name);
|
|
1401
1537
|
if (errorTimer) {
|
|
1402
1538
|
clearTimeout(errorTimer);
|
|
@@ -1405,12 +1541,15 @@ function useForm(options) {
|
|
|
1405
1541
|
ctx.pendingErrors.delete(name);
|
|
1406
1542
|
let defaultValue = opts.defaultValue;
|
|
1407
1543
|
if (defaultValue === void 0) defaultValue = get(ctx.defaultValues, name);
|
|
1408
|
-
else
|
|
1544
|
+
else {
|
|
1545
|
+
set(ctx.defaultValues, name, defaultValue);
|
|
1546
|
+
ctx.defaultValueHashes.set(name, hashValue(defaultValue));
|
|
1547
|
+
}
|
|
1409
1548
|
const clonedValue = defaultValue !== void 0 ? deepClone(defaultValue) : void 0;
|
|
1410
1549
|
set(ctx.formData, name, clonedValue);
|
|
1411
1550
|
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
1412
|
-
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields,
|
|
1413
|
-
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields,
|
|
1551
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
|
|
1552
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
|
|
1414
1553
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
1415
1554
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
1416
1555
|
if (fieldRef?.value) updateDomElement(fieldRef.value, clonedValue ?? (fieldRef.value.type === "checkbox" ? false : ""));
|
|
@@ -1453,10 +1592,16 @@ function useForm(options) {
|
|
|
1453
1592
|
}
|
|
1454
1593
|
if (name === void 0) {
|
|
1455
1594
|
ctx.errors.value = {};
|
|
1595
|
+
ctx.externalErrors.value = {};
|
|
1596
|
+
ctx.persistentErrorFields.clear();
|
|
1456
1597
|
return;
|
|
1457
1598
|
}
|
|
1458
1599
|
const fieldsToClean = Array.isArray(name) ? name : [name];
|
|
1459
|
-
for (const field of fieldsToClean)
|
|
1600
|
+
for (const field of fieldsToClean) {
|
|
1601
|
+
clearFieldErrors(ctx.errors, field);
|
|
1602
|
+
clearFieldErrors(ctx.externalErrors, field);
|
|
1603
|
+
ctx.persistentErrorFields.delete(field);
|
|
1604
|
+
}
|
|
1460
1605
|
}
|
|
1461
1606
|
function setError(name, error) {
|
|
1462
1607
|
const newErrors = { ...ctx.errors.value };
|
|
@@ -1465,6 +1610,7 @@ function useForm(options) {
|
|
|
1465
1610
|
message: error.message
|
|
1466
1611
|
} : error.message);
|
|
1467
1612
|
ctx.errors.value = newErrors;
|
|
1613
|
+
if (error.persistent) ctx.persistentErrorFields.add(name);
|
|
1468
1614
|
}
|
|
1469
1615
|
function setErrors(errors, options$1) {
|
|
1470
1616
|
const newErrors = options$1?.shouldReplace ? {} : { ...ctx.errors.value };
|
|
@@ -1526,7 +1672,7 @@ function useForm(options) {
|
|
|
1526
1672
|
error
|
|
1527
1673
|
};
|
|
1528
1674
|
}
|
|
1529
|
-
async function trigger(name) {
|
|
1675
|
+
async function trigger(name, options$1) {
|
|
1530
1676
|
if (__DEV__ && name) {
|
|
1531
1677
|
const names = Array.isArray(name) ? name : [name];
|
|
1532
1678
|
for (const n of names) {
|
|
@@ -1538,6 +1684,7 @@ function useForm(options) {
|
|
|
1538
1684
|
}
|
|
1539
1685
|
}
|
|
1540
1686
|
}
|
|
1687
|
+
if (options$1?.markAsSubmitted) ctx.submitCount.value++;
|
|
1541
1688
|
if (name === void 0) return await validate();
|
|
1542
1689
|
if (Array.isArray(name)) {
|
|
1543
1690
|
let allValid = true;
|
|
@@ -1565,7 +1712,11 @@ function useForm(options) {
|
|
|
1565
1712
|
getValues,
|
|
1566
1713
|
getFieldState,
|
|
1567
1714
|
trigger,
|
|
1568
|
-
setFocus
|
|
1715
|
+
setFocus,
|
|
1716
|
+
options: {
|
|
1717
|
+
mode: ctx.options.mode ?? "onSubmit",
|
|
1718
|
+
reValidateMode: ctx.options.reValidateMode
|
|
1719
|
+
}
|
|
1569
1720
|
};
|
|
1570
1721
|
}
|
|
1571
1722
|
const FormContextKey = Symbol("FormContext");
|
|
@@ -1604,13 +1755,22 @@ function useController(options) {
|
|
|
1604
1755
|
}
|
|
1605
1756
|
});
|
|
1606
1757
|
const onChange = (newValue) => {
|
|
1607
|
-
form.
|
|
1758
|
+
const isTouched = form.formState.value.touchedFields[name] === true;
|
|
1759
|
+
const hasSubmitted = form.formState.value.submitCount > 0;
|
|
1760
|
+
const mode = form.options.mode ?? "onSubmit";
|
|
1761
|
+
const reValidateMode = form.options.reValidateMode;
|
|
1762
|
+
const shouldValidate = shouldValidateOnChange(mode, isTouched, reValidateMode, hasSubmitted);
|
|
1763
|
+
form.setValue(name, newValue, { shouldValidate });
|
|
1608
1764
|
};
|
|
1609
1765
|
const onBlur = () => {
|
|
1766
|
+
const hasSubmitted = form.formState.value.submitCount > 0;
|
|
1767
|
+
const mode = form.options.mode ?? "onSubmit";
|
|
1768
|
+
const reValidateMode = form.options.reValidateMode;
|
|
1769
|
+
const shouldValidate = shouldValidateOnBlur(mode, hasSubmitted, reValidateMode);
|
|
1610
1770
|
const currentValue = form.getValues(name);
|
|
1611
1771
|
form.setValue(name, currentValue, {
|
|
1612
1772
|
shouldTouch: true,
|
|
1613
|
-
shouldValidate
|
|
1773
|
+
shouldValidate,
|
|
1614
1774
|
shouldDirty: false
|
|
1615
1775
|
});
|
|
1616
1776
|
};
|
|
@@ -1649,8 +1809,13 @@ function isFieldError(error) {
|
|
|
1649
1809
|
return typeof error === "object" && error !== null && "type" in error && "message" in error && typeof error.type === "string" && typeof error.message === "string";
|
|
1650
1810
|
}
|
|
1651
1811
|
exports.FormContextKey = FormContextKey;
|
|
1812
|
+
exports.clearPathCache = clearPathCache;
|
|
1813
|
+
exports.generateId = generateId;
|
|
1814
|
+
exports.get = get;
|
|
1652
1815
|
exports.isFieldError = isFieldError;
|
|
1653
1816
|
exports.provideForm = provideForm;
|
|
1817
|
+
exports.set = set;
|
|
1818
|
+
exports.unset = unset;
|
|
1654
1819
|
exports.useController = useController;
|
|
1655
1820
|
exports.useForm = useForm;
|
|
1656
1821
|
exports.useFormContext = useFormContext;
|