@vuehookform/core 0.3.3 → 0.4.1
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/dist/core/fieldState.d.ts +21 -10
- package/dist/core/formContext.d.ts +8 -1
- package/dist/types.d.ts +18 -2
- package/dist/utils/clone.d.ts +19 -0
- package/dist/utils/hash.d.ts +6 -0
- package/dist/utils/paths.d.ts +5 -0
- package/dist/utils/schemaExtract.d.ts +42 -0
- package/dist/vuehookform.cjs +509 -197
- package/dist/vuehookform.js +509 -197
- package/package.json +1 -1
package/dist/vuehookform.js
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { computed, inject, nextTick, provide, reactive, ref, shallowRef, toValue, watch } from "vue";
|
|
2
|
+
var pathCache = /* @__PURE__ */ new Map();
|
|
3
|
+
var PATH_CACHE_MAX_SIZE = 256;
|
|
4
|
+
function getPathSegments(path) {
|
|
5
|
+
let segments = pathCache.get(path);
|
|
6
|
+
if (segments) return segments;
|
|
7
|
+
segments = path.split(".");
|
|
8
|
+
if (pathCache.size >= PATH_CACHE_MAX_SIZE) {
|
|
9
|
+
const firstKey = pathCache.keys().next().value;
|
|
10
|
+
if (firstKey !== void 0) pathCache.delete(firstKey);
|
|
11
|
+
}
|
|
12
|
+
pathCache.set(path, segments);
|
|
13
|
+
return segments;
|
|
14
|
+
}
|
|
2
15
|
function get(obj, path) {
|
|
3
16
|
if (!path || obj === null || obj === void 0) return obj;
|
|
4
|
-
const keys = path
|
|
17
|
+
const keys = getPathSegments(path);
|
|
5
18
|
let result = obj;
|
|
6
19
|
for (const key of keys) {
|
|
7
20
|
if (result === null || result === void 0) return;
|
|
@@ -11,7 +24,7 @@ function get(obj, path) {
|
|
|
11
24
|
}
|
|
12
25
|
function set(obj, path, value) {
|
|
13
26
|
if (!path) return;
|
|
14
|
-
const keys = path.
|
|
27
|
+
const keys = getPathSegments(path).slice();
|
|
15
28
|
const UNSAFE_KEYS = [
|
|
16
29
|
"__proto__",
|
|
17
30
|
"constructor",
|
|
@@ -36,7 +49,7 @@ function set(obj, path, value) {
|
|
|
36
49
|
}
|
|
37
50
|
function unset(obj, path) {
|
|
38
51
|
if (!path) return;
|
|
39
|
-
const keys = path.
|
|
52
|
+
const keys = getPathSegments(path).slice();
|
|
40
53
|
const lastKey = keys.pop();
|
|
41
54
|
let current = obj;
|
|
42
55
|
for (const key of keys) {
|
|
@@ -52,6 +65,15 @@ function generateId() {
|
|
|
52
65
|
const random = Math.random().toString(36).substring(2, 11);
|
|
53
66
|
return `field_${Date.now()}_${idCounter++}_${random}`;
|
|
54
67
|
}
|
|
68
|
+
function deepClone(obj) {
|
|
69
|
+
if (obj === null || obj === void 0) return obj;
|
|
70
|
+
if (typeof obj !== "object") return obj;
|
|
71
|
+
if (obj instanceof Date) return new Date(obj.getTime());
|
|
72
|
+
if (Array.isArray(obj)) return obj.map((item) => deepClone(item));
|
|
73
|
+
const cloned = {};
|
|
74
|
+
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) cloned[key] = deepClone(obj[key]);
|
|
75
|
+
return cloned;
|
|
76
|
+
}
|
|
55
77
|
const __DEV__ = globalThis.process?.env?.NODE_ENV !== "production";
|
|
56
78
|
var warnedMessages = /* @__PURE__ */ new Set();
|
|
57
79
|
function warnOnce(message, key) {
|
|
@@ -80,7 +102,7 @@ function traverseSchemaPath(schema, path) {
|
|
|
80
102
|
const segment = segments[i];
|
|
81
103
|
if (!segment) continue;
|
|
82
104
|
currentSchema = unwrapSchema(currentSchema);
|
|
83
|
-
if (isZodObject(currentSchema)) {
|
|
105
|
+
if (isZodObject$1(currentSchema)) {
|
|
84
106
|
const shape = currentSchema.shape;
|
|
85
107
|
if (segment in shape) {
|
|
86
108
|
const nextSchema = shape[segment];
|
|
@@ -95,7 +117,7 @@ function traverseSchemaPath(schema, path) {
|
|
|
95
117
|
segmentIndex: i
|
|
96
118
|
};
|
|
97
119
|
}
|
|
98
|
-
if (isZodArray(currentSchema) && /^\d+$/.test(segment)) {
|
|
120
|
+
if (isZodArray$1(currentSchema) && /^\d+$/.test(segment)) {
|
|
99
121
|
currentSchema = currentSchema.element;
|
|
100
122
|
continue;
|
|
101
123
|
}
|
|
@@ -125,7 +147,7 @@ function isArrayFieldInSchema(schema, path) {
|
|
|
125
147
|
try {
|
|
126
148
|
const result = traverseSchemaPath(schema, path);
|
|
127
149
|
if ("error" in result) return null;
|
|
128
|
-
return isZodArray(unwrapSchema(result.schema));
|
|
150
|
+
return isZodArray$1(unwrapSchema(result.schema));
|
|
129
151
|
} catch {
|
|
130
152
|
return null;
|
|
131
153
|
}
|
|
@@ -178,22 +200,22 @@ function warnArrayIndexOutOfBounds(operation, path, index, length) {
|
|
|
178
200
|
if (!__DEV__) return;
|
|
179
201
|
warn(`${operation}() on "${path}": Index ${index} is out of bounds (array length: ${length}). Operation was silently ignored.`);
|
|
180
202
|
}
|
|
181
|
-
function getDefProp(schema, prop) {
|
|
203
|
+
function getDefProp$1(schema, prop) {
|
|
182
204
|
return schema.def[prop];
|
|
183
205
|
}
|
|
184
206
|
function getTypeName(schema) {
|
|
185
|
-
return getDefProp(schema, "typeName");
|
|
207
|
+
return getDefProp$1(schema, "typeName");
|
|
186
208
|
}
|
|
187
|
-
function isZodObject(schema) {
|
|
209
|
+
function isZodObject$1(schema) {
|
|
188
210
|
return getTypeName(schema) === "ZodObject";
|
|
189
211
|
}
|
|
190
|
-
function isZodArray(schema) {
|
|
212
|
+
function isZodArray$1(schema) {
|
|
191
213
|
return getTypeName(schema) === "ZodArray";
|
|
192
214
|
}
|
|
193
215
|
function unwrapSchema(schema) {
|
|
194
216
|
const typeName = getTypeName(schema);
|
|
195
|
-
const innerType = getDefProp(schema, "innerType");
|
|
196
|
-
const schemaType = getDefProp(schema, "schema");
|
|
217
|
+
const innerType = getDefProp$1(schema, "innerType");
|
|
218
|
+
const schemaType = getDefProp$1(schema, "schema");
|
|
197
219
|
if ((typeName === "ZodOptional" || typeName === "ZodNullable" || typeName === "ZodDefault") && innerType) return unwrapSchema(innerType);
|
|
198
220
|
if (typeName === "ZodEffects" && schemaType) return unwrapSchema(schemaType);
|
|
199
221
|
return schema;
|
|
@@ -226,7 +248,7 @@ function createFormContext(options) {
|
|
|
226
248
|
const submitCount = ref(0);
|
|
227
249
|
const defaultValuesError = ref(null);
|
|
228
250
|
const isSubmitSuccessful = ref(false);
|
|
229
|
-
const validatingFields = shallowRef(
|
|
251
|
+
const validatingFields = shallowRef(/* @__PURE__ */ new Set());
|
|
230
252
|
const externalErrors = shallowRef({});
|
|
231
253
|
const errorDelayTimers = /* @__PURE__ */ new Map();
|
|
232
254
|
const pendingErrors = /* @__PURE__ */ new Map();
|
|
@@ -237,6 +259,10 @@ function createFormContext(options) {
|
|
|
237
259
|
const debounceTimers = /* @__PURE__ */ new Map();
|
|
238
260
|
const validationRequestIds = /* @__PURE__ */ new Map();
|
|
239
261
|
const resetGeneration = ref(0);
|
|
262
|
+
const dirtyFieldCount = ref(0);
|
|
263
|
+
const touchedFieldCount = ref(0);
|
|
264
|
+
const validationCache = /* @__PURE__ */ new Map();
|
|
265
|
+
const schemaValidationTimers = /* @__PURE__ */ new Map();
|
|
240
266
|
const isDisabled = ref(false);
|
|
241
267
|
if (options.disabled !== void 0) {
|
|
242
268
|
isDisabled.value = toValue(options.disabled) ?? false;
|
|
@@ -294,24 +320,136 @@ function createFormContext(options) {
|
|
|
294
320
|
validationRequestIds,
|
|
295
321
|
resetGeneration,
|
|
296
322
|
isDisabled,
|
|
323
|
+
dirtyFieldCount,
|
|
324
|
+
touchedFieldCount,
|
|
325
|
+
validationCache,
|
|
326
|
+
schemaValidationTimers,
|
|
297
327
|
options
|
|
298
328
|
};
|
|
299
329
|
}
|
|
330
|
+
var uniqueIdCounter = 0;
|
|
331
|
+
function hashValue(value) {
|
|
332
|
+
if (value === null) return "null";
|
|
333
|
+
if (value === void 0) return "undefined";
|
|
334
|
+
const type = typeof value;
|
|
335
|
+
if (type === "string") return `s:${value}`;
|
|
336
|
+
if (type === "number") return `n:${value}`;
|
|
337
|
+
if (type === "boolean") return `b:${value}`;
|
|
338
|
+
if (type === "object") try {
|
|
339
|
+
return `o:${JSON.stringify(value)}`;
|
|
340
|
+
} catch {
|
|
341
|
+
return `o:_${++uniqueIdCounter}`;
|
|
342
|
+
}
|
|
343
|
+
return `x:_${++uniqueIdCounter}`;
|
|
344
|
+
}
|
|
345
|
+
function getDefType(schema) {
|
|
346
|
+
return schema._def?.type;
|
|
347
|
+
}
|
|
348
|
+
function getDefProp(schema, prop) {
|
|
349
|
+
return schema._def?.[prop];
|
|
350
|
+
}
|
|
351
|
+
function isZodObject(schema) {
|
|
352
|
+
return getDefType(schema) === "object";
|
|
353
|
+
}
|
|
354
|
+
function isZodArray(schema) {
|
|
355
|
+
return getDefType(schema) === "array";
|
|
356
|
+
}
|
|
357
|
+
function hasChecks(schema) {
|
|
358
|
+
const checks = getDefProp(schema, "checks");
|
|
359
|
+
return Array.isArray(checks) && checks.length > 0;
|
|
360
|
+
}
|
|
361
|
+
function unwrapNonEffects(schema) {
|
|
362
|
+
const type = getDefType(schema);
|
|
363
|
+
const innerType = getDefProp(schema, "innerType");
|
|
364
|
+
if ((type === "optional" || type === "nullable" || type === "default") && innerType) return unwrapNonEffects(innerType);
|
|
365
|
+
return schema;
|
|
366
|
+
}
|
|
367
|
+
var analysisCache = /* @__PURE__ */ new WeakMap();
|
|
368
|
+
function hasRootEffects(schema) {
|
|
369
|
+
return hasChecks(schema);
|
|
370
|
+
}
|
|
371
|
+
function extractSubSchema(schema, path) {
|
|
372
|
+
const segments = path.split(".");
|
|
373
|
+
let currentSchema = schema;
|
|
374
|
+
let hasEffects = false;
|
|
375
|
+
for (const segment of segments) {
|
|
376
|
+
if (!segment) continue;
|
|
377
|
+
if (hasChecks(currentSchema)) hasEffects = true;
|
|
378
|
+
const unwrapped = unwrapNonEffects(currentSchema);
|
|
379
|
+
if (hasChecks(unwrapped)) hasEffects = true;
|
|
380
|
+
if (isZodObject(unwrapped)) {
|
|
381
|
+
const shape = getDefProp(unwrapped, "shape");
|
|
382
|
+
if (!shape || !(segment in shape)) return null;
|
|
383
|
+
currentSchema = shape[segment];
|
|
384
|
+
} else if (isZodArray(unwrapped) && /^\d+$/.test(segment)) {
|
|
385
|
+
const element = getDefProp(unwrapped, "element");
|
|
386
|
+
if (!element) return null;
|
|
387
|
+
currentSchema = element;
|
|
388
|
+
} else return null;
|
|
389
|
+
}
|
|
390
|
+
const finalUnwrapped = unwrapNonEffects(currentSchema);
|
|
391
|
+
const finalChecks = getDefProp(finalUnwrapped, "checks");
|
|
392
|
+
if (finalChecks) {
|
|
393
|
+
for (const check of finalChecks) if (check && typeof check === "object" && "type" in check && check.type === "custom") {
|
|
394
|
+
hasEffects = true;
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
schema: finalUnwrapped,
|
|
400
|
+
hasEffects
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
function analyzeSchemaPath(schema, path) {
|
|
404
|
+
let cache = analysisCache.get(schema);
|
|
405
|
+
if (!cache) {
|
|
406
|
+
cache = /* @__PURE__ */ new Map();
|
|
407
|
+
analysisCache.set(schema, cache);
|
|
408
|
+
}
|
|
409
|
+
const cached = cache.get(path);
|
|
410
|
+
if (cached) return cached;
|
|
411
|
+
if (hasRootEffects(schema)) {
|
|
412
|
+
const result$1 = {
|
|
413
|
+
canPartialValidate: false,
|
|
414
|
+
reason: "root-checks"
|
|
415
|
+
};
|
|
416
|
+
cache.set(path, result$1);
|
|
417
|
+
return result$1;
|
|
418
|
+
}
|
|
419
|
+
const extracted = extractSubSchema(schema, path);
|
|
420
|
+
if (!extracted) {
|
|
421
|
+
const result$1 = {
|
|
422
|
+
canPartialValidate: false,
|
|
423
|
+
reason: "invalid-path"
|
|
424
|
+
};
|
|
425
|
+
cache.set(path, result$1);
|
|
426
|
+
return result$1;
|
|
427
|
+
}
|
|
428
|
+
if (extracted.hasEffects) {
|
|
429
|
+
const result$1 = {
|
|
430
|
+
canPartialValidate: false,
|
|
431
|
+
reason: "path-checks"
|
|
432
|
+
};
|
|
433
|
+
cache.set(path, result$1);
|
|
434
|
+
return result$1;
|
|
435
|
+
}
|
|
436
|
+
const result = {
|
|
437
|
+
canPartialValidate: true,
|
|
438
|
+
subSchema: extracted.schema
|
|
439
|
+
};
|
|
440
|
+
cache.set(path, result);
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
300
443
|
function clearFieldErrors$1(errors, fieldPath) {
|
|
301
444
|
const newErrors = { ...errors };
|
|
302
445
|
for (const key of Object.keys(newErrors)) if (key === fieldPath || key.startsWith(`${fieldPath}.`)) delete newErrors[key];
|
|
303
446
|
return newErrors;
|
|
304
447
|
}
|
|
305
448
|
function setValidating(ctx, fieldPath, isValidating) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
else {
|
|
311
|
-
const newValidating = { ...ctx.validatingFields.value };
|
|
312
|
-
delete newValidating[fieldPath];
|
|
313
|
-
ctx.validatingFields.value = newValidating;
|
|
314
|
-
}
|
|
449
|
+
const newSet = new Set(ctx.validatingFields.value);
|
|
450
|
+
if (isValidating) newSet.add(fieldPath);
|
|
451
|
+
else newSet.delete(fieldPath);
|
|
452
|
+
ctx.validatingFields.value = newSet;
|
|
315
453
|
}
|
|
316
454
|
function groupErrorsByPath(issues) {
|
|
317
455
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -353,6 +491,18 @@ function createValidation(ctx) {
|
|
|
353
491
|
if (!ctx.options.shouldUseNativeValidation) return;
|
|
354
492
|
for (const [path] of ctx.fieldRefs) applyNativeValidation(path, null);
|
|
355
493
|
}
|
|
494
|
+
function scheduleErrorsBatch(errors) {
|
|
495
|
+
if ((ctx.options.delayError || 0) <= 0) {
|
|
496
|
+
const newErrors = { ...ctx.errors.value };
|
|
497
|
+
for (const [fieldPath, error] of errors) {
|
|
498
|
+
set(newErrors, fieldPath, error);
|
|
499
|
+
applyNativeValidation(fieldPath, typeof error === "string" ? error : error.message);
|
|
500
|
+
}
|
|
501
|
+
ctx.errors.value = newErrors;
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
for (const [fieldPath, error] of errors) scheduleError(fieldPath, error);
|
|
505
|
+
}
|
|
356
506
|
function scheduleError(fieldPath, error) {
|
|
357
507
|
const delayMs = ctx.options.delayError || 0;
|
|
358
508
|
const errorMessage = typeof error === "string" ? error : error.message;
|
|
@@ -394,42 +544,111 @@ function createValidation(ctx) {
|
|
|
394
544
|
ctx.errorDelayTimers.clear();
|
|
395
545
|
ctx.pendingErrors.clear();
|
|
396
546
|
}
|
|
547
|
+
async function validateFieldPartial(fieldPath, subSchema, valueHash, criteriaMode, generationAtStart) {
|
|
548
|
+
const fieldValue = get(ctx.formData, fieldPath);
|
|
549
|
+
setValidating(ctx, fieldPath, true);
|
|
550
|
+
try {
|
|
551
|
+
const result = await subSchema.safeParseAsync(fieldValue);
|
|
552
|
+
if (ctx.resetGeneration.value !== generationAtStart) return true;
|
|
553
|
+
if (result.success) {
|
|
554
|
+
ctx.errors.value = cancelError(fieldPath);
|
|
555
|
+
if (valueHash) ctx.validationCache.set(fieldPath, {
|
|
556
|
+
hash: valueHash,
|
|
557
|
+
isValid: true
|
|
558
|
+
});
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
const fieldErrors = result.error.issues.map((issue) => ({
|
|
562
|
+
...issue,
|
|
563
|
+
path: fieldPath.split(".").concat(issue.path.map(String))
|
|
564
|
+
}));
|
|
565
|
+
ctx.errors.value = cancelError(fieldPath);
|
|
566
|
+
const grouped = groupErrorsByPath(fieldErrors);
|
|
567
|
+
const errorBatch = [];
|
|
568
|
+
for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
|
|
569
|
+
scheduleErrorsBatch(errorBatch);
|
|
570
|
+
if (valueHash) ctx.validationCache.set(fieldPath, {
|
|
571
|
+
hash: valueHash,
|
|
572
|
+
isValid: false
|
|
573
|
+
});
|
|
574
|
+
return false;
|
|
575
|
+
} finally {
|
|
576
|
+
setValidating(ctx, fieldPath, false);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
async function validateFieldFull(fieldPath, valueHash, criteriaMode, generationAtStart) {
|
|
580
|
+
setValidating(ctx, fieldPath, true);
|
|
581
|
+
try {
|
|
582
|
+
const result = await ctx.options.schema.safeParseAsync(ctx.formData);
|
|
583
|
+
if (ctx.resetGeneration.value !== generationAtStart) return true;
|
|
584
|
+
if (result.success) {
|
|
585
|
+
ctx.errors.value = cancelError(fieldPath);
|
|
586
|
+
if (valueHash) ctx.validationCache.set(fieldPath, {
|
|
587
|
+
hash: valueHash,
|
|
588
|
+
isValid: true
|
|
589
|
+
});
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
const fieldErrors = result.error.issues.filter((issue) => {
|
|
593
|
+
const path = issue.path.join(".");
|
|
594
|
+
return path === fieldPath || path.startsWith(`${fieldPath}.`);
|
|
595
|
+
});
|
|
596
|
+
if (fieldErrors.length === 0) {
|
|
597
|
+
ctx.errors.value = cancelError(fieldPath);
|
|
598
|
+
if (valueHash) ctx.validationCache.set(fieldPath, {
|
|
599
|
+
hash: valueHash,
|
|
600
|
+
isValid: true
|
|
601
|
+
});
|
|
602
|
+
return true;
|
|
603
|
+
}
|
|
604
|
+
ctx.errors.value = cancelError(fieldPath);
|
|
605
|
+
const grouped = groupErrorsByPath(fieldErrors);
|
|
606
|
+
const errorBatch = [];
|
|
607
|
+
for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
|
|
608
|
+
scheduleErrorsBatch(errorBatch);
|
|
609
|
+
if (valueHash) ctx.validationCache.set(fieldPath, {
|
|
610
|
+
hash: valueHash,
|
|
611
|
+
isValid: false
|
|
612
|
+
});
|
|
613
|
+
return false;
|
|
614
|
+
} finally {
|
|
615
|
+
setValidating(ctx, fieldPath, false);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
397
618
|
async function validate(fieldPath) {
|
|
398
619
|
const generationAtStart = ctx.resetGeneration.value;
|
|
399
620
|
const criteriaMode = ctx.options.criteriaMode || "firstError";
|
|
400
|
-
|
|
621
|
+
let valueHash;
|
|
622
|
+
if (fieldPath) {
|
|
623
|
+
valueHash = hashValue(get(ctx.formData, fieldPath));
|
|
624
|
+
const cached = ctx.validationCache.get(fieldPath);
|
|
625
|
+
if (cached && cached.hash === valueHash) {
|
|
626
|
+
if (cached.isValid) ctx.errors.value = cancelError(fieldPath);
|
|
627
|
+
return cached.isValid;
|
|
628
|
+
}
|
|
629
|
+
const analysis = analyzeSchemaPath(ctx.options.schema, fieldPath);
|
|
630
|
+
if (analysis.canPartialValidate && analysis.subSchema) return validateFieldPartial(fieldPath, analysis.subSchema, valueHash, criteriaMode, generationAtStart);
|
|
631
|
+
return validateFieldFull(fieldPath, valueHash, criteriaMode, generationAtStart);
|
|
632
|
+
}
|
|
633
|
+
const validatingKey = "_form";
|
|
401
634
|
setValidating(ctx, validatingKey, true);
|
|
402
635
|
try {
|
|
403
636
|
const result = await ctx.options.schema.safeParseAsync(ctx.formData);
|
|
404
637
|
if (ctx.resetGeneration.value !== generationAtStart) return true;
|
|
405
638
|
if (result.success) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
clearAllNativeValidation();
|
|
411
|
-
}
|
|
639
|
+
clearAllPendingErrors();
|
|
640
|
+
ctx.errors.value = {};
|
|
641
|
+
clearAllNativeValidation();
|
|
642
|
+
ctx.validationCache.clear();
|
|
412
643
|
return true;
|
|
413
644
|
}
|
|
414
|
-
const zodErrors = result.error.issues;
|
|
415
|
-
if (fieldPath) {
|
|
416
|
-
const fieldErrors = zodErrors.filter((issue) => {
|
|
417
|
-
const path = issue.path.join(".");
|
|
418
|
-
return path === fieldPath || path.startsWith(`${fieldPath}.`);
|
|
419
|
-
});
|
|
420
|
-
if (fieldErrors.length === 0) {
|
|
421
|
-
ctx.errors.value = cancelError(fieldPath);
|
|
422
|
-
return true;
|
|
423
|
-
}
|
|
424
|
-
ctx.errors.value = cancelError(fieldPath);
|
|
425
|
-
const grouped$1 = groupErrorsByPath(fieldErrors);
|
|
426
|
-
for (const [path, errors] of grouped$1) scheduleError(path, createFieldError(errors, criteriaMode));
|
|
427
|
-
return false;
|
|
428
|
-
}
|
|
429
645
|
clearAllPendingErrors();
|
|
430
646
|
ctx.errors.value = {};
|
|
431
|
-
const grouped = groupErrorsByPath(
|
|
432
|
-
|
|
647
|
+
const grouped = groupErrorsByPath(result.error.issues);
|
|
648
|
+
const errorBatch = [];
|
|
649
|
+
for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
|
|
650
|
+
scheduleErrorsBatch(errorBatch);
|
|
651
|
+
ctx.validationCache.clear();
|
|
433
652
|
return false;
|
|
434
653
|
} finally {
|
|
435
654
|
setValidating(ctx, validatingKey, false);
|
|
@@ -440,6 +659,48 @@ function createValidation(ctx) {
|
|
|
440
659
|
clearAllPendingErrors
|
|
441
660
|
};
|
|
442
661
|
}
|
|
662
|
+
function markFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
|
|
663
|
+
if (dirtyFields.value[fieldName]) return;
|
|
664
|
+
dirtyFields.value = {
|
|
665
|
+
...dirtyFields.value,
|
|
666
|
+
[fieldName]: true
|
|
667
|
+
};
|
|
668
|
+
dirtyFieldCount.value++;
|
|
669
|
+
}
|
|
670
|
+
function markFieldTouched(touchedFields, touchedFieldCount, fieldName) {
|
|
671
|
+
if (touchedFields.value[fieldName]) return;
|
|
672
|
+
touchedFields.value = {
|
|
673
|
+
...touchedFields.value,
|
|
674
|
+
[fieldName]: true
|
|
675
|
+
};
|
|
676
|
+
touchedFieldCount.value++;
|
|
677
|
+
}
|
|
678
|
+
function clearFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
|
|
679
|
+
if (!(fieldName in dirtyFields.value)) return;
|
|
680
|
+
const newDirty = { ...dirtyFields.value };
|
|
681
|
+
delete newDirty[fieldName];
|
|
682
|
+
dirtyFields.value = newDirty;
|
|
683
|
+
dirtyFieldCount.value--;
|
|
684
|
+
}
|
|
685
|
+
function clearFieldTouched(touchedFields, touchedFieldCount, fieldName) {
|
|
686
|
+
if (!(fieldName in touchedFields.value)) return;
|
|
687
|
+
const newTouched = { ...touchedFields.value };
|
|
688
|
+
delete newTouched[fieldName];
|
|
689
|
+
touchedFields.value = newTouched;
|
|
690
|
+
touchedFieldCount.value--;
|
|
691
|
+
}
|
|
692
|
+
function clearFieldErrors(errors, fieldName) {
|
|
693
|
+
const currentErrors = errors.value;
|
|
694
|
+
const keys = Object.keys(currentErrors);
|
|
695
|
+
if (keys.length === 0) return;
|
|
696
|
+
const prefix = `${fieldName}.`;
|
|
697
|
+
const keysToDelete = [];
|
|
698
|
+
for (const key of keys) if (key === fieldName || key.startsWith(prefix)) keysToDelete.push(key);
|
|
699
|
+
if (keysToDelete.length === 0) return;
|
|
700
|
+
const newErrors = { ...currentErrors };
|
|
701
|
+
for (const key of keysToDelete) delete newErrors[key];
|
|
702
|
+
errors.value = newErrors;
|
|
703
|
+
}
|
|
443
704
|
var validationRequestCounter = 0;
|
|
444
705
|
function createFieldRegistration(ctx, validate) {
|
|
445
706
|
function register(name, registerOptions) {
|
|
@@ -481,16 +742,30 @@ function createFieldRegistration(ctx, validate) {
|
|
|
481
742
|
const target = e.target;
|
|
482
743
|
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
483
744
|
set(ctx.formData, name, value);
|
|
484
|
-
ctx.dirtyFields.
|
|
485
|
-
|
|
486
|
-
[name]: true
|
|
487
|
-
};
|
|
745
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
746
|
+
const fieldOpts = ctx.fieldOptions.get(name);
|
|
488
747
|
if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange") {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
748
|
+
const validationDebounceMs = ctx.options.validationDebounce || 0;
|
|
749
|
+
if (validationDebounceMs > 0) {
|
|
750
|
+
const existingTimer = ctx.schemaValidationTimers.get(name);
|
|
751
|
+
if (existingTimer) clearTimeout(existingTimer);
|
|
752
|
+
const timer = setTimeout(async () => {
|
|
753
|
+
ctx.schemaValidationTimers.delete(name);
|
|
754
|
+
await validate(name);
|
|
755
|
+
if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
|
|
756
|
+
const uniqueDeps = [...new Set(fieldOpts.deps)];
|
|
757
|
+
await Promise.all(uniqueDeps.map((depField) => validate(depField)));
|
|
758
|
+
}
|
|
759
|
+
}, validationDebounceMs);
|
|
760
|
+
ctx.schemaValidationTimers.set(name, timer);
|
|
761
|
+
} else {
|
|
762
|
+
await validate(name);
|
|
763
|
+
if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
|
|
764
|
+
const uniqueDeps = [...new Set(fieldOpts.deps)];
|
|
765
|
+
await Promise.all(uniqueDeps.map((depField) => validate(depField)));
|
|
766
|
+
}
|
|
767
|
+
}
|
|
492
768
|
}
|
|
493
|
-
const fieldOpts = ctx.fieldOptions.get(name);
|
|
494
769
|
if (fieldOpts?.validate && !fieldOpts.disabled) {
|
|
495
770
|
const requestId = ++validationRequestCounter;
|
|
496
771
|
ctx.validationRequestIds.set(name, requestId);
|
|
@@ -499,23 +774,24 @@ function createFieldRegistration(ctx, validate) {
|
|
|
499
774
|
if (debounceMs > 0) {
|
|
500
775
|
const existingTimer = ctx.debounceTimers.get(name);
|
|
501
776
|
if (existingTimer) clearTimeout(existingTimer);
|
|
502
|
-
const timer = setTimeout(() => {
|
|
777
|
+
const timer = setTimeout(async () => {
|
|
503
778
|
ctx.debounceTimers.delete(name);
|
|
504
|
-
runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
779
|
+
await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
780
|
+
ctx.validationRequestIds.delete(name);
|
|
505
781
|
}, debounceMs);
|
|
506
782
|
ctx.debounceTimers.set(name, timer);
|
|
507
783
|
} else await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
508
784
|
}
|
|
509
785
|
};
|
|
510
786
|
const onBlur = async (_e) => {
|
|
511
|
-
ctx.touchedFields.
|
|
512
|
-
...ctx.touchedFields.value,
|
|
513
|
-
[name]: true
|
|
514
|
-
};
|
|
787
|
+
markFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
515
788
|
if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") {
|
|
516
789
|
await validate(name);
|
|
517
790
|
const fieldOpts = ctx.fieldOptions.get(name);
|
|
518
|
-
if (fieldOpts?.deps && fieldOpts.deps.length > 0)
|
|
791
|
+
if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
|
|
792
|
+
const uniqueDeps = [...new Set(fieldOpts.deps)];
|
|
793
|
+
await Promise.all(uniqueDeps.map((depField) => validate(depField)));
|
|
794
|
+
}
|
|
519
795
|
}
|
|
520
796
|
};
|
|
521
797
|
const refCallback = (el) => {
|
|
@@ -537,18 +813,17 @@ function createFieldRegistration(ctx, validate) {
|
|
|
537
813
|
clearTimeout(timer);
|
|
538
814
|
ctx.debounceTimers.delete(name);
|
|
539
815
|
}
|
|
816
|
+
const schemaTimer = ctx.schemaValidationTimers.get(name);
|
|
817
|
+
if (schemaTimer) {
|
|
818
|
+
clearTimeout(schemaTimer);
|
|
819
|
+
ctx.schemaValidationTimers.delete(name);
|
|
820
|
+
}
|
|
540
821
|
ctx.validationRequestIds.delete(name);
|
|
541
822
|
if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
|
|
542
823
|
unset(ctx.formData, name);
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
ctx.
|
|
546
|
-
const newTouched = { ...ctx.touchedFields.value };
|
|
547
|
-
delete newTouched[name];
|
|
548
|
-
ctx.touchedFields.value = newTouched;
|
|
549
|
-
const newDirty = { ...ctx.dirtyFields.value };
|
|
550
|
-
delete newDirty[name];
|
|
551
|
-
ctx.dirtyFields.value = newDirty;
|
|
824
|
+
clearFieldErrors(ctx.errors, name);
|
|
825
|
+
clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
826
|
+
clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
552
827
|
ctx.fieldRefs.delete(name);
|
|
553
828
|
ctx.fieldOptions.delete(name);
|
|
554
829
|
ctx.fieldHandlers.delete(name);
|
|
@@ -572,10 +847,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
572
847
|
get: () => get(ctx.formData, name),
|
|
573
848
|
set: (val) => {
|
|
574
849
|
set(ctx.formData, name, val);
|
|
575
|
-
ctx.dirtyFields.
|
|
576
|
-
...ctx.dirtyFields.value,
|
|
577
|
-
[name]: true
|
|
578
|
-
};
|
|
850
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
579
851
|
}
|
|
580
852
|
}) }
|
|
581
853
|
};
|
|
@@ -583,21 +855,9 @@ function createFieldRegistration(ctx, validate) {
|
|
|
583
855
|
function unregister(name, options) {
|
|
584
856
|
const opts = options || {};
|
|
585
857
|
if (!opts.keepValue) unset(ctx.formData, name);
|
|
586
|
-
if (!opts.keepError)
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
ctx.errors.value = newErrors;
|
|
590
|
-
}
|
|
591
|
-
if (!opts.keepTouched) {
|
|
592
|
-
const newTouched = { ...ctx.touchedFields.value };
|
|
593
|
-
delete newTouched[name];
|
|
594
|
-
ctx.touchedFields.value = newTouched;
|
|
595
|
-
}
|
|
596
|
-
if (!opts.keepDirty) {
|
|
597
|
-
const newDirty = { ...ctx.dirtyFields.value };
|
|
598
|
-
delete newDirty[name];
|
|
599
|
-
ctx.dirtyFields.value = newDirty;
|
|
600
|
-
}
|
|
858
|
+
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
859
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
860
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
601
861
|
ctx.fieldRefs.delete(name);
|
|
602
862
|
ctx.fieldOptions.delete(name);
|
|
603
863
|
ctx.fieldHandlers.delete(name);
|
|
@@ -606,6 +866,11 @@ function createFieldRegistration(ctx, validate) {
|
|
|
606
866
|
clearTimeout(timer);
|
|
607
867
|
ctx.debounceTimers.delete(name);
|
|
608
868
|
}
|
|
869
|
+
const schemaTimer = ctx.schemaValidationTimers.get(name);
|
|
870
|
+
if (schemaTimer) {
|
|
871
|
+
clearTimeout(schemaTimer);
|
|
872
|
+
ctx.schemaValidationTimers.delete(name);
|
|
873
|
+
}
|
|
609
874
|
ctx.validationRequestIds.delete(name);
|
|
610
875
|
}
|
|
611
876
|
return {
|
|
@@ -642,6 +907,35 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
642
907
|
indexCache.set(item.key, idx);
|
|
643
908
|
});
|
|
644
909
|
};
|
|
910
|
+
const appendToCache = (startIndex) => {
|
|
911
|
+
const items = fa.items.value;
|
|
912
|
+
for (let i = startIndex; i < items.length; i++) {
|
|
913
|
+
const item = items[i];
|
|
914
|
+
if (item) indexCache.set(item.key, i);
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
const updateCacheAfterInsert = (insertIndex, _insertCount) => {
|
|
918
|
+
const items = fa.items.value;
|
|
919
|
+
for (let i = insertIndex; i < items.length; i++) {
|
|
920
|
+
const item = items[i];
|
|
921
|
+
if (item) indexCache.set(item.key, i);
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
const swapInCache = (indexA, indexB) => {
|
|
925
|
+
const items = fa.items.value;
|
|
926
|
+
const itemA = items[indexA];
|
|
927
|
+
const itemB = items[indexB];
|
|
928
|
+
if (itemA) indexCache.set(itemA.key, indexA);
|
|
929
|
+
if (itemB) indexCache.set(itemB.key, indexB);
|
|
930
|
+
};
|
|
931
|
+
const updateCacheAfterRemove = (removedKey, startIndex) => {
|
|
932
|
+
indexCache.delete(removedKey);
|
|
933
|
+
const items = fa.items.value;
|
|
934
|
+
for (let i = startIndex; i < items.length; i++) {
|
|
935
|
+
const item = items[i];
|
|
936
|
+
if (item) indexCache.set(item.key, i);
|
|
937
|
+
}
|
|
938
|
+
};
|
|
645
939
|
const createItem = (key) => ({
|
|
646
940
|
key,
|
|
647
941
|
get index() {
|
|
@@ -684,11 +978,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
684
978
|
set(ctx.formData, name, newValues);
|
|
685
979
|
const newItems = values.map(() => createItem(generateId()));
|
|
686
980
|
fa.items.value = [...fa.items.value, ...newItems];
|
|
687
|
-
|
|
688
|
-
ctx.dirtyFields.
|
|
689
|
-
...ctx.dirtyFields.value,
|
|
690
|
-
[name]: true
|
|
691
|
-
};
|
|
981
|
+
appendToCache(insertIndex);
|
|
982
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
692
983
|
if (ctx.options.mode === "onChange") validate(name);
|
|
693
984
|
handleFocus(insertIndex, values.length, focusOptions);
|
|
694
985
|
return true;
|
|
@@ -709,11 +1000,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
709
1000
|
set(ctx.formData, name, newValues);
|
|
710
1001
|
const newItems = values.map(() => createItem(generateId()));
|
|
711
1002
|
fa.items.value = [...newItems, ...fa.items.value];
|
|
712
|
-
|
|
713
|
-
ctx.dirtyFields.
|
|
714
|
-
...ctx.dirtyFields.value,
|
|
715
|
-
[name]: true
|
|
716
|
-
};
|
|
1003
|
+
updateCacheAfterInsert(0, values.length);
|
|
1004
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
717
1005
|
if (ctx.options.mode === "onChange") validate(name);
|
|
718
1006
|
handleFocus(0, values.length, focusOptions);
|
|
719
1007
|
return true;
|
|
@@ -727,10 +1015,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
727
1015
|
const newValues = [...currentValues];
|
|
728
1016
|
newValues[index] = value;
|
|
729
1017
|
set(ctx.formData, name, newValues);
|
|
730
|
-
ctx.dirtyFields.
|
|
731
|
-
...ctx.dirtyFields.value,
|
|
732
|
-
[name]: true
|
|
733
|
-
};
|
|
1018
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
734
1019
|
if (ctx.options.mode === "onChange") validate(name);
|
|
735
1020
|
return true;
|
|
736
1021
|
};
|
|
@@ -752,11 +1037,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
752
1037
|
set(ctx.formData, name, newValues);
|
|
753
1038
|
const keyToRemove = fa.items.value[index]?.key;
|
|
754
1039
|
fa.items.value = fa.items.value.filter((item) => item.key !== keyToRemove);
|
|
755
|
-
|
|
756
|
-
ctx.dirtyFields.
|
|
757
|
-
...ctx.dirtyFields.value,
|
|
758
|
-
[name]: true
|
|
759
|
-
};
|
|
1040
|
+
if (keyToRemove) updateCacheAfterRemove(keyToRemove, index);
|
|
1041
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
760
1042
|
if (ctx.options.mode === "onChange") validate(name);
|
|
761
1043
|
return true;
|
|
762
1044
|
};
|
|
@@ -785,11 +1067,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
785
1067
|
...newItems,
|
|
786
1068
|
...fa.items.value.slice(clampedIndex)
|
|
787
1069
|
];
|
|
788
|
-
|
|
789
|
-
ctx.dirtyFields.
|
|
790
|
-
...ctx.dirtyFields.value,
|
|
791
|
-
[name]: true
|
|
792
|
-
};
|
|
1070
|
+
updateCacheAfterInsert(clampedIndex, values.length);
|
|
1071
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
793
1072
|
if (ctx.options.mode === "onChange") validate(name);
|
|
794
1073
|
handleFocus(clampedIndex, values.length, focusOptions);
|
|
795
1074
|
return true;
|
|
@@ -810,12 +1089,9 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
810
1089
|
newItems[indexA] = itemB;
|
|
811
1090
|
newItems[indexB] = itemA;
|
|
812
1091
|
fa.items.value = newItems;
|
|
813
|
-
|
|
1092
|
+
swapInCache(indexA, indexB);
|
|
814
1093
|
}
|
|
815
|
-
ctx.dirtyFields.
|
|
816
|
-
...ctx.dirtyFields.value,
|
|
817
|
-
[name]: true
|
|
818
|
-
};
|
|
1094
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
819
1095
|
if (ctx.options.mode === "onChange") validate(name);
|
|
820
1096
|
return true;
|
|
821
1097
|
};
|
|
@@ -838,12 +1114,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
838
1114
|
const clampedTo = Math.min(to, newItems.length);
|
|
839
1115
|
newItems.splice(clampedTo, 0, removedItem);
|
|
840
1116
|
fa.items.value = newItems;
|
|
841
|
-
|
|
1117
|
+
const minIdx = Math.min(from, clampedTo);
|
|
1118
|
+
const maxIdx = Math.max(from, clampedTo);
|
|
1119
|
+
const items = fa.items.value;
|
|
1120
|
+
for (let i = minIdx; i <= maxIdx; i++) {
|
|
1121
|
+
const item = items[i];
|
|
1122
|
+
if (item) indexCache.set(item.key, i);
|
|
1123
|
+
}
|
|
842
1124
|
}
|
|
843
|
-
ctx.dirtyFields.
|
|
844
|
-
...ctx.dirtyFields.value,
|
|
845
|
-
[name]: true
|
|
846
|
-
};
|
|
1125
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
847
1126
|
if (ctx.options.mode === "onChange") validate(name);
|
|
848
1127
|
return true;
|
|
849
1128
|
};
|
|
@@ -852,10 +1131,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
852
1131
|
set(ctx.formData, name, newValues);
|
|
853
1132
|
fa.items.value = newValues.map(() => createItem(generateId()));
|
|
854
1133
|
rebuildIndexCache();
|
|
855
|
-
ctx.dirtyFields.
|
|
856
|
-
...ctx.dirtyFields.value,
|
|
857
|
-
[name]: true
|
|
858
|
-
};
|
|
1134
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
859
1135
|
if (ctx.options.mode === "onChange") validate(name);
|
|
860
1136
|
return true;
|
|
861
1137
|
};
|
|
@@ -870,11 +1146,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
870
1146
|
}
|
|
871
1147
|
set(ctx.formData, name, []);
|
|
872
1148
|
fa.items.value = [];
|
|
873
|
-
|
|
874
|
-
ctx.dirtyFields.
|
|
875
|
-
...ctx.dirtyFields.value,
|
|
876
|
-
[name]: true
|
|
877
|
-
};
|
|
1149
|
+
indexCache.clear();
|
|
1150
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
878
1151
|
if (ctx.options.mode === "onChange") validate(name);
|
|
879
1152
|
return true;
|
|
880
1153
|
};
|
|
@@ -893,14 +1166,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
893
1166
|
}
|
|
894
1167
|
const sortedIndices = [...new Set(validIndices)].sort((a, b) => b - a);
|
|
895
1168
|
const indicesToRemove = new Set(sortedIndices);
|
|
1169
|
+
const keysToRemove = fa.items.value.filter((_, i) => indicesToRemove.has(i)).map((item) => item.key);
|
|
896
1170
|
const newValues = currentValues.filter((_, i) => !indicesToRemove.has(i));
|
|
897
1171
|
set(ctx.formData, name, newValues);
|
|
898
1172
|
fa.items.value = fa.items.value.filter((_, i) => !indicesToRemove.has(i));
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1173
|
+
for (const key of keysToRemove) indexCache.delete(key);
|
|
1174
|
+
fa.items.value.forEach((item, idx) => {
|
|
1175
|
+
indexCache.set(item.key, idx);
|
|
1176
|
+
});
|
|
1177
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
904
1178
|
if (ctx.options.mode === "onChange") validate(name);
|
|
905
1179
|
return true;
|
|
906
1180
|
};
|
|
@@ -932,33 +1206,6 @@ function updateDomElement(el, value) {
|
|
|
932
1206
|
if (el.type === "checkbox") el.checked = value;
|
|
933
1207
|
else el.value = value;
|
|
934
1208
|
}
|
|
935
|
-
function markFieldDirty(dirtyFields, fieldName) {
|
|
936
|
-
dirtyFields.value = {
|
|
937
|
-
...dirtyFields.value,
|
|
938
|
-
[fieldName]: true
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
function markFieldTouched(touchedFields, fieldName) {
|
|
942
|
-
touchedFields.value = {
|
|
943
|
-
...touchedFields.value,
|
|
944
|
-
[fieldName]: true
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
function clearFieldDirty(dirtyFields, fieldName) {
|
|
948
|
-
const newDirty = { ...dirtyFields.value };
|
|
949
|
-
delete newDirty[fieldName];
|
|
950
|
-
dirtyFields.value = newDirty;
|
|
951
|
-
}
|
|
952
|
-
function clearFieldTouched(touchedFields, fieldName) {
|
|
953
|
-
const newTouched = { ...touchedFields.value };
|
|
954
|
-
delete newTouched[fieldName];
|
|
955
|
-
touchedFields.value = newTouched;
|
|
956
|
-
}
|
|
957
|
-
function clearFieldErrors(errors, fieldName) {
|
|
958
|
-
const newErrors = { ...errors.value };
|
|
959
|
-
for (const key of Object.keys(newErrors)) if (key === fieldName || key.startsWith(`${fieldName}.`)) delete newErrors[key];
|
|
960
|
-
errors.value = newErrors;
|
|
961
|
-
}
|
|
962
1209
|
function useForm(options) {
|
|
963
1210
|
const ctx = createFormContext(options);
|
|
964
1211
|
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
@@ -982,32 +1229,84 @@ function useForm(options) {
|
|
|
982
1229
|
}
|
|
983
1230
|
const setFocusWrapper = (name) => setFocus(name);
|
|
984
1231
|
const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper);
|
|
1232
|
+
let lastSyncTime = 0;
|
|
1233
|
+
const SYNC_DEBOUNCE_MS = 16;
|
|
1234
|
+
function syncWithDebounce() {
|
|
1235
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
1236
|
+
if (now - lastSyncTime < SYNC_DEBOUNCE_MS) return;
|
|
1237
|
+
syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
|
|
1238
|
+
lastSyncTime = now;
|
|
1239
|
+
}
|
|
1240
|
+
let lastErrors = ctx.errors.value;
|
|
1241
|
+
let lastExternalErrors = ctx.externalErrors.value;
|
|
1242
|
+
let cachedMergedErrors = null;
|
|
985
1243
|
function getMergedErrors() {
|
|
986
|
-
return
|
|
1244
|
+
if (cachedMergedErrors !== null && lastErrors === ctx.errors.value && lastExternalErrors === ctx.externalErrors.value) return cachedMergedErrors;
|
|
1245
|
+
lastErrors = ctx.errors.value;
|
|
1246
|
+
lastExternalErrors = ctx.externalErrors.value;
|
|
1247
|
+
cachedMergedErrors = {
|
|
987
1248
|
...ctx.errors.value,
|
|
988
1249
|
...ctx.externalErrors.value
|
|
989
1250
|
};
|
|
1251
|
+
return cachedMergedErrors;
|
|
990
1252
|
}
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
dirtyFields: ctx.dirtyFields.value,
|
|
997
|
-
isValid: (ctx.submitCount.value > 0 || Object.keys(ctx.touchedFields.value).length > 0) && Object.keys(mergedErrors).length === 0,
|
|
998
|
-
isSubmitting: ctx.isSubmitting.value,
|
|
999
|
-
isLoading: ctx.isLoading.value,
|
|
1000
|
-
isReady: !ctx.isLoading.value,
|
|
1001
|
-
isValidating: Object.keys(ctx.validatingFields.value).some((k) => ctx.validatingFields.value[k]),
|
|
1002
|
-
validatingFields: ctx.validatingFields.value,
|
|
1003
|
-
touchedFields: ctx.touchedFields.value,
|
|
1004
|
-
submitCount: ctx.submitCount.value,
|
|
1005
|
-
defaultValuesError: ctx.defaultValuesError.value,
|
|
1006
|
-
isSubmitted: ctx.submitCount.value > 0,
|
|
1007
|
-
isSubmitSuccessful: ctx.isSubmitSuccessful.value,
|
|
1008
|
-
disabled: ctx.isDisabled.value
|
|
1009
|
-
};
|
|
1253
|
+
const isDirtyComputed = computed(() => ctx.dirtyFieldCount.value > 0);
|
|
1254
|
+
const errorsComputed = computed(() => getMergedErrors());
|
|
1255
|
+
const isValidComputed = computed(() => {
|
|
1256
|
+
if (!(ctx.submitCount.value > 0 || ctx.touchedFieldCount.value > 0)) return false;
|
|
1257
|
+
return Object.keys(errorsComputed.value).length === 0;
|
|
1010
1258
|
});
|
|
1259
|
+
const isReadyComputed = computed(() => !ctx.isLoading.value);
|
|
1260
|
+
const isValidatingComputed = computed(() => ctx.validatingFields.value.size > 0);
|
|
1261
|
+
const isSubmittedComputed = computed(() => ctx.submitCount.value > 0);
|
|
1262
|
+
const formStateInternal = reactive({
|
|
1263
|
+
get errors() {
|
|
1264
|
+
return errorsComputed.value;
|
|
1265
|
+
},
|
|
1266
|
+
get isDirty() {
|
|
1267
|
+
return isDirtyComputed.value;
|
|
1268
|
+
},
|
|
1269
|
+
get dirtyFields() {
|
|
1270
|
+
return ctx.dirtyFields.value;
|
|
1271
|
+
},
|
|
1272
|
+
get isValid() {
|
|
1273
|
+
return isValidComputed.value;
|
|
1274
|
+
},
|
|
1275
|
+
get isSubmitting() {
|
|
1276
|
+
return ctx.isSubmitting.value;
|
|
1277
|
+
},
|
|
1278
|
+
get isLoading() {
|
|
1279
|
+
return ctx.isLoading.value;
|
|
1280
|
+
},
|
|
1281
|
+
get isReady() {
|
|
1282
|
+
return isReadyComputed.value;
|
|
1283
|
+
},
|
|
1284
|
+
get isValidating() {
|
|
1285
|
+
return isValidatingComputed.value;
|
|
1286
|
+
},
|
|
1287
|
+
get validatingFields() {
|
|
1288
|
+
return ctx.validatingFields.value;
|
|
1289
|
+
},
|
|
1290
|
+
get touchedFields() {
|
|
1291
|
+
return ctx.touchedFields.value;
|
|
1292
|
+
},
|
|
1293
|
+
get submitCount() {
|
|
1294
|
+
return ctx.submitCount.value;
|
|
1295
|
+
},
|
|
1296
|
+
get defaultValuesError() {
|
|
1297
|
+
return ctx.defaultValuesError.value;
|
|
1298
|
+
},
|
|
1299
|
+
get isSubmitted() {
|
|
1300
|
+
return isSubmittedComputed.value;
|
|
1301
|
+
},
|
|
1302
|
+
get isSubmitSuccessful() {
|
|
1303
|
+
return ctx.isSubmitSuccessful.value;
|
|
1304
|
+
},
|
|
1305
|
+
get disabled() {
|
|
1306
|
+
return ctx.isDisabled.value;
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1309
|
+
const formState = computed(() => formStateInternal);
|
|
1011
1310
|
function handleSubmit(onValid, onInvalid) {
|
|
1012
1311
|
return async (e) => {
|
|
1013
1312
|
e.preventDefault();
|
|
@@ -1017,7 +1316,7 @@ function useForm(options) {
|
|
|
1017
1316
|
ctx.submitCount.value++;
|
|
1018
1317
|
ctx.isSubmitSuccessful.value = false;
|
|
1019
1318
|
try {
|
|
1020
|
-
|
|
1319
|
+
syncWithDebounce();
|
|
1021
1320
|
if (await validate()) {
|
|
1022
1321
|
await onValid(ctx.formData);
|
|
1023
1322
|
ctx.isSubmitSuccessful.value = true;
|
|
@@ -1043,8 +1342,8 @@ function useForm(options) {
|
|
|
1043
1342
|
}
|
|
1044
1343
|
}
|
|
1045
1344
|
set(ctx.formData, name, value);
|
|
1046
|
-
if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, name);
|
|
1047
|
-
if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, name);
|
|
1345
|
+
if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
1346
|
+
if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
1048
1347
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
1049
1348
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
1050
1349
|
if (fieldRef?.value) updateDomElement(fieldRef.value, value);
|
|
@@ -1055,15 +1354,23 @@ function useForm(options) {
|
|
|
1055
1354
|
const opts = resetOptions || {};
|
|
1056
1355
|
ctx.resetGeneration.value++;
|
|
1057
1356
|
clearAllPendingErrors();
|
|
1058
|
-
ctx.validatingFields.value =
|
|
1357
|
+
ctx.validatingFields.value = /* @__PURE__ */ new Set();
|
|
1358
|
+
ctx.validationCache.clear();
|
|
1359
|
+
for (const timer of ctx.schemaValidationTimers.values()) clearTimeout(timer);
|
|
1360
|
+
ctx.schemaValidationTimers.clear();
|
|
1059
1361
|
if (!opts.keepDefaultValues && values) Object.assign(ctx.defaultValues, values);
|
|
1060
1362
|
Object.keys(ctx.formData).forEach((key) => delete ctx.formData[key]);
|
|
1061
|
-
const
|
|
1062
|
-
const newValues = JSON.parse(JSON.stringify(sourceValues));
|
|
1363
|
+
const newValues = deepClone(values || ctx.defaultValues);
|
|
1063
1364
|
Object.assign(ctx.formData, newValues);
|
|
1064
1365
|
if (!opts.keepErrors) ctx.errors.value = {};
|
|
1065
|
-
if (!opts.keepTouched)
|
|
1066
|
-
|
|
1366
|
+
if (!opts.keepTouched) {
|
|
1367
|
+
ctx.touchedFields.value = {};
|
|
1368
|
+
ctx.touchedFieldCount.value = 0;
|
|
1369
|
+
}
|
|
1370
|
+
if (!opts.keepDirty) {
|
|
1371
|
+
ctx.dirtyFields.value = {};
|
|
1372
|
+
ctx.dirtyFieldCount.value = 0;
|
|
1373
|
+
}
|
|
1067
1374
|
if (!opts.keepSubmitCount) ctx.submitCount.value = 0;
|
|
1068
1375
|
if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
|
|
1069
1376
|
if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
|
|
@@ -1097,11 +1404,11 @@ function useForm(options) {
|
|
|
1097
1404
|
let defaultValue = opts.defaultValue;
|
|
1098
1405
|
if (defaultValue === void 0) defaultValue = get(ctx.defaultValues, name);
|
|
1099
1406
|
else set(ctx.defaultValues, name, defaultValue);
|
|
1100
|
-
const clonedValue = defaultValue !== void 0 ?
|
|
1407
|
+
const clonedValue = defaultValue !== void 0 ? deepClone(defaultValue) : void 0;
|
|
1101
1408
|
set(ctx.formData, name, clonedValue);
|
|
1102
1409
|
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
1103
|
-
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
|
|
1104
|
-
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
|
|
1410
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
1411
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
1105
1412
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
1106
1413
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
1107
1414
|
if (fieldRef?.value) updateDomElement(fieldRef.value, clonedValue ?? (fieldRef.value.type === "checkbox" ? false : ""));
|
|
@@ -1191,7 +1498,7 @@ function useForm(options) {
|
|
|
1191
1498
|
}
|
|
1192
1499
|
}
|
|
1193
1500
|
}
|
|
1194
|
-
|
|
1501
|
+
syncWithDebounce();
|
|
1195
1502
|
if (nameOrNames === void 0) return { ...ctx.formData };
|
|
1196
1503
|
if (Array.isArray(nameOrNames)) {
|
|
1197
1504
|
const result = {};
|
|
@@ -1298,7 +1605,12 @@ function useController(options) {
|
|
|
1298
1605
|
form.setValue(name, newValue);
|
|
1299
1606
|
};
|
|
1300
1607
|
const onBlur = () => {
|
|
1301
|
-
form.
|
|
1608
|
+
const currentValue = form.getValues(name);
|
|
1609
|
+
form.setValue(name, currentValue, {
|
|
1610
|
+
shouldTouch: true,
|
|
1611
|
+
shouldValidate: true,
|
|
1612
|
+
shouldDirty: false
|
|
1613
|
+
});
|
|
1302
1614
|
};
|
|
1303
1615
|
const refCallback = (el) => {
|
|
1304
1616
|
elementRef.value = el;
|