vee-validate 4.5.4 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -24
- package/dist/vee-validate.d.ts +548 -293
- package/dist/vee-validate.esm.js +265 -110
- package/dist/vee-validate.js +261 -108
- package/dist/vee-validate.min.js +3 -3
- package/package.json +3 -3
- package/CHANGELOG.md +0 -830
package/dist/vee-validate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* vee-validate v4.
|
|
3
|
-
* (c)
|
|
2
|
+
* vee-validate v4.6.0
|
|
3
|
+
* (c) 2022 Abdelrahman Awad
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
@@ -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)
|
|
@@ -488,18 +486,34 @@
|
|
|
488
486
|
if (isYupValidator(field.rules)) {
|
|
489
487
|
return validateFieldWithYup(value, field.rules, { bails: field.bails });
|
|
490
488
|
}
|
|
491
|
-
// if a generic function
|
|
492
|
-
if (isCallable(field.rules)) {
|
|
489
|
+
// if a generic function or chain of generic functions
|
|
490
|
+
if (isCallable(field.rules) || Array.isArray(field.rules)) {
|
|
493
491
|
const ctx = {
|
|
494
492
|
field: field.name,
|
|
495
493
|
form: field.formData,
|
|
496
494
|
value: value,
|
|
497
495
|
};
|
|
498
|
-
|
|
499
|
-
const
|
|
500
|
-
const
|
|
496
|
+
// Normalize the pipeline
|
|
497
|
+
const pipeline = Array.isArray(field.rules) ? field.rules : [field.rules];
|
|
498
|
+
const length = pipeline.length;
|
|
499
|
+
const errors = [];
|
|
500
|
+
for (let i = 0; i < length; i++) {
|
|
501
|
+
const rule = pipeline[i];
|
|
502
|
+
const result = await rule(value, ctx);
|
|
503
|
+
const isValid = typeof result !== 'string' && result;
|
|
504
|
+
if (isValid) {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const message = typeof result === 'string' ? result : _generateFieldError(ctx);
|
|
508
|
+
errors.push(message);
|
|
509
|
+
if (field.bails) {
|
|
510
|
+
return {
|
|
511
|
+
errors,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
}
|
|
501
515
|
return {
|
|
502
|
-
errors
|
|
516
|
+
errors,
|
|
503
517
|
};
|
|
504
518
|
}
|
|
505
519
|
const normalizedContext = Object.assign(Object.assign({}, field), { rules: normalizeRules(field.rules) });
|
|
@@ -812,8 +826,8 @@
|
|
|
812
826
|
/**
|
|
813
827
|
* Creates the field value and resolves the initial value
|
|
814
828
|
*/
|
|
815
|
-
function _useFieldValue(path, modelValue, shouldInjectForm) {
|
|
816
|
-
const form = shouldInjectForm ? injectWithSelf(FormContextKey, undefined) : undefined;
|
|
829
|
+
function _useFieldValue(path, modelValue, shouldInjectForm = true) {
|
|
830
|
+
const form = shouldInjectForm === true ? injectWithSelf(FormContextKey, undefined) : undefined;
|
|
817
831
|
const modelRef = vue.ref(vue.unref(modelValue));
|
|
818
832
|
function resolveInitialValue() {
|
|
819
833
|
if (!form) {
|
|
@@ -843,7 +857,7 @@
|
|
|
843
857
|
// prioritize model value over form values
|
|
844
858
|
// #3429
|
|
845
859
|
const currentValue = modelValue ? vue.unref(modelValue) : getFromPath(form.values, vue.unref(path), vue.unref(initialValue));
|
|
846
|
-
form.stageInitialValue(vue.unref(path), currentValue);
|
|
860
|
+
form.stageInitialValue(vue.unref(path), currentValue, true);
|
|
847
861
|
// otherwise use a computed setter that triggers the `setFieldValue`
|
|
848
862
|
const value = vue.computed({
|
|
849
863
|
get() {
|
|
@@ -922,12 +936,17 @@
|
|
|
922
936
|
return _useField(name, rules, opts);
|
|
923
937
|
}
|
|
924
938
|
function _useField(name, rules, opts) {
|
|
925
|
-
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);
|
|
926
940
|
const form = !standalone ? injectWithSelf(FormContextKey) : undefined;
|
|
941
|
+
// a flag indicating if the field is about to be removed/unmounted.
|
|
942
|
+
let markedForRemoval = false;
|
|
927
943
|
const { id, value, initialValue, meta, setState, errors, errorMessage } = useFieldState(name, {
|
|
928
944
|
modelValue,
|
|
929
945
|
standalone,
|
|
930
946
|
});
|
|
947
|
+
if (syncVModel) {
|
|
948
|
+
useVModel({ value, prop: modelPropName, handleChange });
|
|
949
|
+
}
|
|
931
950
|
/**
|
|
932
951
|
* Handles common onBlur meta update
|
|
933
952
|
*/
|
|
@@ -940,7 +959,7 @@
|
|
|
940
959
|
if (schema && !isYupValidator(schema)) {
|
|
941
960
|
rulesValue = extractRuleFromSchema(schema, vue.unref(name)) || rulesValue;
|
|
942
961
|
}
|
|
943
|
-
if (isYupValidator(rulesValue) || isCallable(rulesValue)) {
|
|
962
|
+
if (isYupValidator(rulesValue) || isCallable(rulesValue) || Array.isArray(rulesValue)) {
|
|
944
963
|
return rulesValue;
|
|
945
964
|
}
|
|
946
965
|
return normalizeRules(rulesValue);
|
|
@@ -960,12 +979,19 @@
|
|
|
960
979
|
meta.pending = true;
|
|
961
980
|
meta.validated = true;
|
|
962
981
|
const result = await validateCurrentValue('validated-only');
|
|
982
|
+
if (markedForRemoval) {
|
|
983
|
+
result.valid = true;
|
|
984
|
+
result.errors = [];
|
|
985
|
+
}
|
|
963
986
|
setState({ errors: result.errors });
|
|
964
987
|
meta.pending = false;
|
|
965
988
|
return result;
|
|
966
989
|
}
|
|
967
990
|
async function validateValidStateOnly() {
|
|
968
991
|
const result = await validateCurrentValue('silent');
|
|
992
|
+
if (markedForRemoval) {
|
|
993
|
+
result.valid = true;
|
|
994
|
+
}
|
|
969
995
|
meta.valid = result.valid;
|
|
970
996
|
return result;
|
|
971
997
|
}
|
|
@@ -979,13 +1005,13 @@
|
|
|
979
1005
|
return validateValidStateOnly();
|
|
980
1006
|
}
|
|
981
1007
|
// Common input/change event handler
|
|
982
|
-
|
|
1008
|
+
function handleChange(e, shouldValidate = true) {
|
|
983
1009
|
const newValue = normalizeEventValue(e);
|
|
984
1010
|
value.value = newValue;
|
|
985
1011
|
if (!validateOnValueUpdate && shouldValidate) {
|
|
986
1012
|
validateWithStateMutation();
|
|
987
1013
|
}
|
|
988
|
-
}
|
|
1014
|
+
}
|
|
989
1015
|
// Runs the initial validation
|
|
990
1016
|
vue.onMounted(() => {
|
|
991
1017
|
if (validateOnMount) {
|
|
@@ -1002,7 +1028,13 @@
|
|
|
1002
1028
|
}
|
|
1003
1029
|
let unwatchValue;
|
|
1004
1030
|
function watchValue() {
|
|
1005
|
-
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
|
+
}, {
|
|
1006
1038
|
deep: true,
|
|
1007
1039
|
});
|
|
1008
1040
|
}
|
|
@@ -1043,6 +1075,7 @@
|
|
|
1043
1075
|
checkedValue,
|
|
1044
1076
|
uncheckedValue,
|
|
1045
1077
|
bails,
|
|
1078
|
+
keepValueOnUnmount,
|
|
1046
1079
|
resetField,
|
|
1047
1080
|
handleReset: () => resetField(),
|
|
1048
1081
|
validate: validate$1,
|
|
@@ -1071,13 +1104,14 @@
|
|
|
1071
1104
|
// associate the field with the given form
|
|
1072
1105
|
form.register(field);
|
|
1073
1106
|
vue.onBeforeUnmount(() => {
|
|
1107
|
+
markedForRemoval = true;
|
|
1074
1108
|
form.unregister(field);
|
|
1075
1109
|
});
|
|
1076
1110
|
// extract cross-field dependencies in a computed prop
|
|
1077
1111
|
const dependencies = vue.computed(() => {
|
|
1078
1112
|
const rulesVal = normalizedRules.value;
|
|
1079
1113
|
// is falsy, a function schema or a yup schema
|
|
1080
|
-
if (!rulesVal || isCallable(rulesVal) || isYupValidator(rulesVal)) {
|
|
1114
|
+
if (!rulesVal || isCallable(rulesVal) || isYupValidator(rulesVal) || Array.isArray(rulesVal)) {
|
|
1081
1115
|
return {};
|
|
1082
1116
|
}
|
|
1083
1117
|
return Object.keys(rulesVal).reduce((acc, rule) => {
|
|
@@ -1119,6 +1153,9 @@
|
|
|
1119
1153
|
label: name,
|
|
1120
1154
|
validateOnValueUpdate: true,
|
|
1121
1155
|
standalone: false,
|
|
1156
|
+
keepValueOnUnmount: undefined,
|
|
1157
|
+
modelPropName: 'modelValue',
|
|
1158
|
+
syncVModel: true,
|
|
1122
1159
|
});
|
|
1123
1160
|
if (!opts) {
|
|
1124
1161
|
return defaults();
|
|
@@ -1150,8 +1187,8 @@
|
|
|
1150
1187
|
return Array.isArray(currentValue) ? currentValue.includes(checkedVal) : checkedVal === currentValue;
|
|
1151
1188
|
});
|
|
1152
1189
|
function handleCheckboxChange(e, shouldValidate = true) {
|
|
1153
|
-
var _a
|
|
1154
|
-
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)) {
|
|
1155
1192
|
return;
|
|
1156
1193
|
}
|
|
1157
1194
|
let newValue = normalizeEventValue(e);
|
|
@@ -1162,8 +1199,10 @@
|
|
|
1162
1199
|
handleChange(newValue, shouldValidate);
|
|
1163
1200
|
}
|
|
1164
1201
|
vue.onBeforeUnmount(() => {
|
|
1165
|
-
|
|
1166
|
-
|
|
1202
|
+
var _a, _b;
|
|
1203
|
+
const shouldKeepValue = (_b = (_a = vue.unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : vue.unref(form === null || form === void 0 ? void 0 : form.keepValuesOnUnmount)) !== null && _b !== void 0 ? _b : false;
|
|
1204
|
+
// toggles the checkbox value if it was checked and the unset behavior is set
|
|
1205
|
+
if (checked.value && !shouldKeepValue) {
|
|
1167
1206
|
handleCheckboxChange(vue.unref(checkedValue), false);
|
|
1168
1207
|
}
|
|
1169
1208
|
});
|
|
@@ -1172,6 +1211,38 @@
|
|
|
1172
1211
|
uncheckedValue, handleChange: handleCheckboxChange });
|
|
1173
1212
|
}
|
|
1174
1213
|
return patchCheckboxApi(_useField(name, rules, opts));
|
|
1214
|
+
}
|
|
1215
|
+
function useVModel({ prop, value, handleChange }) {
|
|
1216
|
+
const vm = vue.getCurrentInstance();
|
|
1217
|
+
/* istanbul ignore next */
|
|
1218
|
+
if (!vm) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
const propName = prop || 'modelValue';
|
|
1222
|
+
const emitName = `update:${propName}`;
|
|
1223
|
+
// Component doesn't have a model prop setup (must be defined on the props)
|
|
1224
|
+
if (!(propName in vm.props)) {
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
vue.watch(value, newValue => {
|
|
1228
|
+
if (es6(newValue, getCurrentModelValue(vm, propName))) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
vm.emit(emitName, newValue);
|
|
1232
|
+
});
|
|
1233
|
+
vue.watch(() => getCurrentModelValue(vm, propName), propValue => {
|
|
1234
|
+
if (propValue === IS_ABSENT && value.value === undefined) {
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
const newValue = propValue === IS_ABSENT ? undefined : propValue;
|
|
1238
|
+
if (es6(newValue, applyModelModifiers(value.value, vm.props.modelModifiers))) {
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
handleChange(newValue);
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
function getCurrentModelValue(vm, propName) {
|
|
1245
|
+
return vm.props[propName];
|
|
1175
1246
|
}
|
|
1176
1247
|
|
|
1177
1248
|
const FieldImpl = vue.defineComponent({
|
|
@@ -1238,13 +1309,17 @@
|
|
|
1238
1309
|
type: Boolean,
|
|
1239
1310
|
default: false,
|
|
1240
1311
|
},
|
|
1312
|
+
keepValue: {
|
|
1313
|
+
type: Boolean,
|
|
1314
|
+
default: undefined,
|
|
1315
|
+
},
|
|
1241
1316
|
},
|
|
1242
1317
|
setup(props, ctx) {
|
|
1243
1318
|
const rules = vue.toRef(props, 'rules');
|
|
1244
1319
|
const name = vue.toRef(props, 'name');
|
|
1245
1320
|
const label = vue.toRef(props, 'label');
|
|
1246
1321
|
const uncheckedValue = vue.toRef(props, 'uncheckedValue');
|
|
1247
|
-
const
|
|
1322
|
+
const keepValue = vue.toRef(props, 'keepValue');
|
|
1248
1323
|
const { errors, value, errorMessage, validate: validateField, handleChange, handleBlur, setTouched, resetField, handleReset, meta, checked, setErrors, } = useField(name, rules, {
|
|
1249
1324
|
validateOnMount: props.validateOnMount,
|
|
1250
1325
|
bails: props.bails,
|
|
@@ -1256,25 +1331,22 @@
|
|
|
1256
1331
|
uncheckedValue,
|
|
1257
1332
|
label,
|
|
1258
1333
|
validateOnValueUpdate: false,
|
|
1334
|
+
keepValueOnUnmount: keepValue,
|
|
1259
1335
|
});
|
|
1260
1336
|
// If there is a v-model applied on the component we need to emit the `update:modelValue` whenever the value binding changes
|
|
1261
|
-
const onChangeHandler =
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
}
|
|
1266
|
-
: handleChange;
|
|
1337
|
+
const onChangeHandler = function handleChangeWithModel(e, shouldValidate = true) {
|
|
1338
|
+
handleChange(e, shouldValidate);
|
|
1339
|
+
ctx.emit('update:modelValue', value.value);
|
|
1340
|
+
};
|
|
1267
1341
|
const handleInput = (e) => {
|
|
1268
1342
|
if (!hasCheckedAttr(ctx.attrs.type)) {
|
|
1269
1343
|
value.value = normalizeEventValue(e);
|
|
1270
1344
|
}
|
|
1271
1345
|
};
|
|
1272
|
-
const onInputHandler =
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
}
|
|
1277
|
-
: handleInput;
|
|
1346
|
+
const onInputHandler = function handleInputWithModel(e) {
|
|
1347
|
+
handleInput(e);
|
|
1348
|
+
ctx.emit('update:modelValue', value.value);
|
|
1349
|
+
};
|
|
1278
1350
|
const fieldProps = vue.computed(() => {
|
|
1279
1351
|
const { validateOnInput, validateOnChange, validateOnBlur, validateOnModelUpdate } = resolveValidationTriggers(props);
|
|
1280
1352
|
const baseOnBlur = [handleBlur, ctx.attrs.onBlur, validateOnBlur ? validateField : undefined].filter(Boolean);
|
|
@@ -1286,32 +1358,16 @@
|
|
|
1286
1358
|
onInput: baseOnInput,
|
|
1287
1359
|
onChange: baseOnChange,
|
|
1288
1360
|
};
|
|
1289
|
-
|
|
1290
|
-
attrs['onUpdate:modelValue'] = [onChangeHandler];
|
|
1291
|
-
}
|
|
1361
|
+
attrs['onUpdate:modelValue'] = e => onChangeHandler(e, validateOnModelUpdate);
|
|
1292
1362
|
if (hasCheckedAttr(ctx.attrs.type) && checked) {
|
|
1293
1363
|
attrs.checked = checked.value;
|
|
1294
1364
|
}
|
|
1295
|
-
else {
|
|
1296
|
-
attrs.value = value.value;
|
|
1297
|
-
}
|
|
1298
1365
|
const tag = resolveTag(props, ctx);
|
|
1299
1366
|
if (shouldHaveValueBinding(tag, ctx.attrs)) {
|
|
1300
|
-
|
|
1367
|
+
attrs.value = value.value;
|
|
1301
1368
|
}
|
|
1302
1369
|
return attrs;
|
|
1303
1370
|
});
|
|
1304
|
-
const modelValue = vue.toRef(props, 'modelValue');
|
|
1305
|
-
vue.watch(modelValue, newModelValue => {
|
|
1306
|
-
// Don't attempt to sync absent values
|
|
1307
|
-
if (newModelValue === IS_ABSENT && value.value === undefined) {
|
|
1308
|
-
return;
|
|
1309
|
-
}
|
|
1310
|
-
if (newModelValue !== applyModifiers(value.value, props.modelModifiers)) {
|
|
1311
|
-
value.value = newModelValue === IS_ABSENT ? undefined : newModelValue;
|
|
1312
|
-
validateField();
|
|
1313
|
-
}
|
|
1314
|
-
});
|
|
1315
1371
|
function slotProps() {
|
|
1316
1372
|
return {
|
|
1317
1373
|
field: fieldProps.value,
|
|
@@ -1363,12 +1419,6 @@
|
|
|
1363
1419
|
validateOnModelUpdate: (_d = props.validateOnModelUpdate) !== null && _d !== void 0 ? _d : validateOnModelUpdate,
|
|
1364
1420
|
};
|
|
1365
1421
|
}
|
|
1366
|
-
function applyModifiers(value, modifiers) {
|
|
1367
|
-
if (modifiers.number) {
|
|
1368
|
-
return toNumber(value);
|
|
1369
|
-
}
|
|
1370
|
-
return value;
|
|
1371
|
-
}
|
|
1372
1422
|
function resolveInitialValue(props, ctx) {
|
|
1373
1423
|
// Gets the initial value either from `value` prop/attr or `v-model` binding (modelValue)
|
|
1374
1424
|
// For checkboxes and radio buttons it will always be the model value not the `value` attribute
|
|
@@ -1381,15 +1431,19 @@
|
|
|
1381
1431
|
|
|
1382
1432
|
let FORM_COUNTER = 0;
|
|
1383
1433
|
function useForm(opts) {
|
|
1434
|
+
var _a;
|
|
1384
1435
|
const formId = FORM_COUNTER++;
|
|
1436
|
+
// Prevents fields from double resetting their values, which causes checkboxes to toggle their initial value
|
|
1437
|
+
// TODO: This won't be needed if we centralize all the state inside the `form` for form inputs
|
|
1438
|
+
let RESET_LOCK = false;
|
|
1385
1439
|
// A lookup containing fields or field groups
|
|
1386
1440
|
const fieldsByPath = vue.ref({});
|
|
1387
1441
|
// If the form is currently submitting
|
|
1388
1442
|
const isSubmitting = vue.ref(false);
|
|
1389
1443
|
// The number of times the user tried to submit the form
|
|
1390
1444
|
const submitCount = vue.ref(0);
|
|
1391
|
-
//
|
|
1392
|
-
const
|
|
1445
|
+
// field arrays managed by this form
|
|
1446
|
+
const fieldArrays = [];
|
|
1393
1447
|
// a private ref for all form values
|
|
1394
1448
|
const formValues = vue.reactive(klona(vue.unref(opts === null || opts === void 0 ? void 0 : opts.initialValues) || {}));
|
|
1395
1449
|
// the source of errors for the form fields
|
|
@@ -1436,10 +1490,11 @@
|
|
|
1436
1490
|
// mutable non-reactive reference to initial errors
|
|
1437
1491
|
// we need this to process initial errors then unset them
|
|
1438
1492
|
const initialErrors = Object.assign({}, ((opts === null || opts === void 0 ? void 0 : opts.initialErrors) || {}));
|
|
1493
|
+
const keepValuesOnUnmount = (_a = opts === null || opts === void 0 ? void 0 : opts.keepValuesOnUnmount) !== null && _a !== void 0 ? _a : false;
|
|
1439
1494
|
// initial form values
|
|
1440
1495
|
const { initialValues, originalInitialValues, setInitialValues } = useFormInitialValues(fieldsByPath, formValues, opts === null || opts === void 0 ? void 0 : opts.initialValues);
|
|
1441
1496
|
// form meta aggregations
|
|
1442
|
-
const meta = useFormMeta(fieldsByPath, formValues,
|
|
1497
|
+
const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);
|
|
1443
1498
|
const schema = opts === null || opts === void 0 ? void 0 : opts.validationSchema;
|
|
1444
1499
|
const formCtx = {
|
|
1445
1500
|
formId,
|
|
@@ -1451,7 +1506,8 @@
|
|
|
1451
1506
|
submitCount,
|
|
1452
1507
|
meta,
|
|
1453
1508
|
isSubmitting,
|
|
1454
|
-
|
|
1509
|
+
fieldArrays,
|
|
1510
|
+
keepValuesOnUnmount,
|
|
1455
1511
|
validateSchema: vue.unref(schema) ? validateSchema : undefined,
|
|
1456
1512
|
validate,
|
|
1457
1513
|
register: registerField,
|
|
@@ -1469,6 +1525,7 @@
|
|
|
1469
1525
|
stageInitialValue,
|
|
1470
1526
|
unsetInitialValue,
|
|
1471
1527
|
setFieldInitialValue,
|
|
1528
|
+
useFieldModel,
|
|
1472
1529
|
};
|
|
1473
1530
|
function isFieldGroup(fieldOrGroup) {
|
|
1474
1531
|
return Array.isArray(fieldOrGroup);
|
|
@@ -1479,6 +1536,15 @@
|
|
|
1479
1536
|
}
|
|
1480
1537
|
return mutation(fieldOrGroup);
|
|
1481
1538
|
}
|
|
1539
|
+
function mutateAllFields(mutation) {
|
|
1540
|
+
Object.values(fieldsByPath.value).forEach(field => {
|
|
1541
|
+
if (!field) {
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
// avoid resetting the field values, because they should've been reset already.
|
|
1545
|
+
applyFieldMutation(field, mutation);
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1482
1548
|
/**
|
|
1483
1549
|
* Manually sets an error message on a specific field
|
|
1484
1550
|
*/
|
|
@@ -1503,15 +1569,15 @@
|
|
|
1503
1569
|
setInPath(formValues, field, clonedValue);
|
|
1504
1570
|
return;
|
|
1505
1571
|
}
|
|
1506
|
-
// Multiple checkboxes, and only one of them got updated
|
|
1507
1572
|
if (isFieldGroup(fieldInstance) && ((_a = fieldInstance[0]) === null || _a === void 0 ? void 0 : _a.type) === 'checkbox' && !Array.isArray(value)) {
|
|
1573
|
+
// Multiple checkboxes, and only one of them got updated
|
|
1508
1574
|
const newValue = klona(resolveNextCheckboxValue(getFromPath(formValues, field) || [], value, undefined));
|
|
1509
1575
|
setInPath(formValues, field, newValue);
|
|
1510
1576
|
return;
|
|
1511
1577
|
}
|
|
1512
1578
|
let newValue = value;
|
|
1513
1579
|
// Single Checkbox: toggles the field value unless the field is being reset then force it
|
|
1514
|
-
if (!isFieldGroup(fieldInstance) && fieldInstance.type === 'checkbox' && !force) {
|
|
1580
|
+
if (!isFieldGroup(fieldInstance) && fieldInstance.type === 'checkbox' && !force && !RESET_LOCK) {
|
|
1515
1581
|
newValue = klona(resolveNextCheckboxValue(getFromPath(formValues, field), value, vue.unref(fieldInstance.uncheckedValue)));
|
|
1516
1582
|
}
|
|
1517
1583
|
setInPath(formValues, field, newValue);
|
|
@@ -1529,7 +1595,22 @@
|
|
|
1529
1595
|
setFieldValue(path, fields[path]);
|
|
1530
1596
|
});
|
|
1531
1597
|
// regenerate the arrays when the form values change
|
|
1532
|
-
|
|
1598
|
+
fieldArrays.forEach(f => f && f.reset());
|
|
1599
|
+
}
|
|
1600
|
+
function createModel(path) {
|
|
1601
|
+
const { value } = _useFieldValue(path);
|
|
1602
|
+
vue.watch(value, () => {
|
|
1603
|
+
if (!fieldExists(vue.unref(path))) {
|
|
1604
|
+
validate({ mode: 'validated-only' });
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
return value;
|
|
1608
|
+
}
|
|
1609
|
+
function useFieldModel(path) {
|
|
1610
|
+
if (!Array.isArray(path)) {
|
|
1611
|
+
return createModel(path);
|
|
1612
|
+
}
|
|
1613
|
+
return path.map(createModel);
|
|
1533
1614
|
}
|
|
1534
1615
|
/**
|
|
1535
1616
|
* Sets the touched meta state on a field
|
|
@@ -1552,6 +1633,7 @@
|
|
|
1552
1633
|
* Resets all fields
|
|
1553
1634
|
*/
|
|
1554
1635
|
function resetForm(state) {
|
|
1636
|
+
RESET_LOCK = true;
|
|
1555
1637
|
// set initial values if provided
|
|
1556
1638
|
if (state === null || state === void 0 ? void 0 : state.values) {
|
|
1557
1639
|
setInitialValues(state.values);
|
|
@@ -1563,17 +1645,16 @@
|
|
|
1563
1645
|
// otherwise clean the current values
|
|
1564
1646
|
setValues(originalInitialValues.value);
|
|
1565
1647
|
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
return;
|
|
1569
|
-
}
|
|
1570
|
-
applyFieldMutation(field, f => f.resetField());
|
|
1571
|
-
});
|
|
1648
|
+
// avoid resetting the field values, because they should've been reset already.
|
|
1649
|
+
mutateAllFields(f => f.resetField());
|
|
1572
1650
|
if (state === null || state === void 0 ? void 0 : state.touched) {
|
|
1573
1651
|
setTouched(state.touched);
|
|
1574
1652
|
}
|
|
1575
1653
|
setErrors((state === null || state === void 0 ? void 0 : state.errors) || {});
|
|
1576
1654
|
submitCount.value = (state === null || state === void 0 ? void 0 : state.submitCount) || 0;
|
|
1655
|
+
vue.nextTick(() => {
|
|
1656
|
+
RESET_LOCK = false;
|
|
1657
|
+
});
|
|
1577
1658
|
}
|
|
1578
1659
|
function insertFieldAtPath(field, path) {
|
|
1579
1660
|
const rawField = vue.markRaw(field);
|
|
@@ -1629,6 +1710,8 @@
|
|
|
1629
1710
|
insertFieldAtPath(field, newPath);
|
|
1630
1711
|
// re-validate if either path had errors before
|
|
1631
1712
|
if (errors.value[oldPath] || errors.value[newPath]) {
|
|
1713
|
+
// clear up both paths errors
|
|
1714
|
+
setFieldError(oldPath, undefined);
|
|
1632
1715
|
validateField(newPath);
|
|
1633
1716
|
}
|
|
1634
1717
|
// clean up the old path if no other field is sharing that name
|
|
@@ -1651,18 +1734,32 @@
|
|
|
1651
1734
|
}
|
|
1652
1735
|
function unregisterField(field) {
|
|
1653
1736
|
const fieldName = vue.unref(field.name);
|
|
1737
|
+
const fieldInstance = fieldsByPath.value[fieldName];
|
|
1738
|
+
const isGroup = !!fieldInstance && isFieldGroup(fieldInstance);
|
|
1654
1739
|
removeFieldFromPath(field, fieldName);
|
|
1655
1740
|
vue.nextTick(() => {
|
|
1741
|
+
var _a;
|
|
1656
1742
|
// clears a field error on unmounted
|
|
1657
1743
|
// we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes
|
|
1658
1744
|
// #3384
|
|
1659
1745
|
if (!fieldExists(fieldName)) {
|
|
1660
1746
|
setFieldError(fieldName, undefined);
|
|
1747
|
+
// Checks if the field was configured to be unset during unmount or not
|
|
1748
|
+
// Checks both the form-level config and field-level one
|
|
1749
|
+
// Field has the priority if it is set, otherwise it goes to the form settings
|
|
1750
|
+
const shouldKeepValue = (_a = vue.unref(field.keepValueOnUnmount)) !== null && _a !== void 0 ? _a : vue.unref(keepValuesOnUnmount);
|
|
1751
|
+
if (shouldKeepValue) {
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
if (isGroup && !isEmptyContainer(getFromPath(formValues, fieldName))) {
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1661
1757
|
unsetPath(formValues, fieldName);
|
|
1662
1758
|
}
|
|
1663
1759
|
});
|
|
1664
1760
|
}
|
|
1665
1761
|
async function validate(opts) {
|
|
1762
|
+
mutateAllFields(f => (f.meta.validated = true));
|
|
1666
1763
|
if (formCtx.validateSchema) {
|
|
1667
1764
|
return formCtx.validateSchema((opts === null || opts === void 0 ? void 0 : opts.mode) || 'force');
|
|
1668
1765
|
}
|
|
@@ -1763,9 +1860,12 @@
|
|
|
1763
1860
|
/**
|
|
1764
1861
|
* Sneaky function to set initial field values
|
|
1765
1862
|
*/
|
|
1766
|
-
function stageInitialValue(path, value) {
|
|
1863
|
+
function stageInitialValue(path, value, updateOriginal = false) {
|
|
1767
1864
|
setInPath(formValues, path, value);
|
|
1768
1865
|
setFieldInitialValue(path, value);
|
|
1866
|
+
if (updateOriginal) {
|
|
1867
|
+
setInPath(originalInitialValues.value, path, klona(value));
|
|
1868
|
+
}
|
|
1769
1869
|
}
|
|
1770
1870
|
async function _validateSchema() {
|
|
1771
1871
|
const schemaValue = vue.unref(schema);
|
|
@@ -1782,10 +1882,12 @@
|
|
|
1782
1882
|
}
|
|
1783
1883
|
/**
|
|
1784
1884
|
* Batches validation runs in 5ms batches
|
|
1885
|
+
* Must have two distinct batch queues to make sure they don't override each other settings #3783
|
|
1785
1886
|
*/
|
|
1786
|
-
const
|
|
1887
|
+
const debouncedSilentValidation = debounceAsync(_validateSchema, 5);
|
|
1888
|
+
const debouncedValidation = debounceAsync(_validateSchema, 5);
|
|
1787
1889
|
async function validateSchema(mode) {
|
|
1788
|
-
const formResult = await
|
|
1890
|
+
const formResult = await (mode === 'silent' ? debouncedSilentValidation() : debouncedValidation());
|
|
1789
1891
|
// fields by id lookup
|
|
1790
1892
|
const fieldsById = formCtx.fieldsByPath.value || {};
|
|
1791
1893
|
// errors fields names, we need it to also check if custom errors are updated
|
|
@@ -1875,6 +1977,7 @@
|
|
|
1875
1977
|
setValues,
|
|
1876
1978
|
setFieldTouched,
|
|
1877
1979
|
setTouched,
|
|
1980
|
+
useFieldModel,
|
|
1878
1981
|
};
|
|
1879
1982
|
}
|
|
1880
1983
|
/**
|
|
@@ -1889,16 +1992,23 @@
|
|
|
1889
1992
|
const isDirty = vue.computed(() => {
|
|
1890
1993
|
return !es6(currentValues, vue.unref(initialValues));
|
|
1891
1994
|
});
|
|
1892
|
-
|
|
1995
|
+
function calculateFlags() {
|
|
1893
1996
|
const fields = Object.values(fieldsByPath.value).flat(1).filter(Boolean);
|
|
1894
1997
|
return keysOf(MERGE_STRATEGIES).reduce((acc, flag) => {
|
|
1895
1998
|
const mergeMethod = MERGE_STRATEGIES[flag];
|
|
1896
1999
|
acc[flag] = fields[mergeMethod](field => field.meta[flag]);
|
|
1897
2000
|
return acc;
|
|
1898
2001
|
}, {});
|
|
2002
|
+
}
|
|
2003
|
+
const flags = vue.reactive(calculateFlags());
|
|
2004
|
+
vue.watchEffect(() => {
|
|
2005
|
+
const value = calculateFlags();
|
|
2006
|
+
flags.touched = value.touched;
|
|
2007
|
+
flags.valid = value.valid;
|
|
2008
|
+
flags.pending = value.pending;
|
|
1899
2009
|
});
|
|
1900
2010
|
return vue.computed(() => {
|
|
1901
|
-
return Object.assign(Object.assign({ initialValues: vue.unref(initialValues) }, flags
|
|
2011
|
+
return Object.assign(Object.assign({ initialValues: vue.unref(initialValues) }, flags), { valid: flags.valid && !keysOf(errors.value).length, dirty: isDirty.value });
|
|
1902
2012
|
});
|
|
1903
2013
|
}
|
|
1904
2014
|
/**
|
|
@@ -2019,16 +2129,22 @@
|
|
|
2019
2129
|
type: Function,
|
|
2020
2130
|
default: undefined,
|
|
2021
2131
|
},
|
|
2132
|
+
keepValues: {
|
|
2133
|
+
type: Boolean,
|
|
2134
|
+
default: false,
|
|
2135
|
+
},
|
|
2022
2136
|
},
|
|
2023
2137
|
setup(props, ctx) {
|
|
2024
2138
|
const initialValues = vue.toRef(props, 'initialValues');
|
|
2025
2139
|
const validationSchema = vue.toRef(props, 'validationSchema');
|
|
2140
|
+
const keepValues = vue.toRef(props, 'keepValues');
|
|
2026
2141
|
const { errors, values, meta, isSubmitting, submitCount, validate, validateField, handleReset, resetForm, handleSubmit, submitForm, setErrors, setFieldError, setFieldValue, setValues, setFieldTouched, setTouched, } = useForm({
|
|
2027
2142
|
validationSchema: validationSchema.value ? validationSchema : undefined,
|
|
2028
2143
|
initialValues,
|
|
2029
2144
|
initialErrors: props.initialErrors,
|
|
2030
2145
|
initialTouched: props.initialTouched,
|
|
2031
2146
|
validateOnMount: props.validateOnMount,
|
|
2147
|
+
keepValuesOnUnmount: keepValues,
|
|
2032
2148
|
});
|
|
2033
2149
|
const onSubmit = props.onSubmit ? handleSubmit(props.onSubmit, props.onInvalidSubmit) : submitForm;
|
|
2034
2150
|
function handleFormReset(e) {
|
|
@@ -2098,15 +2214,13 @@
|
|
|
2098
2214
|
});
|
|
2099
2215
|
const Form = FormImpl;
|
|
2100
2216
|
|
|
2101
|
-
let FIELD_ARRAY_COUNTER = 0;
|
|
2102
2217
|
function useFieldArray(arrayPath) {
|
|
2103
|
-
const id = FIELD_ARRAY_COUNTER++;
|
|
2104
2218
|
const form = injectWithSelf(FormContextKey, undefined);
|
|
2105
2219
|
const fields = vue.ref([]);
|
|
2106
2220
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
2107
2221
|
const noOp = () => { };
|
|
2108
2222
|
const noOpApi = {
|
|
2109
|
-
fields
|
|
2223
|
+
fields,
|
|
2110
2224
|
remove: noOp,
|
|
2111
2225
|
push: noOp,
|
|
2112
2226
|
swap: noOp,
|
|
@@ -2114,6 +2228,7 @@
|
|
|
2114
2228
|
update: noOp,
|
|
2115
2229
|
replace: noOp,
|
|
2116
2230
|
prepend: noOp,
|
|
2231
|
+
move: noOp,
|
|
2117
2232
|
};
|
|
2118
2233
|
if (!form) {
|
|
2119
2234
|
warn('FieldArray requires being a child of `<Form/>` or `useForm` being called before it. Array fields may not work correctly');
|
|
@@ -2123,9 +2238,13 @@
|
|
|
2123
2238
|
warn('FieldArray requires a field path to be provided, did you forget to pass the `name` prop?');
|
|
2124
2239
|
return noOpApi;
|
|
2125
2240
|
}
|
|
2241
|
+
const alreadyExists = form.fieldArrays.find(a => vue.unref(a.path) === vue.unref(arrayPath));
|
|
2242
|
+
if (alreadyExists) {
|
|
2243
|
+
return alreadyExists;
|
|
2244
|
+
}
|
|
2126
2245
|
let entryCounter = 0;
|
|
2127
2246
|
function initFields() {
|
|
2128
|
-
const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, vue.unref(arrayPath), []);
|
|
2247
|
+
const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, vue.unref(arrayPath), []) || [];
|
|
2129
2248
|
fields.value = currentValues.map(createEntry);
|
|
2130
2249
|
updateEntryFlags();
|
|
2131
2250
|
}
|
|
@@ -2142,10 +2261,20 @@
|
|
|
2142
2261
|
const key = entryCounter++;
|
|
2143
2262
|
const entry = {
|
|
2144
2263
|
key,
|
|
2145
|
-
value: vue.computed(
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2264
|
+
value: vue.computed({
|
|
2265
|
+
get() {
|
|
2266
|
+
const currentValues = getFromPath(form === null || form === void 0 ? void 0 : form.values, vue.unref(arrayPath), []) || [];
|
|
2267
|
+
const idx = fields.value.findIndex(e => e.key === key);
|
|
2268
|
+
return idx === -1 ? value : currentValues[idx];
|
|
2269
|
+
},
|
|
2270
|
+
set(value) {
|
|
2271
|
+
const idx = fields.value.findIndex(e => e.key === key);
|
|
2272
|
+
if (idx === -1) {
|
|
2273
|
+
warn(`Attempting to update a non-existent array item`);
|
|
2274
|
+
return;
|
|
2275
|
+
}
|
|
2276
|
+
update(idx, value);
|
|
2277
|
+
},
|
|
2149
2278
|
}),
|
|
2150
2279
|
isFirst: false,
|
|
2151
2280
|
isLast: false,
|
|
@@ -2182,7 +2311,7 @@
|
|
|
2182
2311
|
function swap(indexA, indexB) {
|
|
2183
2312
|
const pathName = vue.unref(arrayPath);
|
|
2184
2313
|
const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
|
|
2185
|
-
if (!Array.isArray(pathValue) || !
|
|
2314
|
+
if (!Array.isArray(pathValue) || !(indexA in pathValue) || !(indexB in pathValue)) {
|
|
2186
2315
|
return;
|
|
2187
2316
|
}
|
|
2188
2317
|
const newValue = [...pathValue];
|
|
@@ -2238,14 +2367,26 @@
|
|
|
2238
2367
|
fields.value.unshift(createEntry(value));
|
|
2239
2368
|
updateEntryFlags();
|
|
2240
2369
|
}
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
fields
|
|
2370
|
+
function move(oldIdx, newIdx) {
|
|
2371
|
+
const pathName = vue.unref(arrayPath);
|
|
2372
|
+
const pathValue = getFromPath(form === null || form === void 0 ? void 0 : form.values, pathName);
|
|
2373
|
+
const newValue = isNullOrUndefined(pathValue) ? [] : [...pathValue];
|
|
2374
|
+
if (!Array.isArray(pathValue) || !(oldIdx in pathValue) || !(newIdx in pathValue)) {
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
const newFields = [...fields.value];
|
|
2378
|
+
const movedItem = newFields[oldIdx];
|
|
2379
|
+
newFields.splice(oldIdx, 1);
|
|
2380
|
+
newFields.splice(newIdx, 0, movedItem);
|
|
2381
|
+
const movedValue = newValue[oldIdx];
|
|
2382
|
+
newValue.splice(oldIdx, 1);
|
|
2383
|
+
newValue.splice(newIdx, 0, movedValue);
|
|
2384
|
+
form === null || form === void 0 ? void 0 : form.setFieldValue(pathName, newValue);
|
|
2385
|
+
fields.value = newFields;
|
|
2386
|
+
updateEntryFlags();
|
|
2387
|
+
}
|
|
2388
|
+
const fieldArrayCtx = {
|
|
2389
|
+
fields,
|
|
2249
2390
|
remove,
|
|
2250
2391
|
push,
|
|
2251
2392
|
swap,
|
|
@@ -2253,7 +2394,16 @@
|
|
|
2253
2394
|
update,
|
|
2254
2395
|
replace,
|
|
2255
2396
|
prepend,
|
|
2397
|
+
move,
|
|
2256
2398
|
};
|
|
2399
|
+
form.fieldArrays.push(Object.assign({ path: arrayPath, reset: initFields }, fieldArrayCtx));
|
|
2400
|
+
vue.onBeforeUnmount(() => {
|
|
2401
|
+
const idx = form.fieldArrays.findIndex(i => vue.unref(i.path) === vue.unref(arrayPath));
|
|
2402
|
+
if (idx >= 0) {
|
|
2403
|
+
form.fieldArrays.splice(idx, 1);
|
|
2404
|
+
}
|
|
2405
|
+
});
|
|
2406
|
+
return fieldArrayCtx;
|
|
2257
2407
|
}
|
|
2258
2408
|
|
|
2259
2409
|
const FieldArrayImpl = vue.defineComponent({
|
|
@@ -2266,7 +2416,7 @@
|
|
|
2266
2416
|
},
|
|
2267
2417
|
},
|
|
2268
2418
|
setup(props, ctx) {
|
|
2269
|
-
const { push, remove, swap, insert, replace, update, prepend, fields } = useFieldArray(vue.toRef(props, 'name'));
|
|
2419
|
+
const { push, remove, swap, insert, replace, update, prepend, move, fields } = useFieldArray(vue.toRef(props, 'name'));
|
|
2270
2420
|
function slotProps() {
|
|
2271
2421
|
return {
|
|
2272
2422
|
fields: fields.value,
|
|
@@ -2277,6 +2427,7 @@
|
|
|
2277
2427
|
update,
|
|
2278
2428
|
replace,
|
|
2279
2429
|
prepend,
|
|
2430
|
+
move,
|
|
2280
2431
|
};
|
|
2281
2432
|
}
|
|
2282
2433
|
ctx.expose({
|
|
@@ -2287,6 +2438,7 @@
|
|
|
2287
2438
|
update,
|
|
2288
2439
|
replace,
|
|
2289
2440
|
prepend,
|
|
2441
|
+
move,
|
|
2290
2442
|
});
|
|
2291
2443
|
return () => {
|
|
2292
2444
|
const children = normalizeChildren(undefined, ctx, slotProps);
|
|
@@ -2592,6 +2744,7 @@
|
|
|
2592
2744
|
exports.FieldContextKey = FieldContextKey;
|
|
2593
2745
|
exports.Form = Form;
|
|
2594
2746
|
exports.FormContextKey = FormContextKey;
|
|
2747
|
+
exports.IS_ABSENT = IS_ABSENT;
|
|
2595
2748
|
exports.configure = configure;
|
|
2596
2749
|
exports.defineRule = defineRule;
|
|
2597
2750
|
exports.useField = useField;
|