vee-validate 4.5.10 → 4.6.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,9 +1,9 @@
1
1
  /**
2
- * vee-validate v4.5.10
2
+ * vee-validate v4.6.0
3
3
  * (c) 2022 Abdelrahman Awad
4
4
  * @license MIT
5
5
  */
6
- import { inject, getCurrentInstance, warn as warn$1, ref, unref, computed, reactive, watch, onUnmounted, nextTick, onMounted, provide, isRef, onBeforeUnmount, defineComponent, toRef, resolveDynamicComponent, h, watchEffect, markRaw, readonly } from 'vue';
6
+ import { inject, getCurrentInstance, warn as warn$1, ref, unref, computed, reactive, watch, onUnmounted, nextTick, onMounted, provide, isRef, onBeforeUnmount, defineComponent, toRef, resolveDynamicComponent, h, watchEffect, markRaw } from 'vue';
7
7
  import { setupDevtoolsPlugin } from '@vue/devtools-api';
8
8
 
9
9
  function isCallable(fn) {
@@ -53,18 +53,6 @@ const IS_ABSENT = Symbol('Default empty value');
53
53
  function isLocator(value) {
54
54
  return isCallable(value) && !!value.__locatorRef;
55
55
  }
56
- /**
57
- * Checks if an tag name is a native HTML tag and not a Vue component
58
- */
59
- function isHTMLTag(tag) {
60
- return ['input', 'textarea', 'select'].includes(tag);
61
- }
62
- /**
63
- * Checks if an input is of type file
64
- */
65
- function isFileInputNode(tag, attrs) {
66
- return isHTMLTag(tag) && attrs.type === 'file';
67
- }
68
56
  function isYupValidator(value) {
69
57
  return !!value && isCallable(value.validate);
70
58
  }
@@ -117,7 +105,7 @@ function isNativeMultiSelectNode(tag, attrs) {
117
105
  * For multi-selects because the value binding will reset the value
118
106
  */
119
107
  function shouldHaveValueBinding(tag, attrs) {
120
- return isNativeMultiSelectNode(tag, attrs) || isFileInputNode(tag, attrs);
108
+ return !isNativeMultiSelectNode(tag, attrs) && attrs.type !== 'file' && !hasCheckedAttr(attrs.type);
121
109
  }
122
110
  function isFormSubmitEvent(evt) {
123
111
  return isEvent(evt) && evt.target && 'submit' in evt.target;
@@ -301,6 +289,15 @@ function debounceAsync(inner, ms = 0) {
301
289
  }, ms);
302
290
  return new Promise(resolve => resolves.push(resolve));
303
291
  };
292
+ }
293
+ function applyModelModifiers(value, modifiers) {
294
+ if (!isObject(modifiers)) {
295
+ return;
296
+ }
297
+ if (modifiers.number) {
298
+ return toNumber(value);
299
+ }
300
+ return value;
304
301
  }
305
302
 
306
303
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -344,7 +341,8 @@ function normalizeEventValue(value) {
344
341
  return getBoundValue(input);
345
342
  }
346
343
  if (input.type === 'file' && input.files) {
347
- return Array.from(input.files);
344
+ const files = Array.from(input.files);
345
+ return input.multiple ? files : files[0];
348
346
  }
349
347
  if (isNativeMultiSelect(input)) {
350
348
  return Array.from(input.options)
@@ -503,18 +501,34 @@ async function _validate(field, value) {
503
501
  if (isYupValidator(field.rules)) {
504
502
  return validateFieldWithYup(value, field.rules, { bails: field.bails });
505
503
  }
506
- // if a generic function, use it as the pipeline.
507
- if (isCallable(field.rules)) {
504
+ // if a generic function or chain of generic functions
505
+ if (isCallable(field.rules) || Array.isArray(field.rules)) {
508
506
  const ctx = {
509
507
  field: field.name,
510
508
  form: field.formData,
511
509
  value: value,
512
510
  };
513
- const result = await field.rules(value, ctx);
514
- const isValid = typeof result !== 'string' && result;
515
- const message = typeof result === 'string' ? result : _generateFieldError(ctx);
511
+ // Normalize the pipeline
512
+ const pipeline = Array.isArray(field.rules) ? field.rules : [field.rules];
513
+ const length = pipeline.length;
514
+ const errors = [];
515
+ for (let i = 0; i < length; i++) {
516
+ const rule = pipeline[i];
517
+ const result = await rule(value, ctx);
518
+ const isValid = typeof result !== 'string' && result;
519
+ if (isValid) {
520
+ continue;
521
+ }
522
+ const message = typeof result === 'string' ? result : _generateFieldError(ctx);
523
+ errors.push(message);
524
+ if (field.bails) {
525
+ return {
526
+ errors,
527
+ };
528
+ }
529
+ }
516
530
  return {
517
- errors: !isValid ? [message] : [],
531
+ errors,
518
532
  };
519
533
  }
520
534
  const normalizedContext = Object.assign(Object.assign({}, field), { rules: normalizeRules(field.rules) });
@@ -827,8 +841,8 @@ function useFieldState(path, init) {
827
841
  /**
828
842
  * Creates the field value and resolves the initial value
829
843
  */
830
- function _useFieldValue(path, modelValue, shouldInjectForm) {
831
- const form = shouldInjectForm ? injectWithSelf(FormContextKey, undefined) : undefined;
844
+ function _useFieldValue(path, modelValue, shouldInjectForm = true) {
845
+ const form = shouldInjectForm === true ? injectWithSelf(FormContextKey, undefined) : undefined;
832
846
  const modelRef = ref(unref(modelValue));
833
847
  function resolveInitialValue() {
834
848
  if (!form) {
@@ -858,7 +872,7 @@ function _useFieldValue(path, modelValue, shouldInjectForm) {
858
872
  // prioritize model value over form values
859
873
  // #3429
860
874
  const currentValue = modelValue ? unref(modelValue) : getFromPath(form.values, unref(path), unref(initialValue));
861
- form.stageInitialValue(unref(path), currentValue);
875
+ form.stageInitialValue(unref(path), currentValue, true);
862
876
  // otherwise use a computed setter that triggers the `setFieldValue`
863
877
  const value = computed({
864
878
  get() {
@@ -1297,12 +1311,17 @@ function useField(name, rules, opts) {
1297
1311
  return _useField(name, rules, opts);
1298
1312
  }
1299
1313
  function _useField(name, rules, opts) {
1300
- const { initialValue: modelValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue, standalone, } = normalizeOptions(unref(name), opts);
1314
+ const { initialValue: modelValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue, standalone, keepValueOnUnmount, modelPropName, syncVModel, } = normalizeOptions(unref(name), opts);
1301
1315
  const form = !standalone ? injectWithSelf(FormContextKey) : undefined;
1316
+ // a flag indicating if the field is about to be removed/unmounted.
1317
+ let markedForRemoval = false;
1302
1318
  const { id, value, initialValue, meta, setState, errors, errorMessage } = useFieldState(name, {
1303
1319
  modelValue,
1304
1320
  standalone,
1305
1321
  });
1322
+ if (syncVModel) {
1323
+ useVModel({ value, prop: modelPropName, handleChange });
1324
+ }
1306
1325
  /**
1307
1326
  * Handles common onBlur meta update
1308
1327
  */
@@ -1315,7 +1334,7 @@ function _useField(name, rules, opts) {
1315
1334
  if (schema && !isYupValidator(schema)) {
1316
1335
  rulesValue = extractRuleFromSchema(schema, unref(name)) || rulesValue;
1317
1336
  }
1318
- if (isYupValidator(rulesValue) || isCallable(rulesValue)) {
1337
+ if (isYupValidator(rulesValue) || isCallable(rulesValue) || Array.isArray(rulesValue)) {
1319
1338
  return rulesValue;
1320
1339
  }
1321
1340
  return normalizeRules(rulesValue);
@@ -1335,12 +1354,19 @@ function _useField(name, rules, opts) {
1335
1354
  meta.pending = true;
1336
1355
  meta.validated = true;
1337
1356
  const result = await validateCurrentValue('validated-only');
1357
+ if (markedForRemoval) {
1358
+ result.valid = true;
1359
+ result.errors = [];
1360
+ }
1338
1361
  setState({ errors: result.errors });
1339
1362
  meta.pending = false;
1340
1363
  return result;
1341
1364
  }
1342
1365
  async function validateValidStateOnly() {
1343
1366
  const result = await validateCurrentValue('silent');
1367
+ if (markedForRemoval) {
1368
+ result.valid = true;
1369
+ }
1344
1370
  meta.valid = result.valid;
1345
1371
  return result;
1346
1372
  }
@@ -1354,13 +1380,13 @@ function _useField(name, rules, opts) {
1354
1380
  return validateValidStateOnly();
1355
1381
  }
1356
1382
  // Common input/change event handler
1357
- const handleChange = (e, shouldValidate = true) => {
1383
+ function handleChange(e, shouldValidate = true) {
1358
1384
  const newValue = normalizeEventValue(e);
1359
1385
  value.value = newValue;
1360
1386
  if (!validateOnValueUpdate && shouldValidate) {
1361
1387
  validateWithStateMutation();
1362
1388
  }
1363
- };
1389
+ }
1364
1390
  // Runs the initial validation
1365
1391
  onMounted(() => {
1366
1392
  if (validateOnMount) {
@@ -1377,7 +1403,13 @@ function _useField(name, rules, opts) {
1377
1403
  }
1378
1404
  let unwatchValue;
1379
1405
  function watchValue() {
1380
- unwatchValue = watch(value, validateOnValueUpdate ? validateWithStateMutation : validateValidStateOnly, {
1406
+ unwatchValue = watch(value, (val, oldVal) => {
1407
+ if (es6(val, oldVal)) {
1408
+ return;
1409
+ }
1410
+ const validateFn = validateOnValueUpdate ? validateWithStateMutation : validateValidStateOnly;
1411
+ validateFn();
1412
+ }, {
1381
1413
  deep: true,
1382
1414
  });
1383
1415
  }
@@ -1418,6 +1450,7 @@ function _useField(name, rules, opts) {
1418
1450
  checkedValue,
1419
1451
  uncheckedValue,
1420
1452
  bails,
1453
+ keepValueOnUnmount,
1421
1454
  resetField,
1422
1455
  handleReset: () => resetField(),
1423
1456
  validate: validate$1,
@@ -1455,13 +1488,14 @@ function _useField(name, rules, opts) {
1455
1488
  // associate the field with the given form
1456
1489
  form.register(field);
1457
1490
  onBeforeUnmount(() => {
1491
+ markedForRemoval = true;
1458
1492
  form.unregister(field);
1459
1493
  });
1460
1494
  // extract cross-field dependencies in a computed prop
1461
1495
  const dependencies = computed(() => {
1462
1496
  const rulesVal = normalizedRules.value;
1463
1497
  // is falsy, a function schema or a yup schema
1464
- if (!rulesVal || isCallable(rulesVal) || isYupValidator(rulesVal)) {
1498
+ if (!rulesVal || isCallable(rulesVal) || isYupValidator(rulesVal) || Array.isArray(rulesVal)) {
1465
1499
  return {};
1466
1500
  }
1467
1501
  return Object.keys(rulesVal).reduce((acc, rule) => {
@@ -1503,6 +1537,9 @@ function normalizeOptions(name, opts) {
1503
1537
  label: name,
1504
1538
  validateOnValueUpdate: true,
1505
1539
  standalone: false,
1540
+ keepValueOnUnmount: undefined,
1541
+ modelPropName: 'modelValue',
1542
+ syncVModel: true,
1506
1543
  });
1507
1544
  if (!opts) {
1508
1545
  return defaults();
@@ -1534,8 +1571,8 @@ function useCheckboxField(name, rules, opts) {
1534
1571
  return Array.isArray(currentValue) ? currentValue.includes(checkedVal) : checkedVal === currentValue;
1535
1572
  });
1536
1573
  function handleCheckboxChange(e, shouldValidate = true) {
1537
- var _a, _b;
1538
- if (checked.value === ((_b = (_a = e) === null || _a === void 0 ? void 0 : _a.target) === null || _b === void 0 ? void 0 : _b.checked)) {
1574
+ var _a;
1575
+ if (checked.value === ((_a = e === null || e === void 0 ? void 0 : e.target) === null || _a === void 0 ? void 0 : _a.checked)) {
1539
1576
  return;
1540
1577
  }
1541
1578
  let newValue = normalizeEventValue(e);
@@ -1546,8 +1583,10 @@ function useCheckboxField(name, rules, opts) {
1546
1583
  handleChange(newValue, shouldValidate);
1547
1584
  }
1548
1585
  onBeforeUnmount(() => {
1549
- // toggles the checkbox value if it was checked
1550
- if (checked.value) {
1586
+ var _a, _b;
1587
+ const shouldKeepValue = (_b = (_a = unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : unref(form === null || form === void 0 ? void 0 : form.keepValuesOnUnmount)) !== null && _b !== void 0 ? _b : false;
1588
+ // toggles the checkbox value if it was checked and the unset behavior is set
1589
+ if (checked.value && !shouldKeepValue) {
1551
1590
  handleCheckboxChange(unref(checkedValue), false);
1552
1591
  }
1553
1592
  });
@@ -1556,6 +1595,41 @@ function useCheckboxField(name, rules, opts) {
1556
1595
  uncheckedValue, handleChange: handleCheckboxChange });
1557
1596
  }
1558
1597
  return patchCheckboxApi(_useField(name, rules, opts));
1598
+ }
1599
+ function useVModel({ prop, value, handleChange }) {
1600
+ const vm = getCurrentInstance();
1601
+ /* istanbul ignore next */
1602
+ if (!vm) {
1603
+ if ((process.env.NODE_ENV !== 'production')) {
1604
+ console.warn('Failed to setup model events because `useField` was not called in setup.');
1605
+ }
1606
+ return;
1607
+ }
1608
+ const propName = prop || 'modelValue';
1609
+ const emitName = `update:${propName}`;
1610
+ // Component doesn't have a model prop setup (must be defined on the props)
1611
+ if (!(propName in vm.props)) {
1612
+ return;
1613
+ }
1614
+ watch(value, newValue => {
1615
+ if (es6(newValue, getCurrentModelValue(vm, propName))) {
1616
+ return;
1617
+ }
1618
+ vm.emit(emitName, newValue);
1619
+ });
1620
+ watch(() => getCurrentModelValue(vm, propName), propValue => {
1621
+ if (propValue === IS_ABSENT && value.value === undefined) {
1622
+ return;
1623
+ }
1624
+ const newValue = propValue === IS_ABSENT ? undefined : propValue;
1625
+ if (es6(newValue, applyModelModifiers(value.value, vm.props.modelModifiers))) {
1626
+ return;
1627
+ }
1628
+ handleChange(newValue);
1629
+ });
1630
+ }
1631
+ function getCurrentModelValue(vm, propName) {
1632
+ return vm.props[propName];
1559
1633
  }
1560
1634
 
1561
1635
  const FieldImpl = defineComponent({
@@ -1622,13 +1696,17 @@ const FieldImpl = defineComponent({
1622
1696
  type: Boolean,
1623
1697
  default: false,
1624
1698
  },
1699
+ keepValue: {
1700
+ type: Boolean,
1701
+ default: undefined,
1702
+ },
1625
1703
  },
1626
1704
  setup(props, ctx) {
1627
1705
  const rules = toRef(props, 'rules');
1628
1706
  const name = toRef(props, 'name');
1629
1707
  const label = toRef(props, 'label');
1630
1708
  const uncheckedValue = toRef(props, 'uncheckedValue');
1631
- const hasModelEvents = isPropPresent(props, 'onUpdate:modelValue');
1709
+ const keepValue = toRef(props, 'keepValue');
1632
1710
  const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, setTouched, resetField, handleReset, meta, checked, setErrors, } = useField(name, rules, {
1633
1711
  validateOnMount: props.validateOnMount,
1634
1712
  bails: props.bails,
@@ -1640,25 +1718,22 @@ const FieldImpl = defineComponent({
1640
1718
  uncheckedValue,
1641
1719
  label,
1642
1720
  validateOnValueUpdate: false,
1721
+ keepValueOnUnmount: keepValue,
1643
1722
  });
1644
1723
  // If there is a v-model applied on the component we need to emit the `update:modelValue` whenever the value binding changes
1645
- const onChangeHandler = hasModelEvents
1646
- ? function handleChangeWithModel(e, shouldValidate = true) {
1647
- handleChange(e, shouldValidate);
1648
- ctx.emit('update:modelValue', value.value);
1649
- }
1650
- : handleChange;
1724
+ const onChangeHandler = function handleChangeWithModel(e, shouldValidate = true) {
1725
+ handleChange(e, shouldValidate);
1726
+ ctx.emit('update:modelValue', value.value);
1727
+ };
1651
1728
  const handleInput = (e) => {
1652
1729
  if (!hasCheckedAttr(ctx.attrs.type)) {
1653
1730
  value.value = normalizeEventValue(e);
1654
1731
  }
1655
1732
  };
1656
- const onInputHandler = hasModelEvents
1657
- ? function handleInputWithModel(e) {
1658
- handleInput(e);
1659
- ctx.emit('update:modelValue', value.value);
1660
- }
1661
- : handleInput;
1733
+ const onInputHandler = function handleInputWithModel(e) {
1734
+ handleInput(e);
1735
+ ctx.emit('update:modelValue', value.value);
1736
+ };
1662
1737
  const fieldProps = computed(() => {
1663
1738
  const { validateOnInput, validateOnChange, validateOnBlur, validateOnModelUpdate } = resolveValidationTriggers(props);
1664
1739
  const baseOnBlur = [handleBlur, ctx.attrs.onBlur, validateOnBlur ? validateField : undefined].filter(Boolean);
@@ -1674,26 +1749,12 @@ const FieldImpl = defineComponent({
1674
1749
  if (hasCheckedAttr(ctx.attrs.type) && checked) {
1675
1750
  attrs.checked = checked.value;
1676
1751
  }
1677
- else {
1678
- attrs.value = value.value;
1679
- }
1680
1752
  const tag = resolveTag(props, ctx);
1681
1753
  if (shouldHaveValueBinding(tag, ctx.attrs)) {
1682
- delete attrs.value;
1754
+ attrs.value = value.value;
1683
1755
  }
1684
1756
  return attrs;
1685
1757
  });
1686
- const modelValue = toRef(props, 'modelValue');
1687
- watch(modelValue, newModelValue => {
1688
- // Don't attempt to sync absent values
1689
- if (newModelValue === IS_ABSENT && value.value === undefined) {
1690
- return;
1691
- }
1692
- if (newModelValue !== applyModifiers(value.value, props.modelModifiers)) {
1693
- value.value = newModelValue === IS_ABSENT ? undefined : newModelValue;
1694
- validateField();
1695
- }
1696
- });
1697
1758
  function slotProps() {
1698
1759
  return {
1699
1760
  field: fieldProps.value,
@@ -1745,12 +1806,6 @@ function resolveValidationTriggers(props) {
1745
1806
  validateOnModelUpdate: (_d = props.validateOnModelUpdate) !== null && _d !== void 0 ? _d : validateOnModelUpdate,
1746
1807
  };
1747
1808
  }
1748
- function applyModifiers(value, modifiers) {
1749
- if (modifiers.number) {
1750
- return toNumber(value);
1751
- }
1752
- return value;
1753
- }
1754
1809
  function resolveInitialValue(props, ctx) {
1755
1810
  // Gets the initial value either from `value` prop/attr or `v-model` binding (modelValue)
1756
1811
  // For checkboxes and radio buttons it will always be the model value not the `value` attribute
@@ -1763,6 +1818,7 @@ const Field = FieldImpl;
1763
1818
 
1764
1819
  let FORM_COUNTER = 0;
1765
1820
  function useForm(opts) {
1821
+ var _a;
1766
1822
  const formId = FORM_COUNTER++;
1767
1823
  // Prevents fields from double resetting their values, which causes checkboxes to toggle their initial value
1768
1824
  // TODO: This won't be needed if we centralize all the state inside the `form` for form inputs
@@ -1773,8 +1829,8 @@ function useForm(opts) {
1773
1829
  const isSubmitting = ref(false);
1774
1830
  // The number of times the user tried to submit the form
1775
1831
  const submitCount = ref(0);
1776
- // dictionary for field arrays to receive various signals like reset
1777
- const fieldArraysLookup = {};
1832
+ // field arrays managed by this form
1833
+ const fieldArrays = [];
1778
1834
  // a private ref for all form values
1779
1835
  const formValues = reactive(klona(unref(opts === null || opts === void 0 ? void 0 : opts.initialValues) || {}));
1780
1836
  // the source of errors for the form fields
@@ -1821,10 +1877,11 @@ function useForm(opts) {
1821
1877
  // mutable non-reactive reference to initial errors
1822
1878
  // we need this to process initial errors then unset them
1823
1879
  const initialErrors = Object.assign({}, ((opts === null || opts === void 0 ? void 0 : opts.initialErrors) || {}));
1880
+ const keepValuesOnUnmount = (_a = opts === null || opts === void 0 ? void 0 : opts.keepValuesOnUnmount) !== null && _a !== void 0 ? _a : false;
1824
1881
  // initial form values
1825
1882
  const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts === null || opts === void 0 ? void 0 : opts.initialValues);
1826
1883
  // form meta aggregations
1827
- const meta = useFormMeta(fieldsByPath, formValues, initialValues, errors);
1884
+ const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);
1828
1885
  const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
1829
1886
  const formCtx = {
1830
1887
  formId,
@@ -1836,7 +1893,8 @@ function useForm(opts) {
1836
1893
  submitCount,
1837
1894
  meta,
1838
1895
  isSubmitting,
1839
- fieldArraysLookup,
1896
+ fieldArrays,
1897
+ keepValuesOnUnmount,
1840
1898
  validateSchema: unref(schema) ? validateSchema : undefined,
1841
1899
  validate,
1842
1900
  register: registerField,
@@ -1854,6 +1912,7 @@ function useForm(opts) {
1854
1912
  stageInitialValue,
1855
1913
  unsetInitialValue,
1856
1914
  setFieldInitialValue,
1915
+ useFieldModel,
1857
1916
  };
1858
1917
  function isFieldGroup(fieldOrGroup) {
1859
1918
  return Array.isArray(fieldOrGroup);
@@ -1923,7 +1982,22 @@ function useForm(opts) {
1923
1982
  setFieldValue(path, fields[path]);
1924
1983
  });
1925
1984
  // regenerate the arrays when the form values change
1926
- Object.values(fieldArraysLookup).forEach(f => f && f.reset());
1985
+ fieldArrays.forEach(f => f && f.reset());
1986
+ }
1987
+ function createModel(path) {
1988
+ const { value } = _useFieldValue(path);
1989
+ watch(value, () => {
1990
+ if (!fieldExists(unref(path))) {
1991
+ validate({ mode: 'validated-only' });
1992
+ }
1993
+ });
1994
+ return value;
1995
+ }
1996
+ function useFieldModel(path) {
1997
+ if (!Array.isArray(path)) {
1998
+ return createModel(path);
1999
+ }
2000
+ return path.map(createModel);
1927
2001
  }
1928
2002
  /**
1929
2003
  * Sets the touched meta state on a field
@@ -2047,13 +2121,26 @@ function useForm(opts) {
2047
2121
  }
2048
2122
  function unregisterField(field) {
2049
2123
  const fieldName = unref(field.name);
2124
+ const fieldInstance = fieldsByPath.value[fieldName];
2125
+ const isGroup = !!fieldInstance && isFieldGroup(fieldInstance);
2050
2126
  removeFieldFromPath(field, fieldName);
2051
2127
  nextTick(() => {
2128
+ var _a;
2052
2129
  // clears a field error on unmounted
2053
2130
  // we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes
2054
2131
  // #3384
2055
2132
  if (!fieldExists(fieldName)) {
2056
2133
  setFieldError(fieldName, undefined);
2134
+ // Checks if the field was configured to be unset during unmount or not
2135
+ // Checks both the form-level config and field-level one
2136
+ // Field has the priority if it is set, otherwise it goes to the form settings
2137
+ const shouldKeepValue = (_a = unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : unref(keepValuesOnUnmount);
2138
+ if (shouldKeepValue) {
2139
+ return;
2140
+ }
2141
+ if (isGroup && !isEmptyContainer(getFromPath(formValues, fieldName))) {
2142
+ return;
2143
+ }
2057
2144
  unsetPath(formValues, fieldName);
2058
2145
  }
2059
2146
  });
@@ -2160,9 +2247,12 @@ function useForm(opts) {
2160
2247
  /**
2161
2248
  * Sneaky function to set initial field values
2162
2249
  */
2163
- function stageInitialValue(path, value) {
2250
+ function stageInitialValue(path, value, updateOriginal = false) {
2164
2251
  setInPath(formValues, path, value);
2165
2252
  setFieldInitialValue(path, value);
2253
+ if (updateOriginal) {
2254
+ setInPath(originalInitialValues.value, path, klona(value));
2255
+ }
2166
2256
  }
2167
2257
  async function _validateSchema() {
2168
2258
  const schemaValue = unref(schema);
@@ -2179,10 +2269,12 @@ function useForm(opts) {
2179
2269
  }
2180
2270
  /**
2181
2271
  * Batches validation runs in 5ms batches
2272
+ * Must have two distinct batch queues to make sure they don't override each other settings #3783
2182
2273
  */
2183
- const debouncedSchemaValidation = debounceAsync(_validateSchema, 5);
2274
+ const debouncedSilentValidation = debounceAsync(_validateSchema, 5);
2275
+ const debouncedValidation = debounceAsync(_validateSchema, 5);
2184
2276
  async function validateSchema(mode) {
2185
- const formResult = await debouncedSchemaValidation();
2277
+ const formResult = await (mode === 'silent' ? debouncedSilentValidation() : debouncedValidation());
2186
2278
  // fields by id lookup
2187
2279
  const fieldsById = formCtx.fieldsByPath.value || {};
2188
2280
  // errors fields names, we need it to also check if custom errors are updated
@@ -2278,6 +2370,7 @@ function useForm(opts) {
2278
2370
  setValues,
2279
2371
  setFieldTouched,
2280
2372
  setTouched,
2373
+ useFieldModel,
2281
2374
  };
2282
2375
  }
2283
2376
  /**
@@ -2429,16 +2522,22 @@ const FormImpl = defineComponent({
2429
2522
  type: Function,
2430
2523
  default: undefined,
2431
2524
  },
2525
+ keepValues: {
2526
+ type: Boolean,
2527
+ default: false,
2528
+ },
2432
2529
  },
2433
2530
  setup(props, ctx) {
2434
2531
  const initialValues = toRef(props, 'initialValues');
2435
2532
  const validationSchema = toRef(props, 'validationSchema');
2533
+ const keepValues = toRef(props, 'keepValues');
2436
2534
  const { errors, values, meta, isSubmitting, submitCount, validate, validateField, handleReset, resetForm, handleSubmit, submitForm, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
2437
2535
  validationSchema: validationSchema.value ? validationSchema : undefined,
2438
2536
  initialValues,
2439
2537
  initialErrors: props.initialErrors,
2440
2538
  initialTouched: props.initialTouched,
2441
2539
  validateOnMount: props.validateOnMount,
2540
+ keepValuesOnUnmount: keepValues,
2442
2541
  });
2443
2542
  const onSubmit = props.onSubmit ? handleSubmit(props.onSubmit, props.onInvalidSubmit) : submitForm;
2444
2543
  function handleFormReset(e) {
@@ -2508,15 +2607,13 @@ const FormImpl = defineComponent({
2508
2607
  });
2509
2608
  const Form = FormImpl;
2510
2609
 
2511
- let FIELD_ARRAY_COUNTER = 0;
2512
2610
  function useFieldArray(arrayPath) {
2513
- const id = FIELD_ARRAY_COUNTER++;
2514
2611
  const form = injectWithSelf(FormContextKey, undefined);
2515
2612
  const fields = ref([]);
2516
2613
  // eslint-disable-next-line @typescript-eslint/no-empty-function
2517
2614
  const noOp = () => { };
2518
2615
  const noOpApi = {
2519
- fields: readonly(fields),
2616
+ fields,
2520
2617
  remove: noOp,
2521
2618
  push: noOp,
2522
2619
  swap: noOp,
@@ -2524,6 +2621,7 @@ function useFieldArray(arrayPath) {
2524
2621
  update: noOp,
2525
2622
  replace: noOp,
2526
2623
  prepend: noOp,
2624
+ move: noOp,
2527
2625
  };
2528
2626
  if (!form) {
2529
2627
  warn('FieldArray requires being a child of `<Form/>` or `useForm` being called before it. Array fields may not work correctly');
@@ -2533,9 +2631,13 @@ function useFieldArray(arrayPath) {
2533
2631
  warn('FieldArray requires a field path to be provided, did you forget to pass the `name` prop?');
2534
2632
  return noOpApi;
2535
2633
  }
2634
+ const alreadyExists = form.fieldArrays.find(a => unref(a.path) === unref(arrayPath));
2635
+ if (alreadyExists) {
2636
+ return alreadyExists;
2637
+ }
2536
2638
  let entryCounter = 0;
2537
2639
  function initFields() {
2538
- const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []);
2640
+ const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []) || [];
2539
2641
  fields.value = currentValues.map(createEntry);
2540
2642
  updateEntryFlags();
2541
2643
  }
@@ -2552,10 +2654,20 @@ function useFieldArray(arrayPath) {
2552
2654
  const key = entryCounter++;
2553
2655
  const entry = {
2554
2656
  key,
2555
- value: computed(() => {
2556
- const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []);
2557
- const idx = fields.value.findIndex(e => e.key === key);
2558
- return idx === -1 ? value : currentValues[idx];
2657
+ value: computed({
2658
+ get() {
2659
+ const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []) || [];
2660
+ const idx = fields.value.findIndex(e => e.key === key);
2661
+ return idx === -1 ? value : currentValues[idx];
2662
+ },
2663
+ set(value) {
2664
+ const idx = fields.value.findIndex(e => e.key === key);
2665
+ if (idx === -1) {
2666
+ warn(`Attempting to update a non-existent array item`);
2667
+ return;
2668
+ }
2669
+ update(idx, value);
2670
+ },
2559
2671
  }),
2560
2672
  isFirst: false,
2561
2673
  isLast: false,
@@ -2648,14 +2760,26 @@ function useFieldArray(arrayPath) {
2648
2760
  fields.value.unshift(createEntry(value));
2649
2761
  updateEntryFlags();
2650
2762
  }
2651
- form.fieldArraysLookup[id] = {
2652
- reset: initFields,
2653
- };
2654
- onBeforeUnmount(() => {
2655
- delete form.fieldArraysLookup[id];
2656
- });
2657
- return {
2658
- fields: readonly(fields),
2763
+ function move(oldIdx, newIdx) {
2764
+ const pathName = unref(arrayPath);
2765
+ const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
2766
+ const newValue = isNullOrUndefined(pathValue) ? [] : [...pathValue];
2767
+ if (!Array.isArray(pathValue) || !(oldIdx in pathValue) || !(newIdx in pathValue)) {
2768
+ return;
2769
+ }
2770
+ const newFields = [...fields.value];
2771
+ const movedItem = newFields[oldIdx];
2772
+ newFields.splice(oldIdx, 1);
2773
+ newFields.splice(newIdx, 0, movedItem);
2774
+ const movedValue = newValue[oldIdx];
2775
+ newValue.splice(oldIdx, 1);
2776
+ newValue.splice(newIdx, 0, movedValue);
2777
+ form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
2778
+ fields.value = newFields;
2779
+ updateEntryFlags();
2780
+ }
2781
+ const fieldArrayCtx = {
2782
+ fields,
2659
2783
  remove,
2660
2784
  push,
2661
2785
  swap,
@@ -2663,7 +2787,16 @@ function useFieldArray(arrayPath) {
2663
2787
  update,
2664
2788
  replace,
2665
2789
  prepend,
2790
+ move,
2666
2791
  };
2792
+ form.fieldArrays.push(Object.assign({ path: arrayPath, reset: initFields }, fieldArrayCtx));
2793
+ onBeforeUnmount(() => {
2794
+ const idx = form.fieldArrays.findIndex(i => unref(i.path) === unref(arrayPath));
2795
+ if (idx >= 0) {
2796
+ form.fieldArrays.splice(idx, 1);
2797
+ }
2798
+ });
2799
+ return fieldArrayCtx;
2667
2800
  }
2668
2801
 
2669
2802
  const FieldArrayImpl = defineComponent({
@@ -2676,7 +2809,7 @@ const FieldArrayImpl = defineComponent({
2676
2809
  },
2677
2810
  },
2678
2811
  setup(props, ctx) {
2679
- const { push, remove, swap, insert, replace, update, prepend, fields } = useFieldArray(toRef(props, 'name'));
2812
+ const { push, remove, swap, insert, replace, update, prepend, move, fields } = useFieldArray(toRef(props, 'name'));
2680
2813
  function slotProps() {
2681
2814
  return {
2682
2815
  fields: fields.value,
@@ -2687,6 +2820,7 @@ const FieldArrayImpl = defineComponent({
2687
2820
  update,
2688
2821
  replace,
2689
2822
  prepend,
2823
+ move,
2690
2824
  };
2691
2825
  }
2692
2826
  ctx.expose({
@@ -2697,6 +2831,7 @@ const FieldArrayImpl = defineComponent({
2697
2831
  update,
2698
2832
  replace,
2699
2833
  prepend,
2834
+ move,
2700
2835
  });
2701
2836
  return () => {
2702
2837
  const children = normalizeChildren(undefined, ctx, slotProps);
@@ -2996,4 +3131,4 @@ function useSubmitForm(cb) {
2996
3131
  };
2997
3132
  }
2998
3133
 
2999
- export { ErrorMessage, Field, FieldArray, FieldContextKey, Form, FormContextKey, configure, defineRule, useField, useFieldArray, useFieldError, useFieldValue, useForm, useFormErrors, useFormValues, useIsFieldDirty, useIsFieldTouched, useIsFieldValid, useIsFormDirty, useIsFormTouched, useIsFormValid, useIsSubmitting, useResetForm, useSubmitCount, useSubmitForm, useValidateField, useValidateForm, validate };
3134
+ export { ErrorMessage, Field, FieldArray, FieldContextKey, Form, FormContextKey, IS_ABSENT, configure, defineRule, useField, useFieldArray, useFieldError, useFieldValue, useForm, useFormErrors, useFormValues, useIsFieldDirty, useIsFieldTouched, useIsFieldValid, useIsFormDirty, useIsFormTouched, useIsFormValid, useIsSubmitting, useResetForm, useSubmitCount, useSubmitForm, useValidateField, useValidateForm, validate };