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