@vuehookform/core 0.4.0 → 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 +339 -166
- package/dist/vuehookform.js +336 -168
- 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,11 +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) return cached.isValid;
|
|
628
712
|
const analysis = analyzeSchemaPath(ctx.options.schema, fieldPath);
|
|
629
|
-
|
|
630
|
-
|
|
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);
|
|
631
722
|
}
|
|
632
723
|
const validatingKey = "_form";
|
|
633
724
|
setValidating(ctx, validatingKey, true);
|
|
@@ -658,47 +749,31 @@ function createValidation(ctx) {
|
|
|
658
749
|
clearAllPendingErrors
|
|
659
750
|
};
|
|
660
751
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
}
|
|
669
|
-
function markFieldTouched(touchedFields, touchedFieldCount, fieldName) {
|
|
670
|
-
if (touchedFields.value[fieldName]) return;
|
|
671
|
-
touchedFields.value = {
|
|
672
|
-
...touchedFields.value,
|
|
673
|
-
[fieldName]: true
|
|
674
|
-
};
|
|
675
|
-
touchedFieldCount.value++;
|
|
676
|
-
}
|
|
677
|
-
function clearFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
|
|
678
|
-
if (!(fieldName in dirtyFields.value)) return;
|
|
679
|
-
const newDirty = { ...dirtyFields.value };
|
|
680
|
-
delete newDirty[fieldName];
|
|
681
|
-
dirtyFields.value = newDirty;
|
|
682
|
-
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}`);
|
|
683
760
|
}
|
|
684
|
-
function
|
|
685
|
-
if (
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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;
|
|
690
770
|
}
|
|
691
|
-
function
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
for (const key of keys) if (key === fieldName || key.startsWith(prefix)) keysToDelete.push(key);
|
|
698
|
-
if (keysToDelete.length === 0) return;
|
|
699
|
-
const newErrors = { ...currentErrors };
|
|
700
|
-
for (const key of keysToDelete) delete newErrors[key];
|
|
701
|
-
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");
|
|
702
777
|
}
|
|
703
778
|
var validationRequestCounter = 0;
|
|
704
779
|
function createFieldRegistration(ctx, validate) {
|
|
@@ -739,11 +814,14 @@ function createFieldRegistration(ctx, validate) {
|
|
|
739
814
|
};
|
|
740
815
|
const onInput = async (e) => {
|
|
741
816
|
const target = e.target;
|
|
742
|
-
|
|
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;
|
|
743
821
|
set(ctx.formData, name, value);
|
|
744
|
-
|
|
822
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, value);
|
|
745
823
|
const fieldOpts = ctx.fieldOptions.get(name);
|
|
746
|
-
if (ctx.options.mode
|
|
824
|
+
if (shouldValidateOnChange(ctx.options.mode ?? "onSubmit", ctx.touchedFields.value[name] === true, ctx.options.reValidateMode, ctx.submitCount.value > 0)) {
|
|
747
825
|
const validationDebounceMs = ctx.options.validationDebounce || 0;
|
|
748
826
|
if (validationDebounceMs > 0) {
|
|
749
827
|
const existingTimer = ctx.schemaValidationTimers.get(name);
|
|
@@ -776,15 +854,18 @@ function createFieldRegistration(ctx, validate) {
|
|
|
776
854
|
const timer = setTimeout(async () => {
|
|
777
855
|
ctx.debounceTimers.delete(name);
|
|
778
856
|
await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
779
|
-
ctx.validationRequestIds.delete(name);
|
|
857
|
+
if (ctx.validationRequestIds.get(name) === requestId) ctx.validationRequestIds.delete(name);
|
|
780
858
|
}, debounceMs);
|
|
781
859
|
ctx.debounceTimers.set(name, timer);
|
|
782
|
-
} else
|
|
860
|
+
} else {
|
|
861
|
+
await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
862
|
+
if (ctx.validationRequestIds.get(name) === requestId) ctx.validationRequestIds.delete(name);
|
|
863
|
+
}
|
|
783
864
|
}
|
|
784
865
|
};
|
|
785
866
|
const onBlur = async (_e) => {
|
|
786
|
-
markFieldTouched(ctx.touchedFields,
|
|
787
|
-
if (ctx.options.mode
|
|
867
|
+
markFieldTouched(ctx.touchedFields, name);
|
|
868
|
+
if (shouldValidateOnBlur(ctx.options.mode ?? "onSubmit", ctx.submitCount.value > 0, ctx.options.reValidateMode)) {
|
|
788
869
|
await validate(name);
|
|
789
870
|
const fieldOpts = ctx.fieldOptions.get(name);
|
|
790
871
|
if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
|
|
@@ -817,15 +898,21 @@ function createFieldRegistration(ctx, validate) {
|
|
|
817
898
|
clearTimeout(schemaTimer);
|
|
818
899
|
ctx.schemaValidationTimers.delete(name);
|
|
819
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);
|
|
820
907
|
ctx.validationRequestIds.delete(name);
|
|
908
|
+
ctx.fieldRefs.delete(name);
|
|
909
|
+
ctx.fieldOptions.delete(name);
|
|
910
|
+
ctx.fieldHandlers.delete(name);
|
|
821
911
|
if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
|
|
822
912
|
unset(ctx.formData, name);
|
|
823
913
|
clearFieldErrors(ctx.errors, name);
|
|
824
|
-
clearFieldTouched(ctx.touchedFields,
|
|
825
|
-
clearFieldDirty(ctx.dirtyFields,
|
|
826
|
-
ctx.fieldRefs.delete(name);
|
|
827
|
-
ctx.fieldOptions.delete(name);
|
|
828
|
-
ctx.fieldHandlers.delete(name);
|
|
914
|
+
clearFieldTouched(ctx.touchedFields, name);
|
|
915
|
+
clearFieldDirty(ctx.dirtyFields, name);
|
|
829
916
|
}
|
|
830
917
|
}
|
|
831
918
|
};
|
|
@@ -846,7 +933,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
846
933
|
get: () => get(ctx.formData, name),
|
|
847
934
|
set: (val) => {
|
|
848
935
|
set(ctx.formData, name, val);
|
|
849
|
-
|
|
936
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, val);
|
|
850
937
|
}
|
|
851
938
|
}) }
|
|
852
939
|
};
|
|
@@ -855,8 +942,8 @@ function createFieldRegistration(ctx, validate) {
|
|
|
855
942
|
const opts = options || {};
|
|
856
943
|
if (!opts.keepValue) unset(ctx.formData, name);
|
|
857
944
|
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
858
|
-
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields,
|
|
859
|
-
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields,
|
|
945
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
|
|
946
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
|
|
860
947
|
ctx.fieldRefs.delete(name);
|
|
861
948
|
ctx.fieldOptions.delete(name);
|
|
862
949
|
ctx.fieldHandlers.delete(name);
|
|
@@ -871,6 +958,8 @@ function createFieldRegistration(ctx, validate) {
|
|
|
871
958
|
ctx.schemaValidationTimers.delete(name);
|
|
872
959
|
}
|
|
873
960
|
ctx.validationRequestIds.delete(name);
|
|
961
|
+
ctx.validationCache.delete(`${name}:partial`);
|
|
962
|
+
ctx.validationCache.delete(`${name}:full`);
|
|
874
963
|
}
|
|
875
964
|
return {
|
|
876
965
|
register,
|
|
@@ -960,10 +1049,32 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
960
1049
|
const normalizeToArray = (value) => {
|
|
961
1050
|
return Array.isArray(value) ? value : [value];
|
|
962
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
|
+
};
|
|
963
1073
|
const append = (value, focusOptions) => {
|
|
1074
|
+
clearValidationCache();
|
|
964
1075
|
const values = normalizeToArray(value);
|
|
965
1076
|
if (values.length === 0) return true;
|
|
966
|
-
const currentValues =
|
|
1077
|
+
const currentValues = ensureSync();
|
|
967
1078
|
const insertIndex = currentValues.length;
|
|
968
1079
|
const rules = fa.rules;
|
|
969
1080
|
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
@@ -978,15 +1089,16 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
978
1089
|
const newItems = values.map(() => createItem(generateId()));
|
|
979
1090
|
fa.items.value = [...fa.items.value, ...newItems];
|
|
980
1091
|
appendToCache(insertIndex);
|
|
981
|
-
|
|
982
|
-
|
|
1092
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1093
|
+
validateIfNeeded();
|
|
983
1094
|
handleFocus(insertIndex, values.length, focusOptions);
|
|
984
1095
|
return true;
|
|
985
1096
|
};
|
|
986
1097
|
const prepend = (value, focusOptions) => {
|
|
1098
|
+
clearValidationCache();
|
|
987
1099
|
const values = normalizeToArray(value);
|
|
988
1100
|
if (values.length === 0) return true;
|
|
989
|
-
const currentValues =
|
|
1101
|
+
const currentValues = ensureSync();
|
|
990
1102
|
const rules = fa.rules;
|
|
991
1103
|
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
992
1104
|
if (__DEV__) warnArrayOperationRejected("prepend", name, "maxLength", {
|
|
@@ -1000,13 +1112,14 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1000
1112
|
const newItems = values.map(() => createItem(generateId()));
|
|
1001
1113
|
fa.items.value = [...newItems, ...fa.items.value];
|
|
1002
1114
|
updateCacheAfterInsert(0, values.length);
|
|
1003
|
-
|
|
1004
|
-
|
|
1115
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1116
|
+
validateIfNeeded();
|
|
1005
1117
|
handleFocus(0, values.length, focusOptions);
|
|
1006
1118
|
return true;
|
|
1007
1119
|
};
|
|
1008
1120
|
const update = (index, value) => {
|
|
1009
|
-
|
|
1121
|
+
clearValidationCache();
|
|
1122
|
+
const currentValues = ensureSync();
|
|
1010
1123
|
if (index < 0 || index >= currentValues.length) {
|
|
1011
1124
|
if (__DEV__) warnArrayIndexOutOfBounds("update", name, index, currentValues.length);
|
|
1012
1125
|
return false;
|
|
@@ -1014,12 +1127,13 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1014
1127
|
const newValues = [...currentValues];
|
|
1015
1128
|
newValues[index] = value;
|
|
1016
1129
|
set(ctx.formData, name, newValues);
|
|
1017
|
-
|
|
1018
|
-
|
|
1130
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1131
|
+
validateIfNeeded();
|
|
1019
1132
|
return true;
|
|
1020
1133
|
};
|
|
1021
1134
|
const removeAt = (index) => {
|
|
1022
|
-
|
|
1135
|
+
clearValidationCache();
|
|
1136
|
+
const currentValues = ensureSync();
|
|
1023
1137
|
if (index < 0 || index >= currentValues.length) {
|
|
1024
1138
|
if (__DEV__) warnArrayIndexOutOfBounds("remove", name, index, currentValues.length);
|
|
1025
1139
|
return false;
|
|
@@ -1037,14 +1151,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1037
1151
|
const keyToRemove = fa.items.value[index]?.key;
|
|
1038
1152
|
fa.items.value = fa.items.value.filter((item) => item.key !== keyToRemove);
|
|
1039
1153
|
if (keyToRemove) updateCacheAfterRemove(keyToRemove, index);
|
|
1040
|
-
|
|
1041
|
-
|
|
1154
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1155
|
+
validateIfNeeded();
|
|
1042
1156
|
return true;
|
|
1043
1157
|
};
|
|
1044
1158
|
const insert = (index, value, focusOptions) => {
|
|
1159
|
+
clearValidationCache();
|
|
1045
1160
|
const values = normalizeToArray(value);
|
|
1046
1161
|
if (values.length === 0) return true;
|
|
1047
|
-
const currentValues =
|
|
1162
|
+
const currentValues = ensureSync();
|
|
1048
1163
|
const rules = fa.rules;
|
|
1049
1164
|
if (rules?.maxLength && currentValues.length + values.length > rules.maxLength.value) {
|
|
1050
1165
|
if (__DEV__) warnArrayOperationRejected("insert", name, "maxLength", {
|
|
@@ -1053,27 +1168,31 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1053
1168
|
});
|
|
1054
1169
|
return false;
|
|
1055
1170
|
}
|
|
1056
|
-
|
|
1171
|
+
if (index < 0 || index > currentValues.length) {
|
|
1172
|
+
if (__DEV__) warnArrayIndexOutOfBounds("insert", name, index, currentValues.length);
|
|
1173
|
+
return false;
|
|
1174
|
+
}
|
|
1057
1175
|
const newValues = [
|
|
1058
|
-
...currentValues.slice(0,
|
|
1176
|
+
...currentValues.slice(0, index),
|
|
1059
1177
|
...values,
|
|
1060
|
-
...currentValues.slice(
|
|
1178
|
+
...currentValues.slice(index)
|
|
1061
1179
|
];
|
|
1062
1180
|
set(ctx.formData, name, newValues);
|
|
1063
1181
|
const newItems = values.map(() => createItem(generateId()));
|
|
1064
1182
|
fa.items.value = [
|
|
1065
|
-
...fa.items.value.slice(0,
|
|
1183
|
+
...fa.items.value.slice(0, index),
|
|
1066
1184
|
...newItems,
|
|
1067
|
-
...fa.items.value.slice(
|
|
1185
|
+
...fa.items.value.slice(index)
|
|
1068
1186
|
];
|
|
1069
|
-
updateCacheAfterInsert(
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
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);
|
|
1073
1191
|
return true;
|
|
1074
1192
|
};
|
|
1075
1193
|
const swap = (indexA, indexB) => {
|
|
1076
|
-
|
|
1194
|
+
clearValidationCache();
|
|
1195
|
+
const currentValues = ensureSync();
|
|
1077
1196
|
if (indexA < 0 || indexB < 0 || indexA >= currentValues.length || indexB >= currentValues.length) {
|
|
1078
1197
|
if (__DEV__) warnArrayIndexOutOfBounds("swap", name, indexA < 0 || indexA >= currentValues.length ? indexA : indexB, currentValues.length);
|
|
1079
1198
|
return false;
|
|
@@ -1090,51 +1209,52 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1090
1209
|
fa.items.value = newItems;
|
|
1091
1210
|
swapInCache(indexA, indexB);
|
|
1092
1211
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1212
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1213
|
+
validateIfNeeded();
|
|
1095
1214
|
return true;
|
|
1096
1215
|
};
|
|
1097
1216
|
const move = (from, to) => {
|
|
1098
|
-
|
|
1099
|
-
|
|
1217
|
+
clearValidationCache();
|
|
1218
|
+
const currentValues = ensureSync();
|
|
1219
|
+
if (from < 0 || from >= currentValues.length || to < 0 || to >= currentValues.length) {
|
|
1100
1220
|
if (__DEV__) warnArrayIndexOutOfBounds("move", name, from < 0 || from >= currentValues.length ? from : to, currentValues.length);
|
|
1101
1221
|
return false;
|
|
1102
1222
|
}
|
|
1103
1223
|
const newValues = [...currentValues];
|
|
1104
1224
|
const [removed] = newValues.splice(from, 1);
|
|
1105
1225
|
if (removed !== void 0) {
|
|
1106
|
-
|
|
1107
|
-
newValues.splice(clampedTo, 0, removed);
|
|
1226
|
+
newValues.splice(to, 0, removed);
|
|
1108
1227
|
set(ctx.formData, name, newValues);
|
|
1109
1228
|
}
|
|
1110
1229
|
const newItems = [...fa.items.value];
|
|
1111
1230
|
const [removedItem] = newItems.splice(from, 1);
|
|
1112
1231
|
if (removedItem) {
|
|
1113
|
-
|
|
1114
|
-
newItems.splice(clampedTo, 0, removedItem);
|
|
1232
|
+
newItems.splice(to, 0, removedItem);
|
|
1115
1233
|
fa.items.value = newItems;
|
|
1116
|
-
const minIdx = Math.min(from,
|
|
1117
|
-
const maxIdx = Math.max(from,
|
|
1234
|
+
const minIdx = Math.min(from, to);
|
|
1235
|
+
const maxIdx = Math.max(from, to);
|
|
1118
1236
|
const items = fa.items.value;
|
|
1119
1237
|
for (let i = minIdx; i <= maxIdx; i++) {
|
|
1120
1238
|
const item = items[i];
|
|
1121
1239
|
if (item) indexCache.set(item.key, i);
|
|
1122
1240
|
}
|
|
1123
1241
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1242
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1243
|
+
validateIfNeeded();
|
|
1126
1244
|
return true;
|
|
1127
1245
|
};
|
|
1128
1246
|
const replace = (newValues) => {
|
|
1247
|
+
clearValidationCache();
|
|
1129
1248
|
if (!Array.isArray(newValues)) return false;
|
|
1130
1249
|
set(ctx.formData, name, newValues);
|
|
1131
1250
|
fa.items.value = newValues.map(() => createItem(generateId()));
|
|
1132
1251
|
rebuildIndexCache();
|
|
1133
|
-
|
|
1134
|
-
|
|
1252
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1253
|
+
validateIfNeeded();
|
|
1135
1254
|
return true;
|
|
1136
1255
|
};
|
|
1137
1256
|
const removeAll = () => {
|
|
1257
|
+
clearValidationCache();
|
|
1138
1258
|
const rules = fa.rules;
|
|
1139
1259
|
if (rules?.minLength && rules.minLength.value > 0) {
|
|
1140
1260
|
if (__DEV__) warnArrayOperationRejected("removeAll", name, "minLength", {
|
|
@@ -1146,12 +1266,13 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1146
1266
|
set(ctx.formData, name, []);
|
|
1147
1267
|
fa.items.value = [];
|
|
1148
1268
|
indexCache.clear();
|
|
1149
|
-
|
|
1150
|
-
|
|
1269
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1270
|
+
validateIfNeeded();
|
|
1151
1271
|
return true;
|
|
1152
1272
|
};
|
|
1153
1273
|
const removeMany = (indices) => {
|
|
1154
|
-
|
|
1274
|
+
clearValidationCache();
|
|
1275
|
+
const currentValues = ensureSync();
|
|
1155
1276
|
const validIndices = indices.filter((i) => i >= 0 && i < currentValues.length);
|
|
1156
1277
|
if (validIndices.length === 0) return true;
|
|
1157
1278
|
const rules = fa.rules;
|
|
@@ -1173,8 +1294,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
1173
1294
|
fa.items.value.forEach((item, idx) => {
|
|
1174
1295
|
indexCache.set(item.key, idx);
|
|
1175
1296
|
});
|
|
1176
|
-
|
|
1177
|
-
|
|
1297
|
+
updateFieldDirtyState(ctx.dirtyFields, ctx.defaultValues, ctx.defaultValueHashes, name, get(ctx.formData, name));
|
|
1298
|
+
validateIfNeeded();
|
|
1178
1299
|
return true;
|
|
1179
1300
|
};
|
|
1180
1301
|
return {
|
|
@@ -1197,7 +1318,13 @@ function syncUncontrolledInputs(fieldRefs, fieldOptions, formData) {
|
|
|
1197
1318
|
for (const [name, fieldRef] of Array.from(fieldRefs.entries())) {
|
|
1198
1319
|
const el = fieldRef.value;
|
|
1199
1320
|
if (el) {
|
|
1200
|
-
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
|
+
}
|
|
1201
1328
|
}
|
|
1202
1329
|
}
|
|
1203
1330
|
}
|
|
@@ -1207,6 +1334,10 @@ function updateDomElement(el, value) {
|
|
|
1207
1334
|
}
|
|
1208
1335
|
function useForm(options) {
|
|
1209
1336
|
const ctx = createFormContext(options);
|
|
1337
|
+
let isSubmissionLocked = false;
|
|
1338
|
+
(0, vue.onUnmounted)(() => {
|
|
1339
|
+
ctx.cleanup();
|
|
1340
|
+
});
|
|
1210
1341
|
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
1211
1342
|
const { register, unregister } = createFieldRegistration(ctx, validate);
|
|
1212
1343
|
function setFocus(name, focusOptions) {
|
|
@@ -1249,10 +1380,10 @@ function useForm(options) {
|
|
|
1249
1380
|
};
|
|
1250
1381
|
return cachedMergedErrors;
|
|
1251
1382
|
}
|
|
1252
|
-
const isDirtyComputed = (0, vue.computed)(() => ctx.
|
|
1383
|
+
const isDirtyComputed = (0, vue.computed)(() => Object.keys(ctx.dirtyFields.value).length > 0);
|
|
1253
1384
|
const errorsComputed = (0, vue.computed)(() => getMergedErrors());
|
|
1254
1385
|
const isValidComputed = (0, vue.computed)(() => {
|
|
1255
|
-
if (!(ctx.submitCount.value > 0 || ctx.
|
|
1386
|
+
if (!(ctx.submitCount.value > 0 || Object.keys(ctx.touchedFields.value).length > 0)) return false;
|
|
1256
1387
|
return Object.keys(errorsComputed.value).length === 0;
|
|
1257
1388
|
});
|
|
1258
1389
|
const isReadyComputed = (0, vue.computed)(() => !ctx.isLoading.value);
|
|
@@ -1310,7 +1441,8 @@ function useForm(options) {
|
|
|
1310
1441
|
return async (e) => {
|
|
1311
1442
|
e.preventDefault();
|
|
1312
1443
|
if (ctx.isDisabled.value) return;
|
|
1313
|
-
if (
|
|
1444
|
+
if (isSubmissionLocked) return;
|
|
1445
|
+
isSubmissionLocked = true;
|
|
1314
1446
|
ctx.isSubmitting.value = true;
|
|
1315
1447
|
ctx.submitCount.value++;
|
|
1316
1448
|
ctx.isSubmitSuccessful.value = false;
|
|
@@ -1328,6 +1460,7 @@ function useForm(options) {
|
|
|
1328
1460
|
}
|
|
1329
1461
|
} finally {
|
|
1330
1462
|
ctx.isSubmitting.value = false;
|
|
1463
|
+
isSubmissionLocked = false;
|
|
1331
1464
|
}
|
|
1332
1465
|
};
|
|
1333
1466
|
}
|
|
@@ -1341,8 +1474,9 @@ function useForm(options) {
|
|
|
1341
1474
|
}
|
|
1342
1475
|
}
|
|
1343
1476
|
set(ctx.formData, name, value);
|
|
1344
|
-
if (setValueOptions?.shouldDirty
|
|
1345
|
-
|
|
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);
|
|
1346
1480
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
1347
1481
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
1348
1482
|
if (fieldRef?.value) updateDomElement(fieldRef.value, value);
|
|
@@ -1351,25 +1485,28 @@ function useForm(options) {
|
|
|
1351
1485
|
}
|
|
1352
1486
|
function reset(values, resetOptions) {
|
|
1353
1487
|
const opts = resetOptions || {};
|
|
1488
|
+
ctx.validationCache.clear();
|
|
1354
1489
|
ctx.resetGeneration.value++;
|
|
1355
1490
|
clearAllPendingErrors();
|
|
1356
1491
|
ctx.validatingFields.value = /* @__PURE__ */ new Set();
|
|
1357
|
-
ctx.validationCache.clear();
|
|
1358
1492
|
for (const timer of ctx.schemaValidationTimers.values()) clearTimeout(timer);
|
|
1359
1493
|
ctx.schemaValidationTimers.clear();
|
|
1360
|
-
|
|
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
|
+
}
|
|
1361
1501
|
Object.keys(ctx.formData).forEach((key) => delete ctx.formData[key]);
|
|
1362
1502
|
const newValues = deepClone(values || ctx.defaultValues);
|
|
1363
1503
|
Object.assign(ctx.formData, newValues);
|
|
1364
|
-
if (!opts.keepErrors)
|
|
1365
|
-
|
|
1366
|
-
ctx.
|
|
1367
|
-
ctx.touchedFieldCount.value = 0;
|
|
1368
|
-
}
|
|
1369
|
-
if (!opts.keepDirty) {
|
|
1370
|
-
ctx.dirtyFields.value = {};
|
|
1371
|
-
ctx.dirtyFieldCount.value = 0;
|
|
1504
|
+
if (!opts.keepErrors) {
|
|
1505
|
+
ctx.errors.value = {};
|
|
1506
|
+
ctx.persistentErrorFields.clear();
|
|
1372
1507
|
}
|
|
1508
|
+
if (!opts.keepTouched) ctx.touchedFields.value = {};
|
|
1509
|
+
if (!opts.keepDirty) ctx.dirtyFields.value = {};
|
|
1373
1510
|
if (!opts.keepSubmitCount) ctx.submitCount.value = 0;
|
|
1374
1511
|
if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
|
|
1375
1512
|
if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
|
|
@@ -1394,6 +1531,8 @@ function useForm(options) {
|
|
|
1394
1531
|
}
|
|
1395
1532
|
const opts = resetFieldOptions || {};
|
|
1396
1533
|
ctx.resetGeneration.value++;
|
|
1534
|
+
ctx.validationCache.delete(`${name}:partial`);
|
|
1535
|
+
ctx.validationCache.delete(`${name}:full`);
|
|
1397
1536
|
const errorTimer = ctx.errorDelayTimers.get(name);
|
|
1398
1537
|
if (errorTimer) {
|
|
1399
1538
|
clearTimeout(errorTimer);
|
|
@@ -1402,12 +1541,15 @@ function useForm(options) {
|
|
|
1402
1541
|
ctx.pendingErrors.delete(name);
|
|
1403
1542
|
let defaultValue = opts.defaultValue;
|
|
1404
1543
|
if (defaultValue === void 0) defaultValue = get(ctx.defaultValues, name);
|
|
1405
|
-
else
|
|
1544
|
+
else {
|
|
1545
|
+
set(ctx.defaultValues, name, defaultValue);
|
|
1546
|
+
ctx.defaultValueHashes.set(name, hashValue(defaultValue));
|
|
1547
|
+
}
|
|
1406
1548
|
const clonedValue = defaultValue !== void 0 ? deepClone(defaultValue) : void 0;
|
|
1407
1549
|
set(ctx.formData, name, clonedValue);
|
|
1408
1550
|
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
1409
|
-
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields,
|
|
1410
|
-
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields,
|
|
1551
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
|
|
1552
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
|
|
1411
1553
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
1412
1554
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
1413
1555
|
if (fieldRef?.value) updateDomElement(fieldRef.value, clonedValue ?? (fieldRef.value.type === "checkbox" ? false : ""));
|
|
@@ -1450,10 +1592,16 @@ function useForm(options) {
|
|
|
1450
1592
|
}
|
|
1451
1593
|
if (name === void 0) {
|
|
1452
1594
|
ctx.errors.value = {};
|
|
1595
|
+
ctx.externalErrors.value = {};
|
|
1596
|
+
ctx.persistentErrorFields.clear();
|
|
1453
1597
|
return;
|
|
1454
1598
|
}
|
|
1455
1599
|
const fieldsToClean = Array.isArray(name) ? name : [name];
|
|
1456
|
-
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
|
+
}
|
|
1457
1605
|
}
|
|
1458
1606
|
function setError(name, error) {
|
|
1459
1607
|
const newErrors = { ...ctx.errors.value };
|
|
@@ -1462,6 +1610,7 @@ function useForm(options) {
|
|
|
1462
1610
|
message: error.message
|
|
1463
1611
|
} : error.message);
|
|
1464
1612
|
ctx.errors.value = newErrors;
|
|
1613
|
+
if (error.persistent) ctx.persistentErrorFields.add(name);
|
|
1465
1614
|
}
|
|
1466
1615
|
function setErrors(errors, options$1) {
|
|
1467
1616
|
const newErrors = options$1?.shouldReplace ? {} : { ...ctx.errors.value };
|
|
@@ -1523,7 +1672,7 @@ function useForm(options) {
|
|
|
1523
1672
|
error
|
|
1524
1673
|
};
|
|
1525
1674
|
}
|
|
1526
|
-
async function trigger(name) {
|
|
1675
|
+
async function trigger(name, options$1) {
|
|
1527
1676
|
if (__DEV__ && name) {
|
|
1528
1677
|
const names = Array.isArray(name) ? name : [name];
|
|
1529
1678
|
for (const n of names) {
|
|
@@ -1535,6 +1684,7 @@ function useForm(options) {
|
|
|
1535
1684
|
}
|
|
1536
1685
|
}
|
|
1537
1686
|
}
|
|
1687
|
+
if (options$1?.markAsSubmitted) ctx.submitCount.value++;
|
|
1538
1688
|
if (name === void 0) return await validate();
|
|
1539
1689
|
if (Array.isArray(name)) {
|
|
1540
1690
|
let allValid = true;
|
|
@@ -1562,7 +1712,11 @@ function useForm(options) {
|
|
|
1562
1712
|
getValues,
|
|
1563
1713
|
getFieldState,
|
|
1564
1714
|
trigger,
|
|
1565
|
-
setFocus
|
|
1715
|
+
setFocus,
|
|
1716
|
+
options: {
|
|
1717
|
+
mode: ctx.options.mode ?? "onSubmit",
|
|
1718
|
+
reValidateMode: ctx.options.reValidateMode
|
|
1719
|
+
}
|
|
1566
1720
|
};
|
|
1567
1721
|
}
|
|
1568
1722
|
const FormContextKey = Symbol("FormContext");
|
|
@@ -1601,10 +1755,24 @@ function useController(options) {
|
|
|
1601
1755
|
}
|
|
1602
1756
|
});
|
|
1603
1757
|
const onChange = (newValue) => {
|
|
1604
|
-
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 });
|
|
1605
1764
|
};
|
|
1606
1765
|
const onBlur = () => {
|
|
1607
|
-
form.
|
|
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);
|
|
1770
|
+
const currentValue = form.getValues(name);
|
|
1771
|
+
form.setValue(name, currentValue, {
|
|
1772
|
+
shouldTouch: true,
|
|
1773
|
+
shouldValidate,
|
|
1774
|
+
shouldDirty: false
|
|
1775
|
+
});
|
|
1608
1776
|
};
|
|
1609
1777
|
const refCallback = (el) => {
|
|
1610
1778
|
elementRef.value = el;
|
|
@@ -1641,8 +1809,13 @@ function isFieldError(error) {
|
|
|
1641
1809
|
return typeof error === "object" && error !== null && "type" in error && "message" in error && typeof error.type === "string" && typeof error.message === "string";
|
|
1642
1810
|
}
|
|
1643
1811
|
exports.FormContextKey = FormContextKey;
|
|
1812
|
+
exports.clearPathCache = clearPathCache;
|
|
1813
|
+
exports.generateId = generateId;
|
|
1814
|
+
exports.get = get;
|
|
1644
1815
|
exports.isFieldError = isFieldError;
|
|
1645
1816
|
exports.provideForm = provideForm;
|
|
1817
|
+
exports.set = set;
|
|
1818
|
+
exports.unset = unset;
|
|
1646
1819
|
exports.useController = useController;
|
|
1647
1820
|
exports.useForm = useForm;
|
|
1648
1821
|
exports.useFormContext = useFormContext;
|