vee-validate 4.5.11 → 4.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -22
- package/dist/vee-validate.d.ts +547 -291
- package/dist/vee-validate.esm.js +219 -98
- package/dist/vee-validate.js +215 -96
- package/dist/vee-validate.min.js +2 -2
- package/package.json +3 -3
- package/CHANGELOG.md +0 -912
package/dist/vee-validate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* vee-validate v4.
|
|
2
|
+
* vee-validate v4.6.2
|
|
3
3
|
* (c) 2022 Abdelrahman Awad
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
@@ -56,18 +56,6 @@
|
|
|
56
56
|
function isLocator(value) {
|
|
57
57
|
return isCallable(value) && !!value.__locatorRef;
|
|
58
58
|
}
|
|
59
|
-
/**
|
|
60
|
-
* Checks if an tag name is a native HTML tag and not a Vue component
|
|
61
|
-
*/
|
|
62
|
-
function isHTMLTag(tag) {
|
|
63
|
-
return ['input', 'textarea', 'select'].includes(tag);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Checks if an input is of type file
|
|
67
|
-
*/
|
|
68
|
-
function isFileInputNode(tag, attrs) {
|
|
69
|
-
return isHTMLTag(tag) && attrs.type === 'file';
|
|
70
|
-
}
|
|
71
59
|
function isYupValidator(value) {
|
|
72
60
|
return !!value && isCallable(value.validate);
|
|
73
61
|
}
|
|
@@ -120,7 +108,7 @@
|
|
|
120
108
|
* For multi-selects because the value binding will reset the value
|
|
121
109
|
*/
|
|
122
110
|
function shouldHaveValueBinding(tag, attrs) {
|
|
123
|
-
return isNativeMultiSelectNode(tag, attrs)
|
|
111
|
+
return !isNativeMultiSelectNode(tag, attrs) && attrs.type !== 'file' && !hasCheckedAttr(attrs.type);
|
|
124
112
|
}
|
|
125
113
|
function isFormSubmitEvent(evt) {
|
|
126
114
|
return isEvent(evt) && evt.target && 'submit' in evt.target;
|
|
@@ -286,6 +274,15 @@
|
|
|
286
274
|
}, ms);
|
|
287
275
|
return new Promise(resolve => resolves.push(resolve));
|
|
288
276
|
};
|
|
277
|
+
}
|
|
278
|
+
function applyModelModifiers(value, modifiers) {
|
|
279
|
+
if (!isObject(modifiers)) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (modifiers.number) {
|
|
283
|
+
return toNumber(value);
|
|
284
|
+
}
|
|
285
|
+
return value;
|
|
289
286
|
}
|
|
290
287
|
|
|
291
288
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -329,7 +326,8 @@
|
|
|
329
326
|
return getBoundValue(input);
|
|
330
327
|
}
|
|
331
328
|
if (input.type === 'file' && input.files) {
|
|
332
|
-
|
|
329
|
+
const files = Array.from(input.files);
|
|
330
|
+
return input.multiple ? files : files[0];
|
|
333
331
|
}
|
|
334
332
|
if (isNativeMultiSelect(input)) {
|
|
335
333
|
return Array.from(input.options)
|
|
@@ -828,8 +826,8 @@
|
|
|
828
826
|
/**
|
|
829
827
|
* Creates the field value and resolves the initial value
|
|
830
828
|
*/
|
|
831
|
-
function _useFieldValue(path, modelValue, shouldInjectForm) {
|
|
832
|
-
const form = shouldInjectForm ? injectWithSelf(FormContextKey, undefined) : undefined;
|
|
829
|
+
function _useFieldValue(path, modelValue, shouldInjectForm = true) {
|
|
830
|
+
const form = shouldInjectForm === true ? injectWithSelf(FormContextKey, undefined) : undefined;
|
|
833
831
|
const modelRef = vue.ref(vue.unref(modelValue));
|
|
834
832
|
function resolveInitialValue() {
|
|
835
833
|
if (!form) {
|
|
@@ -859,7 +857,7 @@
|
|
|
859
857
|
// prioritize model value over form values
|
|
860
858
|
// #3429
|
|
861
859
|
const currentValue = modelValue ? vue.unref(modelValue) : getFromPath(form.values, vue.unref(path), vue.unref(initialValue));
|
|
862
|
-
form.stageInitialValue(vue.unref(path), currentValue);
|
|
860
|
+
form.stageInitialValue(vue.unref(path), currentValue, true);
|
|
863
861
|
// otherwise use a computed setter that triggers the `setFieldValue`
|
|
864
862
|
const value = vue.computed({
|
|
865
863
|
get() {
|
|
@@ -938,7 +936,7 @@
|
|
|
938
936
|
return _useField(name, rules, opts);
|
|
939
937
|
}
|
|
940
938
|
function _useField(name, rules, opts) {
|
|
941
|
-
const { initialValue: modelValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue, standalone, } = normalizeOptions(vue.unref(name), opts);
|
|
939
|
+
const { initialValue: modelValue, validateOnMount, bails, type, checkedValue, label, validateOnValueUpdate, uncheckedValue, standalone, keepValueOnUnmount, modelPropName, syncVModel, } = normalizeOptions(vue.unref(name), opts);
|
|
942
940
|
const form = !standalone ? injectWithSelf(FormContextKey) : undefined;
|
|
943
941
|
// a flag indicating if the field is about to be removed/unmounted.
|
|
944
942
|
let markedForRemoval = false;
|
|
@@ -946,6 +944,9 @@
|
|
|
946
944
|
modelValue,
|
|
947
945
|
standalone,
|
|
948
946
|
});
|
|
947
|
+
if (syncVModel) {
|
|
948
|
+
useVModel({ value, prop: modelPropName, handleChange });
|
|
949
|
+
}
|
|
949
950
|
/**
|
|
950
951
|
* Handles common onBlur meta update
|
|
951
952
|
*/
|
|
@@ -1004,13 +1005,13 @@
|
|
|
1004
1005
|
return validateValidStateOnly();
|
|
1005
1006
|
}
|
|
1006
1007
|
// Common input/change event handler
|
|
1007
|
-
|
|
1008
|
+
function handleChange(e, shouldValidate = true) {
|
|
1008
1009
|
const newValue = normalizeEventValue(e);
|
|
1009
1010
|
value.value = newValue;
|
|
1010
1011
|
if (!validateOnValueUpdate && shouldValidate) {
|
|
1011
1012
|
validateWithStateMutation();
|
|
1012
1013
|
}
|
|
1013
|
-
}
|
|
1014
|
+
}
|
|
1014
1015
|
// Runs the initial validation
|
|
1015
1016
|
vue.onMounted(() => {
|
|
1016
1017
|
if (validateOnMount) {
|
|
@@ -1027,7 +1028,13 @@
|
|
|
1027
1028
|
}
|
|
1028
1029
|
let unwatchValue;
|
|
1029
1030
|
function watchValue() {
|
|
1030
|
-
unwatchValue = vue.watch(value,
|
|
1031
|
+
unwatchValue = vue.watch(value, (val, oldVal) => {
|
|
1032
|
+
if (es6(val, oldVal)) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
const validateFn = validateOnValueUpdate ? validateWithStateMutation : validateValidStateOnly;
|
|
1036
|
+
validateFn();
|
|
1037
|
+
}, {
|
|
1031
1038
|
deep: true,
|
|
1032
1039
|
});
|
|
1033
1040
|
}
|
|
@@ -1068,6 +1075,7 @@
|
|
|
1068
1075
|
checkedValue,
|
|
1069
1076
|
uncheckedValue,
|
|
1070
1077
|
bails,
|
|
1078
|
+
keepValueOnUnmount,
|
|
1071
1079
|
resetField,
|
|
1072
1080
|
handleReset: () => resetField(),
|
|
1073
1081
|
validate: validate$1,
|
|
@@ -1145,6 +1153,9 @@
|
|
|
1145
1153
|
label: name,
|
|
1146
1154
|
validateOnValueUpdate: true,
|
|
1147
1155
|
standalone: false,
|
|
1156
|
+
keepValueOnUnmount: undefined,
|
|
1157
|
+
modelPropName: 'modelValue',
|
|
1158
|
+
syncVModel: true,
|
|
1148
1159
|
});
|
|
1149
1160
|
if (!opts) {
|
|
1150
1161
|
return defaults();
|
|
@@ -1176,8 +1187,8 @@
|
|
|
1176
1187
|
return Array.isArray(currentValue) ? currentValue.includes(checkedVal) : checkedVal === currentValue;
|
|
1177
1188
|
});
|
|
1178
1189
|
function handleCheckboxChange(e, shouldValidate = true) {
|
|
1179
|
-
var _a
|
|
1180
|
-
if (checked.value === ((
|
|
1190
|
+
var _a;
|
|
1191
|
+
if (checked.value === ((_a = e === null || e === void 0 ? void 0 : e.target) === null || _a === void 0 ? void 0 : _a.checked)) {
|
|
1181
1192
|
return;
|
|
1182
1193
|
}
|
|
1183
1194
|
let newValue = normalizeEventValue(e);
|
|
@@ -1187,17 +1198,43 @@
|
|
|
1187
1198
|
}
|
|
1188
1199
|
handleChange(newValue, shouldValidate);
|
|
1189
1200
|
}
|
|
1190
|
-
vue.onBeforeUnmount(() => {
|
|
1191
|
-
// toggles the checkbox value if it was checked
|
|
1192
|
-
if (checked.value) {
|
|
1193
|
-
handleCheckboxChange(vue.unref(checkedValue), false);
|
|
1194
|
-
}
|
|
1195
|
-
});
|
|
1196
1201
|
return Object.assign(Object.assign({}, field), { checked,
|
|
1197
1202
|
checkedValue,
|
|
1198
1203
|
uncheckedValue, handleChange: handleCheckboxChange });
|
|
1199
1204
|
}
|
|
1200
1205
|
return patchCheckboxApi(_useField(name, rules, opts));
|
|
1206
|
+
}
|
|
1207
|
+
function useVModel({ prop, value, handleChange }) {
|
|
1208
|
+
const vm = vue.getCurrentInstance();
|
|
1209
|
+
/* istanbul ignore next */
|
|
1210
|
+
if (!vm) {
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
const propName = prop || 'modelValue';
|
|
1214
|
+
const emitName = `update:${propName}`;
|
|
1215
|
+
// Component doesn't have a model prop setup (must be defined on the props)
|
|
1216
|
+
if (!(propName in vm.props)) {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
vue.watch(value, newValue => {
|
|
1220
|
+
if (es6(newValue, getCurrentModelValue(vm, propName))) {
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
vm.emit(emitName, newValue);
|
|
1224
|
+
});
|
|
1225
|
+
vue.watch(() => getCurrentModelValue(vm, propName), propValue => {
|
|
1226
|
+
if (propValue === IS_ABSENT && value.value === undefined) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
const newValue = propValue === IS_ABSENT ? undefined : propValue;
|
|
1230
|
+
if (es6(newValue, applyModelModifiers(value.value, vm.props.modelModifiers))) {
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
handleChange(newValue);
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
function getCurrentModelValue(vm, propName) {
|
|
1237
|
+
return vm.props[propName];
|
|
1201
1238
|
}
|
|
1202
1239
|
|
|
1203
1240
|
const FieldImpl = vue.defineComponent({
|
|
@@ -1264,13 +1301,17 @@
|
|
|
1264
1301
|
type: Boolean,
|
|
1265
1302
|
default: false,
|
|
1266
1303
|
},
|
|
1304
|
+
keepValue: {
|
|
1305
|
+
type: Boolean,
|
|
1306
|
+
default: undefined,
|
|
1307
|
+
},
|
|
1267
1308
|
},
|
|
1268
1309
|
setup(props, ctx) {
|
|
1269
1310
|
const rules = vue.toRef(props, 'rules');
|
|
1270
1311
|
const name = vue.toRef(props, 'name');
|
|
1271
1312
|
const label = vue.toRef(props, 'label');
|
|
1272
1313
|
const uncheckedValue = vue.toRef(props, 'uncheckedValue');
|
|
1273
|
-
const
|
|
1314
|
+
const keepValue = vue.toRef(props, 'keepValue');
|
|
1274
1315
|
const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, setTouched, resetField, handleReset, meta, checked, setErrors, } = useField(name, rules, {
|
|
1275
1316
|
validateOnMount: props.validateOnMount,
|
|
1276
1317
|
bails: props.bails,
|
|
@@ -1282,25 +1323,22 @@
|
|
|
1282
1323
|
uncheckedValue,
|
|
1283
1324
|
label,
|
|
1284
1325
|
validateOnValueUpdate: false,
|
|
1326
|
+
keepValueOnUnmount: keepValue,
|
|
1285
1327
|
});
|
|
1286
1328
|
// If there is a v-model applied on the component we need to emit the `update:modelValue` whenever the value binding changes
|
|
1287
|
-
const onChangeHandler =
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
}
|
|
1292
|
-
: handleChange;
|
|
1329
|
+
const onChangeHandler = function handleChangeWithModel(e, shouldValidate = true) {
|
|
1330
|
+
handleChange(e, shouldValidate);
|
|
1331
|
+
ctx.emit('update:modelValue', value.value);
|
|
1332
|
+
};
|
|
1293
1333
|
const handleInput = (e) => {
|
|
1294
1334
|
if (!hasCheckedAttr(ctx.attrs.type)) {
|
|
1295
1335
|
value.value = normalizeEventValue(e);
|
|
1296
1336
|
}
|
|
1297
1337
|
};
|
|
1298
|
-
const onInputHandler =
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
}
|
|
1303
|
-
: handleInput;
|
|
1338
|
+
const onInputHandler = function handleInputWithModel(e) {
|
|
1339
|
+
handleInput(e);
|
|
1340
|
+
ctx.emit('update:modelValue', value.value);
|
|
1341
|
+
};
|
|
1304
1342
|
const fieldProps = vue.computed(() => {
|
|
1305
1343
|
const { validateOnInput, validateOnChange, validateOnBlur, validateOnModelUpdate } = resolveValidationTriggers(props);
|
|
1306
1344
|
const baseOnBlur = [handleBlur, ctx.attrs.onBlur, validateOnBlur ? validateField : undefined].filter(Boolean);
|
|
@@ -1316,26 +1354,12 @@
|
|
|
1316
1354
|
if (hasCheckedAttr(ctx.attrs.type) && checked) {
|
|
1317
1355
|
attrs.checked = checked.value;
|
|
1318
1356
|
}
|
|
1319
|
-
else {
|
|
1320
|
-
attrs.value = value.value;
|
|
1321
|
-
}
|
|
1322
1357
|
const tag = resolveTag(props, ctx);
|
|
1323
1358
|
if (shouldHaveValueBinding(tag, ctx.attrs)) {
|
|
1324
|
-
|
|
1359
|
+
attrs.value = value.value;
|
|
1325
1360
|
}
|
|
1326
1361
|
return attrs;
|
|
1327
1362
|
});
|
|
1328
|
-
const modelValue = vue.toRef(props, 'modelValue');
|
|
1329
|
-
vue.watch(modelValue, newModelValue => {
|
|
1330
|
-
// Don't attempt to sync absent values
|
|
1331
|
-
if (newModelValue === IS_ABSENT && value.value === undefined) {
|
|
1332
|
-
return;
|
|
1333
|
-
}
|
|
1334
|
-
if (newModelValue !== applyModifiers(value.value, props.modelModifiers)) {
|
|
1335
|
-
value.value = newModelValue === IS_ABSENT ? undefined : newModelValue;
|
|
1336
|
-
validateField();
|
|
1337
|
-
}
|
|
1338
|
-
});
|
|
1339
1363
|
function slotProps() {
|
|
1340
1364
|
return {
|
|
1341
1365
|
field: fieldProps.value,
|
|
@@ -1387,12 +1411,6 @@
|
|
|
1387
1411
|
validateOnModelUpdate: (_d = props.validateOnModelUpdate) !== null && _d !== void 0 ? _d : validateOnModelUpdate,
|
|
1388
1412
|
};
|
|
1389
1413
|
}
|
|
1390
|
-
function applyModifiers(value, modifiers) {
|
|
1391
|
-
if (modifiers.number) {
|
|
1392
|
-
return toNumber(value);
|
|
1393
|
-
}
|
|
1394
|
-
return value;
|
|
1395
|
-
}
|
|
1396
1414
|
function resolveInitialValue(props, ctx) {
|
|
1397
1415
|
// Gets the initial value either from `value` prop/attr or `v-model` binding (modelValue)
|
|
1398
1416
|
// For checkboxes and radio buttons it will always be the model value not the `value` attribute
|
|
@@ -1405,6 +1423,7 @@
|
|
|
1405
1423
|
|
|
1406
1424
|
let FORM_COUNTER = 0;
|
|
1407
1425
|
function useForm(opts) {
|
|
1426
|
+
var _a;
|
|
1408
1427
|
const formId = FORM_COUNTER++;
|
|
1409
1428
|
// Prevents fields from double resetting their values, which causes checkboxes to toggle their initial value
|
|
1410
1429
|
// TODO: This won't be needed if we centralize all the state inside the `form` for form inputs
|
|
@@ -1415,8 +1434,8 @@
|
|
|
1415
1434
|
const isSubmitting = vue.ref(false);
|
|
1416
1435
|
// The number of times the user tried to submit the form
|
|
1417
1436
|
const submitCount = vue.ref(0);
|
|
1418
|
-
//
|
|
1419
|
-
const
|
|
1437
|
+
// field arrays managed by this form
|
|
1438
|
+
const fieldArrays = [];
|
|
1420
1439
|
// a private ref for all form values
|
|
1421
1440
|
const formValues = vue.reactive(klona(vue.unref(opts === null || opts === void 0 ? void 0 : opts.initialValues) || {}));
|
|
1422
1441
|
// the source of errors for the form fields
|
|
@@ -1463,10 +1482,11 @@
|
|
|
1463
1482
|
// mutable non-reactive reference to initial errors
|
|
1464
1483
|
// we need this to process initial errors then unset them
|
|
1465
1484
|
const initialErrors = Object.assign({}, ((opts === null || opts === void 0 ? void 0 : opts.initialErrors) || {}));
|
|
1485
|
+
const keepValuesOnUnmount = (_a = opts === null || opts === void 0 ? void 0 : opts.keepValuesOnUnmount) !== null && _a !== void 0 ? _a : false;
|
|
1466
1486
|
// initial form values
|
|
1467
1487
|
const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts === null || opts === void 0 ? void 0 : opts.initialValues);
|
|
1468
1488
|
// form meta aggregations
|
|
1469
|
-
const meta = useFormMeta(fieldsByPath, formValues,
|
|
1489
|
+
const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);
|
|
1470
1490
|
const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
|
|
1471
1491
|
const formCtx = {
|
|
1472
1492
|
formId,
|
|
@@ -1478,7 +1498,8 @@
|
|
|
1478
1498
|
submitCount,
|
|
1479
1499
|
meta,
|
|
1480
1500
|
isSubmitting,
|
|
1481
|
-
|
|
1501
|
+
fieldArrays,
|
|
1502
|
+
keepValuesOnUnmount,
|
|
1482
1503
|
validateSchema: vue.unref(schema) ? validateSchema : undefined,
|
|
1483
1504
|
validate,
|
|
1484
1505
|
register: registerField,
|
|
@@ -1496,6 +1517,7 @@
|
|
|
1496
1517
|
stageInitialValue,
|
|
1497
1518
|
unsetInitialValue,
|
|
1498
1519
|
setFieldInitialValue,
|
|
1520
|
+
useFieldModel,
|
|
1499
1521
|
};
|
|
1500
1522
|
function isFieldGroup(fieldOrGroup) {
|
|
1501
1523
|
return Array.isArray(fieldOrGroup);
|
|
@@ -1565,7 +1587,22 @@
|
|
|
1565
1587
|
setFieldValue(path, fields[path]);
|
|
1566
1588
|
});
|
|
1567
1589
|
// regenerate the arrays when the form values change
|
|
1568
|
-
|
|
1590
|
+
fieldArrays.forEach(f => f && f.reset());
|
|
1591
|
+
}
|
|
1592
|
+
function createModel(path) {
|
|
1593
|
+
const { value } = _useFieldValue(path);
|
|
1594
|
+
vue.watch(value, () => {
|
|
1595
|
+
if (!fieldExists(vue.unref(path))) {
|
|
1596
|
+
validate({ mode: 'validated-only' });
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1599
|
+
return value;
|
|
1600
|
+
}
|
|
1601
|
+
function useFieldModel(path) {
|
|
1602
|
+
if (!Array.isArray(path)) {
|
|
1603
|
+
return createModel(path);
|
|
1604
|
+
}
|
|
1605
|
+
return path.map(createModel);
|
|
1569
1606
|
}
|
|
1570
1607
|
/**
|
|
1571
1608
|
* Sets the touched meta state on a field
|
|
@@ -1643,10 +1680,6 @@
|
|
|
1643
1680
|
return;
|
|
1644
1681
|
}
|
|
1645
1682
|
fieldAtPath.splice(idx, 1);
|
|
1646
|
-
if (fieldAtPath.length === 1) {
|
|
1647
|
-
fieldsByPath.value[fieldPath] = fieldAtPath[0];
|
|
1648
|
-
return;
|
|
1649
|
-
}
|
|
1650
1683
|
if (!fieldAtPath.length) {
|
|
1651
1684
|
delete fieldsByPath.value[fieldPath];
|
|
1652
1685
|
}
|
|
@@ -1689,13 +1722,45 @@
|
|
|
1689
1722
|
}
|
|
1690
1723
|
function unregisterField(field) {
|
|
1691
1724
|
const fieldName = vue.unref(field.name);
|
|
1725
|
+
const fieldInstance = fieldsByPath.value[fieldName];
|
|
1726
|
+
const isGroup = !!fieldInstance && isFieldGroup(fieldInstance);
|
|
1692
1727
|
removeFieldFromPath(field, fieldName);
|
|
1728
|
+
// clears a field error on unmounted
|
|
1729
|
+
// we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes
|
|
1693
1730
|
vue.nextTick(() => {
|
|
1694
|
-
|
|
1695
|
-
|
|
1731
|
+
var _a;
|
|
1732
|
+
const shouldKeepValue = (_a = vue.unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : vue.unref(keepValuesOnUnmount);
|
|
1733
|
+
const currentGroupValue = getFromPath(formValues, fieldName);
|
|
1734
|
+
// The boolean here is we check if the field still belongs to the same control group with that name
|
|
1735
|
+
// if another group claimed the name, we should avoid handling it since it is no longer the same group
|
|
1736
|
+
// this happens with `v-for` over some checkboxes and field arrays.
|
|
1737
|
+
// also if the group no longer exist we can assume this group was the last one that controlled it
|
|
1738
|
+
const isSameGroup = isGroup && (fieldInstance === fieldsByPath.value[fieldName] || !fieldsByPath.value[fieldName]);
|
|
1739
|
+
// group field that still has a dangling value, the field may exist or not after it was removed.
|
|
1740
|
+
// This used to be handled in the useField composable but the form has better context on when it should/not happen.
|
|
1741
|
+
// if it does belong to it that means the group still exists
|
|
1742
|
+
// #3844
|
|
1743
|
+
if (isSameGroup && Array.isArray(currentGroupValue) && !shouldKeepValue) {
|
|
1744
|
+
const valueIdx = currentGroupValue.findIndex(i => es6(i, vue.unref(field.checkedValue)));
|
|
1745
|
+
if (valueIdx > -1) {
|
|
1746
|
+
const newVal = [...currentGroupValue];
|
|
1747
|
+
newVal.splice(valueIdx, 1);
|
|
1748
|
+
setFieldValue(fieldName, newVal, { force: true });
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
// Field was removed entirely, we should unset its path
|
|
1696
1752
|
// #3384
|
|
1697
1753
|
if (!fieldExists(fieldName)) {
|
|
1698
1754
|
setFieldError(fieldName, undefined);
|
|
1755
|
+
// Checks if the field was configured to be unset during unmount or not
|
|
1756
|
+
// Checks both the form-level config and field-level one
|
|
1757
|
+
// Field has the priority if it is set, otherwise it goes to the form settings
|
|
1758
|
+
if (shouldKeepValue) {
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
if (isGroup && !isEmptyContainer(getFromPath(formValues, fieldName))) {
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1699
1764
|
unsetPath(formValues, fieldName);
|
|
1700
1765
|
}
|
|
1701
1766
|
});
|
|
@@ -1802,9 +1867,12 @@
|
|
|
1802
1867
|
/**
|
|
1803
1868
|
* Sneaky function to set initial field values
|
|
1804
1869
|
*/
|
|
1805
|
-
function stageInitialValue(path, value) {
|
|
1870
|
+
function stageInitialValue(path, value, updateOriginal = false) {
|
|
1806
1871
|
setInPath(formValues, path, value);
|
|
1807
1872
|
setFieldInitialValue(path, value);
|
|
1873
|
+
if (updateOriginal) {
|
|
1874
|
+
setInPath(originalInitialValues.value, path, klona(value));
|
|
1875
|
+
}
|
|
1808
1876
|
}
|
|
1809
1877
|
async function _validateSchema() {
|
|
1810
1878
|
const schemaValue = vue.unref(schema);
|
|
@@ -1821,10 +1889,12 @@
|
|
|
1821
1889
|
}
|
|
1822
1890
|
/**
|
|
1823
1891
|
* Batches validation runs in 5ms batches
|
|
1892
|
+
* Must have two distinct batch queues to make sure they don't override each other settings #3783
|
|
1824
1893
|
*/
|
|
1825
|
-
const
|
|
1894
|
+
const debouncedSilentValidation = debounceAsync(_validateSchema, 5);
|
|
1895
|
+
const debouncedValidation = debounceAsync(_validateSchema, 5);
|
|
1826
1896
|
async function validateSchema(mode) {
|
|
1827
|
-
const formResult = await
|
|
1897
|
+
const formResult = await (mode === 'silent' ? debouncedSilentValidation() : debouncedValidation());
|
|
1828
1898
|
// fields by id lookup
|
|
1829
1899
|
const fieldsById = formCtx.fieldsByPath.value || {};
|
|
1830
1900
|
// errors fields names, we need it to also check if custom errors are updated
|
|
@@ -1914,6 +1984,7 @@
|
|
|
1914
1984
|
setValues,
|
|
1915
1985
|
setFieldTouched,
|
|
1916
1986
|
setTouched,
|
|
1987
|
+
useFieldModel,
|
|
1917
1988
|
};
|
|
1918
1989
|
}
|
|
1919
1990
|
/**
|
|
@@ -2065,17 +2136,28 @@
|
|
|
2065
2136
|
type: Function,
|
|
2066
2137
|
default: undefined,
|
|
2067
2138
|
},
|
|
2139
|
+
keepValues: {
|
|
2140
|
+
type: Boolean,
|
|
2141
|
+
default: false,
|
|
2142
|
+
},
|
|
2068
2143
|
},
|
|
2069
2144
|
setup(props, ctx) {
|
|
2070
2145
|
const initialValues = vue.toRef(props, 'initialValues');
|
|
2071
2146
|
const validationSchema = vue.toRef(props, 'validationSchema');
|
|
2072
|
-
const
|
|
2147
|
+
const keepValues = vue.toRef(props, 'keepValues');
|
|
2148
|
+
const { errors, values, meta, isSubmitting, submitCount, validate, validateField, handleReset, resetForm, handleSubmit, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
|
|
2073
2149
|
validationSchema: validationSchema.value ? validationSchema : undefined,
|
|
2074
2150
|
initialValues,
|
|
2075
2151
|
initialErrors: props.initialErrors,
|
|
2076
2152
|
initialTouched: props.initialTouched,
|
|
2077
2153
|
validateOnMount: props.validateOnMount,
|
|
2154
|
+
keepValuesOnUnmount: keepValues,
|
|
2078
2155
|
});
|
|
2156
|
+
const submitForm = handleSubmit((_, { evt }) => {
|
|
2157
|
+
if (isFormSubmitEvent(evt)) {
|
|
2158
|
+
evt.target.submit();
|
|
2159
|
+
}
|
|
2160
|
+
}, props.onInvalidSubmit);
|
|
2079
2161
|
const onSubmit = props.onSubmit ? handleSubmit(props.onSubmit, props.onInvalidSubmit) : submitForm;
|
|
2080
2162
|
function handleFormReset(e) {
|
|
2081
2163
|
if (isEvent(e)) {
|
|
@@ -2144,15 +2226,13 @@
|
|
|
2144
2226
|
});
|
|
2145
2227
|
const Form = FormImpl;
|
|
2146
2228
|
|
|
2147
|
-
let FIELD_ARRAY_COUNTER = 0;
|
|
2148
2229
|
function useFieldArray(arrayPath) {
|
|
2149
|
-
const id = FIELD_ARRAY_COUNTER++;
|
|
2150
2230
|
const form = injectWithSelf(FormContextKey, undefined);
|
|
2151
2231
|
const fields = vue.ref([]);
|
|
2152
2232
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
2153
2233
|
const noOp = () => { };
|
|
2154
2234
|
const noOpApi = {
|
|
2155
|
-
fields
|
|
2235
|
+
fields,
|
|
2156
2236
|
remove: noOp,
|
|
2157
2237
|
push: noOp,
|
|
2158
2238
|
swap: noOp,
|
|
@@ -2160,6 +2240,7 @@
|
|
|
2160
2240
|
update: noOp,
|
|
2161
2241
|
replace: noOp,
|
|
2162
2242
|
prepend: noOp,
|
|
2243
|
+
move: noOp,
|
|
2163
2244
|
};
|
|
2164
2245
|
if (!form) {
|
|
2165
2246
|
warn('FieldArray requires being a child of `<Form/>` or `useForm` being called before it. Array fields may not work correctly');
|
|
@@ -2169,9 +2250,13 @@
|
|
|
2169
2250
|
warn('FieldArray requires a field path to be provided, did you forget to pass the `name` prop?');
|
|
2170
2251
|
return noOpApi;
|
|
2171
2252
|
}
|
|
2253
|
+
const alreadyExists = form.fieldArrays.find(a => vue.unref(a.path) === vue.unref(arrayPath));
|
|
2254
|
+
if (alreadyExists) {
|
|
2255
|
+
return alreadyExists;
|
|
2256
|
+
}
|
|
2172
2257
|
let entryCounter = 0;
|
|
2173
2258
|
function initFields() {
|
|
2174
|
-
const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, vue.unref(arrayPath), []);
|
|
2259
|
+
const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, vue.unref(arrayPath), []) || [];
|
|
2175
2260
|
fields.value = currentValues.map(createEntry);
|
|
2176
2261
|
updateEntryFlags();
|
|
2177
2262
|
}
|
|
@@ -2188,10 +2273,20 @@
|
|
|
2188
2273
|
const key = entryCounter++;
|
|
2189
2274
|
const entry = {
|
|
2190
2275
|
key,
|
|
2191
|
-
value: vue.computed(
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2276
|
+
value: vue.computed({
|
|
2277
|
+
get() {
|
|
2278
|
+
const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, vue.unref(arrayPath), []) || [];
|
|
2279
|
+
const idx = fields.value.findIndex(e => e.key === key);
|
|
2280
|
+
return idx === -1 ? value : currentValues[idx];
|
|
2281
|
+
},
|
|
2282
|
+
set(value) {
|
|
2283
|
+
const idx = fields.value.findIndex(e => e.key === key);
|
|
2284
|
+
if (idx === -1) {
|
|
2285
|
+
warn(`Attempting to update a non-existent array item`);
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
update(idx, value);
|
|
2289
|
+
},
|
|
2195
2290
|
}),
|
|
2196
2291
|
isFirst: false,
|
|
2197
2292
|
isLast: false,
|
|
@@ -2284,14 +2379,26 @@
|
|
|
2284
2379
|
fields.value.unshift(createEntry(value));
|
|
2285
2380
|
updateEntryFlags();
|
|
2286
2381
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
fields
|
|
2382
|
+
function move(oldIdx, newIdx) {
|
|
2383
|
+
const pathName = vue.unref(arrayPath);
|
|
2384
|
+
const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
|
|
2385
|
+
const newValue = isNullOrUndefined(pathValue) ? [] : [...pathValue];
|
|
2386
|
+
if (!Array.isArray(pathValue) || !(oldIdx in pathValue) || !(newIdx in pathValue)) {
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
const newFields = [...fields.value];
|
|
2390
|
+
const movedItem = newFields[oldIdx];
|
|
2391
|
+
newFields.splice(oldIdx, 1);
|
|
2392
|
+
newFields.splice(newIdx, 0, movedItem);
|
|
2393
|
+
const movedValue = newValue[oldIdx];
|
|
2394
|
+
newValue.splice(oldIdx, 1);
|
|
2395
|
+
newValue.splice(newIdx, 0, movedValue);
|
|
2396
|
+
form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
|
|
2397
|
+
fields.value = newFields;
|
|
2398
|
+
updateEntryFlags();
|
|
2399
|
+
}
|
|
2400
|
+
const fieldArrayCtx = {
|
|
2401
|
+
fields,
|
|
2295
2402
|
remove,
|
|
2296
2403
|
push,
|
|
2297
2404
|
swap,
|
|
@@ -2299,7 +2406,16 @@
|
|
|
2299
2406
|
update,
|
|
2300
2407
|
replace,
|
|
2301
2408
|
prepend,
|
|
2409
|
+
move,
|
|
2302
2410
|
};
|
|
2411
|
+
form.fieldArrays.push(Object.assign({ path: arrayPath, reset: initFields }, fieldArrayCtx));
|
|
2412
|
+
vue.onBeforeUnmount(() => {
|
|
2413
|
+
const idx = form.fieldArrays.findIndex(i => vue.unref(i.path) === vue.unref(arrayPath));
|
|
2414
|
+
if (idx >= 0) {
|
|
2415
|
+
form.fieldArrays.splice(idx, 1);
|
|
2416
|
+
}
|
|
2417
|
+
});
|
|
2418
|
+
return fieldArrayCtx;
|
|
2303
2419
|
}
|
|
2304
2420
|
|
|
2305
2421
|
const FieldArrayImpl = vue.defineComponent({
|
|
@@ -2312,7 +2428,7 @@
|
|
|
2312
2428
|
},
|
|
2313
2429
|
},
|
|
2314
2430
|
setup(props, ctx) {
|
|
2315
|
-
const { push, remove, swap, insert, replace, update, prepend, fields } = useFieldArray(vue.toRef(props, 'name'));
|
|
2431
|
+
const { push, remove, swap, insert, replace, update, prepend, move, fields } = useFieldArray(vue.toRef(props, 'name'));
|
|
2316
2432
|
function slotProps() {
|
|
2317
2433
|
return {
|
|
2318
2434
|
fields: fields.value,
|
|
@@ -2323,6 +2439,7 @@
|
|
|
2323
2439
|
update,
|
|
2324
2440
|
replace,
|
|
2325
2441
|
prepend,
|
|
2442
|
+
move,
|
|
2326
2443
|
};
|
|
2327
2444
|
}
|
|
2328
2445
|
ctx.expose({
|
|
@@ -2333,6 +2450,7 @@
|
|
|
2333
2450
|
update,
|
|
2334
2451
|
replace,
|
|
2335
2452
|
prepend,
|
|
2453
|
+
move,
|
|
2336
2454
|
});
|
|
2337
2455
|
return () => {
|
|
2338
2456
|
const children = normalizeChildren(undefined, ctx, slotProps);
|
|
@@ -2638,6 +2756,7 @@
|
|
|
2638
2756
|
exports.FieldContextKey = FieldContextKey;
|
|
2639
2757
|
exports.Form = Form;
|
|
2640
2758
|
exports.FormContextKey = FormContextKey;
|
|
2759
|
+
exports.IS_ABSENT = IS_ABSENT;
|
|
2641
2760
|
exports.configure = configure;
|
|
2642
2761
|
exports.defineRule = defineRule;
|
|
2643
2762
|
exports.useField = useField;
|