@hi-ui/form 4.2.2 → 4.3.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/CHANGELOG.md +6 -0
- package/lib/cjs/FormItem.js +5 -1
- package/lib/cjs/use-form.js +28 -3
- package/lib/esm/FormItem.js +5 -1
- package/lib/esm/use-form.js +29 -4
- package/lib/types/context.d.ts +3 -1
- package/lib/types/use-form.d.ts +7 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @hi-ui/form
|
|
2
2
|
|
|
3
|
+
## 4.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#3006](https://github.com/XiaoMi/hiui/pull/3006) [`4540c217a`](https://github.com/XiaoMi/hiui/commit/4540c217ade6749c38ee58cefcfe94322889b929) Thanks [@zyprepare](https://github.com/zyprepare)! - feat: Add scrollToFirstError api
|
|
8
|
+
|
|
3
9
|
## 4.2.2
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/lib/cjs/FormItem.js
CHANGED
|
@@ -43,7 +43,8 @@ var FormItem = function FormItem(_a) {
|
|
|
43
43
|
rest = tslib.__rest(_a, ["className", "children", "field", "valueType", "rules", "valuePropName", "valueChangeFuncPropName", "valueDispatchTransform", "valueConnectTransform", "validateTrigger", "render"]);
|
|
44
44
|
var _useFormContext = context.useFormContext(),
|
|
45
45
|
prefixCls = _useFormContext.prefixCls,
|
|
46
|
-
showRequiredOnValidateRequired = _useFormContext.showRequiredOnValidateRequired
|
|
46
|
+
showRequiredOnValidateRequired = _useFormContext.showRequiredOnValidateRequired,
|
|
47
|
+
formItemsRef = _useFormContext.formItemsRef;
|
|
47
48
|
var fieldRules = useFormField.useFiledRules({
|
|
48
49
|
field: field,
|
|
49
50
|
rules: rules,
|
|
@@ -59,6 +60,9 @@ var FormItem = function FormItem(_a) {
|
|
|
59
60
|
return required;
|
|
60
61
|
}, [required, showRequiredOnValidateRequired, fieldRules]);
|
|
61
62
|
return /*#__PURE__*/React__default["default"].createElement(FormLabel.FormLabel, Object.assign({}, rest, {
|
|
63
|
+
ref: function ref(_ref) {
|
|
64
|
+
field && formItemsRef.current.set(field.toString(), _ref);
|
|
65
|
+
},
|
|
62
66
|
required: showRequired,
|
|
63
67
|
// @ts-ignore
|
|
64
68
|
formMessage: /*#__PURE__*/React__default["default"].createElement(FormMessage.FormMessage, {
|
package/lib/cjs/use-form.js
CHANGED
|
@@ -17,6 +17,7 @@ var _regeneratorRuntime = require('@babel/runtime/regenerator');
|
|
|
17
17
|
var tslib = require('tslib');
|
|
18
18
|
var index = require('./utils/index.js');
|
|
19
19
|
var React = require('react');
|
|
20
|
+
var scrollIntoView = require('scroll-into-view-if-needed');
|
|
20
21
|
var useLatest = require('@hi-ui/use-latest');
|
|
21
22
|
var typeAssertion = require('@hi-ui/type-assertion');
|
|
22
23
|
var funcUtils = require('@hi-ui/func-utils');
|
|
@@ -29,6 +30,7 @@ function _interopDefaultCompat(e) {
|
|
|
29
30
|
}
|
|
30
31
|
var _regeneratorRuntime__default = /*#__PURE__*/_interopDefaultCompat(_regeneratorRuntime);
|
|
31
32
|
var React__default = /*#__PURE__*/_interopDefaultCompat(React);
|
|
33
|
+
var scrollIntoView__default = /*#__PURE__*/_interopDefaultCompat(scrollIntoView);
|
|
32
34
|
var EMPTY_RULES = {};
|
|
33
35
|
var EMPTY_ERRORS = {};
|
|
34
36
|
var EMPTY_TOUCHED = {};
|
|
@@ -51,7 +53,8 @@ var useForm = function useForm(_a) {
|
|
|
51
53
|
validateAfterTouched = _a$validateAfterTouch === void 0 ? false : _a$validateAfterTouch,
|
|
52
54
|
_a$validateTrigger = _a.validateTrigger,
|
|
53
55
|
validateTriggerProp = _a$validateTrigger === void 0 ? DEFAULT_VALIDATE_TRIGGER : _a$validateTrigger,
|
|
54
|
-
|
|
56
|
+
scrollToFirstError = _a.scrollToFirstError,
|
|
57
|
+
rest = tslib.__rest(_a, ["initialValues", "initialErrors", "initialTouched", "lazyValidate", "onValuesChange", "onReset", "onSubmit", "rules", "validateAfterTouched", "validateTrigger", "scrollToFirstError"]);
|
|
55
58
|
/**
|
|
56
59
|
* 处理校验触发器,保证 memo 依赖的是数组每个项,避免无效重渲染
|
|
57
60
|
*/
|
|
@@ -60,6 +63,10 @@ var useForm = function useForm(_a) {
|
|
|
60
63
|
var validateTriggersMemo = React.useMemo(function () {
|
|
61
64
|
return validateTrigger;
|
|
62
65
|
}, validateTrigger);
|
|
66
|
+
var formItemsMp = React.useMemo(function () {
|
|
67
|
+
return new Map();
|
|
68
|
+
}, []);
|
|
69
|
+
var formItemsRef = React.useRef(formItemsMp);
|
|
63
70
|
/**
|
|
64
71
|
* 收集 Field 的校验器注册表
|
|
65
72
|
*/
|
|
@@ -85,6 +92,18 @@ var useForm = function useForm(_a) {
|
|
|
85
92
|
// const getFieldNames = useCallback(() => Object.keys(formStateRef.current.values as any), [
|
|
86
93
|
// formStateRef,
|
|
87
94
|
// ])
|
|
95
|
+
var getFormItemNode = React.useCallback(function (fieldName) {
|
|
96
|
+
return formItemsRef.current.get(fieldName.toString());
|
|
97
|
+
}, []);
|
|
98
|
+
var scrollToNode = React.useCallback(function (fieldName, options) {
|
|
99
|
+
if (options === void 0) {
|
|
100
|
+
options = {};
|
|
101
|
+
}
|
|
102
|
+
scrollIntoView__default["default"](getFormItemNode(fieldName), Object.assign({
|
|
103
|
+
scrollMode: 'if-needed',
|
|
104
|
+
block: 'nearest'
|
|
105
|
+
}, options));
|
|
106
|
+
}, [getFormItemNode]);
|
|
88
107
|
var getFieldValue = React.useCallback(function (fieldName) {
|
|
89
108
|
return objectUtils.getNested(formStateRef.current.values, fieldName);
|
|
90
109
|
}, [formStateRef]);
|
|
@@ -183,6 +202,7 @@ var useForm = function useForm(_a) {
|
|
|
183
202
|
type: 'SET_VALIDATING',
|
|
184
203
|
payload: true
|
|
185
204
|
});
|
|
205
|
+
var firstError = false;
|
|
186
206
|
return Promise.all(fieldNames.map(function (fieldName) {
|
|
187
207
|
var value = getFieldValue(fieldName);
|
|
188
208
|
var fieldValidation = getValidation(fieldName);
|
|
@@ -191,6 +211,10 @@ var useForm = function useForm(_a) {
|
|
|
191
211
|
}
|
|
192
212
|
// catch 错误,保证检验所有表单项
|
|
193
213
|
return fieldValidation.validate(value)["catch"](function (error) {
|
|
214
|
+
if (scrollToFirstError && !firstError) {
|
|
215
|
+
firstError = true;
|
|
216
|
+
scrollToNode(fieldName, _typeof(scrollToFirstError) === 'object' ? scrollToFirstError : {});
|
|
217
|
+
}
|
|
194
218
|
// 第一个出错,即退出校验
|
|
195
219
|
if (lazyValidate) {
|
|
196
220
|
throw error;
|
|
@@ -257,7 +281,7 @@ var useForm = function useForm(_a) {
|
|
|
257
281
|
});
|
|
258
282
|
return combinedError;
|
|
259
283
|
});
|
|
260
|
-
}, [
|
|
284
|
+
}, [getFieldValue, getRegisteredKeys, getValidation, lazyValidate, scrollToFirstError, scrollToNode]);
|
|
261
285
|
/**
|
|
262
286
|
* 控件值更新策略
|
|
263
287
|
*/
|
|
@@ -542,7 +566,8 @@ var useForm = function useForm(_a) {
|
|
|
542
566
|
validateValue: validateField,
|
|
543
567
|
getFieldsValue: getFieldsValue,
|
|
544
568
|
setFieldsValue: setFieldsValue,
|
|
545
|
-
getFieldsError: getFieldsError
|
|
569
|
+
getFieldsError: getFieldsError,
|
|
570
|
+
formItemsRef: formItemsRef
|
|
546
571
|
});
|
|
547
572
|
};
|
|
548
573
|
function formReducer(state, action) {
|
package/lib/esm/FormItem.js
CHANGED
|
@@ -31,7 +31,8 @@ var FormItem = function FormItem(_a) {
|
|
|
31
31
|
rest = __rest(_a, ["className", "children", "field", "valueType", "rules", "valuePropName", "valueChangeFuncPropName", "valueDispatchTransform", "valueConnectTransform", "validateTrigger", "render"]);
|
|
32
32
|
var _useFormContext = useFormContext(),
|
|
33
33
|
prefixCls = _useFormContext.prefixCls,
|
|
34
|
-
showRequiredOnValidateRequired = _useFormContext.showRequiredOnValidateRequired
|
|
34
|
+
showRequiredOnValidateRequired = _useFormContext.showRequiredOnValidateRequired,
|
|
35
|
+
formItemsRef = _useFormContext.formItemsRef;
|
|
35
36
|
var fieldRules = useFiledRules({
|
|
36
37
|
field: field,
|
|
37
38
|
rules: rules,
|
|
@@ -47,6 +48,9 @@ var FormItem = function FormItem(_a) {
|
|
|
47
48
|
return required;
|
|
48
49
|
}, [required, showRequiredOnValidateRequired, fieldRules]);
|
|
49
50
|
return /*#__PURE__*/React.createElement(FormLabel, Object.assign({}, rest, {
|
|
51
|
+
ref: function ref(_ref) {
|
|
52
|
+
field && formItemsRef.current.set(field.toString(), _ref);
|
|
53
|
+
},
|
|
50
54
|
required: showRequired,
|
|
51
55
|
// @ts-ignore
|
|
52
56
|
formMessage: /*#__PURE__*/React.createElement(FormMessage, {
|
package/lib/esm/use-form.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _typeof from "@babel/runtime/helpers/esm/typeof";
|
|
1
2
|
/** @LICENSE
|
|
2
3
|
* @hi-ui/form
|
|
3
4
|
* https://github.com/XiaoMi/hiui/tree/master/packages/ui/form#readme
|
|
@@ -10,7 +11,8 @@
|
|
|
10
11
|
import _regeneratorRuntime from '@babel/runtime/regenerator';
|
|
11
12
|
import { __rest, __awaiter } from 'tslib';
|
|
12
13
|
import { stringify, parse, mergeValues, isValidField } from './utils/index.js';
|
|
13
|
-
import React, { useMemo, useReducer, useCallback
|
|
14
|
+
import React, { useMemo, useRef, useReducer, useCallback } from 'react';
|
|
15
|
+
import scrollIntoView from 'scroll-into-view-if-needed';
|
|
14
16
|
import { useLatestRef, useLatestCallback } from '@hi-ui/use-latest';
|
|
15
17
|
import { isArray, isFunction, isObjectLike } from '@hi-ui/type-assertion';
|
|
16
18
|
import { callAllFuncs } from '@hi-ui/func-utils';
|
|
@@ -38,7 +40,8 @@ var useForm = function useForm(_a) {
|
|
|
38
40
|
validateAfterTouched = _a$validateAfterTouch === void 0 ? false : _a$validateAfterTouch,
|
|
39
41
|
_a$validateTrigger = _a.validateTrigger,
|
|
40
42
|
validateTriggerProp = _a$validateTrigger === void 0 ? DEFAULT_VALIDATE_TRIGGER : _a$validateTrigger,
|
|
41
|
-
|
|
43
|
+
scrollToFirstError = _a.scrollToFirstError,
|
|
44
|
+
rest = __rest(_a, ["initialValues", "initialErrors", "initialTouched", "lazyValidate", "onValuesChange", "onReset", "onSubmit", "rules", "validateAfterTouched", "validateTrigger", "scrollToFirstError"]);
|
|
42
45
|
/**
|
|
43
46
|
* 处理校验触发器,保证 memo 依赖的是数组每个项,避免无效重渲染
|
|
44
47
|
*/
|
|
@@ -47,6 +50,10 @@ var useForm = function useForm(_a) {
|
|
|
47
50
|
var validateTriggersMemo = useMemo(function () {
|
|
48
51
|
return validateTrigger;
|
|
49
52
|
}, validateTrigger);
|
|
53
|
+
var formItemsMp = useMemo(function () {
|
|
54
|
+
return new Map();
|
|
55
|
+
}, []);
|
|
56
|
+
var formItemsRef = useRef(formItemsMp);
|
|
50
57
|
/**
|
|
51
58
|
* 收集 Field 的校验器注册表
|
|
52
59
|
*/
|
|
@@ -72,6 +79,18 @@ var useForm = function useForm(_a) {
|
|
|
72
79
|
// const getFieldNames = useCallback(() => Object.keys(formStateRef.current.values as any), [
|
|
73
80
|
// formStateRef,
|
|
74
81
|
// ])
|
|
82
|
+
var getFormItemNode = useCallback(function (fieldName) {
|
|
83
|
+
return formItemsRef.current.get(fieldName.toString());
|
|
84
|
+
}, []);
|
|
85
|
+
var scrollToNode = useCallback(function (fieldName, options) {
|
|
86
|
+
if (options === void 0) {
|
|
87
|
+
options = {};
|
|
88
|
+
}
|
|
89
|
+
scrollIntoView(getFormItemNode(fieldName), Object.assign({
|
|
90
|
+
scrollMode: 'if-needed',
|
|
91
|
+
block: 'nearest'
|
|
92
|
+
}, options));
|
|
93
|
+
}, [getFormItemNode]);
|
|
75
94
|
var getFieldValue = useCallback(function (fieldName) {
|
|
76
95
|
return getNested(formStateRef.current.values, fieldName);
|
|
77
96
|
}, [formStateRef]);
|
|
@@ -170,6 +189,7 @@ var useForm = function useForm(_a) {
|
|
|
170
189
|
type: 'SET_VALIDATING',
|
|
171
190
|
payload: true
|
|
172
191
|
});
|
|
192
|
+
var firstError = false;
|
|
173
193
|
return Promise.all(fieldNames.map(function (fieldName) {
|
|
174
194
|
var value = getFieldValue(fieldName);
|
|
175
195
|
var fieldValidation = getValidation(fieldName);
|
|
@@ -178,6 +198,10 @@ var useForm = function useForm(_a) {
|
|
|
178
198
|
}
|
|
179
199
|
// catch 错误,保证检验所有表单项
|
|
180
200
|
return fieldValidation.validate(value)["catch"](function (error) {
|
|
201
|
+
if (scrollToFirstError && !firstError) {
|
|
202
|
+
firstError = true;
|
|
203
|
+
scrollToNode(fieldName, _typeof(scrollToFirstError) === 'object' ? scrollToFirstError : {});
|
|
204
|
+
}
|
|
181
205
|
// 第一个出错,即退出校验
|
|
182
206
|
if (lazyValidate) {
|
|
183
207
|
throw error;
|
|
@@ -244,7 +268,7 @@ var useForm = function useForm(_a) {
|
|
|
244
268
|
});
|
|
245
269
|
return combinedError;
|
|
246
270
|
});
|
|
247
|
-
}, [
|
|
271
|
+
}, [getFieldValue, getRegisteredKeys, getValidation, lazyValidate, scrollToFirstError, scrollToNode]);
|
|
248
272
|
/**
|
|
249
273
|
* 控件值更新策略
|
|
250
274
|
*/
|
|
@@ -529,7 +553,8 @@ var useForm = function useForm(_a) {
|
|
|
529
553
|
validateValue: validateField,
|
|
530
554
|
getFieldsValue: getFieldsValue,
|
|
531
555
|
setFieldsValue: setFieldsValue,
|
|
532
|
-
getFieldsError: getFieldsError
|
|
556
|
+
getFieldsError: getFieldsError,
|
|
557
|
+
formItemsRef: formItemsRef
|
|
533
558
|
});
|
|
534
559
|
};
|
|
535
560
|
function formReducer(state, action) {
|
package/lib/types/context.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { MutableRefObject } from 'react';
|
|
2
2
|
import { UseFormReturn } from './use-form';
|
|
3
|
+
import { FormFieldPath } from './types';
|
|
3
4
|
export interface FormContextProps extends UseFormReturn {
|
|
4
5
|
labelWidth: React.ReactText;
|
|
5
6
|
labelPlacement: 'left' | 'right' | 'top';
|
|
@@ -8,6 +9,7 @@ export interface FormContextProps extends UseFormReturn {
|
|
|
8
9
|
showRequiredOnValidateRequired: boolean;
|
|
9
10
|
showValidateMessage: boolean;
|
|
10
11
|
prefixCls: string;
|
|
12
|
+
formItemsRef: MutableRefObject<Map<FormFieldPath, HTMLDivElement | null>>;
|
|
11
13
|
}
|
|
12
14
|
export declare const FormProvider: React.Provider<Omit<FormContextProps, "rootProps"> | null>;
|
|
13
15
|
export declare const useFormContext: () => Omit<FormContextProps, "rootProps">;
|
package/lib/types/use-form.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { StandardBehaviorOptions as ScrollOptions } from 'scroll-into-view-if-needed';
|
|
2
3
|
import { FormState, FormFieldCollection, FormErrors, FormRuleModel, FormFieldPath, FormErrorMessage, FormSetState } from './types';
|
|
3
|
-
export declare const useForm: <Values = Record<string, any>>({ initialValues, initialErrors, initialTouched, lazyValidate, onValuesChange, onReset, onSubmit, rules, validateAfterTouched, validateTrigger: validateTriggerProp, ...rest }: UseFormProps<Values>) => {
|
|
4
|
+
export declare const useForm: <Values = Record<string, any>>({ initialValues, initialErrors, initialTouched, lazyValidate, onValuesChange, onReset, onSubmit, rules, validateAfterTouched, validateTrigger: validateTriggerProp, scrollToFirstError, ...rest }: UseFormProps<Values>) => {
|
|
4
5
|
setFormState: (stateOrFunc: FormSetState<Values>) => void;
|
|
5
6
|
setFieldValue: (field: FormFieldPath, value: unknown, shouldValidate?: boolean | undefined) => void;
|
|
6
7
|
setFieldError: (field: FormFieldPath, errorMessage: FormErrorMessage | undefined) => void;
|
|
@@ -24,6 +25,7 @@ export declare const useForm: <Values = Record<string, any>>({ initialValues, in
|
|
|
24
25
|
getFieldsValue: () => any;
|
|
25
26
|
setFieldsValue: (fieldsState: Record<string, any> | Function) => void;
|
|
26
27
|
getFieldsError: () => any;
|
|
28
|
+
formItemsRef: React.MutableRefObject<Map<any, any>>;
|
|
27
29
|
values: any;
|
|
28
30
|
errors: FormErrors<unknown>;
|
|
29
31
|
touched: import("./types").FormTouched<unknown>;
|
|
@@ -74,5 +76,9 @@ export interface UseFormProps<T = Record<string, any>> {
|
|
|
74
76
|
* 重置时回调
|
|
75
77
|
*/
|
|
76
78
|
onReset?: (values: T) => void | Promise<any>;
|
|
79
|
+
/**
|
|
80
|
+
* 提交失败自动滚动到第一个错误字段,配置参考:https://github.com/scroll-into-view/scroll-into-view-if-needed?tab=readme-ov-file#options
|
|
81
|
+
*/
|
|
82
|
+
scrollToFirstError?: boolean | ScrollOptions;
|
|
77
83
|
}
|
|
78
84
|
export declare type UseFormReturn = ReturnType<typeof useForm>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hi-ui/form",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "A sub-package for @hi-ui/hiui.",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"author": "HiUI <mi-hiui@xiaomi.com>",
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"@hi-ui/object-utils": "^4.0.4",
|
|
53
53
|
"@hi-ui/type-assertion": "^4.0.4",
|
|
54
54
|
"@hi-ui/use-latest": "^4.0.4",
|
|
55
|
-
"async-validator": "^4.0.7"
|
|
55
|
+
"async-validator": "^4.0.7",
|
|
56
|
+
"scroll-into-view-if-needed": "^3.1.0"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
59
|
"@hi-ui/core": ">=4.0.8",
|