vee-validate 4.8.6 → 4.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  /**
2
- * vee-validate v4.8.6
2
+ * vee-validate v4.9.1
3
3
  * (c) 2023 Abdelrahman Awad
4
4
  * @license MIT
5
5
  */
6
- import { getCurrentInstance, inject, warn as warn$1, computed, ref, watch, unref, isRef, reactive, onUnmounted, nextTick, onMounted, provide, onBeforeUnmount, defineComponent, toRef, resolveDynamicComponent, h, watchEffect, markRaw } from 'vue';
6
+ import { getCurrentInstance, inject, warn as warn$1, unref, computed, ref, watch, isRef, reactive, onUnmounted, nextTick, onMounted, provide, onBeforeUnmount, defineComponent, toRef, resolveDynamicComponent, h, watchEffect, shallowRef } from 'vue';
7
7
  import { setupDevtoolsPlugin } from '@vue/devtools-api';
8
8
 
9
9
  function isCallable(fn) {
@@ -20,6 +20,19 @@ function toNumber(value) {
20
20
  const n = parseFloat(value);
21
21
  return isNaN(n) ? value : n;
22
22
  }
23
+ function merge(target, source) {
24
+ Object.keys(source).forEach(key => {
25
+ if (isObject(source[key])) {
26
+ if (!target[key]) {
27
+ target[key] = {};
28
+ }
29
+ merge(target[key], source[key]);
30
+ return;
31
+ }
32
+ target[key] = source[key];
33
+ });
34
+ return target;
35
+ }
23
36
 
24
37
  const RULES = {};
25
38
  /**
@@ -390,15 +403,6 @@ function injectWithSelf(symbol, def = undefined) {
390
403
  function warn(message) {
391
404
  warn$1(`[vee-validate]: ${message}`);
392
405
  }
393
- /**
394
- * Ensures we deal with a singular field value
395
- */
396
- function normalizeField(field) {
397
- if (Array.isArray(field)) {
398
- return field[0];
399
- }
400
- return field;
401
- }
402
406
  function resolveNextCheckboxValue(currentValue, checkedValue, uncheckedValue) {
403
407
  if (Array.isArray(currentValue)) {
404
408
  const newVal = [...currentValue];
@@ -497,6 +501,27 @@ function unravel(value) {
497
501
  function lazyToRef(value) {
498
502
  return computed(() => unravel(value));
499
503
  }
504
+ function normalizeErrorItem(message) {
505
+ return Array.isArray(message) ? message : message ? [message] : [];
506
+ }
507
+ function resolveFieldOrPathState(path) {
508
+ const form = injectWithSelf(FormContextKey);
509
+ const state = path ? computed(() => form === null || form === void 0 ? void 0 : form.getPathState(unref(path))) : undefined;
510
+ const field = path ? undefined : inject(FieldContextKey);
511
+ if (!field && !(state === null || state === void 0 ? void 0 : state.value)) {
512
+ warn(`field with name ${unref(path)} was not found`);
513
+ }
514
+ return state || field;
515
+ }
516
+ function omit(obj, keys) {
517
+ const target = {};
518
+ for (const key in obj) {
519
+ if (!keys.includes(key)) {
520
+ target[key] = obj[key];
521
+ }
522
+ }
523
+ return target;
524
+ }
500
525
 
501
526
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
502
527
  const normalizeChildren = (tag, context, slotProps) => {
@@ -921,32 +946,64 @@ async function validateObjectSchema(schema, values, opts) {
921
946
  let ID_COUNTER = 0;
922
947
  function useFieldState(path, init) {
923
948
  const { value, initialValue, setInitialValue } = _useFieldValue(path, init.modelValue, init.form);
924
- const { errorMessage, errors, setErrors } = _useFieldErrors(path, init.form);
925
- const meta = _useFieldMeta(value, initialValue, errors);
926
- const id = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER;
949
+ if (!init.form) {
950
+ const { errors, setErrors } = createFieldErrors();
951
+ const id = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER;
952
+ const meta = createFieldMeta(value, initialValue, errors);
953
+ function setState(state) {
954
+ var _a;
955
+ if ('value' in state) {
956
+ value.value = state.value;
957
+ }
958
+ if ('errors' in state) {
959
+ setErrors(state.errors);
960
+ }
961
+ if ('touched' in state) {
962
+ meta.touched = (_a = state.touched) !== null && _a !== void 0 ? _a : meta.touched;
963
+ }
964
+ if ('initialValue' in state) {
965
+ setInitialValue(state.initialValue);
966
+ }
967
+ }
968
+ return {
969
+ id,
970
+ path,
971
+ value,
972
+ initialValue,
973
+ meta,
974
+ errors,
975
+ setState,
976
+ };
977
+ }
978
+ const state = init.form.createPathState(path, {
979
+ bails: init.bails,
980
+ label: init.label,
981
+ type: init.type,
982
+ validate: init.validate,
983
+ });
984
+ const errors = computed(() => state.errors);
927
985
  function setState(state) {
928
- var _a;
986
+ var _a, _b, _c;
929
987
  if ('value' in state) {
930
988
  value.value = state.value;
931
989
  }
932
990
  if ('errors' in state) {
933
- setErrors(state.errors);
991
+ (_a = init.form) === null || _a === void 0 ? void 0 : _a.setFieldError(unref(path), state.errors);
934
992
  }
935
993
  if ('touched' in state) {
936
- meta.touched = (_a = state.touched) !== null && _a !== void 0 ? _a : meta.touched;
994
+ (_b = init.form) === null || _b === void 0 ? void 0 : _b.setFieldTouched(unref(path), (_c = state.touched) !== null && _c !== void 0 ? _c : false);
937
995
  }
938
996
  if ('initialValue' in state) {
939
997
  setInitialValue(state.initialValue);
940
998
  }
941
999
  }
942
1000
  return {
943
- id,
1001
+ id: Array.isArray(state.id) ? state.id[state.id.length - 1] : state.id,
944
1002
  path,
945
1003
  value,
946
- initialValue,
947
- meta,
948
1004
  errors,
949
- errorMessage,
1005
+ meta: state,
1006
+ initialValue,
950
1007
  setState,
951
1008
  };
952
1009
  }
@@ -959,7 +1016,7 @@ function _useFieldValue(path, modelValue, form) {
959
1016
  if (!form) {
960
1017
  return unref(modelRef);
961
1018
  }
962
- return getFromPath(form.meta.value.initialValues, unref(path), unref(modelRef));
1019
+ return getFromPath(form.initialValues.value, unref(path), unref(modelRef));
963
1020
  }
964
1021
  function setInitialValue(value) {
965
1022
  if (!form) {
@@ -1017,7 +1074,7 @@ function resolveModelValue(modelValue, form, initialValue, path) {
1017
1074
  /**
1018
1075
  * Creates meta flags state and some associated effects with them
1019
1076
  */
1020
- function _useFieldMeta(currentValue, initialValue, errors) {
1077
+ function createFieldMeta(currentValue, initialValue, errors) {
1021
1078
  const meta = reactive({
1022
1079
  touched: false,
1023
1080
  pending: false,
@@ -1039,29 +1096,12 @@ function _useFieldMeta(currentValue, initialValue, errors) {
1039
1096
  /**
1040
1097
  * Creates the error message state for the field state
1041
1098
  */
1042
- function _useFieldErrors(path, form) {
1043
- function normalizeErrors(messages) {
1044
- if (!messages) {
1045
- return [];
1046
- }
1047
- return Array.isArray(messages) ? messages : [messages];
1048
- }
1049
- if (!form) {
1050
- const errors = ref([]);
1051
- return {
1052
- errors,
1053
- errorMessage: computed(() => errors.value[0]),
1054
- setErrors: (messages) => {
1055
- errors.value = normalizeErrors(messages);
1056
- },
1057
- };
1058
- }
1059
- const errors = computed(() => form.errorBag.value[unref(path)] || []);
1099
+ function createFieldErrors() {
1100
+ const errors = ref([]);
1060
1101
  return {
1061
1102
  errors,
1062
- errorMessage: computed(() => errors.value[0]),
1063
1103
  setErrors: (messages) => {
1064
- form.setFieldErrorBag(unref(path), normalizeErrors(messages));
1104
+ errors.value = normalizeErrorItem(messages);
1065
1105
  },
1066
1106
  };
1067
1107
  }
@@ -1151,7 +1191,17 @@ function setupApiHooks(api) {
1151
1191
  console.error('There is not a valid selected vee-validate node or component');
1152
1192
  return;
1153
1193
  }
1154
- await SELECTED_NODE.validate();
1194
+ if (SELECTED_NODE.type === 'field') {
1195
+ await SELECTED_NODE.field.validate();
1196
+ return;
1197
+ }
1198
+ if (SELECTED_NODE.type === 'form') {
1199
+ await SELECTED_NODE.form.validate();
1200
+ return;
1201
+ }
1202
+ if (SELECTED_NODE.type === 'pathState') {
1203
+ await SELECTED_NODE.form.validateField(SELECTED_NODE.state.path);
1204
+ }
1155
1205
  },
1156
1206
  },
1157
1207
  {
@@ -1162,11 +1212,16 @@ function setupApiHooks(api) {
1162
1212
  console.error('There is not a valid selected vee-validate node or component');
1163
1213
  return;
1164
1214
  }
1165
- if ('id' in SELECTED_NODE) {
1166
- SELECTED_NODE.resetField();
1215
+ if (SELECTED_NODE.type === 'field') {
1216
+ SELECTED_NODE.field.resetField();
1167
1217
  return;
1168
1218
  }
1169
- SELECTED_NODE.resetForm();
1219
+ if (SELECTED_NODE.type === 'form') {
1220
+ SELECTED_NODE.form.resetForm();
1221
+ }
1222
+ if (SELECTED_NODE.type === 'pathState') {
1223
+ SELECTED_NODE.form.resetField(SELECTED_NODE.state.path);
1224
+ }
1170
1225
  },
1171
1226
  },
1172
1227
  ],
@@ -1186,29 +1241,37 @@ function setupApiHooks(api) {
1186
1241
  if (payload.inspectorId !== INSPECTOR_ID || ctx.currentTab !== `custom-inspector:${INSPECTOR_ID}`) {
1187
1242
  return;
1188
1243
  }
1189
- const { form, field, type } = decodeNodeId(payload.nodeId);
1244
+ const { form, field, state, type } = decodeNodeId(payload.nodeId);
1190
1245
  if (form && type === 'form') {
1191
1246
  payload.state = buildFormState(form);
1192
- SELECTED_NODE = form;
1247
+ SELECTED_NODE = { type: 'form', form };
1248
+ return;
1249
+ }
1250
+ if (state && type === 'pathState' && form) {
1251
+ payload.state = buildFieldState(state);
1252
+ SELECTED_NODE = { type: 'pathState', state, form };
1193
1253
  return;
1194
1254
  }
1195
1255
  if (field && type === 'field') {
1196
- payload.state = buildFieldState(field);
1197
- SELECTED_NODE = field;
1256
+ payload.state = buildFieldState({
1257
+ errors: field.errors.value,
1258
+ dirty: field.meta.dirty,
1259
+ valid: field.meta.valid,
1260
+ touched: field.meta.touched,
1261
+ value: field.value.value,
1262
+ initialValue: field.meta.initialValue,
1263
+ });
1264
+ SELECTED_NODE = { field, type: 'field' };
1198
1265
  return;
1199
1266
  }
1200
1267
  SELECTED_NODE = null;
1201
1268
  });
1202
1269
  }
1203
1270
  function mapFormForDevtoolsInspector(form) {
1204
- const { textColor, bgColor } = getTagTheme(form);
1271
+ const { textColor, bgColor } = getValidityColors(form.meta.value.valid);
1205
1272
  const formTreeNodes = {};
1206
- Object.values(form.fieldsByPath.value).forEach(field => {
1207
- const fieldInstance = Array.isArray(field) ? field[0] : field;
1208
- if (!fieldInstance) {
1209
- return;
1210
- }
1211
- setInPath(formTreeNodes, unref(fieldInstance.name), mapFieldForDevtoolsInspector(fieldInstance, form));
1273
+ Object.values(form.getAllPathStates()).forEach(state => {
1274
+ setInPath(formTreeNodes, unref(state.path), mapPathForDevtoolsInspector(state, form));
1212
1275
  });
1213
1276
  function buildFormTree(tree, path = []) {
1214
1277
  const key = [...path].pop();
@@ -1243,68 +1306,71 @@ function mapFormForDevtoolsInspector(form) {
1243
1306
  backgroundColor: bgColor,
1244
1307
  },
1245
1308
  {
1246
- label: `${Object.keys(form.fieldsByPath.value).length} fields`,
1309
+ label: `${form.getAllPathStates().length} fields`,
1247
1310
  textColor: COLORS.white,
1248
1311
  backgroundColor: COLORS.unknown,
1249
1312
  },
1250
1313
  ],
1251
1314
  };
1252
1315
  }
1316
+ function mapPathForDevtoolsInspector(state, form) {
1317
+ return {
1318
+ id: encodeNodeId(form, state),
1319
+ label: unref(state.path),
1320
+ tags: getFieldNodeTags(state.multiple, state.fieldsCount, state.type, state.valid, form),
1321
+ };
1322
+ }
1253
1323
  function mapFieldForDevtoolsInspector(field, form) {
1254
- const fieldInstance = normalizeField(field);
1255
- const { textColor, bgColor } = getTagTheme(fieldInstance);
1256
- const isGroup = Array.isArray(field) && field.length > 1;
1257
1324
  return {
1258
- id: encodeNodeId(form, fieldInstance, !isGroup),
1259
- label: unref(fieldInstance.name),
1260
- children: Array.isArray(field) ? field.map(fieldItem => mapFieldForDevtoolsInspector(fieldItem, form)) : undefined,
1261
- tags: [
1262
- isGroup
1263
- ? undefined
1264
- : {
1265
- label: 'Field',
1266
- textColor,
1267
- backgroundColor: bgColor,
1268
- },
1269
- !form
1270
- ? {
1271
- label: 'Standalone',
1272
- textColor: COLORS.black,
1273
- backgroundColor: COLORS.gray,
1274
- }
1275
- : undefined,
1276
- !isGroup && fieldInstance.type === 'checkbox'
1277
- ? {
1278
- label: 'Checkbox',
1279
- textColor: COLORS.white,
1280
- backgroundColor: COLORS.blue,
1281
- }
1282
- : undefined,
1283
- !isGroup && fieldInstance.type === 'radio'
1284
- ? {
1285
- label: 'Radio',
1286
- textColor: COLORS.white,
1287
- backgroundColor: COLORS.purple,
1288
- }
1289
- : undefined,
1290
- isGroup
1291
- ? {
1292
- label: 'Group',
1293
- textColor: COLORS.black,
1294
- backgroundColor: COLORS.orange,
1295
- }
1296
- : undefined,
1297
- ].filter(Boolean),
1325
+ id: encodeNodeId(form, field),
1326
+ label: unref(field.name),
1327
+ tags: getFieldNodeTags(false, 1, field.type, field.meta.valid, form),
1298
1328
  };
1299
1329
  }
1300
- function encodeNodeId(form, field, encodeIndex = true) {
1301
- const fieldPath = form ? unref(field === null || field === void 0 ? void 0 : field.name) : field === null || field === void 0 ? void 0 : field.id;
1302
- const fieldGroup = fieldPath ? form === null || form === void 0 ? void 0 : form.fieldsByPath.value[fieldPath] : undefined;
1303
- let idx;
1304
- if (encodeIndex && field && Array.isArray(fieldGroup)) {
1305
- idx = fieldGroup.indexOf(field);
1306
- }
1307
- const idObject = { f: form === null || form === void 0 ? void 0 : form.formId, ff: fieldPath, idx, type: field ? 'field' : 'form' };
1330
+ function getFieldNodeTags(multiple, fieldsCount, type, valid, form) {
1331
+ const { textColor, bgColor } = getValidityColors(valid);
1332
+ return [
1333
+ multiple
1334
+ ? undefined
1335
+ : {
1336
+ label: 'Field',
1337
+ textColor,
1338
+ backgroundColor: bgColor,
1339
+ },
1340
+ !form
1341
+ ? {
1342
+ label: 'Standalone',
1343
+ textColor: COLORS.black,
1344
+ backgroundColor: COLORS.gray,
1345
+ }
1346
+ : undefined,
1347
+ type === 'checkbox'
1348
+ ? {
1349
+ label: 'Checkbox',
1350
+ textColor: COLORS.white,
1351
+ backgroundColor: COLORS.blue,
1352
+ }
1353
+ : undefined,
1354
+ type === 'radio'
1355
+ ? {
1356
+ label: 'Radio',
1357
+ textColor: COLORS.white,
1358
+ backgroundColor: COLORS.purple,
1359
+ }
1360
+ : undefined,
1361
+ multiple
1362
+ ? {
1363
+ label: 'Multiple',
1364
+ textColor: COLORS.black,
1365
+ backgroundColor: COLORS.orange,
1366
+ }
1367
+ : undefined,
1368
+ ].filter(Boolean);
1369
+ }
1370
+ function encodeNodeId(form, stateOrField) {
1371
+ const type = stateOrField ? ('path' in stateOrField ? 'pathState' : 'field') : 'form';
1372
+ const fieldPath = stateOrField ? ('path' in stateOrField ? stateOrField === null || stateOrField === void 0 ? void 0 : stateOrField.path : unref(stateOrField === null || stateOrField === void 0 ? void 0 : stateOrField.name)) : '';
1373
+ const idObject = { f: form === null || form === void 0 ? void 0 : form.formId, ff: fieldPath, type };
1308
1374
  return btoa(JSON.stringify(idObject));
1309
1375
  }
1310
1376
  function decodeNodeId(nodeId) {
@@ -1324,11 +1390,11 @@ function decodeNodeId(nodeId) {
1324
1390
  if (!form) {
1325
1391
  return {};
1326
1392
  }
1327
- const fieldGroup = form.fieldsByPath.value[idObject.ff];
1393
+ const state = form.getPathState(idObject.ff);
1328
1394
  return {
1329
1395
  type: idObject.type,
1330
1396
  form,
1331
- field: Array.isArray(fieldGroup) ? fieldGroup[idObject.idx || 0] : fieldGroup,
1397
+ state,
1332
1398
  };
1333
1399
  }
1334
1400
  catch (err) {
@@ -1336,30 +1402,29 @@ function decodeNodeId(nodeId) {
1336
1402
  }
1337
1403
  return {};
1338
1404
  }
1339
- function buildFieldState(field) {
1340
- const { errors, meta, value } = field;
1405
+ function buildFieldState(state) {
1341
1406
  return {
1342
1407
  'Field state': [
1343
- { key: 'errors', value: errors.value },
1408
+ { key: 'errors', value: state.errors },
1344
1409
  {
1345
1410
  key: 'initialValue',
1346
- value: meta.initialValue,
1411
+ value: state.initialValue,
1347
1412
  },
1348
1413
  {
1349
1414
  key: 'currentValue',
1350
- value: value.value,
1415
+ value: state.value,
1351
1416
  },
1352
1417
  {
1353
1418
  key: 'touched',
1354
- value: meta.touched,
1419
+ value: state.touched,
1355
1420
  },
1356
1421
  {
1357
1422
  key: 'dirty',
1358
- value: meta.dirty,
1423
+ value: state.dirty,
1359
1424
  },
1360
1425
  {
1361
1426
  key: 'valid',
1362
- value: meta.valid,
1427
+ value: state.valid,
1363
1428
  },
1364
1429
  ],
1365
1430
  };
@@ -1413,15 +1478,10 @@ function buildFormState(form) {
1413
1478
  /**
1414
1479
  * Resolves the tag color based on the form state
1415
1480
  */
1416
- function getTagTheme(fieldOrForm) {
1417
- // const fallbackColors = {
1418
- // bgColor: COLORS.unknown,
1419
- // textColor: COLORS.white,
1420
- // };
1421
- const isValid = 'id' in fieldOrForm ? fieldOrForm.meta.valid : fieldOrForm.meta.value.valid;
1481
+ function getValidityColors(valid) {
1422
1482
  return {
1423
- bgColor: isValid ? COLORS.success : COLORS.error,
1424
- textColor: isValid ? COLORS.black : COLORS.white,
1483
+ bgColor: valid ? COLORS.success : COLORS.error,
1484
+ textColor: valid ? COLORS.black : COLORS.white,
1425
1485
  };
1426
1486
  }
1427
1487
 
@@ -1430,7 +1490,7 @@ function getTagTheme(fieldOrForm) {
1430
1490
  */
1431
1491
  function useField(path, rules, opts) {
1432
1492
  if (hasCheckedAttr(opts === null || opts === void 0 ? void 0 : opts.type)) {
1433
- return useCheckboxField(path, rules, opts);
1493
+ return useFieldWithChecked(path, rules, opts);
1434
1494
  }
1435
1495
  return _useField(path, rules, opts);
1436
1496
  }
@@ -1439,12 +1499,30 @@ function _useField(path, rules, opts) {
1439
1499
  const injectedForm = controlled ? injectWithSelf(FormContextKey) : undefined;
1440
1500
  const form = controlForm || injectedForm;
1441
1501
  const name = lazyToRef(path);
1442
- // a flag indicating if the field is about to be removed/unmounted.
1443
- let markedForRemoval = false;
1444
- const { id, value, initialValue, meta, setState, errors, errorMessage } = useFieldState(name, {
1502
+ let PENDING_UNMOUNT = false;
1503
+ const validator = computed(() => {
1504
+ const schema = unref(form === null || form === void 0 ? void 0 : form.schema);
1505
+ if (schema) {
1506
+ return undefined;
1507
+ }
1508
+ const rulesValue = unref(rules);
1509
+ if (isYupValidator(rulesValue) ||
1510
+ isTypedSchema(rulesValue) ||
1511
+ isCallable(rulesValue) ||
1512
+ Array.isArray(rulesValue)) {
1513
+ return rulesValue;
1514
+ }
1515
+ return normalizeRules(rulesValue);
1516
+ });
1517
+ const { id, value, initialValue, meta, setState, errors } = useFieldState(name, {
1445
1518
  modelValue,
1446
1519
  form,
1520
+ bails,
1521
+ label,
1522
+ type,
1523
+ validate: validator.value ? validate$1 : undefined,
1447
1524
  });
1525
+ const errorMessage = computed(() => errors.value[0]);
1448
1526
  if (syncVModel) {
1449
1527
  useVModel({ value, prop: modelPropName, handleChange });
1450
1528
  }
@@ -1454,51 +1532,37 @@ function _useField(path, rules, opts) {
1454
1532
  const handleBlur = () => {
1455
1533
  meta.touched = true;
1456
1534
  };
1457
- const normalizedRules = computed(() => {
1458
- let rulesValue = unref(rules);
1459
- const schema = unref(form === null || form === void 0 ? void 0 : form.schema);
1460
- if (schema && !isYupValidator(schema) && !isTypedSchema(schema)) {
1461
- rulesValue = extractRuleFromSchema(schema, unref(name)) || rulesValue;
1462
- }
1463
- if (isYupValidator(rulesValue) ||
1464
- isTypedSchema(rulesValue) ||
1465
- isCallable(rulesValue) ||
1466
- Array.isArray(rulesValue)) {
1467
- return rulesValue;
1468
- }
1469
- return normalizeRules(rulesValue);
1470
- });
1471
1535
  async function validateCurrentValue(mode) {
1472
1536
  var _a, _b;
1473
1537
  if (form === null || form === void 0 ? void 0 : form.validateSchema) {
1474
1538
  return (_a = (await form.validateSchema(mode)).results[unref(name)]) !== null && _a !== void 0 ? _a : { valid: true, errors: [] };
1475
1539
  }
1476
- return validate(value.value, normalizedRules.value, {
1477
- name: unref(name),
1478
- label: unref(label),
1479
- values: (_b = form === null || form === void 0 ? void 0 : form.values) !== null && _b !== void 0 ? _b : {},
1480
- bails,
1481
- });
1540
+ if (validator.value) {
1541
+ return validate(value.value, validator.value, {
1542
+ name: unref(name),
1543
+ label: unref(label),
1544
+ values: (_b = form === null || form === void 0 ? void 0 : form.values) !== null && _b !== void 0 ? _b : {},
1545
+ bails,
1546
+ });
1547
+ }
1548
+ return { valid: true, errors: [] };
1482
1549
  }
1483
1550
  const validateWithStateMutation = withLatest(async () => {
1484
1551
  meta.pending = true;
1485
1552
  meta.validated = true;
1486
1553
  return validateCurrentValue('validated-only');
1487
1554
  }, result => {
1488
- if (markedForRemoval) {
1489
- result.valid = true;
1490
- result.errors = [];
1555
+ if (PENDING_UNMOUNT) {
1556
+ return;
1491
1557
  }
1492
1558
  setState({ errors: result.errors });
1493
1559
  meta.pending = false;
1560
+ meta.valid = result.valid;
1494
1561
  return result;
1495
1562
  });
1496
1563
  const validateValidStateOnly = withLatest(async () => {
1497
1564
  return validateCurrentValue('silent');
1498
1565
  }, result => {
1499
- if (markedForRemoval) {
1500
- result.valid = true;
1501
- }
1502
1566
  meta.valid = result.valid;
1503
1567
  return result;
1504
1568
  });
@@ -1511,7 +1575,7 @@ function _useField(path, rules, opts) {
1511
1575
  // Common input/change event handler
1512
1576
  function handleChange(e, shouldValidate = true) {
1513
1577
  const newValue = normalizeEventValue(e);
1514
- value.value = newValue;
1578
+ setValue(newValue, false);
1515
1579
  if (!validateOnValueUpdate && shouldValidate) {
1516
1580
  validateWithStateMutation();
1517
1581
  }
@@ -1530,24 +1594,8 @@ function _useField(path, rules, opts) {
1530
1594
  function setTouched(isTouched) {
1531
1595
  meta.touched = isTouched;
1532
1596
  }
1533
- let unwatchValue;
1534
- let lastWatchedValue = klona(value.value);
1535
- function watchValue() {
1536
- unwatchValue = watch(value, (val, oldVal) => {
1537
- if (isEqual(val, oldVal) && isEqual(val, lastWatchedValue)) {
1538
- return;
1539
- }
1540
- const validateFn = validateOnValueUpdate ? validateWithStateMutation : validateValidStateOnly;
1541
- validateFn();
1542
- lastWatchedValue = klona(val);
1543
- }, {
1544
- deep: true,
1545
- });
1546
- }
1547
- watchValue();
1548
1597
  function resetField(state) {
1549
1598
  var _a;
1550
- unwatchValue === null || unwatchValue === void 0 ? void 0 : unwatchValue();
1551
1599
  const newValue = state && 'value' in state ? state.value : initialValue.value;
1552
1600
  setState({
1553
1601
  value: klona(newValue),
@@ -1558,22 +1606,41 @@ function _useField(path, rules, opts) {
1558
1606
  meta.pending = false;
1559
1607
  meta.validated = false;
1560
1608
  validateValidStateOnly();
1561
- // need to watch at next tick to avoid triggering the value watcher
1562
- nextTick(() => {
1563
- watchValue();
1564
- });
1565
1609
  }
1566
- function setValue(newValue) {
1610
+ function setValue(newValue, validate = true) {
1567
1611
  value.value = newValue;
1612
+ if (!validate) {
1613
+ return;
1614
+ }
1615
+ const validateFn = validateOnValueUpdate ? validateWithStateMutation : validateValidStateOnly;
1616
+ validateFn();
1568
1617
  }
1569
1618
  function setErrors(errors) {
1570
1619
  setState({ errors: Array.isArray(errors) ? errors : [errors] });
1571
1620
  }
1621
+ const valueProxy = computed({
1622
+ get() {
1623
+ return value.value;
1624
+ },
1625
+ set(newValue) {
1626
+ setValue(newValue, validateOnValueUpdate);
1627
+ },
1628
+ });
1629
+ if ((process.env.NODE_ENV !== 'production')) {
1630
+ watch(valueProxy, (value, oldValue) => {
1631
+ if (!isObject(value)) {
1632
+ return;
1633
+ }
1634
+ if (value === oldValue && isEqual(value, oldValue)) {
1635
+ warn$1('Detected a possible deep change on field `value` ref, for nested changes please either set the entire ref value or use `setValue` or `handleChange`.');
1636
+ }
1637
+ }, { deep: true });
1638
+ }
1572
1639
  const field = {
1573
1640
  id,
1574
1641
  name,
1575
1642
  label,
1576
- value,
1643
+ value: valueProxy,
1577
1644
  meta,
1578
1645
  errors,
1579
1646
  errorMessage,
@@ -1617,14 +1684,9 @@ function _useField(path, rules, opts) {
1617
1684
  return field;
1618
1685
  }
1619
1686
  // associate the field with the given form
1620
- form.register(field);
1621
- onBeforeUnmount(() => {
1622
- markedForRemoval = true;
1623
- form.unregister(field);
1624
- });
1625
1687
  // extract cross-field dependencies in a computed prop
1626
1688
  const dependencies = computed(() => {
1627
- const rulesVal = normalizedRules.value;
1689
+ const rulesVal = validator.value;
1628
1690
  // is falsy, a function schema or a yup schema
1629
1691
  if (!rulesVal ||
1630
1692
  isCallable(rulesVal) ||
@@ -1658,6 +1720,37 @@ function _useField(path, rules, opts) {
1658
1720
  meta.validated ? validateWithStateMutation() : validateValidStateOnly();
1659
1721
  }
1660
1722
  });
1723
+ onBeforeUnmount(() => {
1724
+ var _a;
1725
+ PENDING_UNMOUNT = true;
1726
+ const shouldKeepValue = (_a = unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : unref(form.keepValuesOnUnmount);
1727
+ if (shouldKeepValue || !form) {
1728
+ return;
1729
+ }
1730
+ const path = unravel(name);
1731
+ const pathState = form.getPathState(path);
1732
+ const matchesId = Array.isArray(pathState === null || pathState === void 0 ? void 0 : pathState.id) && (pathState === null || pathState === void 0 ? void 0 : pathState.multiple)
1733
+ ? pathState === null || pathState === void 0 ? void 0 : pathState.id.includes(field.id)
1734
+ : (pathState === null || pathState === void 0 ? void 0 : pathState.id) === field.id;
1735
+ if (!matchesId) {
1736
+ return;
1737
+ }
1738
+ if ((pathState === null || pathState === void 0 ? void 0 : pathState.multiple) && Array.isArray(pathState.value)) {
1739
+ const valueIdx = pathState.value.findIndex(i => isEqual(i, unref(field.checkedValue)));
1740
+ if (valueIdx > -1) {
1741
+ const newVal = [...pathState.value];
1742
+ newVal.splice(valueIdx, 1);
1743
+ form.setFieldValue(path, newVal);
1744
+ }
1745
+ if (Array.isArray(pathState.id)) {
1746
+ pathState.id.splice(pathState.id.indexOf(field.id), 1);
1747
+ }
1748
+ }
1749
+ else {
1750
+ form.unsetPathValue(path);
1751
+ }
1752
+ form.removePathState(path);
1753
+ });
1661
1754
  return field;
1662
1755
  }
1663
1756
  /**
@@ -1688,22 +1781,11 @@ function normalizeOptions(opts) {
1688
1781
  const controlled = 'standalone' in opts ? !opts.standalone : opts.controlled;
1689
1782
  return Object.assign(Object.assign(Object.assign({}, defaults()), (opts || {})), { initialValue, controlled: controlled !== null && controlled !== void 0 ? controlled : true, checkedValue });
1690
1783
  }
1691
- /**
1692
- * Extracts the validation rules from a schema
1693
- */
1694
- function extractRuleFromSchema(schema, fieldName) {
1695
- // no schema at all
1696
- if (!schema) {
1697
- return undefined;
1698
- }
1699
- // there is a key on the schema object for this field
1700
- return schema[fieldName];
1701
- }
1702
- function useCheckboxField(name, rules, opts) {
1784
+ function useFieldWithChecked(name, rules, opts) {
1703
1785
  const form = !(opts === null || opts === void 0 ? void 0 : opts.standalone) ? injectWithSelf(FormContextKey) : undefined;
1704
1786
  const checkedValue = opts === null || opts === void 0 ? void 0 : opts.checkedValue;
1705
1787
  const uncheckedValue = opts === null || opts === void 0 ? void 0 : opts.uncheckedValue;
1706
- function patchCheckboxApi(field) {
1788
+ function patchCheckedApi(field) {
1707
1789
  const handleChange = field.handleChange;
1708
1790
  const checked = computed(() => {
1709
1791
  const currentValue = unref(field.value);
@@ -1720,9 +1802,15 @@ function useCheckboxField(name, rules, opts) {
1720
1802
  }
1721
1803
  return;
1722
1804
  }
1723
- let newValue = normalizeEventValue(e);
1724
- // Single checkbox field without a form to toggle it's value
1725
- if (!form) {
1805
+ const path = unravel(name);
1806
+ const pathState = form === null || form === void 0 ? void 0 : form.getPathState(path);
1807
+ const value = normalizeEventValue(e);
1808
+ let newValue;
1809
+ if (form && (pathState === null || pathState === void 0 ? void 0 : pathState.multiple) && pathState.type === 'checkbox') {
1810
+ newValue = resolveNextCheckboxValue(getFromPath(form.values, path) || [], value, undefined);
1811
+ }
1812
+ else {
1813
+ // Single checkbox field without a form to toggle it's value
1726
1814
  newValue = resolveNextCheckboxValue(unref(field.value), unref(checkedValue), unref(uncheckedValue));
1727
1815
  }
1728
1816
  handleChange(newValue, shouldValidate);
@@ -1731,7 +1819,7 @@ function useCheckboxField(name, rules, opts) {
1731
1819
  checkedValue,
1732
1820
  uncheckedValue, handleChange: handleCheckboxChange });
1733
1821
  }
1734
- return patchCheckboxApi(_useField(name, rules, opts));
1822
+ return patchCheckedApi(_useField(name, rules, opts));
1735
1823
  }
1736
1824
  function useVModel({ prop, value, handleChange }) {
1737
1825
  const vm = getCurrentInstance();
@@ -1876,9 +1964,27 @@ const FieldImpl = defineComponent({
1876
1964
  };
1877
1965
  const fieldProps = computed(() => {
1878
1966
  const { validateOnInput, validateOnChange, validateOnBlur, validateOnModelUpdate } = resolveValidationTriggers(props);
1879
- const baseOnBlur = [handleBlur, ctx.attrs.onBlur, validateOnBlur ? validateField : undefined].filter(Boolean);
1880
- const baseOnInput = [(e) => onChangeHandler(e, validateOnInput), ctx.attrs.onInput].filter(Boolean);
1881
- const baseOnChange = [(e) => onChangeHandler(e, validateOnChange), ctx.attrs.onChange].filter(Boolean);
1967
+ function baseOnBlur(e) {
1968
+ handleBlur(e);
1969
+ if (isCallable(ctx.attrs.onBlur)) {
1970
+ ctx.attrs.onBlur(e);
1971
+ }
1972
+ if (validateOnBlur) {
1973
+ validateField();
1974
+ }
1975
+ }
1976
+ function baseOnInput(e) {
1977
+ onChangeHandler(e, validateOnInput);
1978
+ if (isCallable(ctx.attrs.onInput)) {
1979
+ ctx.attrs.onInput(e);
1980
+ }
1981
+ }
1982
+ function baseOnChange(e) {
1983
+ onChangeHandler(e, validateOnChange);
1984
+ if (isCallable(ctx.attrs.onChange)) {
1985
+ ctx.attrs.onChange(e);
1986
+ }
1987
+ }
1882
1988
  const attrs = {
1883
1989
  name: props.name,
1884
1990
  onBlur: baseOnBlur,
@@ -1957,6 +2063,7 @@ function resolveInitialValue(props, ctx) {
1957
2063
  const Field = FieldImpl;
1958
2064
 
1959
2065
  let FORM_COUNTER = 0;
2066
+ const PRIVATE_PATH_STATE_KEYS = ['bails', 'fieldsCount', 'id', 'multiple', 'type', 'validate'];
1960
2067
  function resolveInitialValues(opts) {
1961
2068
  const providedValues = unref(opts === null || opts === void 0 ? void 0 : opts.initialValues) || {};
1962
2069
  const schema = unref(opts === null || opts === void 0 ? void 0 : opts.validationSchema);
@@ -1968,12 +2075,8 @@ function resolveInitialValues(opts) {
1968
2075
  function useForm(opts) {
1969
2076
  var _a;
1970
2077
  const formId = FORM_COUNTER++;
1971
- const controlledModelPaths = new Set();
1972
2078
  // Prevents fields from double resetting their values, which causes checkboxes to toggle their initial value
1973
- // TODO: This won't be needed if we centralize all the state inside the `form` for form inputs
1974
- let RESET_LOCK = false;
1975
- // A lookup containing fields or field groups
1976
- const fieldsByPath = ref({});
2079
+ let FIELD_ID_COUNTER = 0;
1977
2080
  // If the form is currently submitting
1978
2081
  const isSubmitting = ref(false);
1979
2082
  // The number of times the user tried to submit the form
@@ -1982,44 +2085,62 @@ function useForm(opts) {
1982
2085
  const fieldArrays = [];
1983
2086
  // a private ref for all form values
1984
2087
  const formValues = reactive(resolveInitialValues(opts));
1985
- // the source of errors for the form fields
1986
- const { errorBag, setErrorBag, setFieldErrorBag } = useErrorBag(opts === null || opts === void 0 ? void 0 : opts.initialErrors);
2088
+ const pathStates = ref([]);
2089
+ const extraErrorsBag = ref({});
2090
+ /**
2091
+ * Manually sets an error message on a specific field
2092
+ */
2093
+ function setFieldError(field, message) {
2094
+ const state = findPathState(field);
2095
+ if (!state) {
2096
+ extraErrorsBag.value[field] = normalizeErrorItem(message);
2097
+ return;
2098
+ }
2099
+ state.errors = normalizeErrorItem(message);
2100
+ }
2101
+ /**
2102
+ * Sets errors for the fields specified in the object
2103
+ */
2104
+ function setErrors(paths) {
2105
+ keysOf(paths).forEach(path => {
2106
+ setFieldError(path, paths[path]);
2107
+ });
2108
+ }
2109
+ if (opts === null || opts === void 0 ? void 0 : opts.initialErrors) {
2110
+ setErrors(opts.initialErrors);
2111
+ }
2112
+ const errorBag = computed(() => {
2113
+ const pathErrors = pathStates.value.reduce((acc, state) => {
2114
+ if (state.errors.length) {
2115
+ acc[state.path] = state.errors;
2116
+ }
2117
+ return acc;
2118
+ }, {});
2119
+ return Object.assign(Object.assign({}, extraErrorsBag.value), pathErrors);
2120
+ });
1987
2121
  // Gets the first error of each field
1988
2122
  const errors = computed(() => {
1989
2123
  return keysOf(errorBag.value).reduce((acc, key) => {
1990
- const bag = errorBag.value[key];
1991
- if (bag && bag.length) {
1992
- acc[key] = bag[0];
2124
+ const errors = errorBag.value[key];
2125
+ if (errors === null || errors === void 0 ? void 0 : errors.length) {
2126
+ acc[key] = errors[0];
1993
2127
  }
1994
2128
  return acc;
1995
2129
  }, {});
1996
2130
  });
1997
- function getFirstFieldAtPath(path) {
1998
- const fieldOrGroup = fieldsByPath.value[path];
1999
- return Array.isArray(fieldOrGroup) ? fieldOrGroup[0] : fieldOrGroup;
2000
- }
2001
- function fieldExists(path) {
2002
- return !!fieldsByPath.value[path];
2003
- }
2004
2131
  /**
2005
2132
  * Holds a computed reference to all fields names and labels
2006
2133
  */
2007
2134
  const fieldNames = computed(() => {
2008
- return keysOf(fieldsByPath.value).reduce((names, path) => {
2009
- const field = getFirstFieldAtPath(path);
2010
- if (field) {
2011
- names[path] = { name: unref(field.name) || '', label: unref(field.label) || '' };
2012
- }
2135
+ return pathStates.value.reduce((names, state) => {
2136
+ names[state.path] = { name: state.path || '', label: state.label || '' };
2013
2137
  return names;
2014
2138
  }, {});
2015
2139
  });
2016
2140
  const fieldBailsMap = computed(() => {
2017
- return keysOf(fieldsByPath.value).reduce((map, path) => {
2141
+ return pathStates.value.reduce((map, state) => {
2018
2142
  var _a;
2019
- const field = getFirstFieldAtPath(path);
2020
- if (field) {
2021
- map[path] = (_a = field.bails) !== null && _a !== void 0 ? _a : true;
2022
- }
2143
+ map[state.path] = (_a = state.bails) !== null && _a !== void 0 ? _a : true;
2023
2144
  return map;
2024
2145
  }, {});
2025
2146
  });
@@ -2028,17 +2149,74 @@ function useForm(opts) {
2028
2149
  const initialErrors = Object.assign({}, ((opts === null || opts === void 0 ? void 0 : opts.initialErrors) || {}));
2029
2150
  const keepValuesOnUnmount = (_a = opts === null || opts === void 0 ? void 0 : opts.keepValuesOnUnmount) !== null && _a !== void 0 ? _a : false;
2030
2151
  // initial form values
2031
- const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts);
2152
+ const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(pathStates, formValues, opts);
2032
2153
  // form meta aggregations
2033
- const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);
2154
+ const meta = useFormMeta(pathStates, formValues, originalInitialValues, errors);
2034
2155
  const controlledValues = computed(() => {
2035
- return [...controlledModelPaths, ...keysOf(fieldsByPath.value)].reduce((acc, path) => {
2036
- const value = getFromPath(formValues, path);
2037
- setInPath(acc, path, value);
2156
+ return pathStates.value.reduce((acc, state) => {
2157
+ const value = getFromPath(formValues, state.path);
2158
+ setInPath(acc, state.path, value);
2038
2159
  return acc;
2039
2160
  }, {});
2040
2161
  });
2041
2162
  const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
2163
+ function createPathState(path, config) {
2164
+ var _a, _b;
2165
+ const initialValue = computed(() => getFromPath(initialValues.value, unravel(path)));
2166
+ const pathStateExists = pathStates.value.find(state => state.path === unref(path));
2167
+ if (pathStateExists) {
2168
+ if ((config === null || config === void 0 ? void 0 : config.type) === 'checkbox' || (config === null || config === void 0 ? void 0 : config.type) === 'radio') {
2169
+ pathStateExists.multiple = true;
2170
+ }
2171
+ if (Array.isArray(pathStateExists.id)) {
2172
+ pathStateExists.id.push(FIELD_ID_COUNTER++);
2173
+ }
2174
+ else {
2175
+ pathStateExists.id = [pathStateExists.id, FIELD_ID_COUNTER++];
2176
+ }
2177
+ pathStateExists.fieldsCount++;
2178
+ return pathStateExists;
2179
+ }
2180
+ const currentValue = computed(() => getFromPath(formValues, unravel(path)));
2181
+ const pathValue = unravel(path);
2182
+ const state = reactive({
2183
+ id: FIELD_ID_COUNTER++,
2184
+ path,
2185
+ touched: false,
2186
+ pending: false,
2187
+ valid: true,
2188
+ validated: !!((_a = initialErrors[pathValue]) === null || _a === void 0 ? void 0 : _a.length),
2189
+ initialValue,
2190
+ errors: shallowRef([]),
2191
+ bails: (_b = config === null || config === void 0 ? void 0 : config.bails) !== null && _b !== void 0 ? _b : false,
2192
+ label: config === null || config === void 0 ? void 0 : config.label,
2193
+ type: (config === null || config === void 0 ? void 0 : config.type) || 'default',
2194
+ value: currentValue,
2195
+ multiple: false,
2196
+ fieldsCount: 1,
2197
+ validate: config === null || config === void 0 ? void 0 : config.validate,
2198
+ dirty: computed(() => {
2199
+ return !isEqual(unref(currentValue), unref(initialValue));
2200
+ }),
2201
+ });
2202
+ pathStates.value.push(state);
2203
+ // if it has errors before, validate it.
2204
+ if (errors.value[pathValue] && !initialErrors[pathValue]) {
2205
+ nextTick(() => {
2206
+ validateField(pathValue);
2207
+ });
2208
+ }
2209
+ // Handles when a path changes
2210
+ if (isRef(path)) {
2211
+ watch(path, newPath => {
2212
+ const nextValue = klona(currentValue.value);
2213
+ nextTick(() => {
2214
+ setInPath(formValues, newPath, nextValue);
2215
+ });
2216
+ });
2217
+ }
2218
+ return state;
2219
+ }
2042
2220
  /**
2043
2221
  * Batches validation runs in 5ms batches
2044
2222
  * Must have two distinct batch queues to make sure they don't override each other settings #3783
@@ -2049,17 +2227,17 @@ function useForm(opts) {
2049
2227
  return (await mode) === 'silent' ? debouncedSilentValidation() : debouncedValidation();
2050
2228
  }, (formResult, [mode]) => {
2051
2229
  // fields by id lookup
2052
- const fieldsById = formCtx.fieldsByPath.value || {};
2053
2230
  // errors fields names, we need it to also check if custom errors are updated
2054
2231
  const currentErrorsPaths = keysOf(formCtx.errorBag.value);
2055
2232
  // collect all the keys from the schema and all fields
2056
- // this ensures we have a complete keymap of all the fields
2233
+ // this ensures we have a complete key map of all the fields
2057
2234
  const paths = [
2058
- ...new Set([...keysOf(formResult.results), ...keysOf(fieldsById), ...currentErrorsPaths]),
2235
+ ...new Set([...keysOf(formResult.results), ...pathStates.value.map(p => p.path), ...currentErrorsPaths]),
2059
2236
  ];
2060
2237
  // aggregates the paths into a single result object while applying the results on the fields
2061
- return paths.reduce((validation, path) => {
2062
- const field = fieldsById[path];
2238
+ return paths.reduce((validation, _path) => {
2239
+ const path = _path;
2240
+ const pathState = findPathState(path);
2063
2241
  const messages = (formResult.results[path] || { errors: [] }).errors;
2064
2242
  const fieldResult = {
2065
2243
  errors: messages,
@@ -2069,24 +2247,37 @@ function useForm(opts) {
2069
2247
  if (!fieldResult.valid) {
2070
2248
  validation.errors[path] = fieldResult.errors[0];
2071
2249
  }
2250
+ // clean up extra errors if path state exists
2251
+ if (pathState && extraErrorsBag.value[path]) {
2252
+ delete extraErrorsBag.value[path];
2253
+ }
2072
2254
  // field not rendered
2073
- if (!field) {
2255
+ if (!pathState) {
2074
2256
  setFieldError(path, messages);
2075
2257
  return validation;
2076
2258
  }
2077
2259
  // always update the valid flag regardless of the mode
2078
- applyFieldMutation(field, f => (f.meta.valid = fieldResult.valid));
2260
+ pathState.valid = fieldResult.valid;
2079
2261
  if (mode === 'silent') {
2080
2262
  return validation;
2081
2263
  }
2082
- const wasValidated = Array.isArray(field) ? field.some(f => f.meta.validated) : field.meta.validated;
2083
- if (mode === 'validated-only' && !wasValidated) {
2264
+ if (mode === 'validated-only' && !pathState.validated) {
2084
2265
  return validation;
2085
2266
  }
2086
- applyFieldMutation(field, f => f.setState({ errors: fieldResult.errors }));
2267
+ setFieldError(pathState, fieldResult.errors);
2087
2268
  return validation;
2088
2269
  }, { valid: formResult.valid, results: {}, errors: {} });
2089
2270
  });
2271
+ function mutateAllPathState(mutation) {
2272
+ pathStates.value.forEach(mutation);
2273
+ }
2274
+ function findPathState(path) {
2275
+ const pathState = typeof path === 'string' ? pathStates.value.find(state => state.path === path) : path;
2276
+ return pathState;
2277
+ }
2278
+ function unsetPathValue(path) {
2279
+ unsetPath(formValues, path);
2280
+ }
2090
2281
  function makeSubmissionFactory(onlyControlled) {
2091
2282
  return function submitHandlerFactory(fn, onValidationError) {
2092
2283
  return function submissionHandler(e) {
@@ -2095,10 +2286,7 @@ function useForm(opts) {
2095
2286
  e.stopPropagation();
2096
2287
  }
2097
2288
  // Touch all fields
2098
- setTouched(keysOf(fieldsByPath.value).reduce((acc, field) => {
2099
- acc[field] = true;
2100
- return acc;
2101
- }, {}));
2289
+ mutateAllPathState(s => (s.touched = true));
2102
2290
  isSubmitting.value = true;
2103
2291
  submitCount.value++;
2104
2292
  return validate()
@@ -2106,7 +2294,7 @@ function useForm(opts) {
2106
2294
  const values = klona(formValues);
2107
2295
  if (result.valid && typeof fn === 'function') {
2108
2296
  const controlled = klona(controlledValues.value);
2109
- let submittedValues = onlyControlled ? controlled : values;
2297
+ let submittedValues = (onlyControlled ? controlled : values);
2110
2298
  if (result.values) {
2111
2299
  submittedValues = result.values;
2112
2300
  }
@@ -2146,9 +2334,22 @@ function useForm(opts) {
2146
2334
  const handleSubmitImpl = makeSubmissionFactory(false);
2147
2335
  const handleSubmit = handleSubmitImpl;
2148
2336
  handleSubmit.withControlled = makeSubmissionFactory(true);
2337
+ function removePathState(path) {
2338
+ const idx = pathStates.value.findIndex(s => s.path === path);
2339
+ const pathState = pathStates.value[idx];
2340
+ if (idx === -1 || !pathState) {
2341
+ return;
2342
+ }
2343
+ if (pathState.multiple && pathState.fieldsCount) {
2344
+ pathState.fieldsCount--;
2345
+ }
2346
+ if (!pathState.multiple || pathState.fieldsCount <= 0) {
2347
+ pathStates.value.splice(idx, 1);
2348
+ unsetInitialValue(path);
2349
+ }
2350
+ }
2149
2351
  const formCtx = {
2150
2352
  formId,
2151
- fieldsByPath,
2152
2353
  values: formValues,
2153
2354
  controlledValues,
2154
2355
  errorBag,
@@ -2161,14 +2362,11 @@ function useForm(opts) {
2161
2362
  keepValuesOnUnmount,
2162
2363
  validateSchema: unref(schema) ? validateSchema : undefined,
2163
2364
  validate,
2164
- register: registerField,
2165
- unregister: unregisterField,
2166
- setFieldErrorBag,
2365
+ setFieldError,
2167
2366
  validateField,
2168
2367
  setFieldValue,
2169
2368
  setValues,
2170
2369
  setErrors,
2171
- setFieldError,
2172
2370
  setFieldTouched,
2173
2371
  setTouched,
2174
2372
  resetForm,
@@ -2178,102 +2376,63 @@ function useForm(opts) {
2178
2376
  unsetInitialValue,
2179
2377
  setFieldInitialValue,
2180
2378
  useFieldModel,
2379
+ createPathState,
2380
+ getPathState: findPathState,
2381
+ unsetPathValue,
2382
+ removePathState,
2383
+ initialValues: initialValues,
2384
+ getAllPathStates: () => pathStates.value,
2181
2385
  };
2182
- function isFieldGroup(fieldOrGroup) {
2183
- return Array.isArray(fieldOrGroup);
2184
- }
2185
- function applyFieldMutation(fieldOrGroup, mutation) {
2186
- if (Array.isArray(fieldOrGroup)) {
2187
- return fieldOrGroup.forEach(mutation);
2188
- }
2189
- return mutation(fieldOrGroup);
2190
- }
2191
- function mutateAllFields(mutation) {
2192
- Object.values(fieldsByPath.value).forEach(field => {
2193
- if (!field) {
2194
- return;
2195
- }
2196
- // avoid resetting the field values, because they should've been reset already.
2197
- applyFieldMutation(field, mutation);
2198
- });
2199
- }
2200
- /**
2201
- * Manually sets an error message on a specific field
2202
- */
2203
- function setFieldError(field, message) {
2204
- setFieldErrorBag(field, message);
2205
- }
2206
- /**
2207
- * Sets errors for the fields specified in the object
2208
- */
2209
- function setErrors(fields) {
2210
- setErrorBag(fields);
2211
- }
2212
2386
  /**
2213
2387
  * Sets a single field value
2214
2388
  */
2215
- function setFieldValue(field, value, { force } = { force: false }) {
2216
- var _a;
2217
- const fieldInstance = fieldsByPath.value[field];
2389
+ function setFieldValue(field, value) {
2218
2390
  const clonedValue = klona(value);
2219
- // field wasn't found, create a virtual field as a placeholder
2220
- if (!fieldInstance) {
2221
- setInPath(formValues, field, clonedValue);
2222
- return;
2223
- }
2224
- if (isFieldGroup(fieldInstance) && ((_a = fieldInstance[0]) === null || _a === void 0 ? void 0 : _a.type) === 'checkbox' && !Array.isArray(value)) {
2225
- // Multiple checkboxes, and only one of them got updated
2226
- const newValue = klona(resolveNextCheckboxValue(getFromPath(formValues, field) || [], value, undefined));
2227
- setInPath(formValues, field, newValue);
2228
- return;
2391
+ const path = typeof field === 'string' ? field : field.path;
2392
+ const pathState = findPathState(path);
2393
+ if (!pathState) {
2394
+ createPathState(path);
2229
2395
  }
2230
- let newValue = clonedValue;
2231
- // Single Checkbox: toggles the field value unless the field is being reset then force it
2232
- if (!isFieldGroup(fieldInstance) && fieldInstance.type === 'checkbox' && !force && !RESET_LOCK) {
2233
- newValue = klona(resolveNextCheckboxValue(getFromPath(formValues, field), value, unref(fieldInstance.uncheckedValue)));
2234
- }
2235
- setInPath(formValues, field, newValue);
2396
+ setInPath(formValues, path, clonedValue);
2236
2397
  }
2237
2398
  /**
2238
2399
  * Sets multiple fields values
2239
2400
  */
2240
2401
  function setValues(fields) {
2241
- // clean up old values
2242
- keysOf(formValues).forEach(key => {
2243
- delete formValues[key];
2244
- });
2245
- // set up new values
2246
- keysOf(fields).forEach(path => {
2247
- setFieldValue(path, fields[path]);
2248
- });
2402
+ merge(formValues, fields);
2249
2403
  // regenerate the arrays when the form values change
2250
2404
  fieldArrays.forEach(f => f && f.reset());
2251
2405
  }
2252
2406
  function createModel(path) {
2253
- const { value } = _useFieldValue(path, undefined, formCtx);
2254
- watch(value, () => {
2255
- if (!fieldExists(unref(path))) {
2256
- validate({ mode: 'validated-only' });
2257
- }
2258
- }, {
2259
- deep: true,
2407
+ const pathState = findPathState(unref(path)) || createPathState(path);
2408
+ return computed({
2409
+ get() {
2410
+ return pathState.value;
2411
+ },
2412
+ set(value) {
2413
+ const pathValue = unref(path);
2414
+ setFieldValue(pathValue, value);
2415
+ pathState.validated = true;
2416
+ pathState.pending = true;
2417
+ validateField(pathValue).then(() => {
2418
+ pathState.pending = false;
2419
+ });
2420
+ },
2260
2421
  });
2261
- controlledModelPaths.add(unref(path));
2262
- return value;
2263
2422
  }
2264
- function useFieldModel(path) {
2265
- if (!Array.isArray(path)) {
2266
- return createModel(path);
2423
+ function useFieldModel(pathOrPaths) {
2424
+ if (!Array.isArray(pathOrPaths)) {
2425
+ return createModel(pathOrPaths);
2267
2426
  }
2268
- return path.map(createModel);
2427
+ return pathOrPaths.map(createModel);
2269
2428
  }
2270
2429
  /**
2271
2430
  * Sets the touched meta state on a field
2272
2431
  */
2273
2432
  function setFieldTouched(field, isTouched) {
2274
- const fieldInstance = fieldsByPath.value[field];
2275
- if (fieldInstance) {
2276
- applyFieldMutation(fieldInstance, f => f.setTouched(isTouched));
2433
+ const pathState = findPathState(field);
2434
+ if (pathState) {
2435
+ pathState.touched = isTouched;
2277
2436
  }
2278
2437
  }
2279
2438
  /**
@@ -2285,172 +2444,53 @@ function useForm(opts) {
2285
2444
  });
2286
2445
  }
2287
2446
  function resetField(field, state) {
2288
- const fieldInstance = fieldsByPath.value[field];
2289
- if (fieldInstance) {
2290
- applyFieldMutation(fieldInstance, f => f.resetField(state));
2291
- }
2447
+ var _a;
2448
+ const newValue = state && 'value' in state ? state.value : getFromPath(initialValues.value, field);
2449
+ setFieldInitialValue(field, klona(newValue));
2450
+ setFieldValue(field, newValue);
2451
+ setFieldTouched(field, (_a = state === null || state === void 0 ? void 0 : state.touched) !== null && _a !== void 0 ? _a : false);
2452
+ setFieldError(field, (state === null || state === void 0 ? void 0 : state.errors) || []);
2292
2453
  }
2293
2454
  /**
2294
2455
  * Resets all fields
2295
2456
  */
2296
- function resetForm(state) {
2297
- RESET_LOCK = true;
2298
- // Reset all field states first
2299
- mutateAllFields(f => f.resetField());
2300
- // reset values
2301
- const newValues = (state === null || state === void 0 ? void 0 : state.values) ? state.values : originalInitialValues.value;
2457
+ function resetForm(resetState) {
2458
+ const newValues = (resetState === null || resetState === void 0 ? void 0 : resetState.values) ? resetState.values : originalInitialValues.value;
2302
2459
  setInitialValues(newValues);
2303
2460
  setValues(newValues);
2304
- if (state === null || state === void 0 ? void 0 : state.touched) {
2305
- setTouched(state.touched);
2306
- }
2307
- setErrors((state === null || state === void 0 ? void 0 : state.errors) || {});
2308
- submitCount.value = (state === null || state === void 0 ? void 0 : state.submitCount) || 0;
2309
- nextTick(() => {
2310
- RESET_LOCK = false;
2461
+ mutateAllPathState(state => {
2462
+ var _a;
2463
+ state.validated = false;
2464
+ state.touched = ((_a = resetState === null || resetState === void 0 ? void 0 : resetState.touched) === null || _a === void 0 ? void 0 : _a[state.path]) || false;
2465
+ setFieldValue(state.path, getFromPath(newValues, state.path));
2466
+ setFieldError(state.path, undefined);
2311
2467
  });
2312
- }
2313
- function insertFieldAtPath(field, path) {
2314
- const rawField = markRaw(field);
2315
- const fieldPath = path;
2316
- // first field at that path
2317
- if (!fieldsByPath.value[fieldPath]) {
2318
- fieldsByPath.value[fieldPath] = rawField;
2319
- return;
2320
- }
2321
- const fieldAtPath = fieldsByPath.value[fieldPath];
2322
- if (fieldAtPath && !Array.isArray(fieldAtPath)) {
2323
- fieldsByPath.value[fieldPath] = [fieldAtPath];
2324
- }
2325
- // add the new array to that path
2326
- fieldsByPath.value[fieldPath] = [...fieldsByPath.value[fieldPath], rawField];
2327
- }
2328
- function removeFieldFromPath(field, path) {
2329
- const fieldPath = path;
2330
- const fieldAtPath = fieldsByPath.value[fieldPath];
2331
- if (!fieldAtPath) {
2332
- return;
2333
- }
2334
- // same field at path
2335
- if (!isFieldGroup(fieldAtPath) && field.id === fieldAtPath.id) {
2336
- delete fieldsByPath.value[fieldPath];
2337
- return;
2338
- }
2339
- if (isFieldGroup(fieldAtPath)) {
2340
- const idx = fieldAtPath.findIndex(f => f.id === field.id);
2341
- if (idx === -1) {
2342
- return;
2343
- }
2344
- fieldAtPath.splice(idx, 1);
2345
- if (!fieldAtPath.length) {
2346
- delete fieldsByPath.value[fieldPath];
2347
- }
2348
- }
2349
- }
2350
- function registerField(field) {
2351
- const fieldPath = unref(field.name);
2352
- insertFieldAtPath(field, fieldPath);
2353
- if (isRef(field.name)) {
2354
- // ensures when a field's name was already taken that it preserves its same value
2355
- // necessary for fields generated by loops
2356
- watch(field.name, async (newPath, oldPath) => {
2357
- // cache the value
2358
- await nextTick();
2359
- removeFieldFromPath(field, oldPath);
2360
- insertFieldAtPath(field, newPath);
2361
- // re-validate if either path had errors before
2362
- if (errors.value[oldPath] || errors.value[newPath]) {
2363
- // clear up both paths errors
2364
- setFieldError(oldPath, undefined);
2365
- validateField(newPath);
2366
- }
2367
- // clean up the old path if no other field is sharing that name
2368
- // #3325
2369
- await nextTick();
2370
- if (!fieldExists(oldPath)) {
2371
- unsetPath(formValues, oldPath);
2372
- }
2373
- });
2374
- }
2375
- // if field already had errors (initial errors) that's not user-set, validate it again to ensure state is correct
2376
- // the difference being that `initialErrors` will contain the error message while other errors (pre-validated schema) won't have them as initial errors
2377
- // #3342
2378
- const initialErrorMessage = unref(field.errorMessage);
2379
- if (initialErrorMessage && (initialErrors === null || initialErrors === void 0 ? void 0 : initialErrors[fieldPath]) !== initialErrorMessage) {
2380
- validateField(fieldPath);
2381
- }
2382
- // marks the initial error as "consumed" so it won't be matched later with same non-initial error
2383
- delete initialErrors[fieldPath];
2384
- }
2385
- function unregisterField(field) {
2386
- const fieldName = unref(field.name);
2387
- const fieldInstance = fieldsByPath.value[fieldName];
2388
- const isGroup = !!fieldInstance && isFieldGroup(fieldInstance);
2389
- removeFieldFromPath(field, fieldName);
2390
- // clears a field error on unmounted
2391
- // we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes
2468
+ setErrors((resetState === null || resetState === void 0 ? void 0 : resetState.errors) || {});
2469
+ submitCount.value = (resetState === null || resetState === void 0 ? void 0 : resetState.submitCount) || 0;
2392
2470
  nextTick(() => {
2393
- var _a;
2394
- const shouldKeepValue = (_a = unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : unref(keepValuesOnUnmount);
2395
- const currentGroupValue = getFromPath(formValues, fieldName);
2396
- // The boolean here is we check if the field still belongs to the same control group with that name
2397
- // if another group claimed the name, we should avoid handling it since it is no longer the same group
2398
- // this happens with `v-for` over some checkboxes and field arrays.
2399
- // also if the group no longer exist we can assume this group was the last one that controlled it
2400
- const isSameGroup = isGroup && (fieldInstance === fieldsByPath.value[fieldName] || !fieldsByPath.value[fieldName]);
2401
- // group field that still has a dangling value, the field may exist or not after it was removed.
2402
- // This used to be handled in the useField composable but the form has better context on when it should/not happen.
2403
- // if it does belong to it that means the group still exists
2404
- // #3844
2405
- if (isSameGroup && !shouldKeepValue) {
2406
- if (Array.isArray(currentGroupValue)) {
2407
- const valueIdx = currentGroupValue.findIndex(i => isEqual(i, unref(field.checkedValue)));
2408
- if (valueIdx > -1) {
2409
- const newVal = [...currentGroupValue];
2410
- newVal.splice(valueIdx, 1);
2411
- setFieldValue(fieldName, newVal, { force: true });
2412
- }
2413
- }
2414
- else if (currentGroupValue === unref(field.checkedValue)) {
2415
- // Remove field if it is a group but does not have an array value, like for radio inputs #3963
2416
- unsetPath(formValues, fieldName);
2417
- }
2418
- }
2419
- // Field was removed entirely, we should unset its path
2420
- // #3384
2421
- if (!fieldExists(fieldName)) {
2422
- setFieldError(fieldName, undefined);
2423
- // Checks if the field was configured to be unset during unmount or not
2424
- // Checks both the form-level config and field-level one
2425
- // Field has the priority if it is set, otherwise it goes to the form settings
2426
- if (shouldKeepValue) {
2427
- return;
2428
- }
2429
- // Don't apply emptyContainer check unless the current group value is an array
2430
- if (isGroup && Array.isArray(currentGroupValue) && !isEmptyContainer(currentGroupValue)) {
2431
- return;
2432
- }
2433
- unsetPath(formValues, fieldName);
2434
- }
2471
+ validate({ mode: 'silent' });
2435
2472
  });
2436
2473
  }
2437
2474
  async function validate(opts) {
2438
2475
  const mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'force';
2439
2476
  if (mode === 'force') {
2440
- mutateAllFields(f => (f.meta.validated = true));
2477
+ mutateAllPathState(f => (f.validated = true));
2441
2478
  }
2442
2479
  if (formCtx.validateSchema) {
2443
2480
  return formCtx.validateSchema(mode);
2444
2481
  }
2445
2482
  // No schema, each field is responsible to validate itself
2446
- const validations = await Promise.all(Object.values(fieldsByPath.value).map(field => {
2447
- const fieldInstance = Array.isArray(field) ? field[0] : field;
2448
- if (!fieldInstance) {
2449
- return Promise.resolve({ key: '', valid: true, errors: [] });
2483
+ const validations = await Promise.all(pathStates.value.map(state => {
2484
+ if (!state.validate) {
2485
+ return Promise.resolve({
2486
+ key: state.path,
2487
+ valid: true,
2488
+ errors: [],
2489
+ });
2450
2490
  }
2451
- return fieldInstance.validate(opts).then((result) => {
2491
+ return state.validate(opts).then((result) => {
2452
2492
  return {
2453
- key: unref(fieldInstance.name),
2493
+ key: state.path,
2454
2494
  valid: result.valid,
2455
2495
  errors: result.errors,
2456
2496
  };
@@ -2473,16 +2513,22 @@ function useForm(opts) {
2473
2513
  errors,
2474
2514
  };
2475
2515
  }
2476
- async function validateField(field) {
2477
- const fieldInstance = fieldsByPath.value[field];
2478
- if (!fieldInstance) {
2479
- warn$1(`field with name ${field} was not found`);
2480
- return Promise.resolve({ errors: [], valid: true });
2516
+ async function validateField(path) {
2517
+ const state = findPathState(path);
2518
+ if (state) {
2519
+ state.validated = true;
2520
+ }
2521
+ if (schema) {
2522
+ const { results } = await validateSchema('validated-only');
2523
+ return results[path] || { errors: [], valid: true };
2524
+ }
2525
+ if (state === null || state === void 0 ? void 0 : state.validate) {
2526
+ return state.validate();
2481
2527
  }
2482
- if (Array.isArray(fieldInstance)) {
2483
- return fieldInstance.map(f => f.validate())[0];
2528
+ if (!state) {
2529
+ warn$1(`field with path ${path} was not found`);
2484
2530
  }
2485
- return fieldInstance.validate();
2531
+ return Promise.resolve({ errors: [], valid: true });
2486
2532
  }
2487
2533
  function unsetInitialValue(path) {
2488
2534
  unsetPath(initialValues.value, path);
@@ -2491,8 +2537,8 @@ function useForm(opts) {
2491
2537
  * Sneaky function to set initial field values
2492
2538
  */
2493
2539
  function stageInitialValue(path, value, updateOriginal = false) {
2494
- setInPath(formValues, path, value);
2495
2540
  setFieldInitialValue(path, value);
2541
+ setInPath(formValues, path, value);
2496
2542
  if (updateOriginal && !(opts === null || opts === void 0 ? void 0 : opts.initialValues)) {
2497
2543
  setInPath(originalInitialValues.value, path, klona(value));
2498
2544
  }
@@ -2551,12 +2597,95 @@ function useForm(opts) {
2551
2597
  deep: true,
2552
2598
  });
2553
2599
  }
2554
- return Object.assign(Object.assign({}, formCtx), { handleReset: () => resetForm(), submitForm });
2600
+ function defineComponentBinds(path, config) {
2601
+ const pathState = findPathState(unravel(path)) || createPathState(path);
2602
+ const evalConfig = () => (isCallable(config) ? config(omit(pathState, PRIVATE_PATH_STATE_KEYS)) : config || {});
2603
+ function onBlur() {
2604
+ var _a;
2605
+ pathState.touched = true;
2606
+ const validateOnBlur = (_a = evalConfig().validateOnBlur) !== null && _a !== void 0 ? _a : getConfig().validateOnBlur;
2607
+ if (validateOnBlur) {
2608
+ validateField(pathState.path);
2609
+ }
2610
+ }
2611
+ function onUpdateModelValue(value) {
2612
+ var _a;
2613
+ setFieldValue(pathState.path, value);
2614
+ const validateOnModelUpdate = (_a = evalConfig().validateOnModelUpdate) !== null && _a !== void 0 ? _a : getConfig().validateOnModelUpdate;
2615
+ if (validateOnModelUpdate) {
2616
+ validateField(pathState.path);
2617
+ }
2618
+ }
2619
+ const props = computed(() => {
2620
+ const base = {
2621
+ modelValue: pathState.value,
2622
+ 'onUpdate:modelValue': onUpdateModelValue,
2623
+ onBlur,
2624
+ };
2625
+ if (isCallable(config)) {
2626
+ return Object.assign(Object.assign({}, base), (config(pathState).props || {}));
2627
+ }
2628
+ if (config === null || config === void 0 ? void 0 : config.mapProps) {
2629
+ return Object.assign(Object.assign({}, base), config.mapProps(omit(pathState, PRIVATE_PATH_STATE_KEYS)));
2630
+ }
2631
+ return base;
2632
+ });
2633
+ return props;
2634
+ }
2635
+ function defineInputBinds(path, config) {
2636
+ const pathState = (findPathState(unravel(path)) || createPathState(path));
2637
+ const evalConfig = () => (isCallable(config) ? config(omit(pathState, PRIVATE_PATH_STATE_KEYS)) : config || {});
2638
+ function onBlur() {
2639
+ var _a;
2640
+ pathState.touched = true;
2641
+ const validateOnBlur = (_a = evalConfig().validateOnBlur) !== null && _a !== void 0 ? _a : getConfig().validateOnBlur;
2642
+ if (validateOnBlur) {
2643
+ validateField(pathState.path);
2644
+ }
2645
+ }
2646
+ function onInput(e) {
2647
+ var _a;
2648
+ const value = normalizeEventValue(e);
2649
+ setFieldValue(pathState.path, value);
2650
+ const validateOnInput = (_a = evalConfig().validateOnInput) !== null && _a !== void 0 ? _a : getConfig().validateOnInput;
2651
+ if (validateOnInput) {
2652
+ validateField(pathState.path);
2653
+ }
2654
+ }
2655
+ function onChange(e) {
2656
+ var _a;
2657
+ const value = normalizeEventValue(e);
2658
+ setFieldValue(pathState.path, value);
2659
+ const validateOnChange = (_a = evalConfig().validateOnChange) !== null && _a !== void 0 ? _a : getConfig().validateOnChange;
2660
+ if (validateOnChange) {
2661
+ validateField(pathState.path);
2662
+ }
2663
+ }
2664
+ const props = computed(() => {
2665
+ const base = {
2666
+ value: pathState.value,
2667
+ onChange,
2668
+ onInput,
2669
+ onBlur,
2670
+ };
2671
+ if (isCallable(config)) {
2672
+ return Object.assign(Object.assign({}, base), (config(omit(pathState, PRIVATE_PATH_STATE_KEYS)).attrs || {}));
2673
+ }
2674
+ if (config === null || config === void 0 ? void 0 : config.mapAttrs) {
2675
+ return Object.assign(Object.assign({}, base), config.mapAttrs(omit(pathState, PRIVATE_PATH_STATE_KEYS)));
2676
+ }
2677
+ return base;
2678
+ });
2679
+ return props;
2680
+ }
2681
+ return Object.assign(Object.assign({}, formCtx), { handleReset: () => resetForm(), submitForm,
2682
+ defineComponentBinds,
2683
+ defineInputBinds });
2555
2684
  }
2556
2685
  /**
2557
2686
  * Manages form meta aggregation
2558
2687
  */
2559
- function useFormMeta(fieldsByPath, currentValues, initialValues, errors) {
2688
+ function useFormMeta(pathsState, currentValues, initialValues, errors) {
2560
2689
  const MERGE_STRATEGIES = {
2561
2690
  touched: 'some',
2562
2691
  pending: 'some',
@@ -2566,10 +2695,10 @@ function useFormMeta(fieldsByPath, currentValues, initialValues, errors) {
2566
2695
  return !isEqual(currentValues, unref(initialValues));
2567
2696
  });
2568
2697
  function calculateFlags() {
2569
- const fields = Object.values(fieldsByPath.value).flat(1).filter(Boolean);
2698
+ const states = pathsState.value;
2570
2699
  return keysOf(MERGE_STRATEGIES).reduce((acc, flag) => {
2571
2700
  const mergeMethod = MERGE_STRATEGIES[flag];
2572
- acc[flag] = fields[mergeMethod](field => field.meta[flag]);
2701
+ acc[flag] = states[mergeMethod](s => s[flag]);
2573
2702
  return acc;
2574
2703
  }, {});
2575
2704
  }
@@ -2587,7 +2716,7 @@ function useFormMeta(fieldsByPath, currentValues, initialValues, errors) {
2587
2716
  /**
2588
2717
  * Manages the initial values prop
2589
2718
  */
2590
- function useFormInitialValues(fields, formValues, opts) {
2719
+ function useFormInitialValues(pathsState, formValues, opts) {
2591
2720
  const values = resolveInitialValues(opts);
2592
2721
  const providedValues = opts === null || opts === void 0 ? void 0 : opts.initialValues;
2593
2722
  // these are the mutable initial values as the fields are mounted/unmounted
@@ -2608,14 +2737,13 @@ function useFormInitialValues(fields, formValues, opts) {
2608
2737
  // those are excluded because it's unlikely you want to change the form values using initial values
2609
2738
  // we mostly watch them for API population or newly inserted fields
2610
2739
  // 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
2611
- keysOf(fields.value).forEach(fieldPath => {
2612
- const field = fields.value[fieldPath];
2613
- const wasTouched = Array.isArray(field) ? field.some(f => f.meta.touched) : field === null || field === void 0 ? void 0 : field.meta.touched;
2614
- if (!field || wasTouched) {
2740
+ pathsState.value.forEach(state => {
2741
+ const wasTouched = state.touched;
2742
+ if (wasTouched) {
2615
2743
  return;
2616
2744
  }
2617
- const newValue = getFromPath(initialValues.value, fieldPath);
2618
- setInPath(formValues, fieldPath, klona(newValue));
2745
+ const newValue = getFromPath(initialValues.value, state.path);
2746
+ setInPath(formValues, state.path, klona(newValue));
2619
2747
  });
2620
2748
  }
2621
2749
  if (isRef(providedValues)) {
@@ -2631,42 +2759,6 @@ function useFormInitialValues(fields, formValues, opts) {
2631
2759
  setInitialValues,
2632
2760
  };
2633
2761
  }
2634
- function useErrorBag(initialErrors) {
2635
- const errorBag = ref({});
2636
- function normalizeErrorItem(message) {
2637
- return Array.isArray(message) ? message : message ? [message] : [];
2638
- }
2639
- /**
2640
- * Manually sets an error message on a specific field
2641
- */
2642
- function setFieldErrorBag(field, message) {
2643
- if (!message) {
2644
- delete errorBag.value[field];
2645
- return;
2646
- }
2647
- errorBag.value[field] = normalizeErrorItem(message);
2648
- }
2649
- /**
2650
- * Sets errors for the fields specified in the object
2651
- */
2652
- function setErrorBag(fields) {
2653
- errorBag.value = keysOf(fields).reduce((acc, key) => {
2654
- const message = fields[key];
2655
- if (message) {
2656
- acc[key] = normalizeErrorItem(message);
2657
- }
2658
- return acc;
2659
- }, {});
2660
- }
2661
- if (initialErrors) {
2662
- setErrorBag(initialErrors);
2663
- }
2664
- return {
2665
- errorBag,
2666
- setErrorBag,
2667
- setFieldErrorBag,
2668
- };
2669
- }
2670
2762
 
2671
2763
  const FormImpl = defineComponent({
2672
2764
  name: 'Form',
@@ -2847,7 +2939,10 @@ function useFieldArray(arrayPath) {
2847
2939
  }
2848
2940
  function initFields() {
2849
2941
  const currentValues = getCurrentValues();
2850
- fields.value = currentValues.map(createEntry);
2942
+ if (!Array.isArray(currentValues)) {
2943
+ return;
2944
+ }
2945
+ fields.value = currentValues.map((v, idx) => createEntry(v, idx, fields.value));
2851
2946
  updateEntryFlags();
2852
2947
  }
2853
2948
  initFields();
@@ -2859,7 +2954,13 @@ function useFieldArray(arrayPath) {
2859
2954
  entry.isLast = i === fieldsLength - 1;
2860
2955
  }
2861
2956
  }
2862
- function createEntry(value) {
2957
+ function createEntry(value, idx, currentFields) {
2958
+ // Skips the work by returning the current entry if it already exists
2959
+ // This should make the `key` prop stable and doesn't cause more re-renders than needed
2960
+ // The value is computed and should update anyways
2961
+ if (currentFields && !isNullOrUndefined(idx) && currentFields[idx]) {
2962
+ return currentFields[idx];
2963
+ }
2863
2964
  const key = entryCounter++;
2864
2965
  const entry = {
2865
2966
  key,
@@ -2896,8 +2997,9 @@ function useFieldArray(arrayPath) {
2896
2997
  }
2897
2998
  const newValue = [...pathValue];
2898
2999
  newValue.splice(idx, 1);
2899
- form === null || form === void 0 ? void 0 : form.unsetInitialValue(pathName + `[${idx}]`);
2900
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3000
+ const fieldPath = pathName + `[${idx}]`;
3001
+ form.unsetInitialValue(fieldPath);
3002
+ setInPath(form.values, pathName, newValue);
2901
3003
  fields.value.splice(idx, 1);
2902
3004
  afterMutation();
2903
3005
  }
@@ -2910,8 +3012,8 @@ function useFieldArray(arrayPath) {
2910
3012
  }
2911
3013
  const newValue = [...normalizedPathValue];
2912
3014
  newValue.push(value);
2913
- form === null || form === void 0 ? void 0 : form.stageInitialValue(pathName + `[${newValue.length - 1}]`, value);
2914
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3015
+ form.stageInitialValue(pathName + `[${newValue.length - 1}]`, value);
3016
+ setInPath(form.values, pathName, newValue);
2915
3017
  fields.value.push(createEntry(value));
2916
3018
  afterMutation();
2917
3019
  }
@@ -2930,7 +3032,7 @@ function useFieldArray(arrayPath) {
2930
3032
  const tempEntry = newFields[indexA];
2931
3033
  newFields[indexA] = newFields[indexB];
2932
3034
  newFields[indexB] = tempEntry;
2933
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3035
+ setInPath(form.values, pathName, newValue);
2934
3036
  fields.value = newFields;
2935
3037
  updateEntryFlags();
2936
3038
  }
@@ -2944,13 +3046,14 @@ function useFieldArray(arrayPath) {
2944
3046
  const newFields = [...fields.value];
2945
3047
  newValue.splice(idx, 0, value);
2946
3048
  newFields.splice(idx, 0, createEntry(value));
2947
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3049
+ setInPath(form.values, pathName, newValue);
2948
3050
  fields.value = newFields;
2949
3051
  afterMutation();
2950
3052
  }
2951
3053
  function replace(arr) {
2952
3054
  const pathName = unref(arrayPath);
2953
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, arr);
3055
+ form.stageInitialValue(pathName, arr);
3056
+ setInPath(form.values, pathName, arr);
2954
3057
  initFields();
2955
3058
  afterMutation();
2956
3059
  }
@@ -2960,7 +3063,7 @@ function useFieldArray(arrayPath) {
2960
3063
  if (!Array.isArray(pathValue) || pathValue.length - 1 < idx) {
2961
3064
  return;
2962
3065
  }
2963
- form === null || form === void 0 ? void 0 : form.setFieldValue(`${pathName}[${idx}]`, value);
3066
+ setInPath(form.values, `${pathName}[${idx}]`, value);
2964
3067
  form === null || form === void 0 ? void 0 : form.validate({ mode: 'validated-only' });
2965
3068
  }
2966
3069
  function prepend(value) {
@@ -2971,8 +3074,8 @@ function useFieldArray(arrayPath) {
2971
3074
  return;
2972
3075
  }
2973
3076
  const newValue = [value, ...normalizedPathValue];
2974
- form === null || form === void 0 ? void 0 : form.stageInitialValue(pathName + `[${newValue.length - 1}]`, value);
2975
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3077
+ form.stageInitialValue(pathName + `[${newValue.length - 1}]`, value);
3078
+ setInPath(form.values, pathName, newValue);
2976
3079
  fields.value.unshift(createEntry(value));
2977
3080
  afterMutation();
2978
3081
  }
@@ -2990,7 +3093,7 @@ function useFieldArray(arrayPath) {
2990
3093
  const movedValue = newValue[oldIdx];
2991
3094
  newValue.splice(oldIdx, 1);
2992
3095
  newValue.splice(newIdx, 0, movedValue);
2993
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3096
+ setInPath(form.values, pathName, newValue);
2994
3097
  fields.value = newFields;
2995
3098
  afterMutation();
2996
3099
  }
@@ -3129,17 +3232,13 @@ function useResetForm() {
3129
3232
  * If a field is dirty or not
3130
3233
  */
3131
3234
  function useIsFieldDirty(path) {
3132
- const form = injectWithSelf(FormContextKey);
3133
- let field = path ? undefined : inject(FieldContextKey);
3235
+ const fieldOrPath = resolveFieldOrPathState(path);
3134
3236
  return computed(() => {
3135
- if (path) {
3136
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
3137
- }
3138
- if (!field) {
3139
- warn(`field with name ${unref(path)} was not found`);
3237
+ var _a, _b;
3238
+ if (!fieldOrPath) {
3140
3239
  return false;
3141
3240
  }
3142
- return field.meta.dirty;
3241
+ return (_b = ('meta' in fieldOrPath ? fieldOrPath.meta.dirty : (_a = fieldOrPath === null || fieldOrPath === void 0 ? void 0 : fieldOrPath.value) === null || _a === void 0 ? void 0 : _a.dirty)) !== null && _b !== void 0 ? _b : false;
3143
3242
  });
3144
3243
  }
3145
3244
 
@@ -3147,17 +3246,13 @@ function useIsFieldDirty(path) {
3147
3246
  * If a field is touched or not
3148
3247
  */
3149
3248
  function useIsFieldTouched(path) {
3150
- const form = injectWithSelf(FormContextKey);
3151
- let field = path ? undefined : inject(FieldContextKey);
3249
+ const fieldOrPath = resolveFieldOrPathState(path);
3152
3250
  return computed(() => {
3153
- if (path) {
3154
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
3155
- }
3156
- if (!field) {
3157
- warn(`field with name ${unref(path)} was not found`);
3251
+ var _a, _b;
3252
+ if (!fieldOrPath) {
3158
3253
  return false;
3159
3254
  }
3160
- return field.meta.touched;
3255
+ return (_b = ('meta' in fieldOrPath ? fieldOrPath.meta.touched : (_a = fieldOrPath === null || fieldOrPath === void 0 ? void 0 : fieldOrPath.value) === null || _a === void 0 ? void 0 : _a.touched)) !== null && _b !== void 0 ? _b : false;
3161
3256
  });
3162
3257
  }
3163
3258
 
@@ -3165,17 +3260,13 @@ function useIsFieldTouched(path) {
3165
3260
  * If a field is validated and is valid
3166
3261
  */
3167
3262
  function useIsFieldValid(path) {
3168
- const form = injectWithSelf(FormContextKey);
3169
- let field = path ? undefined : inject(FieldContextKey);
3263
+ const fieldOrPath = resolveFieldOrPathState(path);
3170
3264
  return computed(() => {
3171
- if (path) {
3172
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
3173
- }
3174
- if (!field) {
3175
- warn(`field with name ${unref(path)} was not found`);
3265
+ var _a, _b;
3266
+ if (!fieldOrPath) {
3176
3267
  return false;
3177
3268
  }
3178
- return field.meta.valid;
3269
+ return (_b = ('meta' in fieldOrPath ? fieldOrPath.meta.valid : (_a = fieldOrPath === null || fieldOrPath === void 0 ? void 0 : fieldOrPath.value) === null || _a === void 0 ? void 0 : _a.valid)) !== null && _b !== void 0 ? _b : false;
3179
3270
  });
3180
3271
  }
3181
3272
 
@@ -3198,19 +3289,19 @@ function useIsSubmitting() {
3198
3289
  */
3199
3290
  function useValidateField(path) {
3200
3291
  const form = injectWithSelf(FormContextKey);
3201
- let field = path ? undefined : inject(FieldContextKey);
3292
+ const field = path ? undefined : inject(FieldContextKey);
3202
3293
  return function validateField() {
3203
- if (path) {
3204
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
3294
+ if (field) {
3295
+ return field.validate();
3205
3296
  }
3206
- if (!field) {
3207
- warn(`field with name ${unref(path)} was not found`);
3208
- return Promise.resolve({
3209
- errors: [],
3210
- valid: true,
3211
- });
3297
+ if (form && path) {
3298
+ return form === null || form === void 0 ? void 0 : form.validateField(unref(path));
3212
3299
  }
3213
- return field.validate();
3300
+ warn(`field with name ${unref(path)} was not found`);
3301
+ return Promise.resolve({
3302
+ errors: [],
3303
+ valid: true,
3304
+ });
3214
3305
  };
3215
3306
  }
3216
3307