vee-validate 4.4.10 → 4.5.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,10 @@
1
1
  /**
2
- * vee-validate v4.4.10
2
+ * vee-validate v4.5.0
3
3
  * (c) 2021 Abdelrahman Awad
4
4
  * @license MIT
5
5
  */
6
- import { inject, getCurrentInstance, warn as warn$1, unref, computed, onMounted, provide, isRef, watch, onBeforeUnmount, ref, reactive, nextTick, defineComponent, toRef, resolveDynamicComponent, h } 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, markRaw, readonly } from 'vue';
7
+ import { setupDevtoolsPlugin } from '@vue/devtools-api';
7
8
 
8
9
  function isCallable(fn) {
9
10
  return typeof fn === 'function';
@@ -46,8 +47,6 @@ function guardExtend(id, validator) {
46
47
  }
47
48
 
48
49
  const FormContextKey = Symbol('vee-validate-form');
49
- const FormErrorsKey = Symbol('vee-validate-form-errors');
50
- const FormInitialValuesKey = Symbol('vee-validate-form-initial-values');
51
50
  const FieldContextKey = Symbol('vee-validate-field-instance');
52
51
  const IS_ABSENT = Symbol('Default empty value');
53
52
 
@@ -147,17 +146,14 @@ function cleanupNonNestedPath(path) {
147
146
  }
148
147
  return path;
149
148
  }
150
- /**
151
- * Gets a nested property value from an object
152
- */
153
- function getFromPath(object, path, fallback = undefined) {
149
+ function getFromPath(object, path, fallback) {
154
150
  if (!object) {
155
151
  return fallback;
156
152
  }
157
153
  if (isNotNestedPath(path)) {
158
154
  return object[cleanupNonNestedPath(path)];
159
155
  }
160
- const resolvedValue = path
156
+ const resolvedValue = (path || '')
161
157
  .split(/\.|\[(\d+)\]/)
162
158
  .filter(Boolean)
163
159
  .reduce((acc, propKey) => {
@@ -261,20 +257,6 @@ function normalizeField(field) {
261
257
  }
262
258
  return field;
263
259
  }
264
- /**
265
- * Applies a mutation function on a field or field group
266
- */
267
- function applyFieldMutation(field, mutation, onlyFirst = false) {
268
- if (!Array.isArray(field)) {
269
- mutation(field);
270
- return;
271
- }
272
- if (onlyFirst) {
273
- mutation(field[0]);
274
- return;
275
- }
276
- field.forEach(mutation);
277
- }
278
260
  function resolveNextCheckboxValue(currentValue, checkedValue, uncheckedValue) {
279
261
  if (Array.isArray(currentValue)) {
280
262
  const newVal = [...currentValue];
@@ -283,6 +265,42 @@ function resolveNextCheckboxValue(currentValue, checkedValue, uncheckedValue) {
283
265
  return newVal;
284
266
  }
285
267
  return currentValue === checkedValue ? uncheckedValue : checkedValue;
268
+ }
269
+ /**
270
+ * Creates a throttled function that only invokes the provided function (`func`) at most once per within a given number of milliseconds
271
+ * (`limit`)
272
+ */
273
+ function throttle(func, limit) {
274
+ let inThrottle;
275
+ let lastResult;
276
+ return function (...args) {
277
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
278
+ const context = this;
279
+ if (!inThrottle) {
280
+ inThrottle = true;
281
+ setTimeout(() => (inThrottle = false), limit);
282
+ lastResult = func.apply(context, args);
283
+ }
284
+ return lastResult;
285
+ };
286
+ }
287
+ function debounceAsync(inner, ms = 0) {
288
+ let timer = null;
289
+ let resolves = [];
290
+ return function (...args) {
291
+ // Run the function after a certain amount of time
292
+ if (timer) {
293
+ window.clearTimeout(timer);
294
+ }
295
+ timer = window.setTimeout(() => {
296
+ // Get the result of the inner function, then apply it to the resolve function of
297
+ // each promise that has been created since the last time the inner function was run
298
+ const result = inner(...args);
299
+ resolves.forEach(r => r(result));
300
+ resolves = [];
301
+ }, ms);
302
+ return new Promise(resolve => resolves.push(resolve));
303
+ };
286
304
  }
287
305
 
288
306
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -773,21 +791,522 @@ var es6 = function equal(a, b) {
773
791
  };
774
792
 
775
793
  let ID_COUNTER = 0;
794
+ function useFieldState(path, init) {
795
+ const { value, initialValue, setInitialValue } = _useFieldValue(path, init.modelValue, !init.standalone);
796
+ const { errorMessage, errors, setErrors } = _useFieldErrors(path, !init.standalone);
797
+ const meta = _useFieldMeta(value, initialValue, errors);
798
+ const id = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER;
799
+ function setState(state) {
800
+ var _a;
801
+ if ('value' in state) {
802
+ value.value = state.value;
803
+ }
804
+ if ('errors' in state) {
805
+ setErrors(state.errors);
806
+ }
807
+ if ('touched' in state) {
808
+ meta.touched = (_a = state.touched) !== null && _a !== void 0 ? _a : meta.touched;
809
+ }
810
+ if ('initialValue' in state) {
811
+ setInitialValue(state.initialValue);
812
+ }
813
+ }
814
+ return {
815
+ id,
816
+ path,
817
+ value,
818
+ initialValue,
819
+ meta,
820
+ errors,
821
+ errorMessage,
822
+ setState,
823
+ };
824
+ }
825
+ /**
826
+ * Creates the field value and resolves the initial value
827
+ */
828
+ function _useFieldValue(path, modelValue, shouldInjectForm) {
829
+ const form = shouldInjectForm ? injectWithSelf(FormContextKey, undefined) : undefined;
830
+ const modelRef = ref(unref(modelValue));
831
+ function resolveInitialValue() {
832
+ if (!form) {
833
+ return unref(modelRef);
834
+ }
835
+ return getFromPath(form.meta.value.initialValues, unref(path), unref(modelRef));
836
+ }
837
+ function setInitialValue(value) {
838
+ if (!form) {
839
+ modelRef.value = value;
840
+ return;
841
+ }
842
+ form.setFieldInitialValue(unref(path), value);
843
+ }
844
+ const initialValue = computed(resolveInitialValue);
845
+ // if no form is associated, use a regular ref.
846
+ if (!form) {
847
+ const value = ref(resolveInitialValue());
848
+ return {
849
+ value,
850
+ initialValue,
851
+ setInitialValue,
852
+ };
853
+ }
854
+ // to set the initial value, first check if there is a current value, if there is then use it.
855
+ // otherwise use the configured initial value if it exists.
856
+ // prioritize model value over form values
857
+ // #3429
858
+ const currentValue = modelValue ? unref(modelValue) : getFromPath(form.values, unref(path), unref(initialValue));
859
+ form.stageInitialValue(unref(path), currentValue);
860
+ // otherwise use a computed setter that triggers the `setFieldValue`
861
+ const value = computed({
862
+ get() {
863
+ return getFromPath(form.values, unref(path));
864
+ },
865
+ set(newVal) {
866
+ form.setFieldValue(unref(path), newVal);
867
+ },
868
+ });
869
+ return {
870
+ value,
871
+ initialValue,
872
+ setInitialValue,
873
+ };
874
+ }
875
+ /**
876
+ * Creates meta flags state and some associated effects with them
877
+ */
878
+ function _useFieldMeta(currentValue, initialValue, errors) {
879
+ const meta = reactive({
880
+ touched: false,
881
+ pending: false,
882
+ valid: true,
883
+ validated: !!unref(errors).length,
884
+ initialValue: computed(() => unref(initialValue)),
885
+ dirty: computed(() => {
886
+ return !es6(unref(currentValue), unref(initialValue));
887
+ }),
888
+ });
889
+ watch(errors, value => {
890
+ meta.valid = !value.length;
891
+ }, {
892
+ immediate: true,
893
+ flush: 'sync',
894
+ });
895
+ return meta;
896
+ }
897
+ /**
898
+ * Creates the error message state for the field state
899
+ */
900
+ function _useFieldErrors(path, shouldInjectForm) {
901
+ const form = shouldInjectForm ? injectWithSelf(FormContextKey, undefined) : undefined;
902
+ function normalizeErrors(messages) {
903
+ if (!messages) {
904
+ return [];
905
+ }
906
+ return Array.isArray(messages) ? messages : [messages];
907
+ }
908
+ if (!form) {
909
+ const errors = ref([]);
910
+ return {
911
+ errors,
912
+ errorMessage: computed(() => errors.value[0]),
913
+ setErrors: (messages) => {
914
+ errors.value = normalizeErrors(messages);
915
+ },
916
+ };
917
+ }
918
+ const errors = computed(() => form.errorBag.value[unref(path)] || []);
919
+ return {
920
+ errors,
921
+ errorMessage: computed(() => errors.value[0]),
922
+ setErrors: (messages) => {
923
+ form.setFieldErrorBag(unref(path), normalizeErrors(messages));
924
+ },
925
+ };
926
+ }
927
+
928
+ function installDevtoolsPlugin(app) {
929
+ if ((process.env.NODE_ENV !== 'production')) {
930
+ setupDevtoolsPlugin({
931
+ id: 'vee-validate-devtools-plugin',
932
+ label: 'VeeValidate Plugin',
933
+ packageName: 'vee-validate',
934
+ homepage: 'https://vee-validate.logaretm.com/v4',
935
+ app,
936
+ logo: 'https://vee-validate.logaretm.com/v4/logo.png',
937
+ }, setupApiHooks);
938
+ }
939
+ }
940
+ const DEVTOOLS_FORMS = {};
941
+ const DEVTOOLS_FIELDS = {};
942
+ let API;
943
+ const refreshInspector = throttle(() => {
944
+ setTimeout(async () => {
945
+ await nextTick();
946
+ API === null || API === void 0 ? void 0 : API.sendInspectorState(INSPECTOR_ID);
947
+ API === null || API === void 0 ? void 0 : API.sendInspectorTree(INSPECTOR_ID);
948
+ }, 100);
949
+ }, 100);
950
+ function registerFormWithDevTools(form) {
951
+ const vm = getCurrentInstance();
952
+ if (!API) {
953
+ const app = vm === null || vm === void 0 ? void 0 : vm.appContext.app;
954
+ if (!app) {
955
+ return;
956
+ }
957
+ installDevtoolsPlugin(app);
958
+ }
959
+ DEVTOOLS_FORMS[form.formId] = Object.assign({}, form);
960
+ DEVTOOLS_FORMS[form.formId]._vm = vm;
961
+ onUnmounted(() => {
962
+ delete DEVTOOLS_FORMS[form.formId];
963
+ refreshInspector();
964
+ });
965
+ refreshInspector();
966
+ }
967
+ function registerSingleFieldWithDevtools(field) {
968
+ const vm = getCurrentInstance();
969
+ if (!API) {
970
+ const app = vm === null || vm === void 0 ? void 0 : vm.appContext.app;
971
+ if (!app) {
972
+ return;
973
+ }
974
+ installDevtoolsPlugin(app);
975
+ }
976
+ DEVTOOLS_FIELDS[field.id] = Object.assign({}, field);
977
+ DEVTOOLS_FIELDS[field.id]._vm = vm;
978
+ onUnmounted(() => {
979
+ delete DEVTOOLS_FIELDS[field.id];
980
+ refreshInspector();
981
+ });
982
+ refreshInspector();
983
+ }
984
+ const INSPECTOR_ID = 'vee-validate-inspector';
985
+ const COLORS = {
986
+ error: 0xbd4b4b,
987
+ success: 0x06d77b,
988
+ unknown: 0x54436b,
989
+ white: 0xffffff,
990
+ black: 0x000000,
991
+ blue: 0x035397,
992
+ purple: 0xb980f0,
993
+ orange: 0xf5a962,
994
+ gray: 0xbbbfca,
995
+ };
996
+ let SELECTED_NODE = null;
997
+ function setupApiHooks(api) {
998
+ API = api;
999
+ api.addInspector({
1000
+ id: INSPECTOR_ID,
1001
+ icon: 'rule',
1002
+ label: 'vee-validate',
1003
+ noSelectionText: 'Select a vee-validate node to inspect',
1004
+ actions: [
1005
+ {
1006
+ icon: 'done_outline',
1007
+ tooltip: 'Validate selected item',
1008
+ action: async () => {
1009
+ if (!SELECTED_NODE) {
1010
+ console.error('There is not a valid selected vee-validate node or component');
1011
+ return;
1012
+ }
1013
+ const result = await SELECTED_NODE.validate();
1014
+ console.log(result);
1015
+ },
1016
+ },
1017
+ {
1018
+ icon: 'delete_sweep',
1019
+ tooltip: 'Clear validation state of the selected item',
1020
+ action: () => {
1021
+ if (!SELECTED_NODE) {
1022
+ console.error('There is not a valid selected vee-validate node or component');
1023
+ return;
1024
+ }
1025
+ if ('id' in SELECTED_NODE) {
1026
+ SELECTED_NODE.resetField();
1027
+ return;
1028
+ }
1029
+ SELECTED_NODE.resetForm();
1030
+ },
1031
+ },
1032
+ ],
1033
+ });
1034
+ api.on.getInspectorTree(payload => {
1035
+ if (payload.inspectorId !== INSPECTOR_ID) {
1036
+ return;
1037
+ }
1038
+ const forms = Object.values(DEVTOOLS_FORMS);
1039
+ const fields = Object.values(DEVTOOLS_FIELDS);
1040
+ payload.rootNodes = [
1041
+ ...forms.map(mapFormForDevtoolsInspector),
1042
+ ...fields.map(field => mapFieldForDevtoolsInspector(field)),
1043
+ ];
1044
+ });
1045
+ api.on.getInspectorState((payload, ctx) => {
1046
+ if (payload.inspectorId !== INSPECTOR_ID || ctx.currentTab !== `custom-inspector:${INSPECTOR_ID}`) {
1047
+ return;
1048
+ }
1049
+ const { form, field, type } = decodeNodeId(payload.nodeId);
1050
+ if (form && type === 'form') {
1051
+ payload.state = buildFormState(form);
1052
+ SELECTED_NODE = form;
1053
+ return;
1054
+ }
1055
+ if (field && type === 'field') {
1056
+ payload.state = buildFieldState(field);
1057
+ SELECTED_NODE = field;
1058
+ return;
1059
+ }
1060
+ SELECTED_NODE = null;
1061
+ });
1062
+ }
1063
+ function mapFormForDevtoolsInspector(form) {
1064
+ const { textColor, bgColor } = getTagTheme(form);
1065
+ const formTreeNodes = {};
1066
+ Object.values(form.fieldsByPath.value).forEach(field => {
1067
+ const fieldInstance = Array.isArray(field) ? field[0] : field;
1068
+ if (!fieldInstance) {
1069
+ return;
1070
+ }
1071
+ setInPath(formTreeNodes, unref(fieldInstance.name), mapFieldForDevtoolsInspector(fieldInstance, form));
1072
+ });
1073
+ function buildFormTree(tree, path = []) {
1074
+ const key = [...path].pop();
1075
+ if ('id' in tree) {
1076
+ return Object.assign(Object.assign({}, tree), { label: key || tree.label });
1077
+ }
1078
+ if (isObject(tree)) {
1079
+ return {
1080
+ id: `${path.join('.')}`,
1081
+ label: key || '',
1082
+ children: Object.keys(tree).map(key => buildFormTree(tree[key], [...path, key])),
1083
+ };
1084
+ }
1085
+ if (Array.isArray(tree)) {
1086
+ return {
1087
+ id: `${path.join('.')}`,
1088
+ label: `${key}[]`,
1089
+ children: tree.map((c, idx) => buildFormTree(c, [...path, String(idx)])),
1090
+ };
1091
+ }
1092
+ return { id: '', label: '', children: [] };
1093
+ }
1094
+ const { children } = buildFormTree(formTreeNodes);
1095
+ return {
1096
+ id: encodeNodeId(form),
1097
+ label: 'Form',
1098
+ children,
1099
+ tags: [
1100
+ {
1101
+ label: 'Form',
1102
+ textColor,
1103
+ backgroundColor: bgColor,
1104
+ },
1105
+ {
1106
+ label: `${Object.keys(form.fieldsByPath.value).length} fields`,
1107
+ textColor: COLORS.white,
1108
+ backgroundColor: COLORS.unknown,
1109
+ },
1110
+ ],
1111
+ };
1112
+ }
1113
+ function mapFieldForDevtoolsInspector(field, form) {
1114
+ const fieldInstance = normalizeField(field);
1115
+ const { textColor, bgColor } = getTagTheme(fieldInstance);
1116
+ const isGroup = Array.isArray(field) && field.length > 1;
1117
+ return {
1118
+ id: encodeNodeId(form, fieldInstance, !isGroup),
1119
+ label: unref(fieldInstance.name),
1120
+ children: Array.isArray(field) ? field.map(fieldItem => mapFieldForDevtoolsInspector(fieldItem, form)) : undefined,
1121
+ tags: [
1122
+ isGroup
1123
+ ? undefined
1124
+ : {
1125
+ label: 'Field',
1126
+ textColor,
1127
+ backgroundColor: bgColor,
1128
+ },
1129
+ !form
1130
+ ? {
1131
+ label: 'Standalone',
1132
+ textColor: COLORS.black,
1133
+ backgroundColor: COLORS.gray,
1134
+ }
1135
+ : undefined,
1136
+ !isGroup && fieldInstance.type === 'checkbox'
1137
+ ? {
1138
+ label: 'Checkbox',
1139
+ textColor: COLORS.white,
1140
+ backgroundColor: COLORS.blue,
1141
+ }
1142
+ : undefined,
1143
+ !isGroup && fieldInstance.type === 'radio'
1144
+ ? {
1145
+ label: 'Radio',
1146
+ textColor: COLORS.white,
1147
+ backgroundColor: COLORS.purple,
1148
+ }
1149
+ : undefined,
1150
+ isGroup
1151
+ ? {
1152
+ label: 'Group',
1153
+ textColor: COLORS.black,
1154
+ backgroundColor: COLORS.orange,
1155
+ }
1156
+ : undefined,
1157
+ ].filter(Boolean),
1158
+ };
1159
+ }
1160
+ function encodeNodeId(form, field, encodeIndex = true) {
1161
+ const fieldPath = form ? unref(field === null || field === void 0 ? void 0 : field.name) : field === null || field === void 0 ? void 0 : field.id;
1162
+ const fieldGroup = fieldPath ? form === null || form === void 0 ? void 0 : form.fieldsByPath.value[fieldPath] : undefined;
1163
+ let idx;
1164
+ if (encodeIndex && field && Array.isArray(fieldGroup)) {
1165
+ idx = fieldGroup.indexOf(field);
1166
+ }
1167
+ const idObject = { f: form === null || form === void 0 ? void 0 : form.formId, ff: fieldPath, idx, type: field ? 'field' : 'form' };
1168
+ return btoa(JSON.stringify(idObject));
1169
+ }
1170
+ function decodeNodeId(nodeId) {
1171
+ try {
1172
+ const idObject = JSON.parse(atob(nodeId));
1173
+ const form = DEVTOOLS_FORMS[idObject.f];
1174
+ if (!form && idObject.ff) {
1175
+ const field = DEVTOOLS_FIELDS[idObject.ff];
1176
+ if (!field) {
1177
+ return {};
1178
+ }
1179
+ return {
1180
+ type: idObject.type,
1181
+ field,
1182
+ };
1183
+ }
1184
+ if (!form) {
1185
+ return {};
1186
+ }
1187
+ const fieldGroup = form.fieldsByPath.value[idObject.ff];
1188
+ return {
1189
+ type: idObject.type,
1190
+ form,
1191
+ field: Array.isArray(fieldGroup) ? fieldGroup[idObject.idx || 0] : fieldGroup,
1192
+ };
1193
+ }
1194
+ catch (err) {
1195
+ // console.error(`Devtools: [vee-validate] Failed to parse node id ${nodeId}`);
1196
+ }
1197
+ return {};
1198
+ }
1199
+ function buildFieldState(field) {
1200
+ const { errors, meta, value } = field;
1201
+ return {
1202
+ 'Field state': [
1203
+ { key: 'errors', value: errors.value },
1204
+ {
1205
+ key: 'initialValue',
1206
+ value: meta.initialValue,
1207
+ },
1208
+ {
1209
+ key: 'currentValue',
1210
+ value: value.value,
1211
+ },
1212
+ {
1213
+ key: 'touched',
1214
+ value: meta.touched,
1215
+ },
1216
+ {
1217
+ key: 'dirty',
1218
+ value: meta.dirty,
1219
+ },
1220
+ {
1221
+ key: 'valid',
1222
+ value: meta.valid,
1223
+ },
1224
+ ],
1225
+ };
1226
+ }
1227
+ function buildFormState(form) {
1228
+ const { errorBag, meta, values, isSubmitting, submitCount } = form;
1229
+ return {
1230
+ 'Form state': [
1231
+ {
1232
+ key: 'submitCount',
1233
+ value: submitCount.value,
1234
+ },
1235
+ {
1236
+ key: 'isSubmitting',
1237
+ value: isSubmitting.value,
1238
+ },
1239
+ {
1240
+ key: 'touched',
1241
+ value: meta.value.touched,
1242
+ },
1243
+ {
1244
+ key: 'dirty',
1245
+ value: meta.value.dirty,
1246
+ },
1247
+ {
1248
+ key: 'valid',
1249
+ value: meta.value.valid,
1250
+ },
1251
+ {
1252
+ key: 'initialValues',
1253
+ value: meta.value.initialValues,
1254
+ },
1255
+ {
1256
+ key: 'currentValues',
1257
+ value: values,
1258
+ },
1259
+ {
1260
+ key: 'errors',
1261
+ value: keysOf(errorBag.value).reduce((acc, key) => {
1262
+ var _a;
1263
+ const message = (_a = errorBag.value[key]) === null || _a === void 0 ? void 0 : _a[0];
1264
+ if (message) {
1265
+ acc[key] = message;
1266
+ }
1267
+ return acc;
1268
+ }, {}),
1269
+ },
1270
+ ],
1271
+ };
1272
+ }
1273
+ /**
1274
+ * Resolves the tag color based on the form state
1275
+ */
1276
+ function getTagTheme(fieldOrForm) {
1277
+ // const fallbackColors = {
1278
+ // bgColor: COLORS.unknown,
1279
+ // textColor: COLORS.white,
1280
+ // };
1281
+ const isValid = 'id' in fieldOrForm ? fieldOrForm.meta.valid : fieldOrForm.meta.value.valid;
1282
+ return {
1283
+ bgColor: isValid ? COLORS.success : COLORS.error,
1284
+ textColor: isValid ? COLORS.black : COLORS.white,
1285
+ };
1286
+ }
1287
+
776
1288
  /**
777
1289
  * Creates a field composite.
778
1290
  */
779
1291
  function useField(name, rules, opts) {
780
- const fid = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER;
781
- const { initialValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue, standalone, } = normalizeOptions(unref(name), opts);
1292
+ if (hasCheckedAttr(opts === null || opts === void 0 ? void 0 : opts.type)) {
1293
+ return useCheckboxField(name, rules, opts);
1294
+ }
1295
+ return _useField(name, rules, opts);
1296
+ }
1297
+ function _useField(name, rules, opts) {
1298
+ const { initialValue: modelValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue, standalone, } = normalizeOptions(unref(name), opts);
782
1299
  const form = !standalone ? injectWithSelf(FormContextKey) : undefined;
783
- const { meta, errors, errorMessage, handleBlur, handleInput, resetValidationState, setValidationState, setErrors, value, checked, } = useValidationState({
784
- name,
785
- initValue: initialValue,
786
- form,
787
- type,
788
- checkedValue,
1300
+ const { id, value, initialValue, meta, setState, errors, errorMessage } = useFieldState(name, {
1301
+ modelValue,
789
1302
  standalone,
790
1303
  });
1304
+ /**
1305
+ * Handles common onBlur meta update
1306
+ */
1307
+ const handleBlur = () => {
1308
+ meta.touched = true;
1309
+ };
791
1310
  const normalizedRules = computed(() => {
792
1311
  let rulesValue = unref(rules);
793
1312
  const schema = unref(form === null || form === void 0 ? void 0 : form.schema);
@@ -814,27 +1333,30 @@ function useField(name, rules, opts) {
814
1333
  meta.pending = true;
815
1334
  meta.validated = true;
816
1335
  const result = await validateCurrentValue('validated-only');
1336
+ setState({ errors: result.errors });
817
1337
  meta.pending = false;
818
- return setValidationState(result);
1338
+ return result;
819
1339
  }
820
1340
  async function validateValidStateOnly() {
821
1341
  const result = await validateCurrentValue('silent');
822
1342
  meta.valid = result.valid;
1343
+ return result;
823
1344
  }
824
- // Common input/change event handler
825
- const handleChange = (e, shouldValidate = true) => {
826
- var _a, _b;
827
- if (checked && checked.value === ((_b = (_a = e) === null || _a === void 0 ? void 0 : _a.target) === null || _b === void 0 ? void 0 : _b.checked)) {
828
- return;
1345
+ function validate$1(opts) {
1346
+ if (!(opts === null || opts === void 0 ? void 0 : opts.mode) || (opts === null || opts === void 0 ? void 0 : opts.mode) === 'force') {
1347
+ return validateWithStateMutation();
829
1348
  }
830
- let newValue = normalizeEventValue(e);
831
- // Single checkbox field without a form to toggle it's value
832
- if (checked && type === 'checkbox' && !form) {
833
- newValue = resolveNextCheckboxValue(value.value, unref(checkedValue), unref(uncheckedValue));
1349
+ if ((opts === null || opts === void 0 ? void 0 : opts.mode) === 'validated-only') {
1350
+ return validateWithStateMutation();
834
1351
  }
1352
+ return validateValidStateOnly();
1353
+ }
1354
+ // Common input/change event handler
1355
+ const handleChange = (e, shouldValidate = true) => {
1356
+ const newValue = normalizeEventValue(e);
835
1357
  value.value = newValue;
836
1358
  if (!validateOnValueUpdate && shouldValidate) {
837
- return validateWithStateMutation();
1359
+ validateWithStateMutation();
838
1360
  }
839
1361
  };
840
1362
  // Runs the initial validation
@@ -859,17 +1381,31 @@ function useField(name, rules, opts) {
859
1381
  }
860
1382
  watchValue();
861
1383
  function resetField(state) {
1384
+ var _a;
862
1385
  unwatchValue === null || unwatchValue === void 0 ? void 0 : unwatchValue();
863
- resetValidationState(state);
1386
+ const newValue = state && 'value' in state ? state.value : initialValue.value;
1387
+ setState({
1388
+ value: klona(newValue),
1389
+ initialValue: klona(newValue),
1390
+ touched: (_a = state === null || state === void 0 ? void 0 : state.touched) !== null && _a !== void 0 ? _a : false,
1391
+ errors: (state === null || state === void 0 ? void 0 : state.errors) || [],
1392
+ });
1393
+ meta.pending = false;
1394
+ meta.validated = false;
864
1395
  validateValidStateOnly();
865
1396
  // need to watch at next tick to avoid triggering the value watcher
866
1397
  nextTick(() => {
867
1398
  watchValue();
868
1399
  });
869
1400
  }
1401
+ function setValue(newValue) {
1402
+ value.value = newValue;
1403
+ }
1404
+ function setErrors(errors) {
1405
+ setState({ errors: Array.isArray(errors) ? errors : [errors] });
1406
+ }
870
1407
  const field = {
871
- idx: -1,
872
- fid,
1408
+ id,
873
1409
  name,
874
1410
  label,
875
1411
  value,
@@ -879,17 +1415,16 @@ function useField(name, rules, opts) {
879
1415
  type,
880
1416
  checkedValue,
881
1417
  uncheckedValue,
882
- checked,
883
1418
  bails,
884
1419
  resetField,
885
1420
  handleReset: () => resetField(),
886
- validate: validateWithStateMutation,
1421
+ validate: validate$1,
887
1422
  handleChange,
888
1423
  handleBlur,
889
- handleInput,
890
- setValidationState,
1424
+ setState,
891
1425
  setTouched,
892
1426
  setErrors,
1427
+ setValue,
893
1428
  };
894
1429
  provide(FieldContextKey, field);
895
1430
  if (isRef(rules) && typeof unref(rules) !== 'function') {
@@ -897,11 +1432,20 @@ function useField(name, rules, opts) {
897
1432
  if (es6(value, oldValue)) {
898
1433
  return;
899
1434
  }
900
- return validateWithStateMutation();
1435
+ meta.validated ? validateWithStateMutation() : validateValidStateOnly();
901
1436
  }, {
902
1437
  deep: true,
903
1438
  });
904
1439
  }
1440
+ if ((process.env.NODE_ENV !== 'production')) {
1441
+ field._vm = getCurrentInstance();
1442
+ watch(() => (Object.assign(Object.assign({ errors: errors.value }, meta), { value: value.value })), refreshInspector, {
1443
+ deep: true,
1444
+ });
1445
+ if (!form) {
1446
+ registerSingleFieldWithDevtools(field);
1447
+ }
1448
+ }
905
1449
  // if no associated form return the field API immediately
906
1450
  if (!form) {
907
1451
  return field;
@@ -940,7 +1484,7 @@ function useField(name, rules, opts) {
940
1484
  }
941
1485
  const shouldValidate = !es6(deps, oldDeps);
942
1486
  if (shouldValidate) {
943
- meta.dirty ? validateWithStateMutation() : validateValidStateOnly();
1487
+ meta.validated ? validateWithStateMutation() : validateValidStateOnly();
944
1488
  }
945
1489
  });
946
1490
  return field;
@@ -965,104 +1509,6 @@ function normalizeOptions(name, opts) {
965
1509
  const checkedValue = 'valueProp' in opts ? opts.valueProp : opts.checkedValue;
966
1510
  return Object.assign(Object.assign(Object.assign({}, defaults()), (opts || {})), { checkedValue });
967
1511
  }
968
- /**
969
- * Manages the validation state of a field.
970
- */
971
- function useValidationState({ name, initValue, form, type, checkedValue, standalone, }) {
972
- const { errors, errorMessage, setErrors } = useFieldErrors(name, form);
973
- const formInitialValues = standalone ? undefined : injectWithSelf(FormInitialValuesKey, undefined);
974
- // clones the ref value to a mutable version
975
- const initialValueRef = ref(unref(initValue));
976
- const initialValue = computed(() => {
977
- return getFromPath(unref(formInitialValues), unref(name), unref(initialValueRef));
978
- });
979
- const value = useFieldValue$1(initialValue, name, form);
980
- const meta = useFieldMeta(initialValue, value, errors);
981
- const checked = hasCheckedAttr(type)
982
- ? computed(() => {
983
- if (Array.isArray(value.value)) {
984
- return value.value.includes(unref(checkedValue));
985
- }
986
- return unref(checkedValue) === value.value;
987
- })
988
- : undefined;
989
- /**
990
- * Handles common onBlur meta update
991
- */
992
- const handleBlur = () => {
993
- meta.touched = true;
994
- };
995
- /**
996
- * Handles common on blur events
997
- * @deprecated You should use `handleChange` instead
998
- */
999
- const handleInput = (e) => {
1000
- // Checkboxes/Radio will emit a `change` event anyway, custom components will use `update:modelValue`
1001
- // so this is redundant
1002
- if (!hasCheckedAttr(type)) {
1003
- value.value = normalizeEventValue(e);
1004
- }
1005
- };
1006
- // Updates the validation state with the validation result
1007
- function setValidationState(result) {
1008
- setErrors(result.errors);
1009
- return result;
1010
- }
1011
- // Resets the validation state
1012
- function resetValidationState(state) {
1013
- var _a;
1014
- const fieldPath = unref(name);
1015
- const newValue = state && 'value' in state
1016
- ? state.value
1017
- : getFromPath(unref(formInitialValues), fieldPath, unref(initValue));
1018
- if (form) {
1019
- form.setFieldValue(fieldPath, newValue, { force: true });
1020
- form.setFieldInitialValue(fieldPath, newValue);
1021
- }
1022
- else {
1023
- value.value = klona(newValue);
1024
- initialValueRef.value = klona(newValue);
1025
- }
1026
- setErrors((state === null || state === void 0 ? void 0 : state.errors) || []);
1027
- meta.touched = (_a = state === null || state === void 0 ? void 0 : state.touched) !== null && _a !== void 0 ? _a : false;
1028
- meta.pending = false;
1029
- meta.validated = false;
1030
- }
1031
- return {
1032
- meta,
1033
- errors,
1034
- errorMessage,
1035
- setErrors,
1036
- setValidationState,
1037
- resetValidationState,
1038
- handleBlur,
1039
- handleInput,
1040
- value,
1041
- checked,
1042
- };
1043
- }
1044
- /**
1045
- * Exposes meta flags state and some associated actions with them.
1046
- */
1047
- function useFieldMeta(initialValue, currentValue, errors) {
1048
- const meta = reactive({
1049
- touched: false,
1050
- pending: false,
1051
- valid: true,
1052
- validated: !!unref(errors).length,
1053
- initialValue: computed(() => unref(initialValue)),
1054
- dirty: computed(() => {
1055
- return !es6(unref(currentValue), unref(initialValue));
1056
- }),
1057
- });
1058
- watch(errors, value => {
1059
- meta.valid = !value.length;
1060
- }, {
1061
- immediate: true,
1062
- flush: 'sync',
1063
- });
1064
- return meta;
1065
- }
1066
1512
  /**
1067
1513
  * Extracts the validation rules from a schema
1068
1514
  */
@@ -1074,49 +1520,40 @@ function extractRuleFromSchema(schema, fieldName) {
1074
1520
  // there is a key on the schema object for this field
1075
1521
  return schema[fieldName];
1076
1522
  }
1077
- /**
1078
- * Manages the field value
1079
- */
1080
- function useFieldValue$1(initialValue, path, form) {
1081
- // if no form is associated, use a regular ref.
1082
- if (!form) {
1083
- return ref(unref(initialValue));
1084
- }
1085
- // to set the initial value, first check if there is a current value, if there is then use it.
1086
- // otherwise use the configured initial value if it exists.
1087
- // #3429
1088
- const currentValue = getFromPath(form.values, unref(path), unref(initialValue));
1089
- form.stageInitialValue(unref(path), currentValue === undefined ? unref(initialValue) : currentValue);
1090
- // otherwise use a computed setter that triggers the `setFieldValue`
1091
- const value = computed({
1092
- get() {
1093
- return getFromPath(form.values, unref(path));
1094
- },
1095
- set(newVal) {
1096
- form.setFieldValue(unref(path), newVal);
1097
- },
1098
- });
1099
- return value;
1100
- }
1101
- function useFieldErrors(path, form) {
1102
- if (!form) {
1103
- const errors = ref([]);
1104
- return {
1105
- errors: computed(() => errors.value),
1106
- errorMessage: computed(() => errors.value[0]),
1107
- setErrors: (messages) => {
1108
- errors.value = Array.isArray(messages) ? messages : [messages];
1109
- },
1110
- };
1523
+ function useCheckboxField(name, rules, opts) {
1524
+ const form = !(opts === null || opts === void 0 ? void 0 : opts.standalone) ? injectWithSelf(FormContextKey) : undefined;
1525
+ const checkedValue = opts === null || opts === void 0 ? void 0 : opts.checkedValue;
1526
+ const uncheckedValue = opts === null || opts === void 0 ? void 0 : opts.uncheckedValue;
1527
+ function patchCheckboxApi(field) {
1528
+ const handleChange = field.handleChange;
1529
+ const checked = computed(() => {
1530
+ const currentValue = unref(field.value);
1531
+ const checkedVal = unref(checkedValue);
1532
+ return Array.isArray(currentValue) ? currentValue.includes(checkedVal) : checkedVal === currentValue;
1533
+ });
1534
+ function handleCheckboxChange(e, shouldValidate = true) {
1535
+ var _a, _b;
1536
+ if (checked.value === ((_b = (_a = e) === null || _a === void 0 ? void 0 : _a.target) === null || _b === void 0 ? void 0 : _b.checked)) {
1537
+ return;
1538
+ }
1539
+ let newValue = normalizeEventValue(e);
1540
+ // Single checkbox field without a form to toggle it's value
1541
+ if (!form) {
1542
+ newValue = resolveNextCheckboxValue(unref(field.value), unref(checkedValue), unref(uncheckedValue));
1543
+ }
1544
+ handleChange(newValue, shouldValidate);
1545
+ }
1546
+ onBeforeUnmount(() => {
1547
+ // toggles the checkbox value if it was checked
1548
+ if (checked.value) {
1549
+ handleCheckboxChange(unref(checkedValue), false);
1550
+ }
1551
+ });
1552
+ return Object.assign(Object.assign({}, field), { checked,
1553
+ checkedValue,
1554
+ uncheckedValue, handleChange: handleCheckboxChange });
1111
1555
  }
1112
- const errors = computed(() => form.errorBag.value[unref(path)] || []);
1113
- return {
1114
- errors,
1115
- errorMessage: computed(() => errors.value[0]),
1116
- setErrors: (messages) => {
1117
- form.setFieldErrorBag(unref(path), messages);
1118
- },
1119
- };
1556
+ return patchCheckboxApi(_useField(name, rules, opts));
1120
1557
  }
1121
1558
 
1122
1559
  const Field = defineComponent({
@@ -1190,7 +1627,7 @@ const Field = defineComponent({
1190
1627
  const label = toRef(props, 'label');
1191
1628
  const uncheckedValue = toRef(props, 'uncheckedValue');
1192
1629
  const hasModelEvents = isPropPresent(props, 'onUpdate:modelValue');
1193
- const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, handleInput, setTouched, resetField, handleReset, meta, checked, setErrors, } = useField(name, rules, {
1630
+ const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, setTouched, resetField, handleReset, meta, checked, setErrors, } = useField(name, rules, {
1194
1631
  validateOnMount: props.validateOnMount,
1195
1632
  bails: props.bails,
1196
1633
  standalone: props.standalone,
@@ -1209,8 +1646,13 @@ const Field = defineComponent({
1209
1646
  ctx.emit('update:modelValue', value.value);
1210
1647
  }
1211
1648
  : handleChange;
1649
+ const handleInput = (e) => {
1650
+ if (!hasCheckedAttr(ctx.attrs.type)) {
1651
+ value.value = normalizeEventValue(e);
1652
+ }
1653
+ };
1212
1654
  const onInputHandler = hasModelEvents
1213
- ? function handleChangeWithModel(e) {
1655
+ ? function handleInputWithModel(e) {
1214
1656
  handleInput(e);
1215
1657
  ctx.emit('update:modelValue', value.value);
1216
1658
  }
@@ -1269,6 +1711,13 @@ const Field = defineComponent({
1269
1711
  setErrors,
1270
1712
  };
1271
1713
  }
1714
+ ctx.expose({
1715
+ setErrors,
1716
+ setTouched,
1717
+ reset: resetField,
1718
+ validate: validateField,
1719
+ handleChange,
1720
+ });
1272
1721
  return () => {
1273
1722
  const tag = resolveDynamicComponent(resolveTag(props, ctx));
1274
1723
  const children = normalizeChildren(tag, ctx, slotProps);
@@ -1311,40 +1760,19 @@ function resolveInitialValue(props, ctx) {
1311
1760
  return isPropPresent(props, 'modelValue') ? props.modelValue : undefined;
1312
1761
  }
1313
1762
 
1763
+ let FORM_COUNTER = 0;
1314
1764
  function useForm(opts) {
1315
- // A flat array containing field references
1316
- const fields = ref([]);
1765
+ const formId = FORM_COUNTER++;
1766
+ // A lookup containing fields or field groups
1767
+ const fieldsByPath = ref({});
1317
1768
  // If the form is currently submitting
1318
1769
  const isSubmitting = ref(false);
1319
- // a field map object useful for faster access of fields
1320
- const fieldsById = computed(() => {
1321
- return fields.value.reduce((acc, field) => {
1322
- const fieldPath = unref(field.name);
1323
- // if the field was not added before
1324
- if (!acc[fieldPath]) {
1325
- acc[fieldPath] = field;
1326
- field.idx = -1;
1327
- return acc;
1328
- }
1329
- // if the same name is detected
1330
- const existingField = acc[fieldPath];
1331
- if (!Array.isArray(existingField)) {
1332
- existingField.idx = 0;
1333
- acc[fieldPath] = [existingField];
1334
- }
1335
- const fieldGroup = acc[fieldPath];
1336
- field.idx = fieldGroup.length;
1337
- fieldGroup.push(field);
1338
- return acc;
1339
- }, {});
1340
- });
1341
1770
  // The number of times the user tried to submit the form
1342
1771
  const submitCount = ref(0);
1772
+ // dictionary for field arrays to receive various signals like reset
1773
+ const fieldArraysLookup = {};
1343
1774
  // a private ref for all form values
1344
1775
  const formValues = reactive(klona(unref(opts === null || opts === void 0 ? void 0 : opts.initialValues) || {}));
1345
- // a lookup to keep track of values by their field ids
1346
- // this is important because later we need it if fields swap names
1347
- const valuesByFid = {};
1348
1776
  // the source of errors for the form fields
1349
1777
  const { errorBag, setErrorBag, setFieldErrorBag } = useErrorBag(opts === null || opts === void 0 ? void 0 : opts.initialErrors);
1350
1778
  // Gets the first error of each field
@@ -1357,12 +1785,19 @@ function useForm(opts) {
1357
1785
  return acc;
1358
1786
  }, {});
1359
1787
  });
1788
+ function getFirstFieldAtPath(path) {
1789
+ const fieldOrGroup = fieldsByPath.value[path];
1790
+ return Array.isArray(fieldOrGroup) ? fieldOrGroup[0] : fieldOrGroup;
1791
+ }
1792
+ function fieldExists(path) {
1793
+ return !!fieldsByPath.value[path];
1794
+ }
1360
1795
  /**
1361
1796
  * Holds a computed reference to all fields names and labels
1362
1797
  */
1363
1798
  const fieldNames = computed(() => {
1364
- return keysOf(fieldsById.value).reduce((names, path) => {
1365
- const field = normalizeField(fieldsById.value[path]);
1799
+ return keysOf(fieldsByPath.value).reduce((names, path) => {
1800
+ const field = getFirstFieldAtPath(path);
1366
1801
  if (field) {
1367
1802
  names[path] = unref(field.label || field.name) || '';
1368
1803
  }
@@ -1370,9 +1805,9 @@ function useForm(opts) {
1370
1805
  }, {});
1371
1806
  });
1372
1807
  const fieldBailsMap = computed(() => {
1373
- return keysOf(fieldsById.value).reduce((map, path) => {
1808
+ return keysOf(fieldsByPath.value).reduce((map, path) => {
1374
1809
  var _a;
1375
- const field = normalizeField(fieldsById.value[path]);
1810
+ const field = getFirstFieldAtPath(path);
1376
1811
  if (field) {
1377
1812
  map[path] = (_a = field.bails) !== null && _a !== void 0 ? _a : true;
1378
1813
  }
@@ -1383,18 +1818,21 @@ function useForm(opts) {
1383
1818
  // we need this to process initial errors then unset them
1384
1819
  const initialErrors = Object.assign({}, ((opts === null || opts === void 0 ? void 0 : opts.initialErrors) || {}));
1385
1820
  // initial form values
1386
- const { readonlyInitialValues, initialValues, setInitialValues } = useFormInitialValues(fieldsById, formValues, opts === null || opts === void 0 ? void 0 : opts.initialValues);
1821
+ const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts === null || opts === void 0 ? void 0 : opts.initialValues);
1387
1822
  // form meta aggregations
1388
- const meta = useFormMeta(fields, formValues, readonlyInitialValues, errors);
1823
+ const meta = useFormMeta(fieldsByPath, formValues, initialValues, errors);
1389
1824
  const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
1390
1825
  const formCtx = {
1391
- fieldsById,
1826
+ formId,
1827
+ fieldsByPath,
1392
1828
  values: formValues,
1393
1829
  errorBag,
1830
+ errors,
1394
1831
  schema,
1395
1832
  submitCount,
1396
1833
  meta,
1397
1834
  isSubmitting,
1835
+ fieldArraysLookup,
1398
1836
  validateSchema: unref(schema) ? validateSchema : undefined,
1399
1837
  validate,
1400
1838
  register: registerField,
@@ -1410,8 +1848,18 @@ function useForm(opts) {
1410
1848
  resetForm,
1411
1849
  handleSubmit,
1412
1850
  stageInitialValue,
1851
+ unsetInitialValue,
1413
1852
  setFieldInitialValue,
1414
1853
  };
1854
+ function isFieldGroup(fieldOrGroup) {
1855
+ return Array.isArray(fieldOrGroup);
1856
+ }
1857
+ function applyFieldMutation(fieldOrGroup, mutation) {
1858
+ if (Array.isArray(fieldOrGroup)) {
1859
+ return fieldOrGroup.forEach(mutation);
1860
+ }
1861
+ return mutation(fieldOrGroup);
1862
+ }
1415
1863
  /**
1416
1864
  * Manually sets an error message on a specific field
1417
1865
  */
@@ -1429,7 +1877,7 @@ function useForm(opts) {
1429
1877
  */
1430
1878
  function setFieldValue(field, value, { force } = { force: false }) {
1431
1879
  var _a;
1432
- const fieldInstance = fieldsById.value[field];
1880
+ const fieldInstance = fieldsByPath.value[field];
1433
1881
  const clonedValue = klona(value);
1434
1882
  // field wasn't found, create a virtual field as a placeholder
1435
1883
  if (!fieldInstance) {
@@ -1437,28 +1885,17 @@ function useForm(opts) {
1437
1885
  return;
1438
1886
  }
1439
1887
  // Multiple checkboxes, and only one of them got updated
1440
- if (Array.isArray(fieldInstance) && ((_a = fieldInstance[0]) === null || _a === void 0 ? void 0 : _a.type) === 'checkbox' && !Array.isArray(value)) {
1441
- const newVal = klona(resolveNextCheckboxValue(getFromPath(formValues, field) || [], value, undefined));
1442
- setInPath(formValues, field, newVal);
1443
- fieldInstance.forEach(fieldItem => {
1444
- valuesByFid[fieldItem.fid] = newVal;
1445
- });
1888
+ if (isFieldGroup(fieldInstance) && ((_a = fieldInstance[0]) === null || _a === void 0 ? void 0 : _a.type) === 'checkbox' && !Array.isArray(value)) {
1889
+ const newValue = klona(resolveNextCheckboxValue(getFromPath(formValues, field) || [], value, undefined));
1890
+ setInPath(formValues, field, newValue);
1446
1891
  return;
1447
1892
  }
1448
1893
  let newValue = value;
1449
1894
  // Single Checkbox: toggles the field value unless the field is being reset then force it
1450
- if (!Array.isArray(fieldInstance) && (fieldInstance === null || fieldInstance === void 0 ? void 0 : fieldInstance.type) === 'checkbox' && !force) {
1895
+ if (!isFieldGroup(fieldInstance) && fieldInstance.type === 'checkbox' && !force) {
1451
1896
  newValue = klona(resolveNextCheckboxValue(getFromPath(formValues, field), value, unref(fieldInstance.uncheckedValue)));
1452
1897
  }
1453
1898
  setInPath(formValues, field, newValue);
1454
- // multiple radio fields
1455
- if (fieldInstance && Array.isArray(fieldInstance)) {
1456
- fieldInstance.forEach(fieldItem => {
1457
- valuesByFid[fieldItem.fid] = newValue;
1458
- });
1459
- return;
1460
- }
1461
- valuesByFid[fieldInstance.fid] = newValue;
1462
1899
  }
1463
1900
  /**
1464
1901
  * Sets multiple fields values
@@ -1472,16 +1909,17 @@ function useForm(opts) {
1472
1909
  keysOf(fields).forEach(path => {
1473
1910
  setFieldValue(path, fields[path]);
1474
1911
  });
1912
+ // regenerate the arrays when the form values change
1913
+ Object.values(fieldArraysLookup).forEach(f => f && f.reset());
1475
1914
  }
1476
1915
  /**
1477
1916
  * Sets the touched meta state on a field
1478
1917
  */
1479
1918
  function setFieldTouched(field, isTouched) {
1480
- const fieldInstance = fieldsById.value[field];
1481
- if (!fieldInstance) {
1482
- return;
1919
+ const fieldInstance = fieldsByPath.value[field];
1920
+ if (fieldInstance) {
1921
+ applyFieldMutation(fieldInstance, f => f.setTouched(isTouched));
1483
1922
  }
1484
- applyFieldMutation(fieldInstance, f => f.setTouched(isTouched));
1485
1923
  }
1486
1924
  /**
1487
1925
  * Sets the touched meta state on multiple fields
@@ -1501,108 +1939,123 @@ function useForm(opts) {
1501
1939
  setValues(state === null || state === void 0 ? void 0 : state.values);
1502
1940
  }
1503
1941
  else {
1942
+ // clean up the initial values back to the original
1943
+ setInitialValues(originalInitialValues.value);
1504
1944
  // otherwise clean the current values
1505
- setValues(initialValues.value);
1945
+ setValues(originalInitialValues.value);
1506
1946
  }
1507
- // Reset all fields state
1508
- fields.value.forEach(f => f.resetField());
1947
+ Object.values(fieldsByPath.value).forEach(field => {
1948
+ if (!field) {
1949
+ return;
1950
+ }
1951
+ applyFieldMutation(field, f => f.resetField());
1952
+ });
1509
1953
  if (state === null || state === void 0 ? void 0 : state.touched) {
1510
1954
  setTouched(state.touched);
1511
1955
  }
1512
1956
  setErrors((state === null || state === void 0 ? void 0 : state.errors) || {});
1513
1957
  submitCount.value = (state === null || state === void 0 ? void 0 : state.submitCount) || 0;
1514
1958
  }
1959
+ function insertFieldAtPath(field, path) {
1960
+ const rawField = markRaw(field);
1961
+ const fieldPath = path;
1962
+ // first field at that path
1963
+ if (!fieldsByPath.value[fieldPath]) {
1964
+ fieldsByPath.value[fieldPath] = rawField;
1965
+ return;
1966
+ }
1967
+ const fieldAtPath = fieldsByPath.value[fieldPath];
1968
+ if (fieldAtPath && !Array.isArray(fieldAtPath)) {
1969
+ fieldsByPath.value[fieldPath] = [fieldAtPath];
1970
+ }
1971
+ // add the new array to that path
1972
+ fieldsByPath.value[fieldPath] = [...fieldsByPath.value[fieldPath], rawField];
1973
+ }
1974
+ function removeFieldFromPath(field, path) {
1975
+ const fieldPath = path;
1976
+ const fieldAtPath = fieldsByPath.value[fieldPath];
1977
+ if (!fieldAtPath) {
1978
+ return;
1979
+ }
1980
+ // same field at path
1981
+ if (!isFieldGroup(fieldAtPath) && field.id === fieldAtPath.id) {
1982
+ delete fieldsByPath.value[fieldPath];
1983
+ return;
1984
+ }
1985
+ if (isFieldGroup(fieldAtPath)) {
1986
+ const idx = fieldAtPath.findIndex(f => f.id === field.id);
1987
+ if (idx === -1) {
1988
+ return;
1989
+ }
1990
+ fieldAtPath.splice(idx, 1);
1991
+ if (fieldAtPath.length === 1) {
1992
+ fieldsByPath.value[fieldPath] = fieldAtPath[0];
1993
+ return;
1994
+ }
1995
+ if (!fieldAtPath.length) {
1996
+ delete fieldsByPath.value[fieldPath];
1997
+ }
1998
+ }
1999
+ }
1515
2000
  function registerField(field) {
1516
- fields.value.push(field);
1517
- // TODO: Do this automatically on registration
1518
- // eslint-disable-next-line no-unused-expressions
1519
- fieldsById.value; // force computation of the fields ids to properly set their idx
2001
+ const fieldPath = unref(field.name);
2002
+ insertFieldAtPath(field, fieldPath);
1520
2003
  if (isRef(field.name)) {
1521
- valuesByFid[field.fid] = field.value.value;
1522
2004
  // ensures when a field's name was already taken that it preserves its same value
1523
2005
  // necessary for fields generated by loops
1524
- watch(field.name, (newPath, oldPath) => {
1525
- setFieldValue(newPath, valuesByFid[field.fid]);
1526
- const isSharingName = fields.value.find(f => unref(f.name) === oldPath);
2006
+ watch(field.name, async (newPath, oldPath) => {
2007
+ // cache the value
2008
+ await nextTick();
2009
+ removeFieldFromPath(field, oldPath);
2010
+ insertFieldAtPath(field, newPath);
2011
+ // re-validate if either path had errors before
2012
+ if (errors.value[oldPath] || errors.value[newPath]) {
2013
+ validateField(newPath);
2014
+ }
1527
2015
  // clean up the old path if no other field is sharing that name
1528
2016
  // #3325
1529
- if (!isSharingName) {
2017
+ await nextTick();
2018
+ if (!fieldExists(oldPath)) {
1530
2019
  unsetPath(formValues, oldPath);
1531
- unsetPath(initialValues.value, oldPath);
1532
2020
  }
1533
- }, {
1534
- flush: 'post',
1535
2021
  });
1536
2022
  }
1537
2023
  // if field already had errors (initial errors) that's not user-set, validate it again to ensure state is correct
1538
2024
  // the difference being that `initialErrors` will contain the error message while other errors (pre-validated schema) won't have them as initial errors
1539
2025
  // #3342
1540
- const path = unref(field.name);
1541
2026
  const initialErrorMessage = unref(field.errorMessage);
1542
- if (initialErrorMessage && (initialErrors === null || initialErrors === void 0 ? void 0 : initialErrors[path]) !== initialErrorMessage) {
1543
- validateField(path);
2027
+ if (initialErrorMessage && (initialErrors === null || initialErrors === void 0 ? void 0 : initialErrors[fieldPath]) !== initialErrorMessage) {
2028
+ validateField(fieldPath);
1544
2029
  }
1545
2030
  // marks the initial error as "consumed" so it won't be matched later with same non-initial error
1546
- delete initialErrors[path];
2031
+ delete initialErrors[fieldPath];
1547
2032
  }
1548
2033
  function unregisterField(field) {
1549
- var _a, _b;
1550
- const idx = fields.value.indexOf(field);
1551
- if (idx === -1) {
1552
- return;
1553
- }
1554
- fields.value.splice(idx, 1);
1555
- const fid = field.fid;
1556
- // cleans up the field value from fid lookup
2034
+ const fieldName = unref(field.name);
2035
+ removeFieldFromPath(field, fieldName);
1557
2036
  nextTick(() => {
1558
- delete valuesByFid[fid];
1559
2037
  // clears a field error on unmounted
1560
2038
  // we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes
1561
2039
  // #3384
1562
- if (!fieldsById.value[fieldName]) {
2040
+ if (!fieldExists(fieldName)) {
1563
2041
  setFieldError(fieldName, undefined);
2042
+ unsetPath(formValues, fieldName);
1564
2043
  }
1565
2044
  });
1566
- const fieldName = unref(field.name);
1567
- // in this case, this is a single field not a group (checkbox or radio)
1568
- // so remove the field value key immediately
1569
- if (field.idx === -1) {
1570
- // avoid un-setting the value if the field was switched with another that shares the same name
1571
- // they will be unset once the new field takes over the new name, look at `#registerField()`
1572
- // #3166
1573
- const isSharingName = fields.value.find(f => unref(f.name) === fieldName);
1574
- if (isSharingName) {
1575
- return;
1576
- }
1577
- unsetPath(formValues, fieldName);
1578
- unsetPath(initialValues.value, fieldName);
1579
- return;
1580
- }
1581
- // otherwise find the actual value in the current array of values and remove it
1582
- const valueIdx = (_b = (_a = getFromPath(formValues, fieldName)) === null || _a === void 0 ? void 0 : _a.indexOf) === null || _b === void 0 ? void 0 : _b.call(_a, unref(field.checkedValue));
1583
- if (valueIdx === undefined) {
1584
- unsetPath(formValues, fieldName);
1585
- return;
1586
- }
1587
- if (valueIdx === -1) {
1588
- return;
1589
- }
1590
- if (Array.isArray(formValues[fieldName])) {
1591
- unsetPath(formValues, `${fieldName}.${valueIdx}`);
1592
- return;
1593
- }
1594
- unsetPath(formValues, fieldName);
1595
- unsetPath(initialValues.value, fieldName);
1596
2045
  }
1597
- async function validate() {
2046
+ async function validate(opts) {
1598
2047
  if (formCtx.validateSchema) {
1599
- return formCtx.validateSchema('force');
2048
+ return formCtx.validateSchema((opts === null || opts === void 0 ? void 0 : opts.mode) || 'force');
1600
2049
  }
1601
2050
  // No schema, each field is responsible to validate itself
1602
- const validations = await Promise.all(fields.value.map(f => {
1603
- return f.validate().then((result) => {
2051
+ const validations = await Promise.all(Object.values(fieldsByPath.value).map(field => {
2052
+ const fieldInstance = Array.isArray(field) ? field[0] : field;
2053
+ if (!fieldInstance) {
2054
+ return Promise.resolve({ key: '', valid: true, errors: [] });
2055
+ }
2056
+ return fieldInstance.validate(opts).then((result) => {
1604
2057
  return {
1605
- key: unref(f.name),
2058
+ key: unref(fieldInstance.name),
1606
2059
  valid: result.valid,
1607
2060
  errors: result.errors,
1608
2061
  };
@@ -1625,8 +2078,8 @@ function useForm(opts) {
1625
2078
  errors,
1626
2079
  };
1627
2080
  }
1628
- function validateField(field) {
1629
- const fieldInstance = fieldsById.value[field];
2081
+ async function validateField(field) {
2082
+ const fieldInstance = fieldsByPath.value[field];
1630
2083
  if (!fieldInstance) {
1631
2084
  warn$1(`field with name ${field} was not found`);
1632
2085
  return Promise.resolve({ errors: [], valid: true });
@@ -1636,14 +2089,14 @@ function useForm(opts) {
1636
2089
  }
1637
2090
  return fieldInstance.validate();
1638
2091
  }
1639
- function handleSubmit(fn) {
2092
+ function handleSubmit(fn, onValidationError) {
1640
2093
  return function submissionHandler(e) {
1641
2094
  if (e instanceof Event) {
1642
2095
  e.preventDefault();
1643
2096
  e.stopPropagation();
1644
2097
  }
1645
2098
  // Touch all fields
1646
- setTouched(keysOf(fieldsById.value).reduce((acc, field) => {
2099
+ setTouched(keysOf(fieldsByPath.value).reduce((acc, field) => {
1647
2100
  acc[field] = true;
1648
2101
  return acc;
1649
2102
  }, {}));
@@ -1663,9 +2116,18 @@ function useForm(opts) {
1663
2116
  resetForm,
1664
2117
  });
1665
2118
  }
2119
+ if (!result.valid && typeof onValidationError === 'function') {
2120
+ onValidationError({
2121
+ values: klona(formValues),
2122
+ evt: e,
2123
+ errors: result.errors,
2124
+ results: result.results,
2125
+ });
2126
+ }
1666
2127
  })
1667
- .then(() => {
2128
+ .then(returnVal => {
1668
2129
  isSubmitting.value = false;
2130
+ return returnVal;
1669
2131
  }, err => {
1670
2132
  isSubmitting.value = false;
1671
2133
  // re-throw the err so it doesn't go silent
@@ -1676,6 +2138,9 @@ function useForm(opts) {
1676
2138
  function setFieldInitialValue(path, value) {
1677
2139
  setInPath(initialValues.value, path, klona(value));
1678
2140
  }
2141
+ function unsetInitialValue(path) {
2142
+ unsetPath(initialValues.value, path);
2143
+ }
1679
2144
  /**
1680
2145
  * Sneaky function to set initial field values
1681
2146
  */
@@ -1683,7 +2148,7 @@ function useForm(opts) {
1683
2148
  setInPath(formValues, path, value);
1684
2149
  setFieldInitialValue(path, value);
1685
2150
  }
1686
- async function validateSchema(mode) {
2151
+ async function _validateSchema() {
1687
2152
  const schemaValue = unref(schema);
1688
2153
  if (!schemaValue) {
1689
2154
  return { valid: true, results: {}, errors: {} };
@@ -1694,8 +2159,16 @@ function useForm(opts) {
1694
2159
  names: fieldNames.value,
1695
2160
  bailsMap: fieldBailsMap.value,
1696
2161
  });
2162
+ return formResult;
2163
+ }
2164
+ /**
2165
+ * Batches validation runs in 5ms batches
2166
+ */
2167
+ const debouncedSchemaValidation = debounceAsync(_validateSchema, 5);
2168
+ async function validateSchema(mode) {
2169
+ const formResult = await debouncedSchemaValidation();
1697
2170
  // fields by id lookup
1698
- const fieldsById = formCtx.fieldsById.value || {};
2171
+ const fieldsById = formCtx.fieldsByPath.value || {};
1699
2172
  // errors fields names, we need it to also check if custom errors are updated
1700
2173
  const currentErrorsPaths = keysOf(formCtx.errorBag.value);
1701
2174
  // collect all the keys from the schema and all fields
@@ -1729,7 +2202,7 @@ function useForm(opts) {
1729
2202
  if (mode === 'validated-only' && !wasValidated) {
1730
2203
  return validation;
1731
2204
  }
1732
- applyFieldMutation(field, f => f.setValidationState(fieldResult), true);
2205
+ applyFieldMutation(field, f => f.setState({ errors: fieldResult.errors }));
1733
2206
  return validation;
1734
2207
  }, { valid: formResult.valid, results: {}, errors: {} });
1735
2208
  }
@@ -1765,7 +2238,12 @@ function useForm(opts) {
1765
2238
  }
1766
2239
  // Provide injections
1767
2240
  provide(FormContextKey, formCtx);
1768
- provide(FormErrorsKey, errors);
2241
+ if ((process.env.NODE_ENV !== 'production')) {
2242
+ registerFormWithDevTools(formCtx);
2243
+ watch(() => (Object.assign(Object.assign({ errors: errorBag.value }, meta.value), { values: formValues, isSubmitting: isSubmitting.value, submitCount: submitCount.value })), refreshInspector, {
2244
+ deep: true,
2245
+ });
2246
+ }
1769
2247
  return {
1770
2248
  errors,
1771
2249
  meta,
@@ -1789,7 +2267,7 @@ function useForm(opts) {
1789
2267
  /**
1790
2268
  * Manages form meta aggregation
1791
2269
  */
1792
- function useFormMeta(fields, currentValues, initialValues, errors) {
2270
+ function useFormMeta(fieldsByPath, currentValues, initialValues, errors) {
1793
2271
  const MERGE_STRATEGIES = {
1794
2272
  touched: 'some',
1795
2273
  pending: 'some',
@@ -1799,9 +2277,10 @@ function useFormMeta(fields, currentValues, initialValues, errors) {
1799
2277
  return !es6(currentValues, unref(initialValues));
1800
2278
  });
1801
2279
  const flags = computed(() => {
2280
+ const fields = Object.values(fieldsByPath.value).flat(1).filter(Boolean);
1802
2281
  return keysOf(MERGE_STRATEGIES).reduce((acc, flag) => {
1803
2282
  const mergeMethod = MERGE_STRATEGIES[flag];
1804
- acc[flag] = fields.value[mergeMethod](field => field.meta[flag]);
2283
+ acc[flag] = fields[mergeMethod](field => field.meta[flag]);
1805
2284
  return acc;
1806
2285
  }, {});
1807
2286
  });
@@ -1813,13 +2292,17 @@ function useFormMeta(fields, currentValues, initialValues, errors) {
1813
2292
  * Manages the initial values prop
1814
2293
  */
1815
2294
  function useFormInitialValues(fields, formValues, providedValues) {
1816
- const initialValues = ref(unref(providedValues) || {});
1817
- // acts as a read only proxy of the initial values object
1818
- const computedInitials = computed(() => {
1819
- return initialValues.value;
1820
- });
2295
+ // these are the mutable initial values as the fields are mounted/unmounted
2296
+ const initialValues = ref(klona(unref(providedValues)) || {});
2297
+ // these are the original initial value as provided by the user initially, they don't keep track of conditional fields
2298
+ // this is important because some conditional fields will overwrite the initial values for other fields who had the same name
2299
+ // like array fields, any push/insert operation will overwrite the initial values because they "create new fields"
2300
+ // so these are the values that the reset function should use
2301
+ // these only change when the user explicitly chanegs the initial values or when the user resets them with new values.
2302
+ const originalInitialValues = ref(klona(unref(providedValues)) || {});
1821
2303
  function setInitialValues(values, updateFields = false) {
1822
2304
  initialValues.value = klona(values);
2305
+ originalInitialValues.value = klona(values);
1823
2306
  if (!updateFields) {
1824
2307
  return;
1825
2308
  }
@@ -1827,15 +2310,14 @@ function useFormInitialValues(fields, formValues, providedValues) {
1827
2310
  // those are excluded because it's unlikely you want to change the form values using initial values
1828
2311
  // we mostly watch them for API population or newly inserted fields
1829
2312
  // if the user API is taking too much time before user interaction they should consider disabling or hiding their inputs until the values are ready
1830
- const hadInteraction = (f) => f.meta.touched;
1831
2313
  keysOf(fields.value).forEach(fieldPath => {
1832
2314
  const field = fields.value[fieldPath];
1833
- const touchedByUser = Array.isArray(field) ? field.some(hadInteraction) : hadInteraction(field);
1834
- if (touchedByUser) {
2315
+ const wasTouched = Array.isArray(field) ? field.some(f => f.meta.touched) : field === null || field === void 0 ? void 0 : field.meta.touched;
2316
+ if (!field || wasTouched) {
1835
2317
  return;
1836
2318
  }
1837
2319
  const newValue = getFromPath(initialValues.value, fieldPath);
1838
- setInPath(formValues, fieldPath, newValue);
2320
+ setInPath(formValues, fieldPath, klona(newValue));
1839
2321
  });
1840
2322
  }
1841
2323
  if (isRef(providedValues)) {
@@ -1845,10 +2327,9 @@ function useFormInitialValues(fields, formValues, providedValues) {
1845
2327
  deep: true,
1846
2328
  });
1847
2329
  }
1848
- provide(FormInitialValuesKey, computedInitials);
1849
2330
  return {
1850
- readonlyInitialValues: computedInitials,
1851
2331
  initialValues,
2332
+ originalInitialValues,
1852
2333
  setInitialValues,
1853
2334
  };
1854
2335
  }
@@ -1921,6 +2402,10 @@ const Form = defineComponent({
1921
2402
  type: Function,
1922
2403
  default: undefined,
1923
2404
  },
2405
+ onInvalidSubmit: {
2406
+ type: Function,
2407
+ default: undefined,
2408
+ },
1924
2409
  },
1925
2410
  setup(props, ctx) {
1926
2411
  const initialValues = toRef(props, 'initialValues');
@@ -1932,7 +2417,7 @@ const Form = defineComponent({
1932
2417
  initialTouched: props.initialTouched,
1933
2418
  validateOnMount: props.validateOnMount,
1934
2419
  });
1935
- const onSubmit = props.onSubmit ? handleSubmit(props.onSubmit) : submitForm;
2420
+ const onSubmit = props.onSubmit ? handleSubmit(props.onSubmit, props.onInvalidSubmit) : submitForm;
1936
2421
  function handleFormReset(e) {
1937
2422
  if (isEvent(e)) {
1938
2423
  // Prevent default form reset behavior
@@ -1945,7 +2430,7 @@ const Form = defineComponent({
1945
2430
  }
1946
2431
  function handleScopedSlotSubmit(evt, onSubmit) {
1947
2432
  const onSuccess = typeof evt === 'function' && !onSubmit ? evt : onSubmit;
1948
- return handleSubmit(onSuccess)(evt);
2433
+ return handleSubmit(onSuccess, props.onInvalidSubmit)(evt);
1949
2434
  }
1950
2435
  function slotProps() {
1951
2436
  return {
@@ -1999,6 +2484,203 @@ const Form = defineComponent({
1999
2484
  },
2000
2485
  });
2001
2486
 
2487
+ let FIELD_ARRAY_COUNTER = 0;
2488
+ function useFieldArray(arrayPath) {
2489
+ const id = FIELD_ARRAY_COUNTER++;
2490
+ const form = injectWithSelf(FormContextKey, undefined);
2491
+ const fields = ref([]);
2492
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2493
+ const noOp = () => { };
2494
+ const noOpApi = {
2495
+ fields: readonly(fields),
2496
+ remove: noOp,
2497
+ push: noOp,
2498
+ swap: noOp,
2499
+ insert: noOp,
2500
+ update: noOp,
2501
+ replace: noOp,
2502
+ prepend: noOp,
2503
+ };
2504
+ if (!form) {
2505
+ warn('FieldArray requires being a child of `<Form/>` or `useForm` being called before it. Array fields may not work correctly');
2506
+ return noOpApi;
2507
+ }
2508
+ if (!unref(arrayPath)) {
2509
+ warn('FieldArray requires a field path to be provided, did you forget to pass the `name` prop?');
2510
+ return noOpApi;
2511
+ }
2512
+ let entryCounter = 0;
2513
+ function initFields() {
2514
+ const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []);
2515
+ fields.value = currentValues.map(createEntry);
2516
+ updateEntryFlags();
2517
+ }
2518
+ initFields();
2519
+ function updateEntryFlags() {
2520
+ const fieldsLength = fields.value.length;
2521
+ for (let i = 0; i < fieldsLength; i++) {
2522
+ const entry = fields.value[i];
2523
+ entry.isFirst = i === 0;
2524
+ entry.isLast = i === fieldsLength - 1;
2525
+ }
2526
+ }
2527
+ function createEntry(value) {
2528
+ const key = entryCounter++;
2529
+ const entry = {
2530
+ key,
2531
+ value: computed(() => {
2532
+ const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(arrayPath), []);
2533
+ const idx = fields.value.findIndex(e => e.key === key);
2534
+ return idx === -1 ? value : currentValues[idx];
2535
+ }),
2536
+ isFirst: false,
2537
+ isLast: false,
2538
+ };
2539
+ return entry;
2540
+ }
2541
+ function remove(idx) {
2542
+ const pathName = unref(arrayPath);
2543
+ const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
2544
+ if (!pathValue || !Array.isArray(pathValue)) {
2545
+ return;
2546
+ }
2547
+ const newValue = [...pathValue];
2548
+ newValue.splice(idx, 1);
2549
+ form === null || form === void 0 ? void 0 : form.unsetInitialValue(pathName + `[${idx}]`);
2550
+ form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
2551
+ fields.value.splice(idx, 1);
2552
+ updateEntryFlags();
2553
+ }
2554
+ function push(value) {
2555
+ const pathName = unref(arrayPath);
2556
+ const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
2557
+ const normalizedPathValue = isNullOrUndefined(pathValue) ? [] : pathValue;
2558
+ if (!Array.isArray(normalizedPathValue)) {
2559
+ return;
2560
+ }
2561
+ const newValue = [...normalizedPathValue];
2562
+ newValue.push(value);
2563
+ form === null || form === void 0 ? void 0 : form.stageInitialValue(pathName + `[${newValue.length - 1}]`, value);
2564
+ form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
2565
+ fields.value.push(createEntry(value));
2566
+ updateEntryFlags();
2567
+ }
2568
+ function swap(indexA, indexB) {
2569
+ const pathName = unref(arrayPath);
2570
+ const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
2571
+ if (!Array.isArray(pathValue) || !pathValue[indexA] || !pathValue[indexB]) {
2572
+ return;
2573
+ }
2574
+ const newValue = [...pathValue];
2575
+ const newFields = [...fields.value];
2576
+ // the old switcheroo
2577
+ const temp = newValue[indexA];
2578
+ newValue[indexA] = newValue[indexB];
2579
+ newValue[indexB] = temp;
2580
+ const tempEntry = newFields[indexA];
2581
+ newFields[indexA] = newFields[indexB];
2582
+ newFields[indexB] = tempEntry;
2583
+ form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
2584
+ fields.value = newFields;
2585
+ updateEntryFlags();
2586
+ }
2587
+ function insert(idx, value) {
2588
+ const pathName = unref(arrayPath);
2589
+ const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
2590
+ if (!Array.isArray(pathValue) || pathValue.length < idx) {
2591
+ return;
2592
+ }
2593
+ const newValue = [...pathValue];
2594
+ const newFields = [...fields.value];
2595
+ newValue.splice(idx, 0, value);
2596
+ newFields.splice(idx, 0, createEntry(value));
2597
+ form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
2598
+ fields.value = newFields;
2599
+ updateEntryFlags();
2600
+ }
2601
+ function replace(arr) {
2602
+ const pathName = unref(arrayPath);
2603
+ form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, arr);
2604
+ initFields();
2605
+ }
2606
+ function update(idx, value) {
2607
+ const pathName = unref(arrayPath);
2608
+ const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
2609
+ if (!Array.isArray(pathValue) || pathValue.length - 1 < idx) {
2610
+ return;
2611
+ }
2612
+ form === null || form === void 0 ? void 0 : form.setFieldValue(`${pathName}[${idx}]`, value);
2613
+ }
2614
+ function prepend(value) {
2615
+ const pathName = unref(arrayPath);
2616
+ const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
2617
+ const normalizedPathValue = isNullOrUndefined(pathValue) ? [] : pathValue;
2618
+ if (!Array.isArray(normalizedPathValue)) {
2619
+ return;
2620
+ }
2621
+ const newValue = [value, ...normalizedPathValue];
2622
+ form === null || form === void 0 ? void 0 : form.stageInitialValue(pathName + `[${newValue.length - 1}]`, value);
2623
+ form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
2624
+ fields.value.unshift(createEntry(value));
2625
+ updateEntryFlags();
2626
+ }
2627
+ form.fieldArraysLookup[id] = {
2628
+ reset: initFields,
2629
+ };
2630
+ onBeforeUnmount(() => {
2631
+ delete form.fieldArraysLookup[id];
2632
+ });
2633
+ return {
2634
+ fields: readonly(fields),
2635
+ remove,
2636
+ push,
2637
+ swap,
2638
+ insert,
2639
+ update,
2640
+ replace,
2641
+ prepend,
2642
+ };
2643
+ }
2644
+
2645
+ const FieldArray = defineComponent({
2646
+ name: 'FieldArray',
2647
+ inheritAttrs: false,
2648
+ props: {
2649
+ name: {
2650
+ type: String,
2651
+ required: true,
2652
+ },
2653
+ },
2654
+ setup(props, ctx) {
2655
+ const { push, remove, swap, insert, replace, update, prepend, fields } = useFieldArray(toRef(props, 'name'));
2656
+ function slotProps() {
2657
+ return {
2658
+ fields: fields.value,
2659
+ push,
2660
+ remove,
2661
+ swap,
2662
+ insert,
2663
+ update,
2664
+ replace,
2665
+ prepend,
2666
+ };
2667
+ }
2668
+ ctx.expose({
2669
+ push,
2670
+ remove,
2671
+ swap,
2672
+ insert,
2673
+ update,
2674
+ replace,
2675
+ prepend,
2676
+ });
2677
+ return () => {
2678
+ const children = normalizeChildren(undefined, ctx, slotProps);
2679
+ return children;
2680
+ };
2681
+ },
2682
+ });
2683
+
2002
2684
  const ErrorMessage = defineComponent({
2003
2685
  name: 'ErrorMessage',
2004
2686
  props: {
@@ -2012,9 +2694,9 @@ const ErrorMessage = defineComponent({
2012
2694
  },
2013
2695
  },
2014
2696
  setup(props, ctx) {
2015
- const errors = inject(FormErrorsKey, undefined);
2697
+ const form = inject(FormContextKey, undefined);
2016
2698
  const message = computed(() => {
2017
- return errors === null || errors === void 0 ? void 0 : errors.value[props.name];
2699
+ return form === null || form === void 0 ? void 0 : form.errors.value[props.name];
2018
2700
  });
2019
2701
  function slotProps() {
2020
2702
  return {
@@ -2065,7 +2747,7 @@ function useIsFieldDirty(path) {
2065
2747
  let field = path ? undefined : inject(FieldContextKey);
2066
2748
  return computed(() => {
2067
2749
  if (path) {
2068
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsById.value[unref(path)]);
2750
+ field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
2069
2751
  }
2070
2752
  if (!field) {
2071
2753
  warn(`field with name ${unref(path)} was not found`);
@@ -2083,7 +2765,7 @@ function useIsFieldTouched(path) {
2083
2765
  let field = path ? undefined : inject(FieldContextKey);
2084
2766
  return computed(() => {
2085
2767
  if (path) {
2086
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsById.value[unref(path)]);
2768
+ field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
2087
2769
  }
2088
2770
  if (!field) {
2089
2771
  warn(`field with name ${unref(path)} was not found`);
@@ -2101,7 +2783,7 @@ function useIsFieldValid(path) {
2101
2783
  let field = path ? undefined : inject(FieldContextKey);
2102
2784
  return computed(() => {
2103
2785
  if (path) {
2104
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsById.value[unref(path)]);
2786
+ field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
2105
2787
  }
2106
2788
  if (!field) {
2107
2789
  warn(`field with name ${unref(path)} was not found`);
@@ -2133,7 +2815,7 @@ function useValidateField(path) {
2133
2815
  let field = path ? undefined : inject(FieldContextKey);
2134
2816
  return function validateField() {
2135
2817
  if (path) {
2136
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsById.value[unref(path)]);
2818
+ field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
2137
2819
  }
2138
2820
  if (!field) {
2139
2821
  warn(`field with name ${unref(path)} was not found`);
@@ -2226,11 +2908,10 @@ function useFieldValue(path) {
2226
2908
  // We don't want to use self injected context as it doesn't make sense
2227
2909
  const field = path ? undefined : inject(FieldContextKey);
2228
2910
  return computed(() => {
2229
- var _a;
2230
2911
  if (path) {
2231
2912
  return getFromPath(form === null || form === void 0 ? void 0 : form.values, unref(path));
2232
2913
  }
2233
- return (_a = field === null || field === void 0 ? void 0 : field.value) === null || _a === void 0 ? void 0 : _a.value;
2914
+ return unref(field === null || field === void 0 ? void 0 : field.value);
2234
2915
  });
2235
2916
  }
2236
2917
 
@@ -2251,24 +2932,25 @@ function useFormValues() {
2251
2932
  * Gives access to all form errors
2252
2933
  */
2253
2934
  function useFormErrors() {
2254
- const errors = injectWithSelf(FormErrorsKey);
2255
- if (!errors) {
2935
+ const form = injectWithSelf(FormContextKey);
2936
+ if (!form) {
2256
2937
  warn('No vee-validate <Form /> or `useForm` was detected in the component tree');
2257
2938
  }
2258
- return errors || computed(() => ({}));
2939
+ return computed(() => {
2940
+ return ((form === null || form === void 0 ? void 0 : form.errors.value) || {});
2941
+ });
2259
2942
  }
2260
2943
 
2261
2944
  /**
2262
2945
  * Gives access to a single field error
2263
2946
  */
2264
2947
  function useFieldError(path) {
2265
- const errors = injectWithSelf(FormErrorsKey);
2948
+ const form = injectWithSelf(FormContextKey);
2266
2949
  // We don't want to use self injected context as it doesn't make sense
2267
2950
  const field = path ? undefined : inject(FieldContextKey);
2268
2951
  return computed(() => {
2269
- var _a;
2270
2952
  if (path) {
2271
- return (_a = errors === null || errors === void 0 ? void 0 : errors.value) === null || _a === void 0 ? void 0 : _a[unref(path)];
2953
+ return form === null || form === void 0 ? void 0 : form.errors.value[unref(path)];
2272
2954
  }
2273
2955
  return field === null || field === void 0 ? void 0 : field.errorMessage.value;
2274
2956
  });
@@ -2288,4 +2970,4 @@ function useSubmitForm(cb) {
2288
2970
  };
2289
2971
  }
2290
2972
 
2291
- export { ErrorMessage, Field, FieldContextKey, Form, FormContextKey, configure, defineRule, useField, useFieldError, useFieldValue, useForm, useFormErrors, useFormValues, useIsFieldDirty, useIsFieldTouched, useIsFieldValid, useIsFormDirty, useIsFormTouched, useIsFormValid, useIsSubmitting, useResetForm, useSubmitCount, useSubmitForm, useValidateField, useValidateForm, validate };
2973
+ 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 };