vee-validate 4.5.11 → 4.6.2

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.11
2
+ * vee-validate v4.6.2
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)
@@ -843,8 +841,8 @@ function useFieldState(path, init) {
843
841
  /**
844
842
  * Creates the field value and resolves the initial value
845
843
  */
846
- function _useFieldValue(path, modelValue, shouldInjectForm) {
847
- const form = shouldInjectForm ? injectWithSelf(FormContextKey, undefined) : undefined;
844
+ function _useFieldValue(path, modelValue, shouldInjectForm = true) {
845
+ const form = shouldInjectForm === true ? injectWithSelf(FormContextKey, undefined) : undefined;
848
846
  const modelRef = ref(unref(modelValue));
849
847
  function resolveInitialValue() {
850
848
  if (!form) {
@@ -874,7 +872,7 @@ function _useFieldValue(path, modelValue, shouldInjectForm) {
874
872
  // prioritize model value over form values
875
873
  // #3429
876
874
  const currentValue = modelValue ? unref(modelValue) : getFromPath(form.values, unref(path), unref(initialValue));
877
- form.stageInitialValue(unref(path), currentValue);
875
+ form.stageInitialValue(unref(path), currentValue, true);
878
876
  // otherwise use a computed setter that triggers the `setFieldValue`
879
877
  const value = computed({
880
878
  get() {
@@ -1313,7 +1311,7 @@ function useField(name, rules, opts) {
1313
1311
  return _useField(name, rules, opts);
1314
1312
  }
1315
1313
  function _useField(name, rules, opts) {
1316
- 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);
1317
1315
  const form = !standalone ? injectWithSelf(FormContextKey) : undefined;
1318
1316
  // a flag indicating if the field is about to be removed/unmounted.
1319
1317
  let markedForRemoval = false;
@@ -1321,6 +1319,9 @@ function _useField(name, rules, opts) {
1321
1319
  modelValue,
1322
1320
  standalone,
1323
1321
  });
1322
+ if (syncVModel) {
1323
+ useVModel({ value, prop: modelPropName, handleChange });
1324
+ }
1324
1325
  /**
1325
1326
  * Handles common onBlur meta update
1326
1327
  */
@@ -1379,13 +1380,13 @@ function _useField(name, rules, opts) {
1379
1380
  return validateValidStateOnly();
1380
1381
  }
1381
1382
  // Common input/change event handler
1382
- const handleChange = (e, shouldValidate = true) => {
1383
+ function handleChange(e, shouldValidate = true) {
1383
1384
  const newValue = normalizeEventValue(e);
1384
1385
  value.value = newValue;
1385
1386
  if (!validateOnValueUpdate && shouldValidate) {
1386
1387
  validateWithStateMutation();
1387
1388
  }
1388
- };
1389
+ }
1389
1390
  // Runs the initial validation
1390
1391
  onMounted(() => {
1391
1392
  if (validateOnMount) {
@@ -1402,7 +1403,13 @@ function _useField(name, rules, opts) {
1402
1403
  }
1403
1404
  let unwatchValue;
1404
1405
  function watchValue() {
1405
- 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
+ }, {
1406
1413
  deep: true,
1407
1414
  });
1408
1415
  }
@@ -1443,6 +1450,7 @@ function _useField(name, rules, opts) {
1443
1450
  checkedValue,
1444
1451
  uncheckedValue,
1445
1452
  bails,
1453
+ keepValueOnUnmount,
1446
1454
  resetField,
1447
1455
  handleReset: () => resetField(),
1448
1456
  validate: validate$1,
@@ -1529,6 +1537,9 @@ function normalizeOptions(name, opts) {
1529
1537
  label: name,
1530
1538
  validateOnValueUpdate: true,
1531
1539
  standalone: false,
1540
+ keepValueOnUnmount: undefined,
1541
+ modelPropName: 'modelValue',
1542
+ syncVModel: true,
1532
1543
  });
1533
1544
  if (!opts) {
1534
1545
  return defaults();
@@ -1560,8 +1571,8 @@ function useCheckboxField(name, rules, opts) {
1560
1571
  return Array.isArray(currentValue) ? currentValue.includes(checkedVal) : checkedVal === currentValue;
1561
1572
  });
1562
1573
  function handleCheckboxChange(e, shouldValidate = true) {
1563
- var _a, _b;
1564
- 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)) {
1565
1576
  return;
1566
1577
  }
1567
1578
  let newValue = normalizeEventValue(e);
@@ -1571,17 +1582,46 @@ function useCheckboxField(name, rules, opts) {
1571
1582
  }
1572
1583
  handleChange(newValue, shouldValidate);
1573
1584
  }
1574
- onBeforeUnmount(() => {
1575
- // toggles the checkbox value if it was checked
1576
- if (checked.value) {
1577
- handleCheckboxChange(unref(checkedValue), false);
1578
- }
1579
- });
1580
1585
  return Object.assign(Object.assign({}, field), { checked,
1581
1586
  checkedValue,
1582
1587
  uncheckedValue, handleChange: handleCheckboxChange });
1583
1588
  }
1584
1589
  return patchCheckboxApi(_useField(name, rules, opts));
1590
+ }
1591
+ function useVModel({ prop, value, handleChange }) {
1592
+ const vm = getCurrentInstance();
1593
+ /* istanbul ignore next */
1594
+ if (!vm) {
1595
+ if ((process.env.NODE_ENV !== 'production')) {
1596
+ console.warn('Failed to setup model events because `useField` was not called in setup.');
1597
+ }
1598
+ return;
1599
+ }
1600
+ const propName = prop || 'modelValue';
1601
+ const emitName = `update:${propName}`;
1602
+ // Component doesn't have a model prop setup (must be defined on the props)
1603
+ if (!(propName in vm.props)) {
1604
+ return;
1605
+ }
1606
+ watch(value, newValue => {
1607
+ if (es6(newValue, getCurrentModelValue(vm, propName))) {
1608
+ return;
1609
+ }
1610
+ vm.emit(emitName, newValue);
1611
+ });
1612
+ watch(() => getCurrentModelValue(vm, propName), propValue => {
1613
+ if (propValue === IS_ABSENT && value.value === undefined) {
1614
+ return;
1615
+ }
1616
+ const newValue = propValue === IS_ABSENT ? undefined : propValue;
1617
+ if (es6(newValue, applyModelModifiers(value.value, vm.props.modelModifiers))) {
1618
+ return;
1619
+ }
1620
+ handleChange(newValue);
1621
+ });
1622
+ }
1623
+ function getCurrentModelValue(vm, propName) {
1624
+ return vm.props[propName];
1585
1625
  }
1586
1626
 
1587
1627
  const FieldImpl = defineComponent({
@@ -1648,13 +1688,17 @@ const FieldImpl = defineComponent({
1648
1688
  type: Boolean,
1649
1689
  default: false,
1650
1690
  },
1691
+ keepValue: {
1692
+ type: Boolean,
1693
+ default: undefined,
1694
+ },
1651
1695
  },
1652
1696
  setup(props, ctx) {
1653
1697
  const rules = toRef(props, 'rules');
1654
1698
  const name = toRef(props, 'name');
1655
1699
  const label = toRef(props, 'label');
1656
1700
  const uncheckedValue = toRef(props, 'uncheckedValue');
1657
- const hasModelEvents = isPropPresent(props, 'onUpdate:modelValue');
1701
+ const keepValue = toRef(props, 'keepValue');
1658
1702
  const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, setTouched, resetField, handleReset, meta, checked, setErrors, } = useField(name, rules, {
1659
1703
  validateOnMount: props.validateOnMount,
1660
1704
  bails: props.bails,
@@ -1666,25 +1710,22 @@ const FieldImpl = defineComponent({
1666
1710
  uncheckedValue,
1667
1711
  label,
1668
1712
  validateOnValueUpdate: false,
1713
+ keepValueOnUnmount: keepValue,
1669
1714
  });
1670
1715
  // If there is a v-model applied on the component we need to emit the `update:modelValue` whenever the value binding changes
1671
- const onChangeHandler = hasModelEvents
1672
- ? function handleChangeWithModel(e, shouldValidate = true) {
1673
- handleChange(e, shouldValidate);
1674
- ctx.emit('update:modelValue', value.value);
1675
- }
1676
- : handleChange;
1716
+ const onChangeHandler = function handleChangeWithModel(e, shouldValidate = true) {
1717
+ handleChange(e, shouldValidate);
1718
+ ctx.emit('update:modelValue', value.value);
1719
+ };
1677
1720
  const handleInput = (e) => {
1678
1721
  if (!hasCheckedAttr(ctx.attrs.type)) {
1679
1722
  value.value = normalizeEventValue(e);
1680
1723
  }
1681
1724
  };
1682
- const onInputHandler = hasModelEvents
1683
- ? function handleInputWithModel(e) {
1684
- handleInput(e);
1685
- ctx.emit('update:modelValue', value.value);
1686
- }
1687
- : handleInput;
1725
+ const onInputHandler = function handleInputWithModel(e) {
1726
+ handleInput(e);
1727
+ ctx.emit('update:modelValue', value.value);
1728
+ };
1688
1729
  const fieldProps = computed(() => {
1689
1730
  const { validateOnInput, validateOnChange, validateOnBlur, validateOnModelUpdate } = resolveValidationTriggers(props);
1690
1731
  const baseOnBlur = [handleBlur, ctx.attrs.onBlur, validateOnBlur ? validateField : undefined].filter(Boolean);
@@ -1700,26 +1741,12 @@ const FieldImpl = defineComponent({
1700
1741
  if (hasCheckedAttr(ctx.attrs.type) && checked) {
1701
1742
  attrs.checked = checked.value;
1702
1743
  }
1703
- else {
1704
- attrs.value = value.value;
1705
- }
1706
1744
  const tag = resolveTag(props, ctx);
1707
1745
  if (shouldHaveValueBinding(tag, ctx.attrs)) {
1708
- delete attrs.value;
1746
+ attrs.value = value.value;
1709
1747
  }
1710
1748
  return attrs;
1711
1749
  });
1712
- const modelValue = toRef(props, 'modelValue');
1713
- watch(modelValue, newModelValue => {
1714
- // Don't attempt to sync absent values
1715
- if (newModelValue === IS_ABSENT && value.value === undefined) {
1716
- return;
1717
- }
1718
- if (newModelValue !== applyModifiers(value.value, props.modelModifiers)) {
1719
- value.value = newModelValue === IS_ABSENT ? undefined : newModelValue;
1720
- validateField();
1721
- }
1722
- });
1723
1750
  function slotProps() {
1724
1751
  return {
1725
1752
  field: fieldProps.value,
@@ -1771,12 +1798,6 @@ function resolveValidationTriggers(props) {
1771
1798
  validateOnModelUpdate: (_d = props.validateOnModelUpdate) !== null && _d !== void 0 ? _d : validateOnModelUpdate,
1772
1799
  };
1773
1800
  }
1774
- function applyModifiers(value, modifiers) {
1775
- if (modifiers.number) {
1776
- return toNumber(value);
1777
- }
1778
- return value;
1779
- }
1780
1801
  function resolveInitialValue(props, ctx) {
1781
1802
  // Gets the initial value either from `value` prop/attr or `v-model` binding (modelValue)
1782
1803
  // For checkboxes and radio buttons it will always be the model value not the `value` attribute
@@ -1789,6 +1810,7 @@ const Field = FieldImpl;
1789
1810
 
1790
1811
  let FORM_COUNTER = 0;
1791
1812
  function useForm(opts) {
1813
+ var _a;
1792
1814
  const formId = FORM_COUNTER++;
1793
1815
  // Prevents fields from double resetting their values, which causes checkboxes to toggle their initial value
1794
1816
  // TODO: This won't be needed if we centralize all the state inside the `form` for form inputs
@@ -1799,8 +1821,8 @@ function useForm(opts) {
1799
1821
  const isSubmitting = ref(false);
1800
1822
  // The number of times the user tried to submit the form
1801
1823
  const submitCount = ref(0);
1802
- // dictionary for field arrays to receive various signals like reset
1803
- const fieldArraysLookup = {};
1824
+ // field arrays managed by this form
1825
+ const fieldArrays = [];
1804
1826
  // a private ref for all form values
1805
1827
  const formValues = reactive(klona(unref(opts === null || opts === void 0 ? void 0 : opts.initialValues) || {}));
1806
1828
  // the source of errors for the form fields
@@ -1847,10 +1869,11 @@ function useForm(opts) {
1847
1869
  // mutable non-reactive reference to initial errors
1848
1870
  // we need this to process initial errors then unset them
1849
1871
  const initialErrors = Object.assign({}, ((opts === null || opts === void 0 ? void 0 : opts.initialErrors) || {}));
1872
+ const keepValuesOnUnmount = (_a = opts === null || opts === void 0 ? void 0 : opts.keepValuesOnUnmount) !== null && _a !== void 0 ? _a : false;
1850
1873
  // initial form values
1851
1874
  const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts === null || opts === void 0 ? void 0 : opts.initialValues);
1852
1875
  // form meta aggregations
1853
- const meta = useFormMeta(fieldsByPath, formValues, initialValues, errors);
1876
+ const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);
1854
1877
  const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
1855
1878
  const formCtx = {
1856
1879
  formId,
@@ -1862,7 +1885,8 @@ function useForm(opts) {
1862
1885
  submitCount,
1863
1886
  meta,
1864
1887
  isSubmitting,
1865
- fieldArraysLookup,
1888
+ fieldArrays,
1889
+ keepValuesOnUnmount,
1866
1890
  validateSchema: unref(schema) ? validateSchema : undefined,
1867
1891
  validate,
1868
1892
  register: registerField,
@@ -1880,6 +1904,7 @@ function useForm(opts) {
1880
1904
  stageInitialValue,
1881
1905
  unsetInitialValue,
1882
1906
  setFieldInitialValue,
1907
+ useFieldModel,
1883
1908
  };
1884
1909
  function isFieldGroup(fieldOrGroup) {
1885
1910
  return Array.isArray(fieldOrGroup);
@@ -1949,7 +1974,22 @@ function useForm(opts) {
1949
1974
  setFieldValue(path, fields[path]);
1950
1975
  });
1951
1976
  // regenerate the arrays when the form values change
1952
- Object.values(fieldArraysLookup).forEach(f => f && f.reset());
1977
+ fieldArrays.forEach(f => f && f.reset());
1978
+ }
1979
+ function createModel(path) {
1980
+ const { value } = _useFieldValue(path);
1981
+ watch(value, () => {
1982
+ if (!fieldExists(unref(path))) {
1983
+ validate({ mode: 'validated-only' });
1984
+ }
1985
+ });
1986
+ return value;
1987
+ }
1988
+ function useFieldModel(path) {
1989
+ if (!Array.isArray(path)) {
1990
+ return createModel(path);
1991
+ }
1992
+ return path.map(createModel);
1953
1993
  }
1954
1994
  /**
1955
1995
  * Sets the touched meta state on a field
@@ -2027,10 +2067,6 @@ function useForm(opts) {
2027
2067
  return;
2028
2068
  }
2029
2069
  fieldAtPath.splice(idx, 1);
2030
- if (fieldAtPath.length === 1) {
2031
- fieldsByPath.value[fieldPath] = fieldAtPath[0];
2032
- return;
2033
- }
2034
2070
  if (!fieldAtPath.length) {
2035
2071
  delete fieldsByPath.value[fieldPath];
2036
2072
  }
@@ -2073,13 +2109,45 @@ function useForm(opts) {
2073
2109
  }
2074
2110
  function unregisterField(field) {
2075
2111
  const fieldName = unref(field.name);
2112
+ const fieldInstance = fieldsByPath.value[fieldName];
2113
+ const isGroup = !!fieldInstance && isFieldGroup(fieldInstance);
2076
2114
  removeFieldFromPath(field, fieldName);
2115
+ // clears a field error on unmounted
2116
+ // we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes
2077
2117
  nextTick(() => {
2078
- // clears a field error on unmounted
2079
- // we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes
2118
+ var _a;
2119
+ const shouldKeepValue = (_a = unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : unref(keepValuesOnUnmount);
2120
+ const currentGroupValue = getFromPath(formValues, fieldName);
2121
+ // The boolean here is we check if the field still belongs to the same control group with that name
2122
+ // if another group claimed the name, we should avoid handling it since it is no longer the same group
2123
+ // this happens with `v-for` over some checkboxes and field arrays.
2124
+ // also if the group no longer exist we can assume this group was the last one that controlled it
2125
+ const isSameGroup = isGroup && (fieldInstance === fieldsByPath.value[fieldName] || !fieldsByPath.value[fieldName]);
2126
+ // group field that still has a dangling value, the field may exist or not after it was removed.
2127
+ // This used to be handled in the useField composable but the form has better context on when it should/not happen.
2128
+ // if it does belong to it that means the group still exists
2129
+ // #3844
2130
+ if (isSameGroup && Array.isArray(currentGroupValue) && !shouldKeepValue) {
2131
+ const valueIdx = currentGroupValue.findIndex(i => es6(i, unref(field.checkedValue)));
2132
+ if (valueIdx > -1) {
2133
+ const newVal = [...currentGroupValue];
2134
+ newVal.splice(valueIdx, 1);
2135
+ setFieldValue(fieldName, newVal, { force: true });
2136
+ }
2137
+ }
2138
+ // Field was removed entirely, we should unset its path
2080
2139
  // #3384
2081
2140
  if (!fieldExists(fieldName)) {
2082
2141
  setFieldError(fieldName, undefined);
2142
+ // Checks if the field was configured to be unset during unmount or not
2143
+ // Checks both the form-level config and field-level one
2144
+ // Field has the priority if it is set, otherwise it goes to the form settings
2145
+ if (shouldKeepValue) {
2146
+ return;
2147
+ }
2148
+ if (isGroup && !isEmptyContainer(getFromPath(formValues, fieldName))) {
2149
+ return;
2150
+ }
2083
2151
  unsetPath(formValues, fieldName);
2084
2152
  }
2085
2153
  });
@@ -2186,9 +2254,12 @@ function useForm(opts) {
2186
2254
  /**
2187
2255
  * Sneaky function to set initial field values
2188
2256
  */
2189
- function stageInitialValue(path, value) {
2257
+ function stageInitialValue(path, value, updateOriginal = false) {
2190
2258
  setInPath(formValues, path, value);
2191
2259
  setFieldInitialValue(path, value);
2260
+ if (updateOriginal) {
2261
+ setInPath(originalInitialValues.value, path, klona(value));
2262
+ }
2192
2263
  }
2193
2264
  async function _validateSchema() {
2194
2265
  const schemaValue = unref(schema);
@@ -2205,10 +2276,12 @@ function useForm(opts) {
2205
2276
  }
2206
2277
  /**
2207
2278
  * Batches validation runs in 5ms batches
2279
+ * Must have two distinct batch queues to make sure they don't override each other settings #3783
2208
2280
  */
2209
- const debouncedSchemaValidation = debounceAsync(_validateSchema, 5);
2281
+ const debouncedSilentValidation = debounceAsync(_validateSchema, 5);
2282
+ const debouncedValidation = debounceAsync(_validateSchema, 5);
2210
2283
  async function validateSchema(mode) {
2211
- const formResult = await debouncedSchemaValidation();
2284
+ const formResult = await (mode === 'silent' ? debouncedSilentValidation() : debouncedValidation());
2212
2285
  // fields by id lookup
2213
2286
  const fieldsById = formCtx.fieldsByPath.value || {};
2214
2287
  // errors fields names, we need it to also check if custom errors are updated
@@ -2304,6 +2377,7 @@ function useForm(opts) {
2304
2377
  setValues,
2305
2378
  setFieldTouched,
2306
2379
  setTouched,
2380
+ useFieldModel,
2307
2381
  };
2308
2382
  }
2309
2383
  /**
@@ -2455,17 +2529,28 @@ const FormImpl = defineComponent({
2455
2529
  type: Function,
2456
2530
  default: undefined,
2457
2531
  },
2532
+ keepValues: {
2533
+ type: Boolean,
2534
+ default: false,
2535
+ },
2458
2536
  },
2459
2537
  setup(props, ctx) {
2460
2538
  const initialValues = toRef(props, 'initialValues');
2461
2539
  const validationSchema = toRef(props, 'validationSchema');
2462
- const { errors, values, meta, isSubmitting, submitCount, validate, validateField, handleReset, resetForm, handleSubmit, submitForm, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
2540
+ const keepValues = toRef(props, 'keepValues');
2541
+ const { errors, values, meta, isSubmitting, submitCount, validate, validateField, handleReset, resetForm, handleSubmit, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
2463
2542
  validationSchema: validationSchema.value ? validationSchema : undefined,
2464
2543
  initialValues,
2465
2544
  initialErrors: props.initialErrors,
2466
2545
  initialTouched: props.initialTouched,
2467
2546
  validateOnMount: props.validateOnMount,
2547
+ keepValuesOnUnmount: keepValues,
2468
2548
  });
2549
+ const submitForm = handleSubmit((_, { evt }) => {
2550
+ if (isFormSubmitEvent(evt)) {
2551
+ evt.target.submit();
2552
+ }
2553
+ }, props.onInvalidSubmit);
2469
2554
  const onSubmit = props.onSubmit ? handleSubmit(props.onSubmit, props.onInvalidSubmit) : submitForm;
2470
2555
  function handleFormReset(e) {
2471
2556
  if (isEvent(e)) {
@@ -2534,15 +2619,13 @@ const FormImpl = defineComponent({
2534
2619
  });
2535
2620
  const Form = FormImpl;
2536
2621
 
2537
- let FIELD_ARRAY_COUNTER = 0;
2538
2622
  function useFieldArray(arrayPath) {
2539
- const id = FIELD_ARRAY_COUNTER++;
2540
2623
  const form = injectWithSelf(FormContextKey, undefined);
2541
2624
  const fields = ref([]);
2542
2625
  // eslint-disable-next-line @typescript-eslint/no-empty-function
2543
2626
  const noOp = () => { };
2544
2627
  const noOpApi = {
2545
- fields: readonly(fields),
2628
+ fields,
2546
2629
  remove: noOp,
2547
2630
  push: noOp,
2548
2631
  swap: noOp,
@@ -2550,6 +2633,7 @@ function useFieldArray(arrayPath) {
2550
2633
  update: noOp,
2551
2634
  replace: noOp,
2552
2635
  prepend: noOp,
2636
+ move: noOp,
2553
2637
  };
2554
2638
  if (!form) {
2555
2639
  warn('FieldArray requires being a child of `<Form/>` or `useForm` being called before it. Array fields may not work correctly');
@@ -2559,9 +2643,13 @@ function useFieldArray(arrayPath) {
2559
2643
  warn('FieldArray requires a field path to be provided, did you forget to pass the `name` prop?');
2560
2644
  return noOpApi;
2561
2645
  }
2646
+ const alreadyExists = form.fieldArrays.find(a => unref(a.path) === unref(arrayPath));
2647
+ if (alreadyExists) {
2648
+ return alreadyExists;
2649
+ }
2562
2650
  let entryCounter = 0;
2563
2651
  function initFields() {
2564
- const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []);
2652
+ const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []) || [];
2565
2653
  fields.value = currentValues.map(createEntry);
2566
2654
  updateEntryFlags();
2567
2655
  }
@@ -2578,10 +2666,20 @@ function useFieldArray(arrayPath) {
2578
2666
  const key = entryCounter++;
2579
2667
  const entry = {
2580
2668
  key,
2581
- value: computed(() => {
2582
- const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []);
2583
- const idx = fields.value.findIndex(e => e.key === key);
2584
- return idx === -1 ? value : currentValues[idx];
2669
+ value: computed({
2670
+ get() {
2671
+ const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []) || [];
2672
+ const idx = fields.value.findIndex(e => e.key === key);
2673
+ return idx === -1 ? value : currentValues[idx];
2674
+ },
2675
+ set(value) {
2676
+ const idx = fields.value.findIndex(e => e.key === key);
2677
+ if (idx === -1) {
2678
+ warn(`Attempting to update a non-existent array item`);
2679
+ return;
2680
+ }
2681
+ update(idx, value);
2682
+ },
2585
2683
  }),
2586
2684
  isFirst: false,
2587
2685
  isLast: false,
@@ -2674,14 +2772,26 @@ function useFieldArray(arrayPath) {
2674
2772
  fields.value.unshift(createEntry(value));
2675
2773
  updateEntryFlags();
2676
2774
  }
2677
- form.fieldArraysLookup[id] = {
2678
- reset: initFields,
2679
- };
2680
- onBeforeUnmount(() => {
2681
- delete form.fieldArraysLookup[id];
2682
- });
2683
- return {
2684
- fields: readonly(fields),
2775
+ function move(oldIdx, newIdx) {
2776
+ const pathName = unref(arrayPath);
2777
+ const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
2778
+ const newValue = isNullOrUndefined(pathValue) ? [] : [...pathValue];
2779
+ if (!Array.isArray(pathValue) || !(oldIdx in pathValue) || !(newIdx in pathValue)) {
2780
+ return;
2781
+ }
2782
+ const newFields = [...fields.value];
2783
+ const movedItem = newFields[oldIdx];
2784
+ newFields.splice(oldIdx, 1);
2785
+ newFields.splice(newIdx, 0, movedItem);
2786
+ const movedValue = newValue[oldIdx];
2787
+ newValue.splice(oldIdx, 1);
2788
+ newValue.splice(newIdx, 0, movedValue);
2789
+ form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
2790
+ fields.value = newFields;
2791
+ updateEntryFlags();
2792
+ }
2793
+ const fieldArrayCtx = {
2794
+ fields,
2685
2795
  remove,
2686
2796
  push,
2687
2797
  swap,
@@ -2689,7 +2799,16 @@ function useFieldArray(arrayPath) {
2689
2799
  update,
2690
2800
  replace,
2691
2801
  prepend,
2802
+ move,
2692
2803
  };
2804
+ form.fieldArrays.push(Object.assign({ path: arrayPath, reset: initFields }, fieldArrayCtx));
2805
+ onBeforeUnmount(() => {
2806
+ const idx = form.fieldArrays.findIndex(i => unref(i.path) === unref(arrayPath));
2807
+ if (idx >= 0) {
2808
+ form.fieldArrays.splice(idx, 1);
2809
+ }
2810
+ });
2811
+ return fieldArrayCtx;
2693
2812
  }
2694
2813
 
2695
2814
  const FieldArrayImpl = defineComponent({
@@ -2702,7 +2821,7 @@ const FieldArrayImpl = defineComponent({
2702
2821
  },
2703
2822
  },
2704
2823
  setup(props, ctx) {
2705
- const { push, remove, swap, insert, replace, update, prepend, fields } = useFieldArray(toRef(props, 'name'));
2824
+ const { push, remove, swap, insert, replace, update, prepend, move, fields } = useFieldArray(toRef(props, 'name'));
2706
2825
  function slotProps() {
2707
2826
  return {
2708
2827
  fields: fields.value,
@@ -2713,6 +2832,7 @@ const FieldArrayImpl = defineComponent({
2713
2832
  update,
2714
2833
  replace,
2715
2834
  prepend,
2835
+ move,
2716
2836
  };
2717
2837
  }
2718
2838
  ctx.expose({
@@ -2723,6 +2843,7 @@ const FieldArrayImpl = defineComponent({
2723
2843
  update,
2724
2844
  replace,
2725
2845
  prepend,
2846
+ move,
2726
2847
  });
2727
2848
  return () => {
2728
2849
  const children = normalizeChildren(undefined, ctx, slotProps);
@@ -3022,4 +3143,4 @@ function useSubmitForm(cb) {
3022
3143
  };
3023
3144
  }
3024
3145
 
3025
- 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 };
3146
+ 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 };