@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.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,111 @@ 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) {
|
|
628
|
+
if (cached.isValid) ctx.errors.value = cancelError(fieldPath);
|
|
629
|
+
return cached.isValid;
|
|
630
|
+
}
|
|
631
|
+
const analysis = analyzeSchemaPath(ctx.options.schema, fieldPath);
|
|
632
|
+
if (analysis.canPartialValidate && analysis.subSchema) return validateFieldPartial(fieldPath, analysis.subSchema, valueHash, criteriaMode, generationAtStart);
|
|
633
|
+
return validateFieldFull(fieldPath, valueHash, criteriaMode, generationAtStart);
|
|
634
|
+
}
|
|
635
|
+
const validatingKey = "_form";
|
|
403
636
|
setValidating(ctx, validatingKey, true);
|
|
404
637
|
try {
|
|
405
638
|
const result = await ctx.options.schema.safeParseAsync(ctx.formData);
|
|
406
639
|
if (ctx.resetGeneration.value !== generationAtStart) return true;
|
|
407
640
|
if (result.success) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
clearAllNativeValidation();
|
|
413
|
-
}
|
|
641
|
+
clearAllPendingErrors();
|
|
642
|
+
ctx.errors.value = {};
|
|
643
|
+
clearAllNativeValidation();
|
|
644
|
+
ctx.validationCache.clear();
|
|
414
645
|
return true;
|
|
415
646
|
}
|
|
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
647
|
clearAllPendingErrors();
|
|
432
648
|
ctx.errors.value = {};
|
|
433
|
-
const grouped = groupErrorsByPath(
|
|
434
|
-
|
|
649
|
+
const grouped = groupErrorsByPath(result.error.issues);
|
|
650
|
+
const errorBatch = [];
|
|
651
|
+
for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
|
|
652
|
+
scheduleErrorsBatch(errorBatch);
|
|
653
|
+
ctx.validationCache.clear();
|
|
435
654
|
return false;
|
|
436
655
|
} finally {
|
|
437
656
|
setValidating(ctx, validatingKey, false);
|
|
@@ -442,6 +661,48 @@ function createValidation(ctx) {
|
|
|
442
661
|
clearAllPendingErrors
|
|
443
662
|
};
|
|
444
663
|
}
|
|
664
|
+
function markFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
|
|
665
|
+
if (dirtyFields.value[fieldName]) return;
|
|
666
|
+
dirtyFields.value = {
|
|
667
|
+
...dirtyFields.value,
|
|
668
|
+
[fieldName]: true
|
|
669
|
+
};
|
|
670
|
+
dirtyFieldCount.value++;
|
|
671
|
+
}
|
|
672
|
+
function markFieldTouched(touchedFields, touchedFieldCount, fieldName) {
|
|
673
|
+
if (touchedFields.value[fieldName]) return;
|
|
674
|
+
touchedFields.value = {
|
|
675
|
+
...touchedFields.value,
|
|
676
|
+
[fieldName]: true
|
|
677
|
+
};
|
|
678
|
+
touchedFieldCount.value++;
|
|
679
|
+
}
|
|
680
|
+
function clearFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
|
|
681
|
+
if (!(fieldName in dirtyFields.value)) return;
|
|
682
|
+
const newDirty = { ...dirtyFields.value };
|
|
683
|
+
delete newDirty[fieldName];
|
|
684
|
+
dirtyFields.value = newDirty;
|
|
685
|
+
dirtyFieldCount.value--;
|
|
686
|
+
}
|
|
687
|
+
function clearFieldTouched(touchedFields, touchedFieldCount, fieldName) {
|
|
688
|
+
if (!(fieldName in touchedFields.value)) return;
|
|
689
|
+
const newTouched = { ...touchedFields.value };
|
|
690
|
+
delete newTouched[fieldName];
|
|
691
|
+
touchedFields.value = newTouched;
|
|
692
|
+
touchedFieldCount.value--;
|
|
693
|
+
}
|
|
694
|
+
function clearFieldErrors(errors, fieldName) {
|
|
695
|
+
const currentErrors = errors.value;
|
|
696
|
+
const keys = Object.keys(currentErrors);
|
|
697
|
+
if (keys.length === 0) return;
|
|
698
|
+
const prefix = `${fieldName}.`;
|
|
699
|
+
const keysToDelete = [];
|
|
700
|
+
for (const key of keys) if (key === fieldName || key.startsWith(prefix)) keysToDelete.push(key);
|
|
701
|
+
if (keysToDelete.length === 0) return;
|
|
702
|
+
const newErrors = { ...currentErrors };
|
|
703
|
+
for (const key of keysToDelete) delete newErrors[key];
|
|
704
|
+
errors.value = newErrors;
|
|
705
|
+
}
|
|
445
706
|
var validationRequestCounter = 0;
|
|
446
707
|
function createFieldRegistration(ctx, validate) {
|
|
447
708
|
function register(name, registerOptions) {
|
|
@@ -483,16 +744,30 @@ function createFieldRegistration(ctx, validate) {
|
|
|
483
744
|
const target = e.target;
|
|
484
745
|
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
485
746
|
set(ctx.formData, name, value);
|
|
486
|
-
ctx.dirtyFields.
|
|
487
|
-
|
|
488
|
-
[name]: true
|
|
489
|
-
};
|
|
747
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
748
|
+
const fieldOpts = ctx.fieldOptions.get(name);
|
|
490
749
|
if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange") {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
750
|
+
const validationDebounceMs = ctx.options.validationDebounce || 0;
|
|
751
|
+
if (validationDebounceMs > 0) {
|
|
752
|
+
const existingTimer = ctx.schemaValidationTimers.get(name);
|
|
753
|
+
if (existingTimer) clearTimeout(existingTimer);
|
|
754
|
+
const timer = setTimeout(async () => {
|
|
755
|
+
ctx.schemaValidationTimers.delete(name);
|
|
756
|
+
await validate(name);
|
|
757
|
+
if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
|
|
758
|
+
const uniqueDeps = [...new Set(fieldOpts.deps)];
|
|
759
|
+
await Promise.all(uniqueDeps.map((depField) => validate(depField)));
|
|
760
|
+
}
|
|
761
|
+
}, validationDebounceMs);
|
|
762
|
+
ctx.schemaValidationTimers.set(name, timer);
|
|
763
|
+
} else {
|
|
764
|
+
await validate(name);
|
|
765
|
+
if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
|
|
766
|
+
const uniqueDeps = [...new Set(fieldOpts.deps)];
|
|
767
|
+
await Promise.all(uniqueDeps.map((depField) => validate(depField)));
|
|
768
|
+
}
|
|
769
|
+
}
|
|
494
770
|
}
|
|
495
|
-
const fieldOpts = ctx.fieldOptions.get(name);
|
|
496
771
|
if (fieldOpts?.validate && !fieldOpts.disabled) {
|
|
497
772
|
const requestId = ++validationRequestCounter;
|
|
498
773
|
ctx.validationRequestIds.set(name, requestId);
|
|
@@ -501,23 +776,24 @@ function createFieldRegistration(ctx, validate) {
|
|
|
501
776
|
if (debounceMs > 0) {
|
|
502
777
|
const existingTimer = ctx.debounceTimers.get(name);
|
|
503
778
|
if (existingTimer) clearTimeout(existingTimer);
|
|
504
|
-
const timer = setTimeout(() => {
|
|
779
|
+
const timer = setTimeout(async () => {
|
|
505
780
|
ctx.debounceTimers.delete(name);
|
|
506
|
-
runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
781
|
+
await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
782
|
+
ctx.validationRequestIds.delete(name);
|
|
507
783
|
}, debounceMs);
|
|
508
784
|
ctx.debounceTimers.set(name, timer);
|
|
509
785
|
} else await runCustomValidation(name, value, requestId, resetGenAtStart);
|
|
510
786
|
}
|
|
511
787
|
};
|
|
512
788
|
const onBlur = async (_e) => {
|
|
513
|
-
ctx.touchedFields.
|
|
514
|
-
...ctx.touchedFields.value,
|
|
515
|
-
[name]: true
|
|
516
|
-
};
|
|
789
|
+
markFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
517
790
|
if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") {
|
|
518
791
|
await validate(name);
|
|
519
792
|
const fieldOpts = ctx.fieldOptions.get(name);
|
|
520
|
-
if (fieldOpts?.deps && fieldOpts.deps.length > 0)
|
|
793
|
+
if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
|
|
794
|
+
const uniqueDeps = [...new Set(fieldOpts.deps)];
|
|
795
|
+
await Promise.all(uniqueDeps.map((depField) => validate(depField)));
|
|
796
|
+
}
|
|
521
797
|
}
|
|
522
798
|
};
|
|
523
799
|
const refCallback = (el) => {
|
|
@@ -539,18 +815,17 @@ function createFieldRegistration(ctx, validate) {
|
|
|
539
815
|
clearTimeout(timer);
|
|
540
816
|
ctx.debounceTimers.delete(name);
|
|
541
817
|
}
|
|
818
|
+
const schemaTimer = ctx.schemaValidationTimers.get(name);
|
|
819
|
+
if (schemaTimer) {
|
|
820
|
+
clearTimeout(schemaTimer);
|
|
821
|
+
ctx.schemaValidationTimers.delete(name);
|
|
822
|
+
}
|
|
542
823
|
ctx.validationRequestIds.delete(name);
|
|
543
824
|
if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
|
|
544
825
|
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;
|
|
826
|
+
clearFieldErrors(ctx.errors, name);
|
|
827
|
+
clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
828
|
+
clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
554
829
|
ctx.fieldRefs.delete(name);
|
|
555
830
|
ctx.fieldOptions.delete(name);
|
|
556
831
|
ctx.fieldHandlers.delete(name);
|
|
@@ -574,10 +849,7 @@ function createFieldRegistration(ctx, validate) {
|
|
|
574
849
|
get: () => get(ctx.formData, name),
|
|
575
850
|
set: (val) => {
|
|
576
851
|
set(ctx.formData, name, val);
|
|
577
|
-
ctx.dirtyFields.
|
|
578
|
-
...ctx.dirtyFields.value,
|
|
579
|
-
[name]: true
|
|
580
|
-
};
|
|
852
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
581
853
|
}
|
|
582
854
|
}) }
|
|
583
855
|
};
|
|
@@ -585,21 +857,9 @@ function createFieldRegistration(ctx, validate) {
|
|
|
585
857
|
function unregister(name, options) {
|
|
586
858
|
const opts = options || {};
|
|
587
859
|
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
|
-
}
|
|
860
|
+
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
861
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
862
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
603
863
|
ctx.fieldRefs.delete(name);
|
|
604
864
|
ctx.fieldOptions.delete(name);
|
|
605
865
|
ctx.fieldHandlers.delete(name);
|
|
@@ -608,6 +868,11 @@ function createFieldRegistration(ctx, validate) {
|
|
|
608
868
|
clearTimeout(timer);
|
|
609
869
|
ctx.debounceTimers.delete(name);
|
|
610
870
|
}
|
|
871
|
+
const schemaTimer = ctx.schemaValidationTimers.get(name);
|
|
872
|
+
if (schemaTimer) {
|
|
873
|
+
clearTimeout(schemaTimer);
|
|
874
|
+
ctx.schemaValidationTimers.delete(name);
|
|
875
|
+
}
|
|
611
876
|
ctx.validationRequestIds.delete(name);
|
|
612
877
|
}
|
|
613
878
|
return {
|
|
@@ -644,6 +909,35 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
644
909
|
indexCache.set(item.key, idx);
|
|
645
910
|
});
|
|
646
911
|
};
|
|
912
|
+
const appendToCache = (startIndex) => {
|
|
913
|
+
const items = fa.items.value;
|
|
914
|
+
for (let i = startIndex; i < items.length; i++) {
|
|
915
|
+
const item = items[i];
|
|
916
|
+
if (item) indexCache.set(item.key, i);
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
const updateCacheAfterInsert = (insertIndex, _insertCount) => {
|
|
920
|
+
const items = fa.items.value;
|
|
921
|
+
for (let i = insertIndex; i < items.length; i++) {
|
|
922
|
+
const item = items[i];
|
|
923
|
+
if (item) indexCache.set(item.key, i);
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
const swapInCache = (indexA, indexB) => {
|
|
927
|
+
const items = fa.items.value;
|
|
928
|
+
const itemA = items[indexA];
|
|
929
|
+
const itemB = items[indexB];
|
|
930
|
+
if (itemA) indexCache.set(itemA.key, indexA);
|
|
931
|
+
if (itemB) indexCache.set(itemB.key, indexB);
|
|
932
|
+
};
|
|
933
|
+
const updateCacheAfterRemove = (removedKey, startIndex) => {
|
|
934
|
+
indexCache.delete(removedKey);
|
|
935
|
+
const items = fa.items.value;
|
|
936
|
+
for (let i = startIndex; i < items.length; i++) {
|
|
937
|
+
const item = items[i];
|
|
938
|
+
if (item) indexCache.set(item.key, i);
|
|
939
|
+
}
|
|
940
|
+
};
|
|
647
941
|
const createItem = (key) => ({
|
|
648
942
|
key,
|
|
649
943
|
get index() {
|
|
@@ -686,11 +980,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
686
980
|
set(ctx.formData, name, newValues);
|
|
687
981
|
const newItems = values.map(() => createItem(generateId()));
|
|
688
982
|
fa.items.value = [...fa.items.value, ...newItems];
|
|
689
|
-
|
|
690
|
-
ctx.dirtyFields.
|
|
691
|
-
...ctx.dirtyFields.value,
|
|
692
|
-
[name]: true
|
|
693
|
-
};
|
|
983
|
+
appendToCache(insertIndex);
|
|
984
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
694
985
|
if (ctx.options.mode === "onChange") validate(name);
|
|
695
986
|
handleFocus(insertIndex, values.length, focusOptions);
|
|
696
987
|
return true;
|
|
@@ -711,11 +1002,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
711
1002
|
set(ctx.formData, name, newValues);
|
|
712
1003
|
const newItems = values.map(() => createItem(generateId()));
|
|
713
1004
|
fa.items.value = [...newItems, ...fa.items.value];
|
|
714
|
-
|
|
715
|
-
ctx.dirtyFields.
|
|
716
|
-
...ctx.dirtyFields.value,
|
|
717
|
-
[name]: true
|
|
718
|
-
};
|
|
1005
|
+
updateCacheAfterInsert(0, values.length);
|
|
1006
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
719
1007
|
if (ctx.options.mode === "onChange") validate(name);
|
|
720
1008
|
handleFocus(0, values.length, focusOptions);
|
|
721
1009
|
return true;
|
|
@@ -729,10 +1017,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
729
1017
|
const newValues = [...currentValues];
|
|
730
1018
|
newValues[index] = value;
|
|
731
1019
|
set(ctx.formData, name, newValues);
|
|
732
|
-
ctx.dirtyFields.
|
|
733
|
-
...ctx.dirtyFields.value,
|
|
734
|
-
[name]: true
|
|
735
|
-
};
|
|
1020
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
736
1021
|
if (ctx.options.mode === "onChange") validate(name);
|
|
737
1022
|
return true;
|
|
738
1023
|
};
|
|
@@ -754,11 +1039,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
754
1039
|
set(ctx.formData, name, newValues);
|
|
755
1040
|
const keyToRemove = fa.items.value[index]?.key;
|
|
756
1041
|
fa.items.value = fa.items.value.filter((item) => item.key !== keyToRemove);
|
|
757
|
-
|
|
758
|
-
ctx.dirtyFields.
|
|
759
|
-
...ctx.dirtyFields.value,
|
|
760
|
-
[name]: true
|
|
761
|
-
};
|
|
1042
|
+
if (keyToRemove) updateCacheAfterRemove(keyToRemove, index);
|
|
1043
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
762
1044
|
if (ctx.options.mode === "onChange") validate(name);
|
|
763
1045
|
return true;
|
|
764
1046
|
};
|
|
@@ -787,11 +1069,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
787
1069
|
...newItems,
|
|
788
1070
|
...fa.items.value.slice(clampedIndex)
|
|
789
1071
|
];
|
|
790
|
-
|
|
791
|
-
ctx.dirtyFields.
|
|
792
|
-
...ctx.dirtyFields.value,
|
|
793
|
-
[name]: true
|
|
794
|
-
};
|
|
1072
|
+
updateCacheAfterInsert(clampedIndex, values.length);
|
|
1073
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
795
1074
|
if (ctx.options.mode === "onChange") validate(name);
|
|
796
1075
|
handleFocus(clampedIndex, values.length, focusOptions);
|
|
797
1076
|
return true;
|
|
@@ -812,12 +1091,9 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
812
1091
|
newItems[indexA] = itemB;
|
|
813
1092
|
newItems[indexB] = itemA;
|
|
814
1093
|
fa.items.value = newItems;
|
|
815
|
-
|
|
1094
|
+
swapInCache(indexA, indexB);
|
|
816
1095
|
}
|
|
817
|
-
ctx.dirtyFields.
|
|
818
|
-
...ctx.dirtyFields.value,
|
|
819
|
-
[name]: true
|
|
820
|
-
};
|
|
1096
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
821
1097
|
if (ctx.options.mode === "onChange") validate(name);
|
|
822
1098
|
return true;
|
|
823
1099
|
};
|
|
@@ -840,12 +1116,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
840
1116
|
const clampedTo = Math.min(to, newItems.length);
|
|
841
1117
|
newItems.splice(clampedTo, 0, removedItem);
|
|
842
1118
|
fa.items.value = newItems;
|
|
843
|
-
|
|
1119
|
+
const minIdx = Math.min(from, clampedTo);
|
|
1120
|
+
const maxIdx = Math.max(from, clampedTo);
|
|
1121
|
+
const items = fa.items.value;
|
|
1122
|
+
for (let i = minIdx; i <= maxIdx; i++) {
|
|
1123
|
+
const item = items[i];
|
|
1124
|
+
if (item) indexCache.set(item.key, i);
|
|
1125
|
+
}
|
|
844
1126
|
}
|
|
845
|
-
ctx.dirtyFields.
|
|
846
|
-
...ctx.dirtyFields.value,
|
|
847
|
-
[name]: true
|
|
848
|
-
};
|
|
1127
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
849
1128
|
if (ctx.options.mode === "onChange") validate(name);
|
|
850
1129
|
return true;
|
|
851
1130
|
};
|
|
@@ -854,10 +1133,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
854
1133
|
set(ctx.formData, name, newValues);
|
|
855
1134
|
fa.items.value = newValues.map(() => createItem(generateId()));
|
|
856
1135
|
rebuildIndexCache();
|
|
857
|
-
ctx.dirtyFields.
|
|
858
|
-
...ctx.dirtyFields.value,
|
|
859
|
-
[name]: true
|
|
860
|
-
};
|
|
1136
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
861
1137
|
if (ctx.options.mode === "onChange") validate(name);
|
|
862
1138
|
return true;
|
|
863
1139
|
};
|
|
@@ -872,11 +1148,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
872
1148
|
}
|
|
873
1149
|
set(ctx.formData, name, []);
|
|
874
1150
|
fa.items.value = [];
|
|
875
|
-
|
|
876
|
-
ctx.dirtyFields.
|
|
877
|
-
...ctx.dirtyFields.value,
|
|
878
|
-
[name]: true
|
|
879
|
-
};
|
|
1151
|
+
indexCache.clear();
|
|
1152
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
880
1153
|
if (ctx.options.mode === "onChange") validate(name);
|
|
881
1154
|
return true;
|
|
882
1155
|
};
|
|
@@ -895,14 +1168,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
|
|
|
895
1168
|
}
|
|
896
1169
|
const sortedIndices = [...new Set(validIndices)].sort((a, b) => b - a);
|
|
897
1170
|
const indicesToRemove = new Set(sortedIndices);
|
|
1171
|
+
const keysToRemove = fa.items.value.filter((_, i) => indicesToRemove.has(i)).map((item) => item.key);
|
|
898
1172
|
const newValues = currentValues.filter((_, i) => !indicesToRemove.has(i));
|
|
899
1173
|
set(ctx.formData, name, newValues);
|
|
900
1174
|
fa.items.value = fa.items.value.filter((_, i) => !indicesToRemove.has(i));
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1175
|
+
for (const key of keysToRemove) indexCache.delete(key);
|
|
1176
|
+
fa.items.value.forEach((item, idx) => {
|
|
1177
|
+
indexCache.set(item.key, idx);
|
|
1178
|
+
});
|
|
1179
|
+
markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
906
1180
|
if (ctx.options.mode === "onChange") validate(name);
|
|
907
1181
|
return true;
|
|
908
1182
|
};
|
|
@@ -934,33 +1208,6 @@ function updateDomElement(el, value) {
|
|
|
934
1208
|
if (el.type === "checkbox") el.checked = value;
|
|
935
1209
|
else el.value = value;
|
|
936
1210
|
}
|
|
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
1211
|
function useForm(options) {
|
|
965
1212
|
const ctx = createFormContext(options);
|
|
966
1213
|
const { validate, clearAllPendingErrors } = createValidation(ctx);
|
|
@@ -984,32 +1231,84 @@ function useForm(options) {
|
|
|
984
1231
|
}
|
|
985
1232
|
const setFocusWrapper = (name) => setFocus(name);
|
|
986
1233
|
const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper);
|
|
1234
|
+
let lastSyncTime = 0;
|
|
1235
|
+
const SYNC_DEBOUNCE_MS = 16;
|
|
1236
|
+
function syncWithDebounce() {
|
|
1237
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
1238
|
+
if (now - lastSyncTime < SYNC_DEBOUNCE_MS) return;
|
|
1239
|
+
syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
|
|
1240
|
+
lastSyncTime = now;
|
|
1241
|
+
}
|
|
1242
|
+
let lastErrors = ctx.errors.value;
|
|
1243
|
+
let lastExternalErrors = ctx.externalErrors.value;
|
|
1244
|
+
let cachedMergedErrors = null;
|
|
987
1245
|
function getMergedErrors() {
|
|
988
|
-
return
|
|
1246
|
+
if (cachedMergedErrors !== null && lastErrors === ctx.errors.value && lastExternalErrors === ctx.externalErrors.value) return cachedMergedErrors;
|
|
1247
|
+
lastErrors = ctx.errors.value;
|
|
1248
|
+
lastExternalErrors = ctx.externalErrors.value;
|
|
1249
|
+
cachedMergedErrors = {
|
|
989
1250
|
...ctx.errors.value,
|
|
990
1251
|
...ctx.externalErrors.value
|
|
991
1252
|
};
|
|
1253
|
+
return cachedMergedErrors;
|
|
992
1254
|
}
|
|
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
|
-
};
|
|
1255
|
+
const isDirtyComputed = (0, vue.computed)(() => ctx.dirtyFieldCount.value > 0);
|
|
1256
|
+
const errorsComputed = (0, vue.computed)(() => getMergedErrors());
|
|
1257
|
+
const isValidComputed = (0, vue.computed)(() => {
|
|
1258
|
+
if (!(ctx.submitCount.value > 0 || ctx.touchedFieldCount.value > 0)) return false;
|
|
1259
|
+
return Object.keys(errorsComputed.value).length === 0;
|
|
1012
1260
|
});
|
|
1261
|
+
const isReadyComputed = (0, vue.computed)(() => !ctx.isLoading.value);
|
|
1262
|
+
const isValidatingComputed = (0, vue.computed)(() => ctx.validatingFields.value.size > 0);
|
|
1263
|
+
const isSubmittedComputed = (0, vue.computed)(() => ctx.submitCount.value > 0);
|
|
1264
|
+
const formStateInternal = (0, vue.reactive)({
|
|
1265
|
+
get errors() {
|
|
1266
|
+
return errorsComputed.value;
|
|
1267
|
+
},
|
|
1268
|
+
get isDirty() {
|
|
1269
|
+
return isDirtyComputed.value;
|
|
1270
|
+
},
|
|
1271
|
+
get dirtyFields() {
|
|
1272
|
+
return ctx.dirtyFields.value;
|
|
1273
|
+
},
|
|
1274
|
+
get isValid() {
|
|
1275
|
+
return isValidComputed.value;
|
|
1276
|
+
},
|
|
1277
|
+
get isSubmitting() {
|
|
1278
|
+
return ctx.isSubmitting.value;
|
|
1279
|
+
},
|
|
1280
|
+
get isLoading() {
|
|
1281
|
+
return ctx.isLoading.value;
|
|
1282
|
+
},
|
|
1283
|
+
get isReady() {
|
|
1284
|
+
return isReadyComputed.value;
|
|
1285
|
+
},
|
|
1286
|
+
get isValidating() {
|
|
1287
|
+
return isValidatingComputed.value;
|
|
1288
|
+
},
|
|
1289
|
+
get validatingFields() {
|
|
1290
|
+
return ctx.validatingFields.value;
|
|
1291
|
+
},
|
|
1292
|
+
get touchedFields() {
|
|
1293
|
+
return ctx.touchedFields.value;
|
|
1294
|
+
},
|
|
1295
|
+
get submitCount() {
|
|
1296
|
+
return ctx.submitCount.value;
|
|
1297
|
+
},
|
|
1298
|
+
get defaultValuesError() {
|
|
1299
|
+
return ctx.defaultValuesError.value;
|
|
1300
|
+
},
|
|
1301
|
+
get isSubmitted() {
|
|
1302
|
+
return isSubmittedComputed.value;
|
|
1303
|
+
},
|
|
1304
|
+
get isSubmitSuccessful() {
|
|
1305
|
+
return ctx.isSubmitSuccessful.value;
|
|
1306
|
+
},
|
|
1307
|
+
get disabled() {
|
|
1308
|
+
return ctx.isDisabled.value;
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
const formState = (0, vue.computed)(() => formStateInternal);
|
|
1013
1312
|
function handleSubmit(onValid, onInvalid) {
|
|
1014
1313
|
return async (e) => {
|
|
1015
1314
|
e.preventDefault();
|
|
@@ -1019,7 +1318,7 @@ function useForm(options) {
|
|
|
1019
1318
|
ctx.submitCount.value++;
|
|
1020
1319
|
ctx.isSubmitSuccessful.value = false;
|
|
1021
1320
|
try {
|
|
1022
|
-
|
|
1321
|
+
syncWithDebounce();
|
|
1023
1322
|
if (await validate()) {
|
|
1024
1323
|
await onValid(ctx.formData);
|
|
1025
1324
|
ctx.isSubmitSuccessful.value = true;
|
|
@@ -1045,8 +1344,8 @@ function useForm(options) {
|
|
|
1045
1344
|
}
|
|
1046
1345
|
}
|
|
1047
1346
|
set(ctx.formData, name, value);
|
|
1048
|
-
if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, name);
|
|
1049
|
-
if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, name);
|
|
1347
|
+
if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
1348
|
+
if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
1050
1349
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
1051
1350
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
1052
1351
|
if (fieldRef?.value) updateDomElement(fieldRef.value, value);
|
|
@@ -1057,15 +1356,23 @@ function useForm(options) {
|
|
|
1057
1356
|
const opts = resetOptions || {};
|
|
1058
1357
|
ctx.resetGeneration.value++;
|
|
1059
1358
|
clearAllPendingErrors();
|
|
1060
|
-
ctx.validatingFields.value =
|
|
1359
|
+
ctx.validatingFields.value = /* @__PURE__ */ new Set();
|
|
1360
|
+
ctx.validationCache.clear();
|
|
1361
|
+
for (const timer of ctx.schemaValidationTimers.values()) clearTimeout(timer);
|
|
1362
|
+
ctx.schemaValidationTimers.clear();
|
|
1061
1363
|
if (!opts.keepDefaultValues && values) Object.assign(ctx.defaultValues, values);
|
|
1062
1364
|
Object.keys(ctx.formData).forEach((key) => delete ctx.formData[key]);
|
|
1063
|
-
const
|
|
1064
|
-
const newValues = JSON.parse(JSON.stringify(sourceValues));
|
|
1365
|
+
const newValues = deepClone(values || ctx.defaultValues);
|
|
1065
1366
|
Object.assign(ctx.formData, newValues);
|
|
1066
1367
|
if (!opts.keepErrors) ctx.errors.value = {};
|
|
1067
|
-
if (!opts.keepTouched)
|
|
1068
|
-
|
|
1368
|
+
if (!opts.keepTouched) {
|
|
1369
|
+
ctx.touchedFields.value = {};
|
|
1370
|
+
ctx.touchedFieldCount.value = 0;
|
|
1371
|
+
}
|
|
1372
|
+
if (!opts.keepDirty) {
|
|
1373
|
+
ctx.dirtyFields.value = {};
|
|
1374
|
+
ctx.dirtyFieldCount.value = 0;
|
|
1375
|
+
}
|
|
1069
1376
|
if (!opts.keepSubmitCount) ctx.submitCount.value = 0;
|
|
1070
1377
|
if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
|
|
1071
1378
|
if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
|
|
@@ -1099,11 +1406,11 @@ function useForm(options) {
|
|
|
1099
1406
|
let defaultValue = opts.defaultValue;
|
|
1100
1407
|
if (defaultValue === void 0) defaultValue = get(ctx.defaultValues, name);
|
|
1101
1408
|
else set(ctx.defaultValues, name, defaultValue);
|
|
1102
|
-
const clonedValue = defaultValue !== void 0 ?
|
|
1409
|
+
const clonedValue = defaultValue !== void 0 ? deepClone(defaultValue) : void 0;
|
|
1103
1410
|
set(ctx.formData, name, clonedValue);
|
|
1104
1411
|
if (!opts.keepError) clearFieldErrors(ctx.errors, name);
|
|
1105
|
-
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
|
|
1106
|
-
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
|
|
1412
|
+
if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
|
|
1413
|
+
if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
|
|
1107
1414
|
if (!ctx.fieldOptions.get(name)?.controlled) {
|
|
1108
1415
|
const fieldRef = ctx.fieldRefs.get(name);
|
|
1109
1416
|
if (fieldRef?.value) updateDomElement(fieldRef.value, clonedValue ?? (fieldRef.value.type === "checkbox" ? false : ""));
|
|
@@ -1193,7 +1500,7 @@ function useForm(options) {
|
|
|
1193
1500
|
}
|
|
1194
1501
|
}
|
|
1195
1502
|
}
|
|
1196
|
-
|
|
1503
|
+
syncWithDebounce();
|
|
1197
1504
|
if (nameOrNames === void 0) return { ...ctx.formData };
|
|
1198
1505
|
if (Array.isArray(nameOrNames)) {
|
|
1199
1506
|
const result = {};
|
|
@@ -1300,7 +1607,12 @@ function useController(options) {
|
|
|
1300
1607
|
form.setValue(name, newValue);
|
|
1301
1608
|
};
|
|
1302
1609
|
const onBlur = () => {
|
|
1303
|
-
form.
|
|
1610
|
+
const currentValue = form.getValues(name);
|
|
1611
|
+
form.setValue(name, currentValue, {
|
|
1612
|
+
shouldTouch: true,
|
|
1613
|
+
shouldValidate: true,
|
|
1614
|
+
shouldDirty: false
|
|
1615
|
+
});
|
|
1304
1616
|
};
|
|
1305
1617
|
const refCallback = (el) => {
|
|
1306
1618
|
elementRef.value = el;
|