vee-validate 4.6.10 → 4.7.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * vee-validate v4.6.10
2
+ * vee-validate v4.7.1
3
3
  * (c) 2022 Abdelrahman Awad
4
4
  * @license MIT
5
5
  */
@@ -228,50 +228,59 @@
228
228
  return a instanceof File;
229
229
  }
230
230
 
231
- // do not edit .js files directly - edit src/index.jst
232
-
233
-
234
-
235
- var fastDeepEqual = function equal(a, b) {
236
- if (a === b) return true;
237
-
238
- if (a && b && typeof a == 'object' && typeof b == 'object') {
239
- if (a.constructor !== b.constructor) return false;
240
-
241
- var length, i, keys;
242
- if (Array.isArray(a)) {
243
- length = a.length;
244
- if (length != b.length) return false;
245
- for (i = length; i-- !== 0;)
246
- if (!equal(a[i], b[i])) return false;
247
- return true;
248
- }
249
-
250
-
251
-
252
- if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
253
- if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
254
- if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
231
+ function set(obj, key, val) {
232
+ if (typeof val.value === 'object') val.value = klona(val.value);
233
+ if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === '__proto__') {
234
+ Object.defineProperty(obj, key, val);
235
+ } else obj[key] = val.value;
236
+ }
255
237
 
256
- keys = Object.keys(a);
257
- length = keys.length;
258
- if (length !== Object.keys(b).length) return false;
238
+ function klona(x) {
239
+ if (typeof x !== 'object') return x;
259
240
 
260
- for (i = length; i-- !== 0;)
261
- if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
241
+ var i=0, k, list, tmp, str=Object.prototype.toString.call(x);
262
242
 
263
- for (i = length; i-- !== 0;) {
264
- var key = keys[i];
243
+ if (str === '[object Object]') {
244
+ tmp = Object.create(x.__proto__ || null);
245
+ } else if (str === '[object Array]') {
246
+ tmp = Array(x.length);
247
+ } else if (str === '[object Set]') {
248
+ tmp = new Set;
249
+ x.forEach(function (val) {
250
+ tmp.add(klona(val));
251
+ });
252
+ } else if (str === '[object Map]') {
253
+ tmp = new Map;
254
+ x.forEach(function (val, key) {
255
+ tmp.set(klona(key), klona(val));
256
+ });
257
+ } else if (str === '[object Date]') {
258
+ tmp = new Date(+x);
259
+ } else if (str === '[object RegExp]') {
260
+ tmp = new RegExp(x.source, x.flags);
261
+ } else if (str === '[object DataView]') {
262
+ tmp = new x.constructor( klona(x.buffer) );
263
+ } else if (str === '[object ArrayBuffer]') {
264
+ tmp = x.slice(0);
265
+ } else if (str.slice(-6) === 'Array]') {
266
+ // ArrayBuffer.isView(x)
267
+ // ~> `new` bcuz `Buffer.slice` => ref
268
+ tmp = new x.constructor(x);
269
+ }
265
270
 
266
- if (!equal(a[key], b[key])) return false;
267
- }
271
+ if (tmp) {
272
+ for (list=Object.getOwnPropertySymbols(x); i < list.length; i++) {
273
+ set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i]));
274
+ }
268
275
 
269
- return true;
270
- }
276
+ for (i=0, list=Object.getOwnPropertyNames(x); i < list.length; i++) {
277
+ if (Object.hasOwnProperty.call(tmp, k=list[i]) && tmp[k] === x[k]) continue;
278
+ set(tmp, k, Object.getOwnPropertyDescriptor(x, k));
279
+ }
280
+ }
271
281
 
272
- // true if both NaN, false otherwise
273
- return a!==a && b!==b;
274
- };
282
+ return tmp || x;
283
+ }
275
284
 
276
285
  function cleanupNonNestedPath(path) {
277
286
  if (isNotNestedPath(path)) {
@@ -394,11 +403,11 @@
394
403
  if (Array.isArray(currentValue)) {
395
404
  const newVal = [...currentValue];
396
405
  // Use isEqual since checked object values can possibly fail the equality check #3883
397
- const idx = newVal.findIndex(v => fastDeepEqual(v, checkedValue));
406
+ const idx = newVal.findIndex(v => isEqual(v, checkedValue));
398
407
  idx >= 0 ? newVal.splice(idx, 1) : newVal.push(checkedValue);
399
408
  return newVal;
400
409
  }
401
- return fastDeepEqual(currentValue, checkedValue) ? uncheckedValue : checkedValue;
410
+ return isEqual(currentValue, checkedValue) ? uncheckedValue : checkedValue;
402
411
  }
403
412
  function debounceAsync(inner, ms = 0) {
404
413
  let timer = null;
@@ -440,6 +449,26 @@
440
449
  onDone(result, args);
441
450
  return result;
442
451
  };
452
+ }
453
+ function computedDeep({ get, set }) {
454
+ const baseRef = vue.ref(klona(get()));
455
+ vue.watch(get, newValue => {
456
+ if (isEqual(newValue, baseRef.value)) {
457
+ return;
458
+ }
459
+ baseRef.value = klona(newValue);
460
+ }, {
461
+ deep: true,
462
+ });
463
+ vue.watch(baseRef, newValue => {
464
+ if (isEqual(newValue, get())) {
465
+ return;
466
+ }
467
+ set(klona(newValue));
468
+ }, {
469
+ deep: true,
470
+ });
471
+ return baseRef;
443
472
  }
444
473
 
445
474
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -829,64 +858,10 @@
829
858
  };
830
859
  }
831
860
 
832
- function set(obj, key, val) {
833
- if (typeof val.value === 'object') val.value = klona(val.value);
834
- if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === '__proto__') {
835
- Object.defineProperty(obj, key, val);
836
- } else obj[key] = val.value;
837
- }
838
-
839
- function klona(x) {
840
- if (typeof x !== 'object') return x;
841
-
842
- var i=0, k, list, tmp, str=Object.prototype.toString.call(x);
843
-
844
- if (str === '[object Object]') {
845
- tmp = Object.create(x.__proto__ || null);
846
- } else if (str === '[object Array]') {
847
- tmp = Array(x.length);
848
- } else if (str === '[object Set]') {
849
- tmp = new Set;
850
- x.forEach(function (val) {
851
- tmp.add(klona(val));
852
- });
853
- } else if (str === '[object Map]') {
854
- tmp = new Map;
855
- x.forEach(function (val, key) {
856
- tmp.set(klona(key), klona(val));
857
- });
858
- } else if (str === '[object Date]') {
859
- tmp = new Date(+x);
860
- } else if (str === '[object RegExp]') {
861
- tmp = new RegExp(x.source, x.flags);
862
- } else if (str === '[object DataView]') {
863
- tmp = new x.constructor( klona(x.buffer) );
864
- } else if (str === '[object ArrayBuffer]') {
865
- tmp = x.slice(0);
866
- } else if (str.slice(-6) === 'Array]') {
867
- // ArrayBuffer.isView(x)
868
- // ~> `new` bcuz `Buffer.slice` => ref
869
- tmp = new x.constructor(x);
870
- }
871
-
872
- if (tmp) {
873
- for (list=Object.getOwnPropertySymbols(x); i < list.length; i++) {
874
- set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i]));
875
- }
876
-
877
- for (i=0, list=Object.getOwnPropertyNames(x); i < list.length; i++) {
878
- if (Object.hasOwnProperty.call(tmp, k=list[i]) && tmp[k] === x[k]) continue;
879
- set(tmp, k, Object.getOwnPropertyDescriptor(x, k));
880
- }
881
- }
882
-
883
- return tmp || x;
884
- }
885
-
886
861
  let ID_COUNTER = 0;
887
862
  function useFieldState(path, init) {
888
- const { value, initialValue, setInitialValue } = _useFieldValue(path, init.modelValue, !init.standalone);
889
- const { errorMessage, errors, setErrors } = _useFieldErrors(path, !init.standalone);
863
+ const { value, initialValue, setInitialValue } = _useFieldValue(path, init.modelValue, init.form);
864
+ const { errorMessage, errors, setErrors } = _useFieldErrors(path, init.form);
890
865
  const meta = _useFieldMeta(value, initialValue, errors);
891
866
  const id = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER;
892
867
  function setState(state) {
@@ -918,8 +893,7 @@
918
893
  /**
919
894
  * Creates the field value and resolves the initial value
920
895
  */
921
- function _useFieldValue(path, modelValue, shouldInjectForm = true) {
922
- const form = shouldInjectForm === true ? injectWithSelf(FormContextKey, undefined) : undefined;
896
+ function _useFieldValue(path, modelValue, form) {
923
897
  const modelRef = vue.ref(vue.unref(modelValue));
924
898
  function resolveInitialValue() {
925
899
  if (!form) {
@@ -990,8 +964,7 @@
990
964
  /**
991
965
  * Creates the error message state for the field state
992
966
  */
993
- function _useFieldErrors(path, shouldInjectForm) {
994
- const form = shouldInjectForm ? injectWithSelf(FormContextKey, undefined) : undefined;
967
+ function _useFieldErrors(path, form) {
995
968
  function normalizeErrors(messages) {
996
969
  if (!messages) {
997
970
  return [];
@@ -1028,13 +1001,14 @@
1028
1001
  return _useField(name, rules, opts);
1029
1002
  }
1030
1003
  function _useField(name, rules, opts) {
1031
- const { initialValue: modelValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue, standalone, keepValueOnUnmount, modelPropName, syncVModel, } = normalizeOptions(vue.unref(name), opts);
1032
- const form = !standalone ? injectWithSelf(FormContextKey) : undefined;
1004
+ const { initialValue: modelValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue, controlled, keepValueOnUnmount, modelPropName, syncVModel, form: controlForm, } = normalizeOptions(vue.unref(name), opts);
1005
+ const injectedForm = controlled ? injectWithSelf(FormContextKey) : undefined;
1006
+ const form = controlForm || injectedForm;
1033
1007
  // a flag indicating if the field is about to be removed/unmounted.
1034
1008
  let markedForRemoval = false;
1035
1009
  const { id, value, initialValue, meta, setState, errors, errorMessage } = useFieldState(name, {
1036
1010
  modelValue,
1037
- standalone,
1011
+ form,
1038
1012
  });
1039
1013
  if (syncVModel) {
1040
1014
  useVModel({ value, prop: modelPropName, handleChange });
@@ -1242,20 +1216,20 @@
1242
1216
  initialValue: undefined,
1243
1217
  validateOnMount: false,
1244
1218
  bails: true,
1245
- rules: '',
1246
1219
  label: name,
1247
1220
  validateOnValueUpdate: true,
1248
- standalone: false,
1249
1221
  keepValueOnUnmount: undefined,
1250
1222
  modelPropName: 'modelValue',
1251
1223
  syncVModel: true,
1224
+ controlled: true,
1252
1225
  });
1253
1226
  if (!opts) {
1254
1227
  return defaults();
1255
1228
  }
1256
1229
  // TODO: Deprecate this in next major release
1257
1230
  const checkedValue = 'valueProp' in opts ? opts.valueProp : opts.checkedValue;
1258
- return Object.assign(Object.assign(Object.assign({}, defaults()), (opts || {})), { checkedValue });
1231
+ const controlled = 'standalone' in opts ? !opts.standalone : opts.controlled;
1232
+ return Object.assign(Object.assign(Object.assign({}, defaults()), (opts || {})), { controlled: controlled !== null && controlled !== void 0 ? controlled : true, checkedValue });
1259
1233
  }
1260
1234
  /**
1261
1235
  * Extracts the validation rules from a schema
@@ -1523,6 +1497,7 @@
1523
1497
  function useForm(opts) {
1524
1498
  var _a;
1525
1499
  const formId = FORM_COUNTER++;
1500
+ const controlledModelPaths = new Set();
1526
1501
  // Prevents fields from double resetting their values, which causes checkboxes to toggle their initial value
1527
1502
  // TODO: This won't be needed if we centralize all the state inside the `form` for form inputs
1528
1503
  let RESET_LOCK = false;
@@ -1585,6 +1560,13 @@
1585
1560
  const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts === null || opts === void 0 ? void 0 : opts.initialValues);
1586
1561
  // form meta aggregations
1587
1562
  const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);
1563
+ const controlledValues = vue.computed(() => {
1564
+ return [...controlledModelPaths, ...keysOf(fieldsByPath.value)].reduce((acc, path) => {
1565
+ const value = getFromPath(formValues, path);
1566
+ setInPath(acc, path, value);
1567
+ return acc;
1568
+ }, {});
1569
+ });
1588
1570
  const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
1589
1571
  /**
1590
1572
  * Batches validation runs in 5ms batches
@@ -1634,10 +1616,65 @@
1634
1616
  return validation;
1635
1617
  }, { valid: formResult.valid, results: {}, errors: {} });
1636
1618
  });
1619
+ function makeSubmissionFactory(onlyControlled) {
1620
+ return function submitHandlerFactory(fn, onValidationError) {
1621
+ return function submissionHandler(e) {
1622
+ if (e instanceof Event) {
1623
+ e.preventDefault();
1624
+ e.stopPropagation();
1625
+ }
1626
+ // Touch all fields
1627
+ setTouched(keysOf(fieldsByPath.value).reduce((acc, field) => {
1628
+ acc[field] = true;
1629
+ return acc;
1630
+ }, {}));
1631
+ isSubmitting.value = true;
1632
+ submitCount.value++;
1633
+ return validate()
1634
+ .then(result => {
1635
+ const values = klona(formValues);
1636
+ if (result.valid && typeof fn === 'function') {
1637
+ const controlled = klona(controlledValues.value);
1638
+ return fn(onlyControlled ? controlled : values, {
1639
+ evt: e,
1640
+ controlledValues: controlled,
1641
+ setErrors,
1642
+ setFieldError,
1643
+ setTouched,
1644
+ setFieldTouched,
1645
+ setValues,
1646
+ setFieldValue,
1647
+ resetForm,
1648
+ });
1649
+ }
1650
+ if (!result.valid && typeof onValidationError === 'function') {
1651
+ onValidationError({
1652
+ values,
1653
+ evt: e,
1654
+ errors: result.errors,
1655
+ results: result.results,
1656
+ });
1657
+ }
1658
+ })
1659
+ .then(returnVal => {
1660
+ isSubmitting.value = false;
1661
+ return returnVal;
1662
+ }, err => {
1663
+ isSubmitting.value = false;
1664
+ // re-throw the err so it doesn't go silent
1665
+ throw err;
1666
+ });
1667
+ };
1668
+ };
1669
+ }
1670
+ const handleSubmitImpl = makeSubmissionFactory(false);
1671
+ const handleSubmit = handleSubmitImpl;
1672
+ handleSubmit.withControlled = makeSubmissionFactory(true);
1637
1673
  const formCtx = {
1638
1674
  formId,
1639
1675
  fieldsByPath,
1640
1676
  values: formValues,
1677
+ controlledValues,
1641
1678
  errorBag,
1642
1679
  errors,
1643
1680
  schema,
@@ -1736,7 +1773,7 @@
1736
1773
  fieldArrays.forEach(f => f && f.reset());
1737
1774
  }
1738
1775
  function createModel(path) {
1739
- const { value } = _useFieldValue(path);
1776
+ const { value } = _useFieldValue(path, undefined, formCtx);
1740
1777
  vue.watch(value, () => {
1741
1778
  if (!fieldExists(vue.unref(path))) {
1742
1779
  validate({ mode: 'validated-only' });
@@ -1744,6 +1781,7 @@
1744
1781
  }, {
1745
1782
  deep: true,
1746
1783
  });
1784
+ controlledModelPaths.add(vue.unref(path));
1747
1785
  return value;
1748
1786
  }
1749
1787
  function useFieldModel(path) {
@@ -1888,12 +1926,18 @@
1888
1926
  // This used to be handled in the useField composable but the form has better context on when it should/not happen.
1889
1927
  // if it does belong to it that means the group still exists
1890
1928
  // #3844
1891
- if (isSameGroup && Array.isArray(currentGroupValue) && !shouldKeepValue) {
1892
- const valueIdx = currentGroupValue.findIndex(i => isEqual(i, vue.unref(field.checkedValue)));
1893
- if (valueIdx > -1) {
1894
- const newVal = [...currentGroupValue];
1895
- newVal.splice(valueIdx, 1);
1896
- setFieldValue(fieldName, newVal, { force: true });
1929
+ if (isSameGroup && !shouldKeepValue) {
1930
+ if (Array.isArray(currentGroupValue)) {
1931
+ const valueIdx = currentGroupValue.findIndex(i => isEqual(i, vue.unref(field.checkedValue)));
1932
+ if (valueIdx > -1) {
1933
+ const newVal = [...currentGroupValue];
1934
+ newVal.splice(valueIdx, 1);
1935
+ setFieldValue(fieldName, newVal, { force: true });
1936
+ }
1937
+ }
1938
+ else if (currentGroupValue === vue.unref(field.checkedValue)) {
1939
+ // Remove field if it is a group but does not have an array value, like for radio inputs #3963
1940
+ unsetPath(formValues, fieldName);
1897
1941
  }
1898
1942
  }
1899
1943
  // Field was removed entirely, we should unset its path
@@ -1906,7 +1950,8 @@
1906
1950
  if (shouldKeepValue) {
1907
1951
  return;
1908
1952
  }
1909
- if (isGroup && !isEmptyContainer(getFromPath(formValues, fieldName))) {
1953
+ // Don't apply emptyContainer check unless the current group value is an array
1954
+ if (isGroup && Array.isArray(currentGroupValue) && !isEmptyContainer(currentGroupValue)) {
1910
1955
  return;
1911
1956
  }
1912
1957
  unsetPath(formValues, fieldName);
@@ -1960,55 +2005,6 @@
1960
2005
  }
1961
2006
  return fieldInstance.validate();
1962
2007
  }
1963
- function handleSubmit(fn, onValidationError) {
1964
- return function submissionHandler(e) {
1965
- if (e instanceof Event) {
1966
- e.preventDefault();
1967
- e.stopPropagation();
1968
- }
1969
- // Touch all fields
1970
- setTouched(keysOf(fieldsByPath.value).reduce((acc, field) => {
1971
- acc[field] = true;
1972
- return acc;
1973
- }, {}));
1974
- isSubmitting.value = true;
1975
- submitCount.value++;
1976
- return validate()
1977
- .then(result => {
1978
- if (result.valid && typeof fn === 'function') {
1979
- return fn(klona(formValues), {
1980
- evt: e,
1981
- setErrors,
1982
- setFieldError,
1983
- setTouched,
1984
- setFieldTouched,
1985
- setValues,
1986
- setFieldValue,
1987
- resetForm,
1988
- });
1989
- }
1990
- if (!result.valid && typeof onValidationError === 'function') {
1991
- onValidationError({
1992
- values: klona(formValues),
1993
- evt: e,
1994
- errors: result.errors,
1995
- results: result.results,
1996
- });
1997
- }
1998
- })
1999
- .then(returnVal => {
2000
- isSubmitting.value = false;
2001
- return returnVal;
2002
- }, err => {
2003
- isSubmitting.value = false;
2004
- // re-throw the err so it doesn't go silent
2005
- throw err;
2006
- });
2007
- };
2008
- }
2009
- function setFieldInitialValue(path, value) {
2010
- setInPath(initialValues.value, path, klona(value));
2011
- }
2012
2008
  function unsetInitialValue(path) {
2013
2009
  unsetPath(initialValues.value, path);
2014
2010
  }
@@ -2022,6 +2018,9 @@
2022
2018
  setInPath(originalInitialValues.value, path, klona(value));
2023
2019
  }
2024
2020
  }
2021
+ function setFieldInitialValue(path, value) {
2022
+ setInPath(initialValues.value, path, klona(value));
2023
+ }
2025
2024
  async function _validateSchema() {
2026
2025
  const schemaValue = vue.unref(schema);
2027
2026
  if (!schemaValue) {
@@ -2067,26 +2066,7 @@
2067
2066
  }
2068
2067
  // Provide injections
2069
2068
  vue.provide(FormContextKey, formCtx);
2070
- return {
2071
- errors,
2072
- meta,
2073
- values: formValues,
2074
- isSubmitting,
2075
- submitCount,
2076
- validate,
2077
- validateField,
2078
- handleReset: () => resetForm(),
2079
- resetForm,
2080
- handleSubmit,
2081
- submitForm,
2082
- setFieldError,
2083
- setErrors,
2084
- setFieldValue,
2085
- setValues,
2086
- setFieldTouched,
2087
- setTouched,
2088
- useFieldModel,
2089
- };
2069
+ return Object.assign(Object.assign({}, formCtx), { handleReset: () => resetForm(), submitForm });
2090
2070
  }
2091
2071
  /**
2092
2072
  * Manages form meta aggregation
@@ -2246,7 +2226,7 @@
2246
2226
  const initialValues = vue.toRef(props, 'initialValues');
2247
2227
  const validationSchema = vue.toRef(props, 'validationSchema');
2248
2228
  const keepValues = vue.toRef(props, 'keepValues');
2249
- const { errors, values, meta, isSubmitting, submitCount, validate, validateField, handleReset, resetForm, handleSubmit, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
2229
+ const { errors, values, meta, isSubmitting, submitCount, controlledValues, validate, validateField, handleReset, resetForm, handleSubmit, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
2250
2230
  validationSchema: validationSchema.value ? validationSchema : undefined,
2251
2231
  initialValues,
2252
2232
  initialErrors: props.initialErrors,
@@ -2281,6 +2261,7 @@
2281
2261
  values: values,
2282
2262
  isSubmitting: isSubmitting.value,
2283
2263
  submitCount: submitCount.value,
2264
+ controlledValues: controlledValues.value,
2284
2265
  validate,
2285
2266
  validateField,
2286
2267
  handleSubmit: handleScopedSlotSubmit,
@@ -2374,7 +2355,7 @@
2374
2355
  const key = entryCounter++;
2375
2356
  const entry = {
2376
2357
  key,
2377
- value: vue.computed({
2358
+ value: computedDeep({
2378
2359
  get() {
2379
2360
  const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, vue.unref(arrayPath), []) || [];
2380
2361
  const idx = fields.value.findIndex(e => e.key === key);
@@ -2466,6 +2447,7 @@
2466
2447
  return;
2467
2448
  }
2468
2449
  form === null || form === void 0 ? void 0 : form.setFieldValue(`${pathName}[${idx}]`, value);
2450
+ form === null || form === void 0 ? void 0 : form.validate({ mode: 'validated-only' });
2469
2451
  }
2470
2452
  function prepend(value) {
2471
2453
  const pathName = vue.unref(arrayPath);