vee-validate 4.2.3 → 4.3.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * vee-validate v4.2.3
2
+ * vee-validate v4.3.0
3
3
  * (c) 2021 Abdelrahman Awad
4
4
  * @license MIT
5
5
  */
@@ -11,6 +11,10 @@ function isCallable(fn) {
11
11
  const isObject = (obj) => obj !== null && !!obj && typeof obj === 'object' && !Array.isArray(obj);
12
12
  function isIndex(value) {
13
13
  return Number(value) >= 0;
14
+ }
15
+ function toNumber(value) {
16
+ const n = parseFloat(value);
17
+ return isNaN(n) ? value : n;
14
18
  }
15
19
 
16
20
  const RULES = {};
@@ -38,6 +42,66 @@ function guardExtend(id, validator) {
38
42
  throw new Error(`Extension Error: The validator '${id}' must be a function.`);
39
43
  }
40
44
 
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ const normalizeChildren = (context, slotProps) => {
47
+ if (!context.slots.default) {
48
+ return context.slots.default;
49
+ }
50
+ return context.slots.default(slotProps);
51
+ };
52
+ /**
53
+ * Vue adds a `_value` prop at the moment on the input elements to store the REAL value on them, real values are different than the `value` attribute
54
+ * as they do not get casted to strings unlike `el.value` which preserves user-code behavior
55
+ */
56
+ function getBoundValue(el) {
57
+ if (hasValueBinding(el)) {
58
+ return el._value;
59
+ }
60
+ return undefined;
61
+ }
62
+ /**
63
+ * Vue adds a `_value` prop at the moment on the input elements to store the REAL value on them, real values are different than the `value` attribute
64
+ * as they do not get casted to strings unlike `el.value` which preserves user-code behavior
65
+ */
66
+ function hasValueBinding(el) {
67
+ return '_value' in el;
68
+ }
69
+
70
+ const isEvent = (evt) => {
71
+ if (!evt) {
72
+ return false;
73
+ }
74
+ if (typeof Event !== 'undefined' && isCallable(Event) && evt instanceof Event) {
75
+ return true;
76
+ }
77
+ // this is for IE and Cypress #3161
78
+ /* istanbul ignore next */
79
+ if (evt && evt.srcElement) {
80
+ return true;
81
+ }
82
+ return false;
83
+ };
84
+ function normalizeEventValue(value) {
85
+ if (!isEvent(value)) {
86
+ return value;
87
+ }
88
+ const input = value.target;
89
+ // Vue sets the current bound value on `_value` prop
90
+ // for checkboxes it it should fetch the value binding type as is (boolean instead of string)
91
+ if (hasCheckedAttr(input.type) && hasValueBinding(input)) {
92
+ return getBoundValue(input);
93
+ }
94
+ if (input.type === 'file' && input.files) {
95
+ return Array.from(input.files);
96
+ }
97
+ if (isNativeMultiSelect(input)) {
98
+ return Array.from(input.options)
99
+ .filter(opt => opt.selected && !opt.disabled)
100
+ .map(getBoundValue);
101
+ }
102
+ return input.value;
103
+ }
104
+
41
105
  function isLocator(value) {
42
106
  return isCallable(value) && !!value.__locatorRef;
43
107
  }
@@ -100,6 +164,9 @@ function isNativeMultiSelectNode(tag, attrs) {
100
164
  */
101
165
  function shouldHaveValueBinding(tag, attrs) {
102
166
  return isNativeMultiSelectNode(tag, attrs) || isFileInputNode(tag, attrs);
167
+ }
168
+ function isFormSubmitEvent(evt) {
169
+ return isEvent(evt) && evt.target && 'submit' in evt.target;
103
170
  }
104
171
 
105
172
  function cleanupNonNestedPath(path) {
@@ -222,6 +289,20 @@ function normalizeField(field) {
222
289
  }
223
290
  return field;
224
291
  }
292
+ /**
293
+ * Applies a mutation function on a field or field group
294
+ */
295
+ function applyFieldMutation(field, mutation, onlyFirst = false) {
296
+ if (!Array.isArray(field)) {
297
+ mutation(field);
298
+ return;
299
+ }
300
+ if (onlyFirst) {
301
+ mutation(field[0]);
302
+ return;
303
+ }
304
+ field.forEach(mutation);
305
+ }
225
306
  function resolveNextCheckboxValue(currentValue, checkedValue, uncheckedValue) {
226
307
  if (Array.isArray(currentValue)) {
227
308
  const newVal = [...currentValue];
@@ -232,66 +313,6 @@ function resolveNextCheckboxValue(currentValue, checkedValue, uncheckedValue) {
232
313
  return currentValue === checkedValue ? uncheckedValue : checkedValue;
233
314
  }
234
315
 
235
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
236
- const normalizeChildren = (context, slotProps) => {
237
- if (!context.slots.default) {
238
- return context.slots.default;
239
- }
240
- return context.slots.default(slotProps);
241
- };
242
- /**
243
- * Vue adds a `_value` prop at the moment on the input elements to store the REAL value on them, real values are different than the `value` attribute
244
- * as they do not get casted to strings unlike `el.value` which preserves user-code behavior
245
- */
246
- function getBoundValue(el) {
247
- if (hasValueBinding(el)) {
248
- return el._value;
249
- }
250
- return undefined;
251
- }
252
- /**
253
- * Vue adds a `_value` prop at the moment on the input elements to store the REAL value on them, real values are different than the `value` attribute
254
- * as they do not get casted to strings unlike `el.value` which preserves user-code behavior
255
- */
256
- function hasValueBinding(el) {
257
- return '_value' in el;
258
- }
259
-
260
- const isEvent = (evt) => {
261
- if (!evt) {
262
- return false;
263
- }
264
- if (typeof Event !== 'undefined' && isCallable(Event) && evt instanceof Event) {
265
- return true;
266
- }
267
- // this is for IE and Cypress #3161
268
- /* istanbul ignore next */
269
- if (evt && evt.srcElement) {
270
- return true;
271
- }
272
- return false;
273
- };
274
- function normalizeEventValue(value) {
275
- if (!isEvent(value)) {
276
- return value;
277
- }
278
- const input = value.target;
279
- // Vue sets the current bound value on `_value` prop
280
- // for checkboxes it it should fetch the value binding type as is (boolean instead of string)
281
- if (hasCheckedAttr(input.type) && hasValueBinding(input)) {
282
- return getBoundValue(input);
283
- }
284
- if (input.type === 'file' && input.files) {
285
- return Array.from(input.files);
286
- }
287
- if (isNativeMultiSelect(input)) {
288
- return Array.from(input.options)
289
- .filter(opt => opt.selected && !opt.disabled)
290
- .map(getBoundValue);
291
- }
292
- return input.value;
293
- }
294
-
295
316
  /**
296
317
  * Normalizes the given rules expression.
297
318
  */
@@ -624,7 +645,7 @@ function useField(name, rules, opts) {
624
645
  const fid = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER;
625
646
  const { initialValue, validateOnMount, bails, type, valueProp, label, validateOnValueUpdate, uncheckedValue, } = normalizeOptions(unref(name), opts);
626
647
  const form = injectWithSelf(FormContextSymbol);
627
- const { meta, errors, errorMessage, handleBlur, handleInput, resetValidationState, setValidationState, value, checked, } = useValidationState({
648
+ const { meta, errors, errorMessage, handleBlur, handleInput, resetValidationState, setValidationState, setErrors, value, checked, } = useValidationState({
628
649
  name,
629
650
  initValue: initialValue,
630
651
  form,
@@ -633,7 +654,7 @@ function useField(name, rules, opts) {
633
654
  });
634
655
  const normalizedRules = computed(() => {
635
656
  let rulesValue = unref(rules);
636
- const schema = form === null || form === void 0 ? void 0 : form.schema;
657
+ const schema = unref(form === null || form === void 0 ? void 0 : form.schema);
637
658
  if (schema && !isYupValidator(schema)) {
638
659
  rulesValue = extractRuleFromSchema(schema, unref(name)) || rulesValue;
639
660
  }
@@ -646,6 +667,7 @@ function useField(name, rules, opts) {
646
667
  var _a, _b;
647
668
  meta.pending = true;
648
669
  let result;
670
+ meta.validated = true;
649
671
  if (!form || !form.validateSchema) {
650
672
  result = await validate(value.value, normalizedRules.value, {
651
673
  name: unref(label) || unref(name),
@@ -654,7 +676,7 @@ function useField(name, rules, opts) {
654
676
  });
655
677
  }
656
678
  else {
657
- result = (_b = (await form.validateSchema())[unref(name)]) !== null && _b !== void 0 ? _b : { valid: true, errors: [] };
679
+ result = (_b = (await form.validateSchema('validated-only'))[unref(name)]) !== null && _b !== void 0 ? _b : { valid: true, errors: [] };
658
680
  }
659
681
  meta.pending = false;
660
682
  return setValidationState(result);
@@ -670,7 +692,7 @@ function useField(name, rules, opts) {
670
692
  });
671
693
  }
672
694
  else {
673
- result = (_c = (_b = (await form.validateSchema(false))) === null || _b === void 0 ? void 0 : _b[unref(name)]) !== null && _c !== void 0 ? _c : { valid: true, errors: [] };
695
+ result = (_c = (_b = (await form.validateSchema('silent'))) === null || _b === void 0 ? void 0 : _b[unref(name)]) !== null && _c !== void 0 ? _c : { valid: true, errors: [] };
674
696
  }
675
697
  meta.valid = result.valid;
676
698
  }
@@ -686,7 +708,6 @@ function useField(name, rules, opts) {
686
708
  newValue = resolveNextCheckboxValue(value.value, unref(valueProp), unref(uncheckedValue));
687
709
  }
688
710
  value.value = newValue;
689
- meta.hadValueUserInteraction = true;
690
711
  if (!validateOnValueUpdate) {
691
712
  return validateWithStateMutation();
692
713
  }
@@ -737,6 +758,7 @@ function useField(name, rules, opts) {
737
758
  handleInput,
738
759
  setValidationState,
739
760
  setTouched,
761
+ setErrors,
740
762
  };
741
763
  provide(FieldContextSymbol, field);
742
764
  if (isRef(rules) && typeof unref(rules) !== 'function') {
@@ -844,7 +866,6 @@ function useValidationState({ name, initValue, form, type, valueProp, }) {
844
866
  if (!hasCheckedAttr(type)) {
845
867
  value.value = normalizeEventValue(e);
846
868
  }
847
- meta.hadValueUserInteraction = true;
848
869
  };
849
870
  // Updates the validation state with the validation result
850
871
  function setValidationState(result) {
@@ -867,12 +888,13 @@ function useValidationState({ name, initValue, form, type, valueProp, }) {
867
888
  setErrors((state === null || state === void 0 ? void 0 : state.errors) || []);
868
889
  meta.touched = (_b = state === null || state === void 0 ? void 0 : state.touched) !== null && _b !== void 0 ? _b : false;
869
890
  meta.pending = false;
870
- meta.hadValueUserInteraction = false;
891
+ meta.validated = false;
871
892
  }
872
893
  return {
873
894
  meta,
874
895
  errors,
875
896
  errorMessage,
897
+ setErrors,
876
898
  setValidationState,
877
899
  resetValidationState,
878
900
  handleBlur,
@@ -889,7 +911,7 @@ function useMeta(initialValue, currentValue, errors) {
889
911
  touched: false,
890
912
  pending: false,
891
913
  valid: true,
892
- hadValueUserInteraction: false,
914
+ validated: false,
893
915
  initialValue: computed(() => unref(initialValue)),
894
916
  dirty: computed(() => {
895
917
  return !es6(currentValue.value, unref(initialValue));
@@ -942,7 +964,7 @@ function useErrorsSource(path, form) {
942
964
  errors: computed(() => errors.value),
943
965
  errorMessage: computed(() => errors.value[0]),
944
966
  setErrors: (messages) => {
945
- errors.value = messages;
967
+ errors.value = Array.isArray(messages) ? messages : [messages];
946
968
  },
947
969
  };
948
970
  }
@@ -1007,6 +1029,10 @@ const Field = defineComponent({
1007
1029
  modelValue: {
1008
1030
  type: null,
1009
1031
  },
1032
+ modelModifiers: {
1033
+ type: null,
1034
+ default: () => ({}),
1035
+ },
1010
1036
  },
1011
1037
  emits: ['update:modelValue'],
1012
1038
  setup(props, ctx) {
@@ -1014,7 +1040,7 @@ const Field = defineComponent({
1014
1040
  const name = toRef(props, 'name');
1015
1041
  const label = toRef(props, 'label');
1016
1042
  const uncheckedValue = toRef(props, 'uncheckedValue');
1017
- const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, handleInput, setTouched, resetField, handleReset, meta, checked, } = useField(name, rules, {
1043
+ const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, handleInput, setTouched, resetField, handleReset, meta, checked, setErrors, } = useField(name, rules, {
1018
1044
  validateOnMount: props.validateOnMount,
1019
1045
  bails: props.bails,
1020
1046
  type: ctx.attrs.type,
@@ -1084,12 +1110,13 @@ const Field = defineComponent({
1084
1110
  handleReset,
1085
1111
  handleBlur,
1086
1112
  setTouched,
1113
+ setErrors,
1087
1114
  };
1088
1115
  });
1089
1116
  if ('modelValue' in props) {
1090
1117
  const modelValue = toRef(props, 'modelValue');
1091
1118
  watch(modelValue, newModelValue => {
1092
- if (newModelValue !== value.value) {
1119
+ if (newModelValue !== applyModifiers(value.value, props.modelModifiers)) {
1093
1120
  value.value = newModelValue;
1094
1121
  validateField();
1095
1122
  }
@@ -1121,6 +1148,12 @@ function resolveValidationTriggers(props) {
1121
1148
  validateOnBlur: (_c = props.validateOnBlur) !== null && _c !== void 0 ? _c : validateOnBlur,
1122
1149
  validateOnModelUpdate: (_d = props.validateOnModelUpdate) !== null && _d !== void 0 ? _d : validateOnModelUpdate,
1123
1150
  };
1151
+ }
1152
+ function applyModifiers(value, modifiers) {
1153
+ if (modifiers.number) {
1154
+ return toNumber(value);
1155
+ }
1156
+ return value;
1124
1157
  }
1125
1158
 
1126
1159
  function useForm(opts) {
@@ -1233,11 +1266,7 @@ function useForm(opts) {
1233
1266
  if (!fieldInstance) {
1234
1267
  return;
1235
1268
  }
1236
- if (Array.isArray(fieldInstance)) {
1237
- fieldInstance.forEach(f => f.setTouched(isTouched));
1238
- return;
1239
- }
1240
- fieldInstance.setTouched(isTouched);
1269
+ applyFieldMutation(fieldInstance, f => f.setTouched(isTouched));
1241
1270
  }
1242
1271
  /**
1243
1272
  * Sets the touched meta state on multiple fields
@@ -1330,7 +1359,7 @@ function useForm(opts) {
1330
1359
  return acc;
1331
1360
  }
1332
1361
  if (formCtx.validateSchema) {
1333
- return formCtx.validateSchema(true).then(results => {
1362
+ return formCtx.validateSchema('force').then(results => {
1334
1363
  return keysOf(results)
1335
1364
  .map(r => ({ key: r, errors: results[r].errors }))
1336
1365
  .reduce(resultReducer, { errors: {}, valid: true });
@@ -1396,6 +1425,7 @@ function useForm(opts) {
1396
1425
  setInPath(formValues, path, value);
1397
1426
  setInPath(initialValues.value, path, value);
1398
1427
  }
1428
+ const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
1399
1429
  const formCtx = {
1400
1430
  register: registerField,
1401
1431
  unregister: unregisterField,
@@ -1403,11 +1433,11 @@ function useForm(opts) {
1403
1433
  values: formValues,
1404
1434
  setFieldErrorBag,
1405
1435
  errorBag,
1406
- schema: opts === null || opts === void 0 ? void 0 : opts.validationSchema,
1436
+ schema,
1407
1437
  submitCount,
1408
- validateSchema: isYupValidator(opts === null || opts === void 0 ? void 0 : opts.validationSchema)
1409
- ? (shouldMutate = false) => {
1410
- return validateYupSchema(formCtx, shouldMutate);
1438
+ validateSchema: isYupValidator(unref(schema))
1439
+ ? mode => {
1440
+ return validateYupSchema(formCtx, mode);
1411
1441
  }
1412
1442
  : undefined,
1413
1443
  validate,
@@ -1431,9 +1461,8 @@ function useForm(opts) {
1431
1461
  }, {});
1432
1462
  });
1433
1463
  const submitForm = handleSubmit((_, { evt }) => {
1434
- var _a, _b;
1435
- if (evt) {
1436
- (_b = (_a = evt === null || evt === void 0 ? void 0 : evt.target) === null || _a === void 0 ? void 0 : _a.submit) === null || _b === void 0 ? void 0 : _b.call(_a);
1464
+ if (isFormSubmitEvent(evt)) {
1465
+ evt.target.submit();
1437
1466
  }
1438
1467
  });
1439
1468
  // Trigger initial validation
@@ -1452,9 +1481,15 @@ function useForm(opts) {
1452
1481
  // otherwise run initial silent validation through schema if available
1453
1482
  // the useField should skip their own silent validation if a yup schema is present
1454
1483
  if (formCtx.validateSchema) {
1455
- formCtx.validateSchema(false);
1484
+ formCtx.validateSchema('silent');
1456
1485
  }
1457
1486
  });
1487
+ if (isRef(schema)) {
1488
+ watch(schema, () => {
1489
+ var _a;
1490
+ (_a = formCtx.validateSchema) === null || _a === void 0 ? void 0 : _a.call(formCtx, 'validated-only');
1491
+ });
1492
+ }
1458
1493
  // Provide injections
1459
1494
  provide(FormContextSymbol, formCtx);
1460
1495
  provide(FormErrorsSymbol, errors);
@@ -1499,8 +1534,8 @@ function useFormMeta(fields, currentValues, initialValues) {
1499
1534
  return Object.assign(Object.assign({ initialValues: unref(initialValues) }, flags), { dirty: isDirty.value });
1500
1535
  });
1501
1536
  }
1502
- async function validateYupSchema(form, shouldMutate = false) {
1503
- const errors = await form.schema
1537
+ async function validateYupSchema(form, mode) {
1538
+ const errors = await unref(form.schema)
1504
1539
  .validate(form.values, { abortEarly: false })
1505
1540
  .then(() => [])
1506
1541
  .catch((err) => {
@@ -1526,24 +1561,15 @@ async function validateYupSchema(form, shouldMutate = false) {
1526
1561
  valid: !messages.length,
1527
1562
  };
1528
1563
  result[fieldId] = fieldResult;
1529
- const hadInteraction = Array.isArray(field)
1530
- ? field.some(f => f.meta.hadValueUserInteraction)
1531
- : field.meta.hadValueUserInteraction;
1532
- if (!shouldMutate && !hadInteraction) {
1533
- // Update the valid flag regardless to keep it accurate
1534
- if (Array.isArray(field)) {
1535
- field.forEach(f => (f.meta.valid = fieldResult.valid));
1536
- }
1537
- else {
1538
- field.meta.valid = fieldResult.valid;
1539
- }
1564
+ if (mode === 'silent') {
1565
+ applyFieldMutation(field, f => (f.meta.valid = fieldResult.valid));
1540
1566
  return result;
1541
1567
  }
1542
- if (Array.isArray(field)) {
1543
- field[0].setValidationState(fieldResult);
1568
+ const wasValidated = Array.isArray(field) ? field.some(f => f.meta.validated) : field.meta.validated;
1569
+ if (mode === 'validated-only' && !wasValidated) {
1544
1570
  return result;
1545
1571
  }
1546
- field.setValidationState(fieldResult);
1572
+ applyFieldMutation(field, f => f.setValidationState(fieldResult), true);
1547
1573
  return result;
1548
1574
  }, {});
1549
1575
  return aggregatedResult;
@@ -1562,14 +1588,14 @@ function useFormInitialValues(fields, formValues, providedValues) {
1562
1588
  if (!updateFields) {
1563
1589
  return;
1564
1590
  }
1565
- // update the non-interacted-by-user fields
1591
+ // update the pristine non-touched fields
1566
1592
  // those are excluded because it's unlikely you want to change the form values using initial values
1567
1593
  // we mostly watch them for API population or newly inserted fields
1568
1594
  // if the user API is taking too much time before user interaction they should consider disabling or hiding their inputs until the values are ready
1569
- const isSafeToUpdate = (f) => f.meta.hadValueUserInteraction;
1595
+ const hadInteraction = (f) => f.meta.touched;
1570
1596
  keysOf(fields.value).forEach(fieldPath => {
1571
1597
  const field = fields.value[fieldPath];
1572
- const touchedByUser = Array.isArray(field) ? field.some(isSafeToUpdate) : isSafeToUpdate(field);
1598
+ const touchedByUser = Array.isArray(field) ? field.some(hadInteraction) : hadInteraction(field);
1573
1599
  if (touchedByUser) {
1574
1600
  return;
1575
1601
  }
@@ -1652,8 +1678,9 @@ const Form = defineComponent({
1652
1678
  },
1653
1679
  setup(props, ctx) {
1654
1680
  const initialValues = toRef(props, 'initialValues');
1681
+ const validationSchema = toRef(props, 'validationSchema');
1655
1682
  const { errors, values, meta, isSubmitting, submitCount, validate, validateField, handleReset, resetForm, handleSubmit, submitForm, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
1656
- validationSchema: props.validationSchema,
1683
+ validationSchema: validationSchema.value ? validationSchema : undefined,
1657
1684
  initialValues,
1658
1685
  initialErrors: props.initialErrors,
1659
1686
  initialTouched: props.initialTouched,