vee-validate 4.8.6 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  /**
2
- * vee-validate v4.8.6
2
+ * vee-validate v4.9.0
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,38 @@ 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 (value === oldValue && isEqual(value, oldValue)) {
1632
+ 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`.');
1633
+ }
1634
+ }, { deep: true });
1635
+ }
1572
1636
  const field = {
1573
1637
  id,
1574
1638
  name,
1575
1639
  label,
1576
- value,
1640
+ value: valueProxy,
1577
1641
  meta,
1578
1642
  errors,
1579
1643
  errorMessage,
@@ -1617,14 +1681,9 @@ function _useField(path, rules, opts) {
1617
1681
  return field;
1618
1682
  }
1619
1683
  // associate the field with the given form
1620
- form.register(field);
1621
- onBeforeUnmount(() => {
1622
- markedForRemoval = true;
1623
- form.unregister(field);
1624
- });
1625
1684
  // extract cross-field dependencies in a computed prop
1626
1685
  const dependencies = computed(() => {
1627
- const rulesVal = normalizedRules.value;
1686
+ const rulesVal = validator.value;
1628
1687
  // is falsy, a function schema or a yup schema
1629
1688
  if (!rulesVal ||
1630
1689
  isCallable(rulesVal) ||
@@ -1658,6 +1717,37 @@ function _useField(path, rules, opts) {
1658
1717
  meta.validated ? validateWithStateMutation() : validateValidStateOnly();
1659
1718
  }
1660
1719
  });
1720
+ onBeforeUnmount(() => {
1721
+ var _a;
1722
+ PENDING_UNMOUNT = true;
1723
+ const shouldKeepValue = (_a = unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : unref(form.keepValuesOnUnmount);
1724
+ if (shouldKeepValue || !form) {
1725
+ return;
1726
+ }
1727
+ const path = unravel(name);
1728
+ const pathState = form.getPathState(path);
1729
+ const matchesId = Array.isArray(pathState === null || pathState === void 0 ? void 0 : pathState.id) && (pathState === null || pathState === void 0 ? void 0 : pathState.multiple)
1730
+ ? pathState === null || pathState === void 0 ? void 0 : pathState.id.includes(field.id)
1731
+ : (pathState === null || pathState === void 0 ? void 0 : pathState.id) === field.id;
1732
+ if (!matchesId) {
1733
+ return;
1734
+ }
1735
+ if ((pathState === null || pathState === void 0 ? void 0 : pathState.multiple) && Array.isArray(pathState.value)) {
1736
+ const valueIdx = pathState.value.findIndex(i => isEqual(i, unref(field.checkedValue)));
1737
+ if (valueIdx > -1) {
1738
+ const newVal = [...pathState.value];
1739
+ newVal.splice(valueIdx, 1);
1740
+ form.setFieldValue(path, newVal);
1741
+ }
1742
+ if (Array.isArray(pathState.id)) {
1743
+ pathState.id.splice(pathState.id.indexOf(field.id), 1);
1744
+ }
1745
+ }
1746
+ else {
1747
+ form.unsetPathValue(path);
1748
+ }
1749
+ form.removePathState(path);
1750
+ });
1661
1751
  return field;
1662
1752
  }
1663
1753
  /**
@@ -1688,22 +1778,11 @@ function normalizeOptions(opts) {
1688
1778
  const controlled = 'standalone' in opts ? !opts.standalone : opts.controlled;
1689
1779
  return Object.assign(Object.assign(Object.assign({}, defaults()), (opts || {})), { initialValue, controlled: controlled !== null && controlled !== void 0 ? controlled : true, checkedValue });
1690
1780
  }
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) {
1781
+ function useFieldWithChecked(name, rules, opts) {
1703
1782
  const form = !(opts === null || opts === void 0 ? void 0 : opts.standalone) ? injectWithSelf(FormContextKey) : undefined;
1704
1783
  const checkedValue = opts === null || opts === void 0 ? void 0 : opts.checkedValue;
1705
1784
  const uncheckedValue = opts === null || opts === void 0 ? void 0 : opts.uncheckedValue;
1706
- function patchCheckboxApi(field) {
1785
+ function patchCheckedApi(field) {
1707
1786
  const handleChange = field.handleChange;
1708
1787
  const checked = computed(() => {
1709
1788
  const currentValue = unref(field.value);
@@ -1720,9 +1799,15 @@ function useCheckboxField(name, rules, opts) {
1720
1799
  }
1721
1800
  return;
1722
1801
  }
1723
- let newValue = normalizeEventValue(e);
1724
- // Single checkbox field without a form to toggle it's value
1725
- if (!form) {
1802
+ const path = unravel(name);
1803
+ const pathState = form === null || form === void 0 ? void 0 : form.getPathState(path);
1804
+ const value = normalizeEventValue(e);
1805
+ let newValue;
1806
+ if (form && (pathState === null || pathState === void 0 ? void 0 : pathState.multiple) && pathState.type === 'checkbox') {
1807
+ newValue = resolveNextCheckboxValue(getFromPath(form.values, path) || [], value, undefined);
1808
+ }
1809
+ else {
1810
+ // Single checkbox field without a form to toggle it's value
1726
1811
  newValue = resolveNextCheckboxValue(unref(field.value), unref(checkedValue), unref(uncheckedValue));
1727
1812
  }
1728
1813
  handleChange(newValue, shouldValidate);
@@ -1731,7 +1816,7 @@ function useCheckboxField(name, rules, opts) {
1731
1816
  checkedValue,
1732
1817
  uncheckedValue, handleChange: handleCheckboxChange });
1733
1818
  }
1734
- return patchCheckboxApi(_useField(name, rules, opts));
1819
+ return patchCheckedApi(_useField(name, rules, opts));
1735
1820
  }
1736
1821
  function useVModel({ prop, value, handleChange }) {
1737
1822
  const vm = getCurrentInstance();
@@ -1957,6 +2042,7 @@ function resolveInitialValue(props, ctx) {
1957
2042
  const Field = FieldImpl;
1958
2043
 
1959
2044
  let FORM_COUNTER = 0;
2045
+ const PRIVATE_PATH_STATE_KEYS = ['bails', 'fieldsCount', 'id', 'multiple', 'type', 'validate'];
1960
2046
  function resolveInitialValues(opts) {
1961
2047
  const providedValues = unref(opts === null || opts === void 0 ? void 0 : opts.initialValues) || {};
1962
2048
  const schema = unref(opts === null || opts === void 0 ? void 0 : opts.validationSchema);
@@ -1968,12 +2054,8 @@ function resolveInitialValues(opts) {
1968
2054
  function useForm(opts) {
1969
2055
  var _a;
1970
2056
  const formId = FORM_COUNTER++;
1971
- const controlledModelPaths = new Set();
1972
2057
  // 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({});
2058
+ let FIELD_ID_COUNTER = 0;
1977
2059
  // If the form is currently submitting
1978
2060
  const isSubmitting = ref(false);
1979
2061
  // The number of times the user tried to submit the form
@@ -1982,44 +2064,62 @@ function useForm(opts) {
1982
2064
  const fieldArrays = [];
1983
2065
  // a private ref for all form values
1984
2066
  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);
2067
+ const pathStates = ref([]);
2068
+ const extraErrorsBag = ref({});
2069
+ /**
2070
+ * Manually sets an error message on a specific field
2071
+ */
2072
+ function setFieldError(field, message) {
2073
+ const state = findPathState(field);
2074
+ if (!state) {
2075
+ extraErrorsBag.value[field] = normalizeErrorItem(message);
2076
+ return;
2077
+ }
2078
+ state.errors = normalizeErrorItem(message);
2079
+ }
2080
+ /**
2081
+ * Sets errors for the fields specified in the object
2082
+ */
2083
+ function setErrors(paths) {
2084
+ keysOf(paths).forEach(path => {
2085
+ setFieldError(path, paths[path]);
2086
+ });
2087
+ }
2088
+ if (opts === null || opts === void 0 ? void 0 : opts.initialErrors) {
2089
+ setErrors(opts.initialErrors);
2090
+ }
2091
+ const errorBag = computed(() => {
2092
+ const pathErrors = pathStates.value.reduce((acc, state) => {
2093
+ if (state.errors.length) {
2094
+ acc[state.path] = state.errors;
2095
+ }
2096
+ return acc;
2097
+ }, {});
2098
+ return Object.assign(Object.assign({}, extraErrorsBag.value), pathErrors);
2099
+ });
1987
2100
  // Gets the first error of each field
1988
2101
  const errors = computed(() => {
1989
2102
  return keysOf(errorBag.value).reduce((acc, key) => {
1990
- const bag = errorBag.value[key];
1991
- if (bag && bag.length) {
1992
- acc[key] = bag[0];
2103
+ const errors = errorBag.value[key];
2104
+ if (errors === null || errors === void 0 ? void 0 : errors.length) {
2105
+ acc[key] = errors[0];
1993
2106
  }
1994
2107
  return acc;
1995
2108
  }, {});
1996
2109
  });
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
2110
  /**
2005
2111
  * Holds a computed reference to all fields names and labels
2006
2112
  */
2007
2113
  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
- }
2114
+ return pathStates.value.reduce((names, state) => {
2115
+ names[state.path] = { name: state.path || '', label: state.label || '' };
2013
2116
  return names;
2014
2117
  }, {});
2015
2118
  });
2016
2119
  const fieldBailsMap = computed(() => {
2017
- return keysOf(fieldsByPath.value).reduce((map, path) => {
2120
+ return pathStates.value.reduce((map, state) => {
2018
2121
  var _a;
2019
- const field = getFirstFieldAtPath(path);
2020
- if (field) {
2021
- map[path] = (_a = field.bails) !== null && _a !== void 0 ? _a : true;
2022
- }
2122
+ map[state.path] = (_a = state.bails) !== null && _a !== void 0 ? _a : true;
2023
2123
  return map;
2024
2124
  }, {});
2025
2125
  });
@@ -2028,17 +2128,74 @@ function useForm(opts) {
2028
2128
  const initialErrors = Object.assign({}, ((opts === null || opts === void 0 ? void 0 : opts.initialErrors) || {}));
2029
2129
  const keepValuesOnUnmount = (_a = opts === null || opts === void 0 ? void 0 : opts.keepValuesOnUnmount) !== null && _a !== void 0 ? _a : false;
2030
2130
  // initial form values
2031
- const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts);
2131
+ const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(pathStates, formValues, opts);
2032
2132
  // form meta aggregations
2033
- const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);
2133
+ const meta = useFormMeta(pathStates, formValues, originalInitialValues, errors);
2034
2134
  const controlledValues = computed(() => {
2035
- return [...controlledModelPaths, ...keysOf(fieldsByPath.value)].reduce((acc, path) => {
2036
- const value = getFromPath(formValues, path);
2037
- setInPath(acc, path, value);
2135
+ return pathStates.value.reduce((acc, state) => {
2136
+ const value = getFromPath(formValues, state.path);
2137
+ setInPath(acc, state.path, value);
2038
2138
  return acc;
2039
2139
  }, {});
2040
2140
  });
2041
2141
  const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
2142
+ function createPathState(path, config) {
2143
+ var _a, _b;
2144
+ const initialValue = computed(() => getFromPath(initialValues.value, unravel(path)));
2145
+ const pathStateExists = pathStates.value.find(state => state.path === unref(path));
2146
+ if (pathStateExists) {
2147
+ if ((config === null || config === void 0 ? void 0 : config.type) === 'checkbox' || (config === null || config === void 0 ? void 0 : config.type) === 'radio') {
2148
+ pathStateExists.multiple = true;
2149
+ }
2150
+ if (Array.isArray(pathStateExists.id)) {
2151
+ pathStateExists.id.push(FIELD_ID_COUNTER++);
2152
+ }
2153
+ else {
2154
+ pathStateExists.id = [pathStateExists.id, FIELD_ID_COUNTER++];
2155
+ }
2156
+ pathStateExists.fieldsCount++;
2157
+ return pathStateExists;
2158
+ }
2159
+ const currentValue = computed(() => getFromPath(formValues, unravel(path)));
2160
+ const pathValue = unravel(path);
2161
+ const state = reactive({
2162
+ id: FIELD_ID_COUNTER++,
2163
+ path,
2164
+ touched: false,
2165
+ pending: false,
2166
+ valid: true,
2167
+ validated: !!((_a = initialErrors[pathValue]) === null || _a === void 0 ? void 0 : _a.length),
2168
+ initialValue,
2169
+ errors: shallowRef([]),
2170
+ bails: (_b = config === null || config === void 0 ? void 0 : config.bails) !== null && _b !== void 0 ? _b : false,
2171
+ label: config === null || config === void 0 ? void 0 : config.label,
2172
+ type: (config === null || config === void 0 ? void 0 : config.type) || 'default',
2173
+ value: currentValue,
2174
+ multiple: false,
2175
+ fieldsCount: 1,
2176
+ validate: config === null || config === void 0 ? void 0 : config.validate,
2177
+ dirty: computed(() => {
2178
+ return !isEqual(unref(currentValue), unref(initialValue));
2179
+ }),
2180
+ });
2181
+ pathStates.value.push(state);
2182
+ // if it has errors before, validate it.
2183
+ if (errors.value[pathValue] && !initialErrors[pathValue]) {
2184
+ nextTick(() => {
2185
+ validateField(pathValue);
2186
+ });
2187
+ }
2188
+ // Handles when a path changes
2189
+ if (isRef(path)) {
2190
+ watch(path, newPath => {
2191
+ const nextValue = klona(currentValue.value);
2192
+ nextTick(() => {
2193
+ setInPath(formValues, newPath, nextValue);
2194
+ });
2195
+ });
2196
+ }
2197
+ return state;
2198
+ }
2042
2199
  /**
2043
2200
  * Batches validation runs in 5ms batches
2044
2201
  * Must have two distinct batch queues to make sure they don't override each other settings #3783
@@ -2049,17 +2206,17 @@ function useForm(opts) {
2049
2206
  return (await mode) === 'silent' ? debouncedSilentValidation() : debouncedValidation();
2050
2207
  }, (formResult, [mode]) => {
2051
2208
  // fields by id lookup
2052
- const fieldsById = formCtx.fieldsByPath.value || {};
2053
2209
  // errors fields names, we need it to also check if custom errors are updated
2054
2210
  const currentErrorsPaths = keysOf(formCtx.errorBag.value);
2055
2211
  // collect all the keys from the schema and all fields
2056
- // this ensures we have a complete keymap of all the fields
2212
+ // this ensures we have a complete key map of all the fields
2057
2213
  const paths = [
2058
- ...new Set([...keysOf(formResult.results), ...keysOf(fieldsById), ...currentErrorsPaths]),
2214
+ ...new Set([...keysOf(formResult.results), ...pathStates.value.map(p => p.path), ...currentErrorsPaths]),
2059
2215
  ];
2060
2216
  // 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];
2217
+ return paths.reduce((validation, _path) => {
2218
+ const path = _path;
2219
+ const pathState = findPathState(path);
2063
2220
  const messages = (formResult.results[path] || { errors: [] }).errors;
2064
2221
  const fieldResult = {
2065
2222
  errors: messages,
@@ -2069,24 +2226,37 @@ function useForm(opts) {
2069
2226
  if (!fieldResult.valid) {
2070
2227
  validation.errors[path] = fieldResult.errors[0];
2071
2228
  }
2229
+ // clean up extra errors if path state exists
2230
+ if (pathState && extraErrorsBag.value[path]) {
2231
+ delete extraErrorsBag.value[path];
2232
+ }
2072
2233
  // field not rendered
2073
- if (!field) {
2234
+ if (!pathState) {
2074
2235
  setFieldError(path, messages);
2075
2236
  return validation;
2076
2237
  }
2077
2238
  // always update the valid flag regardless of the mode
2078
- applyFieldMutation(field, f => (f.meta.valid = fieldResult.valid));
2239
+ pathState.valid = fieldResult.valid;
2079
2240
  if (mode === 'silent') {
2080
2241
  return validation;
2081
2242
  }
2082
- const wasValidated = Array.isArray(field) ? field.some(f => f.meta.validated) : field.meta.validated;
2083
- if (mode === 'validated-only' && !wasValidated) {
2243
+ if (mode === 'validated-only' && !pathState.validated) {
2084
2244
  return validation;
2085
2245
  }
2086
- applyFieldMutation(field, f => f.setState({ errors: fieldResult.errors }));
2246
+ setFieldError(pathState, fieldResult.errors);
2087
2247
  return validation;
2088
2248
  }, { valid: formResult.valid, results: {}, errors: {} });
2089
2249
  });
2250
+ function mutateAllPathState(mutation) {
2251
+ pathStates.value.forEach(mutation);
2252
+ }
2253
+ function findPathState(path) {
2254
+ const pathState = typeof path === 'string' ? pathStates.value.find(state => state.path === path) : path;
2255
+ return pathState;
2256
+ }
2257
+ function unsetPathValue(path) {
2258
+ unsetPath(formValues, path);
2259
+ }
2090
2260
  function makeSubmissionFactory(onlyControlled) {
2091
2261
  return function submitHandlerFactory(fn, onValidationError) {
2092
2262
  return function submissionHandler(e) {
@@ -2095,10 +2265,7 @@ function useForm(opts) {
2095
2265
  e.stopPropagation();
2096
2266
  }
2097
2267
  // Touch all fields
2098
- setTouched(keysOf(fieldsByPath.value).reduce((acc, field) => {
2099
- acc[field] = true;
2100
- return acc;
2101
- }, {}));
2268
+ mutateAllPathState(s => (s.touched = true));
2102
2269
  isSubmitting.value = true;
2103
2270
  submitCount.value++;
2104
2271
  return validate()
@@ -2106,7 +2273,7 @@ function useForm(opts) {
2106
2273
  const values = klona(formValues);
2107
2274
  if (result.valid && typeof fn === 'function') {
2108
2275
  const controlled = klona(controlledValues.value);
2109
- let submittedValues = onlyControlled ? controlled : values;
2276
+ let submittedValues = (onlyControlled ? controlled : values);
2110
2277
  if (result.values) {
2111
2278
  submittedValues = result.values;
2112
2279
  }
@@ -2146,9 +2313,22 @@ function useForm(opts) {
2146
2313
  const handleSubmitImpl = makeSubmissionFactory(false);
2147
2314
  const handleSubmit = handleSubmitImpl;
2148
2315
  handleSubmit.withControlled = makeSubmissionFactory(true);
2316
+ function removePathState(path) {
2317
+ const idx = pathStates.value.findIndex(s => s.path === path);
2318
+ const pathState = pathStates.value[idx];
2319
+ if (idx === -1 || !pathState) {
2320
+ return;
2321
+ }
2322
+ if (pathState.multiple && pathState.fieldsCount) {
2323
+ pathState.fieldsCount--;
2324
+ }
2325
+ if (!pathState.multiple || pathState.fieldsCount <= 0) {
2326
+ pathStates.value.splice(idx, 1);
2327
+ unsetInitialValue(path);
2328
+ }
2329
+ }
2149
2330
  const formCtx = {
2150
2331
  formId,
2151
- fieldsByPath,
2152
2332
  values: formValues,
2153
2333
  controlledValues,
2154
2334
  errorBag,
@@ -2161,14 +2341,11 @@ function useForm(opts) {
2161
2341
  keepValuesOnUnmount,
2162
2342
  validateSchema: unref(schema) ? validateSchema : undefined,
2163
2343
  validate,
2164
- register: registerField,
2165
- unregister: unregisterField,
2166
- setFieldErrorBag,
2344
+ setFieldError,
2167
2345
  validateField,
2168
2346
  setFieldValue,
2169
2347
  setValues,
2170
2348
  setErrors,
2171
- setFieldError,
2172
2349
  setFieldTouched,
2173
2350
  setTouched,
2174
2351
  resetForm,
@@ -2178,102 +2355,63 @@ function useForm(opts) {
2178
2355
  unsetInitialValue,
2179
2356
  setFieldInitialValue,
2180
2357
  useFieldModel,
2358
+ createPathState,
2359
+ getPathState: findPathState,
2360
+ unsetPathValue,
2361
+ removePathState,
2362
+ initialValues: initialValues,
2363
+ getAllPathStates: () => pathStates.value,
2181
2364
  };
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
2365
  /**
2213
2366
  * Sets a single field value
2214
2367
  */
2215
- function setFieldValue(field, value, { force } = { force: false }) {
2216
- var _a;
2217
- const fieldInstance = fieldsByPath.value[field];
2368
+ function setFieldValue(field, value) {
2218
2369
  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;
2370
+ const path = typeof field === 'string' ? field : field.path;
2371
+ const pathState = findPathState(path);
2372
+ if (!pathState) {
2373
+ createPathState(path);
2223
2374
  }
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;
2229
- }
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);
2375
+ setInPath(formValues, path, clonedValue);
2236
2376
  }
2237
2377
  /**
2238
2378
  * Sets multiple fields values
2239
2379
  */
2240
2380
  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
- });
2381
+ merge(formValues, fields);
2249
2382
  // regenerate the arrays when the form values change
2250
2383
  fieldArrays.forEach(f => f && f.reset());
2251
2384
  }
2252
2385
  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,
2386
+ const pathState = findPathState(unref(path)) || createPathState(path);
2387
+ return computed({
2388
+ get() {
2389
+ return pathState.value;
2390
+ },
2391
+ set(value) {
2392
+ const pathValue = unref(path);
2393
+ setFieldValue(pathValue, value);
2394
+ pathState.validated = true;
2395
+ pathState.pending = true;
2396
+ validateField(pathValue).then(() => {
2397
+ pathState.pending = false;
2398
+ });
2399
+ },
2260
2400
  });
2261
- controlledModelPaths.add(unref(path));
2262
- return value;
2263
2401
  }
2264
- function useFieldModel(path) {
2265
- if (!Array.isArray(path)) {
2266
- return createModel(path);
2402
+ function useFieldModel(pathOrPaths) {
2403
+ if (!Array.isArray(pathOrPaths)) {
2404
+ return createModel(pathOrPaths);
2267
2405
  }
2268
- return path.map(createModel);
2406
+ return pathOrPaths.map(createModel);
2269
2407
  }
2270
2408
  /**
2271
2409
  * Sets the touched meta state on a field
2272
2410
  */
2273
2411
  function setFieldTouched(field, isTouched) {
2274
- const fieldInstance = fieldsByPath.value[field];
2275
- if (fieldInstance) {
2276
- applyFieldMutation(fieldInstance, f => f.setTouched(isTouched));
2412
+ const pathState = findPathState(field);
2413
+ if (pathState) {
2414
+ pathState.touched = isTouched;
2277
2415
  }
2278
2416
  }
2279
2417
  /**
@@ -2285,172 +2423,53 @@ function useForm(opts) {
2285
2423
  });
2286
2424
  }
2287
2425
  function resetField(field, state) {
2288
- const fieldInstance = fieldsByPath.value[field];
2289
- if (fieldInstance) {
2290
- applyFieldMutation(fieldInstance, f => f.resetField(state));
2291
- }
2426
+ var _a;
2427
+ const newValue = state && 'value' in state ? state.value : getFromPath(initialValues.value, field);
2428
+ setFieldInitialValue(field, klona(newValue));
2429
+ setFieldValue(field, newValue);
2430
+ setFieldTouched(field, (_a = state === null || state === void 0 ? void 0 : state.touched) !== null && _a !== void 0 ? _a : false);
2431
+ setFieldError(field, (state === null || state === void 0 ? void 0 : state.errors) || []);
2292
2432
  }
2293
2433
  /**
2294
2434
  * Resets all fields
2295
2435
  */
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;
2436
+ function resetForm(resetState) {
2437
+ const newValues = (resetState === null || resetState === void 0 ? void 0 : resetState.values) ? resetState.values : originalInitialValues.value;
2302
2438
  setInitialValues(newValues);
2303
2439
  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;
2440
+ mutateAllPathState(state => {
2441
+ var _a;
2442
+ state.validated = false;
2443
+ state.touched = ((_a = resetState === null || resetState === void 0 ? void 0 : resetState.touched) === null || _a === void 0 ? void 0 : _a[state.path]) || false;
2444
+ setFieldValue(state.path, getFromPath(newValues, state.path));
2445
+ setFieldError(state.path, undefined);
2311
2446
  });
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
2447
+ setErrors((resetState === null || resetState === void 0 ? void 0 : resetState.errors) || {});
2448
+ submitCount.value = (resetState === null || resetState === void 0 ? void 0 : resetState.submitCount) || 0;
2392
2449
  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
- }
2450
+ validate({ mode: 'silent' });
2435
2451
  });
2436
2452
  }
2437
2453
  async function validate(opts) {
2438
2454
  const mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'force';
2439
2455
  if (mode === 'force') {
2440
- mutateAllFields(f => (f.meta.validated = true));
2456
+ mutateAllPathState(f => (f.validated = true));
2441
2457
  }
2442
2458
  if (formCtx.validateSchema) {
2443
2459
  return formCtx.validateSchema(mode);
2444
2460
  }
2445
2461
  // 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: [] });
2462
+ const validations = await Promise.all(pathStates.value.map(state => {
2463
+ if (!state.validate) {
2464
+ return Promise.resolve({
2465
+ key: state.path,
2466
+ valid: true,
2467
+ errors: [],
2468
+ });
2450
2469
  }
2451
- return fieldInstance.validate(opts).then((result) => {
2470
+ return state.validate(opts).then((result) => {
2452
2471
  return {
2453
- key: unref(fieldInstance.name),
2472
+ key: state.path,
2454
2473
  valid: result.valid,
2455
2474
  errors: result.errors,
2456
2475
  };
@@ -2473,16 +2492,22 @@ function useForm(opts) {
2473
2492
  errors,
2474
2493
  };
2475
2494
  }
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 });
2495
+ async function validateField(path) {
2496
+ const state = findPathState(path);
2497
+ if (state) {
2498
+ state.validated = true;
2499
+ }
2500
+ if (schema) {
2501
+ const { results } = await validateSchema('validated-only');
2502
+ return results[path] || { errors: [], valid: true };
2481
2503
  }
2482
- if (Array.isArray(fieldInstance)) {
2483
- return fieldInstance.map(f => f.validate())[0];
2504
+ if (state === null || state === void 0 ? void 0 : state.validate) {
2505
+ return state.validate();
2484
2506
  }
2485
- return fieldInstance.validate();
2507
+ if (!state) {
2508
+ warn$1(`field with path ${path} was not found`);
2509
+ }
2510
+ return Promise.resolve({ errors: [], valid: true });
2486
2511
  }
2487
2512
  function unsetInitialValue(path) {
2488
2513
  unsetPath(initialValues.value, path);
@@ -2491,8 +2516,8 @@ function useForm(opts) {
2491
2516
  * Sneaky function to set initial field values
2492
2517
  */
2493
2518
  function stageInitialValue(path, value, updateOriginal = false) {
2494
- setInPath(formValues, path, value);
2495
2519
  setFieldInitialValue(path, value);
2520
+ setInPath(formValues, path, value);
2496
2521
  if (updateOriginal && !(opts === null || opts === void 0 ? void 0 : opts.initialValues)) {
2497
2522
  setInPath(originalInitialValues.value, path, klona(value));
2498
2523
  }
@@ -2551,12 +2576,95 @@ function useForm(opts) {
2551
2576
  deep: true,
2552
2577
  });
2553
2578
  }
2554
- return Object.assign(Object.assign({}, formCtx), { handleReset: () => resetForm(), submitForm });
2579
+ function defineComponentBinds(path, config) {
2580
+ const pathState = findPathState(unravel(path)) || createPathState(path);
2581
+ const evalConfig = () => (isCallable(config) ? config(omit(pathState, PRIVATE_PATH_STATE_KEYS)) : config || {});
2582
+ function onBlur() {
2583
+ var _a;
2584
+ pathState.touched = true;
2585
+ const validateOnBlur = (_a = evalConfig().validateOnBlur) !== null && _a !== void 0 ? _a : getConfig().validateOnBlur;
2586
+ if (validateOnBlur) {
2587
+ validateField(pathState.path);
2588
+ }
2589
+ }
2590
+ function onUpdateModelValue(value) {
2591
+ var _a;
2592
+ setFieldValue(pathState.path, value);
2593
+ const validateOnModelUpdate = (_a = evalConfig().validateOnModelUpdate) !== null && _a !== void 0 ? _a : getConfig().validateOnModelUpdate;
2594
+ if (validateOnModelUpdate) {
2595
+ validateField(pathState.path);
2596
+ }
2597
+ }
2598
+ const props = computed(() => {
2599
+ const base = {
2600
+ modelValue: pathState.value,
2601
+ 'onUpdate:modelValue': onUpdateModelValue,
2602
+ onBlur,
2603
+ };
2604
+ if (isCallable(config)) {
2605
+ return Object.assign(Object.assign({}, base), (config(pathState).props || {}));
2606
+ }
2607
+ if (config === null || config === void 0 ? void 0 : config.mapProps) {
2608
+ return Object.assign(Object.assign({}, base), config.mapProps(omit(pathState, PRIVATE_PATH_STATE_KEYS)));
2609
+ }
2610
+ return base;
2611
+ });
2612
+ return props;
2613
+ }
2614
+ function defineInputBinds(path, config) {
2615
+ const pathState = (findPathState(unravel(path)) || createPathState(path));
2616
+ const evalConfig = () => (isCallable(config) ? config(omit(pathState, PRIVATE_PATH_STATE_KEYS)) : config || {});
2617
+ function onBlur() {
2618
+ var _a;
2619
+ pathState.touched = true;
2620
+ const validateOnBlur = (_a = evalConfig().validateOnBlur) !== null && _a !== void 0 ? _a : getConfig().validateOnBlur;
2621
+ if (validateOnBlur) {
2622
+ validateField(pathState.path);
2623
+ }
2624
+ }
2625
+ function onInput(e) {
2626
+ var _a;
2627
+ const value = normalizeEventValue(e);
2628
+ setFieldValue(pathState.path, value);
2629
+ const validateOnInput = (_a = evalConfig().validateOnInput) !== null && _a !== void 0 ? _a : getConfig().validateOnInput;
2630
+ if (validateOnInput) {
2631
+ validateField(pathState.path);
2632
+ }
2633
+ }
2634
+ function onChange(e) {
2635
+ var _a;
2636
+ const value = normalizeEventValue(e);
2637
+ setFieldValue(pathState.path, value);
2638
+ const validateOnChange = (_a = evalConfig().validateOnChange) !== null && _a !== void 0 ? _a : getConfig().validateOnChange;
2639
+ if (validateOnChange) {
2640
+ validateField(pathState.path);
2641
+ }
2642
+ }
2643
+ const props = computed(() => {
2644
+ const base = {
2645
+ value: pathState.value,
2646
+ onChange,
2647
+ onInput,
2648
+ onBlur,
2649
+ };
2650
+ if (isCallable(config)) {
2651
+ return Object.assign(Object.assign({}, base), (config(omit(pathState, PRIVATE_PATH_STATE_KEYS)).attrs || {}));
2652
+ }
2653
+ if (config === null || config === void 0 ? void 0 : config.mapAttrs) {
2654
+ return Object.assign(Object.assign({}, base), config.mapAttrs(omit(pathState, PRIVATE_PATH_STATE_KEYS)));
2655
+ }
2656
+ return base;
2657
+ });
2658
+ return props;
2659
+ }
2660
+ return Object.assign(Object.assign({}, formCtx), { handleReset: () => resetForm(), submitForm,
2661
+ defineComponentBinds,
2662
+ defineInputBinds });
2555
2663
  }
2556
2664
  /**
2557
2665
  * Manages form meta aggregation
2558
2666
  */
2559
- function useFormMeta(fieldsByPath, currentValues, initialValues, errors) {
2667
+ function useFormMeta(pathsState, currentValues, initialValues, errors) {
2560
2668
  const MERGE_STRATEGIES = {
2561
2669
  touched: 'some',
2562
2670
  pending: 'some',
@@ -2566,10 +2674,10 @@ function useFormMeta(fieldsByPath, currentValues, initialValues, errors) {
2566
2674
  return !isEqual(currentValues, unref(initialValues));
2567
2675
  });
2568
2676
  function calculateFlags() {
2569
- const fields = Object.values(fieldsByPath.value).flat(1).filter(Boolean);
2677
+ const states = pathsState.value;
2570
2678
  return keysOf(MERGE_STRATEGIES).reduce((acc, flag) => {
2571
2679
  const mergeMethod = MERGE_STRATEGIES[flag];
2572
- acc[flag] = fields[mergeMethod](field => field.meta[flag]);
2680
+ acc[flag] = states[mergeMethod](s => s[flag]);
2573
2681
  return acc;
2574
2682
  }, {});
2575
2683
  }
@@ -2587,7 +2695,7 @@ function useFormMeta(fieldsByPath, currentValues, initialValues, errors) {
2587
2695
  /**
2588
2696
  * Manages the initial values prop
2589
2697
  */
2590
- function useFormInitialValues(fields, formValues, opts) {
2698
+ function useFormInitialValues(pathsState, formValues, opts) {
2591
2699
  const values = resolveInitialValues(opts);
2592
2700
  const providedValues = opts === null || opts === void 0 ? void 0 : opts.initialValues;
2593
2701
  // these are the mutable initial values as the fields are mounted/unmounted
@@ -2608,14 +2716,13 @@ function useFormInitialValues(fields, formValues, opts) {
2608
2716
  // those are excluded because it's unlikely you want to change the form values using initial values
2609
2717
  // we mostly watch them for API population or newly inserted fields
2610
2718
  // 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) {
2719
+ pathsState.value.forEach(state => {
2720
+ const wasTouched = state.touched;
2721
+ if (wasTouched) {
2615
2722
  return;
2616
2723
  }
2617
- const newValue = getFromPath(initialValues.value, fieldPath);
2618
- setInPath(formValues, fieldPath, klona(newValue));
2724
+ const newValue = getFromPath(initialValues.value, state.path);
2725
+ setInPath(formValues, state.path, klona(newValue));
2619
2726
  });
2620
2727
  }
2621
2728
  if (isRef(providedValues)) {
@@ -2631,42 +2738,6 @@ function useFormInitialValues(fields, formValues, opts) {
2631
2738
  setInitialValues,
2632
2739
  };
2633
2740
  }
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
2741
 
2671
2742
  const FormImpl = defineComponent({
2672
2743
  name: 'Form',
@@ -2847,7 +2918,10 @@ function useFieldArray(arrayPath) {
2847
2918
  }
2848
2919
  function initFields() {
2849
2920
  const currentValues = getCurrentValues();
2850
- fields.value = currentValues.map(createEntry);
2921
+ if (!Array.isArray(currentValues)) {
2922
+ return;
2923
+ }
2924
+ fields.value = currentValues.map((v, idx) => createEntry(v, idx, fields.value));
2851
2925
  updateEntryFlags();
2852
2926
  }
2853
2927
  initFields();
@@ -2859,7 +2933,13 @@ function useFieldArray(arrayPath) {
2859
2933
  entry.isLast = i === fieldsLength - 1;
2860
2934
  }
2861
2935
  }
2862
- function createEntry(value) {
2936
+ function createEntry(value, idx, currentFields) {
2937
+ // Skips the work by returning the current entry if it already exists
2938
+ // This should make the `key` prop stable and doesn't cause more re-renders than needed
2939
+ // The value is computed and should update anyways
2940
+ if (currentFields && !isNullOrUndefined(idx) && currentFields[idx]) {
2941
+ return currentFields[idx];
2942
+ }
2863
2943
  const key = entryCounter++;
2864
2944
  const entry = {
2865
2945
  key,
@@ -2896,8 +2976,9 @@ function useFieldArray(arrayPath) {
2896
2976
  }
2897
2977
  const newValue = [...pathValue];
2898
2978
  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);
2979
+ const fieldPath = pathName + `[${idx}]`;
2980
+ form.unsetInitialValue(fieldPath);
2981
+ setInPath(form.values, pathName, newValue);
2901
2982
  fields.value.splice(idx, 1);
2902
2983
  afterMutation();
2903
2984
  }
@@ -2910,8 +2991,8 @@ function useFieldArray(arrayPath) {
2910
2991
  }
2911
2992
  const newValue = [...normalizedPathValue];
2912
2993
  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);
2994
+ form.stageInitialValue(pathName + `[${newValue.length - 1}]`, value);
2995
+ setInPath(form.values, pathName, newValue);
2915
2996
  fields.value.push(createEntry(value));
2916
2997
  afterMutation();
2917
2998
  }
@@ -2930,7 +3011,7 @@ function useFieldArray(arrayPath) {
2930
3011
  const tempEntry = newFields[indexA];
2931
3012
  newFields[indexA] = newFields[indexB];
2932
3013
  newFields[indexB] = tempEntry;
2933
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3014
+ setInPath(form.values, pathName, newValue);
2934
3015
  fields.value = newFields;
2935
3016
  updateEntryFlags();
2936
3017
  }
@@ -2944,13 +3025,14 @@ function useFieldArray(arrayPath) {
2944
3025
  const newFields = [...fields.value];
2945
3026
  newValue.splice(idx, 0, value);
2946
3027
  newFields.splice(idx, 0, createEntry(value));
2947
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3028
+ setInPath(form.values, pathName, newValue);
2948
3029
  fields.value = newFields;
2949
3030
  afterMutation();
2950
3031
  }
2951
3032
  function replace(arr) {
2952
3033
  const pathName = unref(arrayPath);
2953
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, arr);
3034
+ form.stageInitialValue(pathName, arr);
3035
+ setInPath(form.values, pathName, arr);
2954
3036
  initFields();
2955
3037
  afterMutation();
2956
3038
  }
@@ -2960,7 +3042,7 @@ function useFieldArray(arrayPath) {
2960
3042
  if (!Array.isArray(pathValue) || pathValue.length - 1 < idx) {
2961
3043
  return;
2962
3044
  }
2963
- form === null || form === void 0 ? void 0 : form.setFieldValue(`${pathName}[${idx}]`, value);
3045
+ setInPath(form.values, `${pathName}[${idx}]`, value);
2964
3046
  form === null || form === void 0 ? void 0 : form.validate({ mode: 'validated-only' });
2965
3047
  }
2966
3048
  function prepend(value) {
@@ -2971,8 +3053,8 @@ function useFieldArray(arrayPath) {
2971
3053
  return;
2972
3054
  }
2973
3055
  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);
3056
+ form.stageInitialValue(pathName + `[${newValue.length - 1}]`, value);
3057
+ setInPath(form.values, pathName, newValue);
2976
3058
  fields.value.unshift(createEntry(value));
2977
3059
  afterMutation();
2978
3060
  }
@@ -2990,7 +3072,7 @@ function useFieldArray(arrayPath) {
2990
3072
  const movedValue = newValue[oldIdx];
2991
3073
  newValue.splice(oldIdx, 1);
2992
3074
  newValue.splice(newIdx, 0, movedValue);
2993
- form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
3075
+ setInPath(form.values, pathName, newValue);
2994
3076
  fields.value = newFields;
2995
3077
  afterMutation();
2996
3078
  }
@@ -3129,17 +3211,13 @@ function useResetForm() {
3129
3211
  * If a field is dirty or not
3130
3212
  */
3131
3213
  function useIsFieldDirty(path) {
3132
- const form = injectWithSelf(FormContextKey);
3133
- let field = path ? undefined : inject(FieldContextKey);
3214
+ const fieldOrPath = resolveFieldOrPathState(path);
3134
3215
  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`);
3216
+ var _a, _b;
3217
+ if (!fieldOrPath) {
3140
3218
  return false;
3141
3219
  }
3142
- return field.meta.dirty;
3220
+ 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
3221
  });
3144
3222
  }
3145
3223
 
@@ -3147,17 +3225,13 @@ function useIsFieldDirty(path) {
3147
3225
  * If a field is touched or not
3148
3226
  */
3149
3227
  function useIsFieldTouched(path) {
3150
- const form = injectWithSelf(FormContextKey);
3151
- let field = path ? undefined : inject(FieldContextKey);
3228
+ const fieldOrPath = resolveFieldOrPathState(path);
3152
3229
  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`);
3230
+ var _a, _b;
3231
+ if (!fieldOrPath) {
3158
3232
  return false;
3159
3233
  }
3160
- return field.meta.touched;
3234
+ 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
3235
  });
3162
3236
  }
3163
3237
 
@@ -3165,17 +3239,13 @@ function useIsFieldTouched(path) {
3165
3239
  * If a field is validated and is valid
3166
3240
  */
3167
3241
  function useIsFieldValid(path) {
3168
- const form = injectWithSelf(FormContextKey);
3169
- let field = path ? undefined : inject(FieldContextKey);
3242
+ const fieldOrPath = resolveFieldOrPathState(path);
3170
3243
  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`);
3244
+ var _a, _b;
3245
+ if (!fieldOrPath) {
3176
3246
  return false;
3177
3247
  }
3178
- return field.meta.valid;
3248
+ 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
3249
  });
3180
3250
  }
3181
3251
 
@@ -3198,19 +3268,19 @@ function useIsSubmitting() {
3198
3268
  */
3199
3269
  function useValidateField(path) {
3200
3270
  const form = injectWithSelf(FormContextKey);
3201
- let field = path ? undefined : inject(FieldContextKey);
3271
+ const field = path ? undefined : inject(FieldContextKey);
3202
3272
  return function validateField() {
3203
- if (path) {
3204
- field = normalizeField(form === null || form === void 0 ? void 0 : form.fieldsByPath.value[unref(path)]);
3273
+ if (field) {
3274
+ return field.validate();
3205
3275
  }
3206
- if (!field) {
3207
- warn(`field with name ${unref(path)} was not found`);
3208
- return Promise.resolve({
3209
- errors: [],
3210
- valid: true,
3211
- });
3276
+ if (form && path) {
3277
+ return form === null || form === void 0 ? void 0 : form.validateField(unref(path));
3212
3278
  }
3213
- return field.validate();
3279
+ warn(`field with name ${unref(path)} was not found`);
3280
+ return Promise.resolve({
3281
+ errors: [],
3282
+ valid: true,
3283
+ });
3214
3284
  };
3215
3285
  }
3216
3286