vee-validate 4.5.11 → 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.11
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)
@@ -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);
@@ -1572,8 +1583,10 @@ function useCheckboxField(name, rules, opts) {
1572
1583
  handleChange(newValue, shouldValidate);
1573
1584
  }
1574
1585
  onBeforeUnmount(() => {
1575
- // toggles the checkbox value if it was checked
1576
- 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) {
1577
1590
  handleCheckboxChange(unref(checkedValue), false);
1578
1591
  }
1579
1592
  });
@@ -1582,6 +1595,41 @@ function useCheckboxField(name, rules, opts) {
1582
1595
  uncheckedValue, handleChange: handleCheckboxChange });
1583
1596
  }
1584
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];
1585
1633
  }
1586
1634
 
1587
1635
  const FieldImpl = defineComponent({
@@ -1648,13 +1696,17 @@ const FieldImpl = defineComponent({
1648
1696
  type: Boolean,
1649
1697
  default: false,
1650
1698
  },
1699
+ keepValue: {
1700
+ type: Boolean,
1701
+ default: undefined,
1702
+ },
1651
1703
  },
1652
1704
  setup(props, ctx) {
1653
1705
  const rules = toRef(props, 'rules');
1654
1706
  const name = toRef(props, 'name');
1655
1707
  const label = toRef(props, 'label');
1656
1708
  const uncheckedValue = toRef(props, 'uncheckedValue');
1657
- const hasModelEvents = isPropPresent(props, 'onUpdate:modelValue');
1709
+ const keepValue = toRef(props, 'keepValue');
1658
1710
  const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, setTouched, resetField, handleReset, meta, checked, setErrors, } = useField(name, rules, {
1659
1711
  validateOnMount: props.validateOnMount,
1660
1712
  bails: props.bails,
@@ -1666,25 +1718,22 @@ const FieldImpl = defineComponent({
1666
1718
  uncheckedValue,
1667
1719
  label,
1668
1720
  validateOnValueUpdate: false,
1721
+ keepValueOnUnmount: keepValue,
1669
1722
  });
1670
1723
  // 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;
1724
+ const onChangeHandler = function handleChangeWithModel(e, shouldValidate = true) {
1725
+ handleChange(e, shouldValidate);
1726
+ ctx.emit('update:modelValue', value.value);
1727
+ };
1677
1728
  const handleInput = (e) => {
1678
1729
  if (!hasCheckedAttr(ctx.attrs.type)) {
1679
1730
  value.value = normalizeEventValue(e);
1680
1731
  }
1681
1732
  };
1682
- const onInputHandler = hasModelEvents
1683
- ? function handleInputWithModel(e) {
1684
- handleInput(e);
1685
- ctx.emit('update:modelValue', value.value);
1686
- }
1687
- : handleInput;
1733
+ const onInputHandler = function handleInputWithModel(e) {
1734
+ handleInput(e);
1735
+ ctx.emit('update:modelValue', value.value);
1736
+ };
1688
1737
  const fieldProps = computed(() => {
1689
1738
  const { validateOnInput, validateOnChange, validateOnBlur, validateOnModelUpdate } = resolveValidationTriggers(props);
1690
1739
  const baseOnBlur = [handleBlur, ctx.attrs.onBlur, validateOnBlur ? validateField : undefined].filter(Boolean);
@@ -1700,26 +1749,12 @@ const FieldImpl = defineComponent({
1700
1749
  if (hasCheckedAttr(ctx.attrs.type) && checked) {
1701
1750
  attrs.checked = checked.value;
1702
1751
  }
1703
- else {
1704
- attrs.value = value.value;
1705
- }
1706
1752
  const tag = resolveTag(props, ctx);
1707
1753
  if (shouldHaveValueBinding(tag, ctx.attrs)) {
1708
- delete attrs.value;
1754
+ attrs.value = value.value;
1709
1755
  }
1710
1756
  return attrs;
1711
1757
  });
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
1758
  function slotProps() {
1724
1759
  return {
1725
1760
  field: fieldProps.value,
@@ -1771,12 +1806,6 @@ function resolveValidationTriggers(props) {
1771
1806
  validateOnModelUpdate: (_d = props.validateOnModelUpdate) !== null && _d !== void 0 ? _d : validateOnModelUpdate,
1772
1807
  };
1773
1808
  }
1774
- function applyModifiers(value, modifiers) {
1775
- if (modifiers.number) {
1776
- return toNumber(value);
1777
- }
1778
- return value;
1779
- }
1780
1809
  function resolveInitialValue(props, ctx) {
1781
1810
  // Gets the initial value either from `value` prop/attr or `v-model` binding (modelValue)
1782
1811
  // For checkboxes and radio buttons it will always be the model value not the `value` attribute
@@ -1789,6 +1818,7 @@ const Field = FieldImpl;
1789
1818
 
1790
1819
  let FORM_COUNTER = 0;
1791
1820
  function useForm(opts) {
1821
+ var _a;
1792
1822
  const formId = FORM_COUNTER++;
1793
1823
  // Prevents fields from double resetting their values, which causes checkboxes to toggle their initial value
1794
1824
  // TODO: This won't be needed if we centralize all the state inside the `form` for form inputs
@@ -1799,8 +1829,8 @@ function useForm(opts) {
1799
1829
  const isSubmitting = ref(false);
1800
1830
  // The number of times the user tried to submit the form
1801
1831
  const submitCount = ref(0);
1802
- // dictionary for field arrays to receive various signals like reset
1803
- const fieldArraysLookup = {};
1832
+ // field arrays managed by this form
1833
+ const fieldArrays = [];
1804
1834
  // a private ref for all form values
1805
1835
  const formValues = reactive(klona(unref(opts === null || opts === void 0 ? void 0 : opts.initialValues) || {}));
1806
1836
  // the source of errors for the form fields
@@ -1847,10 +1877,11 @@ function useForm(opts) {
1847
1877
  // mutable non-reactive reference to initial errors
1848
1878
  // we need this to process initial errors then unset them
1849
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;
1850
1881
  // initial form values
1851
1882
  const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts === null || opts === void 0 ? void 0 : opts.initialValues);
1852
1883
  // form meta aggregations
1853
- const meta = useFormMeta(fieldsByPath, formValues, initialValues, errors);
1884
+ const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);
1854
1885
  const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
1855
1886
  const formCtx = {
1856
1887
  formId,
@@ -1862,7 +1893,8 @@ function useForm(opts) {
1862
1893
  submitCount,
1863
1894
  meta,
1864
1895
  isSubmitting,
1865
- fieldArraysLookup,
1896
+ fieldArrays,
1897
+ keepValuesOnUnmount,
1866
1898
  validateSchema: unref(schema) ? validateSchema : undefined,
1867
1899
  validate,
1868
1900
  register: registerField,
@@ -1880,6 +1912,7 @@ function useForm(opts) {
1880
1912
  stageInitialValue,
1881
1913
  unsetInitialValue,
1882
1914
  setFieldInitialValue,
1915
+ useFieldModel,
1883
1916
  };
1884
1917
  function isFieldGroup(fieldOrGroup) {
1885
1918
  return Array.isArray(fieldOrGroup);
@@ -1949,7 +1982,22 @@ function useForm(opts) {
1949
1982
  setFieldValue(path, fields[path]);
1950
1983
  });
1951
1984
  // regenerate the arrays when the form values change
1952
- 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);
1953
2001
  }
1954
2002
  /**
1955
2003
  * Sets the touched meta state on a field
@@ -2073,13 +2121,26 @@ function useForm(opts) {
2073
2121
  }
2074
2122
  function unregisterField(field) {
2075
2123
  const fieldName = unref(field.name);
2124
+ const fieldInstance = fieldsByPath.value[fieldName];
2125
+ const isGroup = !!fieldInstance && isFieldGroup(fieldInstance);
2076
2126
  removeFieldFromPath(field, fieldName);
2077
2127
  nextTick(() => {
2128
+ var _a;
2078
2129
  // clears a field error on unmounted
2079
2130
  // we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes
2080
2131
  // #3384
2081
2132
  if (!fieldExists(fieldName)) {
2082
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
+ }
2083
2144
  unsetPath(formValues, fieldName);
2084
2145
  }
2085
2146
  });
@@ -2186,9 +2247,12 @@ function useForm(opts) {
2186
2247
  /**
2187
2248
  * Sneaky function to set initial field values
2188
2249
  */
2189
- function stageInitialValue(path, value) {
2250
+ function stageInitialValue(path, value, updateOriginal = false) {
2190
2251
  setInPath(formValues, path, value);
2191
2252
  setFieldInitialValue(path, value);
2253
+ if (updateOriginal) {
2254
+ setInPath(originalInitialValues.value, path, klona(value));
2255
+ }
2192
2256
  }
2193
2257
  async function _validateSchema() {
2194
2258
  const schemaValue = unref(schema);
@@ -2205,10 +2269,12 @@ function useForm(opts) {
2205
2269
  }
2206
2270
  /**
2207
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
2208
2273
  */
2209
- const debouncedSchemaValidation = debounceAsync(_validateSchema, 5);
2274
+ const debouncedSilentValidation = debounceAsync(_validateSchema, 5);
2275
+ const debouncedValidation = debounceAsync(_validateSchema, 5);
2210
2276
  async function validateSchema(mode) {
2211
- const formResult = await debouncedSchemaValidation();
2277
+ const formResult = await (mode === 'silent' ? debouncedSilentValidation() : debouncedValidation());
2212
2278
  // fields by id lookup
2213
2279
  const fieldsById = formCtx.fieldsByPath.value || {};
2214
2280
  // errors fields names, we need it to also check if custom errors are updated
@@ -2304,6 +2370,7 @@ function useForm(opts) {
2304
2370
  setValues,
2305
2371
  setFieldTouched,
2306
2372
  setTouched,
2373
+ useFieldModel,
2307
2374
  };
2308
2375
  }
2309
2376
  /**
@@ -2455,16 +2522,22 @@ const FormImpl = defineComponent({
2455
2522
  type: Function,
2456
2523
  default: undefined,
2457
2524
  },
2525
+ keepValues: {
2526
+ type: Boolean,
2527
+ default: false,
2528
+ },
2458
2529
  },
2459
2530
  setup(props, ctx) {
2460
2531
  const initialValues = toRef(props, 'initialValues');
2461
2532
  const validationSchema = toRef(props, 'validationSchema');
2533
+ const keepValues = toRef(props, 'keepValues');
2462
2534
  const { errors, values, meta, isSubmitting, submitCount, validate, validateField, handleReset, resetForm, handleSubmit, submitForm, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
2463
2535
  validationSchema: validationSchema.value ? validationSchema : undefined,
2464
2536
  initialValues,
2465
2537
  initialErrors: props.initialErrors,
2466
2538
  initialTouched: props.initialTouched,
2467
2539
  validateOnMount: props.validateOnMount,
2540
+ keepValuesOnUnmount: keepValues,
2468
2541
  });
2469
2542
  const onSubmit = props.onSubmit ? handleSubmit(props.onSubmit, props.onInvalidSubmit) : submitForm;
2470
2543
  function handleFormReset(e) {
@@ -2534,15 +2607,13 @@ const FormImpl = defineComponent({
2534
2607
  });
2535
2608
  const Form = FormImpl;
2536
2609
 
2537
- let FIELD_ARRAY_COUNTER = 0;
2538
2610
  function useFieldArray(arrayPath) {
2539
- const id = FIELD_ARRAY_COUNTER++;
2540
2611
  const form = injectWithSelf(FormContextKey, undefined);
2541
2612
  const fields = ref([]);
2542
2613
  // eslint-disable-next-line @typescript-eslint/no-empty-function
2543
2614
  const noOp = () => { };
2544
2615
  const noOpApi = {
2545
- fields: readonly(fields),
2616
+ fields,
2546
2617
  remove: noOp,
2547
2618
  push: noOp,
2548
2619
  swap: noOp,
@@ -2550,6 +2621,7 @@ function useFieldArray(arrayPath) {
2550
2621
  update: noOp,
2551
2622
  replace: noOp,
2552
2623
  prepend: noOp,
2624
+ move: noOp,
2553
2625
  };
2554
2626
  if (!form) {
2555
2627
  warn('FieldArray requires being a child of `<Form/>` or `useForm` being called before it. Array fields may not work correctly');
@@ -2559,9 +2631,13 @@ function useFieldArray(arrayPath) {
2559
2631
  warn('FieldArray requires a field path to be provided, did you forget to pass the `name` prop?');
2560
2632
  return noOpApi;
2561
2633
  }
2634
+ const alreadyExists = form.fieldArrays.find(a => unref(a.path) === unref(arrayPath));
2635
+ if (alreadyExists) {
2636
+ return alreadyExists;
2637
+ }
2562
2638
  let entryCounter = 0;
2563
2639
  function initFields() {
2564
- 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), []) || [];
2565
2641
  fields.value = currentValues.map(createEntry);
2566
2642
  updateEntryFlags();
2567
2643
  }
@@ -2578,10 +2654,20 @@ function useFieldArray(arrayPath) {
2578
2654
  const key = entryCounter++;
2579
2655
  const entry = {
2580
2656
  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];
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
+ },
2585
2671
  }),
2586
2672
  isFirst: false,
2587
2673
  isLast: false,
@@ -2674,14 +2760,26 @@ function useFieldArray(arrayPath) {
2674
2760
  fields.value.unshift(createEntry(value));
2675
2761
  updateEntryFlags();
2676
2762
  }
2677
- form.fieldArraysLookup[id] = {
2678
- reset: initFields,
2679
- };
2680
- onBeforeUnmount(() => {
2681
- delete form.fieldArraysLookup[id];
2682
- });
2683
- return {
2684
- 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,
2685
2783
  remove,
2686
2784
  push,
2687
2785
  swap,
@@ -2689,7 +2787,16 @@ function useFieldArray(arrayPath) {
2689
2787
  update,
2690
2788
  replace,
2691
2789
  prepend,
2790
+ move,
2692
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;
2693
2800
  }
2694
2801
 
2695
2802
  const FieldArrayImpl = defineComponent({
@@ -2702,7 +2809,7 @@ const FieldArrayImpl = defineComponent({
2702
2809
  },
2703
2810
  },
2704
2811
  setup(props, ctx) {
2705
- 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'));
2706
2813
  function slotProps() {
2707
2814
  return {
2708
2815
  fields: fields.value,
@@ -2713,6 +2820,7 @@ const FieldArrayImpl = defineComponent({
2713
2820
  update,
2714
2821
  replace,
2715
2822
  prepend,
2823
+ move,
2716
2824
  };
2717
2825
  }
2718
2826
  ctx.expose({
@@ -2723,6 +2831,7 @@ const FieldArrayImpl = defineComponent({
2723
2831
  update,
2724
2832
  replace,
2725
2833
  prepend,
2834
+ move,
2726
2835
  });
2727
2836
  return () => {
2728
2837
  const children = normalizeChildren(undefined, ctx, slotProps);
@@ -3022,4 +3131,4 @@ function useSubmitForm(cb) {
3022
3131
  };
3023
3132
  }
3024
3133
 
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 };
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 };