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