@tendaui/components 1.0.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/LICENSE +21 -0
- package/README.md +176 -0
- package/alert/Alert.tsx +147 -0
- package/alert/defaultProps.ts +3 -0
- package/alert/index.ts +9 -0
- package/alert/style/css.js +1 -0
- package/alert/style/index.js +1 -0
- package/alert/type.ts +44 -0
- package/badge/Badge.tsx +85 -0
- package/badge/defaultProps.ts +10 -0
- package/badge/index.ts +9 -0
- package/badge/style/css.js +1 -0
- package/badge/style/index.js +1 -0
- package/badge/type.ts +51 -0
- package/button/Button.tsx +95 -0
- package/button/defaultProps.ts +13 -0
- package/button/index.ts +7 -0
- package/button/style/css.js +1 -0
- package/button/style/index.js +1 -0
- package/button/type.ts +82 -0
- package/checkbox/Checkbox.tsx +19 -0
- package/checkbox/CheckboxGroup.tsx +207 -0
- package/checkbox/defaultProps.ts +14 -0
- package/checkbox/index.ts +10 -0
- package/checkbox/style/css.js +1 -0
- package/checkbox/style/index.js +1 -0
- package/checkbox/type.ts +117 -0
- package/common/Check.tsx +131 -0
- package/common/FakeArrow.tsx +36 -0
- package/common/PluginContainer.tsx +21 -0
- package/common/Portal.tsx +67 -0
- package/common.ts +76 -0
- package/config-provider/ConfigContext.tsx +21 -0
- package/config-provider/ConfigProvider.tsx +53 -0
- package/config-provider/index.ts +9 -0
- package/config-provider/type.ts +1062 -0
- package/dialog/Dialog.tsx +254 -0
- package/dialog/DialogCard.tsx +152 -0
- package/dialog/defaultProps.ts +25 -0
- package/dialog/hooks/useDialogDrag.ts +50 -0
- package/dialog/hooks/useDialogEsc.ts +31 -0
- package/dialog/hooks/useDialogPosition.ts +36 -0
- package/dialog/hooks/useLockStyle.ts +54 -0
- package/dialog/index.ts +13 -0
- package/dialog/plugin.tsx +78 -0
- package/dialog/style/css.js +1 -0
- package/dialog/style/index.js +1 -0
- package/dialog/type.ts +241 -0
- package/dialog/utils.ts +4 -0
- package/form/Form.tsx +136 -0
- package/form/FormContext.tsx +64 -0
- package/form/FormItem.tsx +554 -0
- package/form/FormList.tsx +303 -0
- package/form/const.ts +6 -0
- package/form/defaultProps.ts +26 -0
- package/form/formModel.ts +117 -0
- package/form/hooks/interface.ts +20 -0
- package/form/hooks/useForm.ts +122 -0
- package/form/hooks/useFormItemInitialData.ts +95 -0
- package/form/hooks/useFormItemStyle.tsx +122 -0
- package/form/hooks/useInstance.tsx +275 -0
- package/form/hooks/useWatch.ts +42 -0
- package/form/index.ts +11 -0
- package/form/style/css.js +1 -0
- package/form/style/index.js +1 -0
- package/form/type.ts +519 -0
- package/form/utils/index.ts +69 -0
- package/hooks/useAttach.ts +24 -0
- package/hooks/useCommonClassName.ts +45 -0
- package/hooks/useConfig.ts +3 -0
- package/hooks/useControlled.ts +39 -0
- package/hooks/useDefaultProps.ts +16 -0
- package/hooks/useDomCallback.ts +13 -0
- package/hooks/useDomRefCallback.ts +12 -0
- package/hooks/useDragSorter.tsx +151 -0
- package/hooks/useEventCallback.ts +47 -0
- package/hooks/useGlobalConfig.ts +14 -0
- package/hooks/useGlobalIcon.ts +14 -0
- package/hooks/useLastest.ts +13 -0
- package/hooks/useLayoutEffect.ts +7 -0
- package/hooks/useMouseEvent.ts +142 -0
- package/hooks/useMutationObserver.ts +56 -0
- package/hooks/usePopper.ts +189 -0
- package/hooks/useRipple.ts +0 -0
- package/hooks/useSetState.ts +25 -0
- package/hooks/useVirtualScroll.ts +246 -0
- package/hooks/useWindowSize.ts +31 -0
- package/index.ts +70 -0
- package/input/Input.tsx +383 -0
- package/input/InputGroup.tsx +29 -0
- package/input/defaultProps.ts +22 -0
- package/input/index.ts +11 -0
- package/input/style/css.js +1 -0
- package/input/style/index.js +1 -0
- package/input/type.ts +219 -0
- package/loading/Gradient.tsx +36 -0
- package/loading/Loading.tsx +169 -0
- package/loading/circleAdapter.ts +44 -0
- package/loading/defaultProps.ts +12 -0
- package/loading/index.ts +13 -0
- package/loading/style/css.js +1 -0
- package/loading/style/index.js +1 -0
- package/loading/type.ts +71 -0
- package/loading/utils/setStyle.ts +13 -0
- package/myform/index.ts +0 -0
- package/notification/Notify.ts +24 -0
- package/notification/NotifyContainer.tsx +90 -0
- package/notification/NotifyContext.tsx +173 -0
- package/notification/NotifyItem.tsx +121 -0
- package/notification/index.ts +3 -0
- package/notification/style/css.js +1 -0
- package/notification/style/index.js +1 -0
- package/notification/type.ts +23 -0
- package/package.json +52 -0
- package/popup/Popup.tsx +264 -0
- package/popup/defaultProps.ts +13 -0
- package/popup/hooks/useTrigger.ts +276 -0
- package/popup/index.ts +6 -0
- package/popup/style/css.js +1 -0
- package/popup/style/index.js +1 -0
- package/popup/type.ts +130 -0
- package/portal/Portal.tsx +63 -0
- package/portal/index.ts +1 -0
- package/select/Option.tsx +162 -0
- package/select/OptionGroup.tsx +30 -0
- package/select/PopupContent.tsx +271 -0
- package/select/Select.tsx +586 -0
- package/select/defaultProps.ts +27 -0
- package/select/hooks/useOptions.ts +120 -0
- package/select/hooks/usePanelVirtualScroll.ts +111 -0
- package/select/index.ts +9 -0
- package/select/style/css.js +1 -0
- package/select/style/index.js +2 -0
- package/select/type.ts +382 -0
- package/select/utils/helper.ts +256 -0
- package/select-input/SelectInput.tsx +98 -0
- package/select-input/defaultProps.ts +15 -0
- package/select-input/hook/useMultiple.tsx +100 -0
- package/select-input/hook/useOverlayInnerStyle.ts +84 -0
- package/select-input/hook/useSingle.tsx +112 -0
- package/select-input/index.ts +6 -0
- package/select-input/interface.ts +18 -0
- package/select-input/style/css.js +1 -0
- package/select-input/style/index.js +1 -0
- package/select-input/type.ts +280 -0
- package/space/defaultProps.ts +0 -0
- package/space/index.ts +0 -0
- package/space/type.ts +0 -0
- package/style/index.js +2 -0
- package/styles/_global.scss +39 -0
- package/styles/_vars.scss +386 -0
- package/styles/components/alert/_index.scss +175 -0
- package/styles/components/alert/_vars.scss +39 -0
- package/styles/components/badge/_index.scss +70 -0
- package/styles/components/badge/_vars.scss +25 -0
- package/styles/components/button/_index.scss +511 -0
- package/styles/components/button/_mixins.scss +39 -0
- package/styles/components/button/_vars.scss +122 -0
- package/styles/components/checkbox/_index.scss +158 -0
- package/styles/components/checkbox/_mixin.scss +0 -0
- package/styles/components/checkbox/_var.scss +60 -0
- package/styles/components/dialog/_animate.scss +135 -0
- package/styles/components/dialog/_index.scss +311 -0
- package/styles/components/dialog/_mixins.scss +0 -0
- package/styles/components/dialog/_vars.scss +59 -0
- package/styles/components/form/_index.scss +174 -0
- package/styles/components/form/_mixins.scss +76 -0
- package/styles/components/form/_vars.scss +100 -0
- package/styles/components/input/_index.scss +349 -0
- package/styles/components/input/_map.scss +0 -0
- package/styles/components/input/_mixins.scss +116 -0
- package/styles/components/input/_vars.scss +134 -0
- package/styles/components/loading/_index.scss +112 -0
- package/styles/components/loading/_vars.scss +39 -0
- package/styles/components/notification/_index.scss +160 -0
- package/styles/components/notification/_mixins.scss +12 -0
- package/styles/components/notification/_vars.scss +59 -0
- package/styles/components/popup/_index.scss +82 -0
- package/styles/components/popup/_mixin.scss +149 -0
- package/styles/components/popup/_var.scss +31 -0
- package/styles/components/select/_index.scss +290 -0
- package/styles/components/select/_var.scss +65 -0
- package/styles/components/select-input/_index.scss +5 -0
- package/styles/components/select-input/_var.scss +3 -0
- package/styles/components/switch/_index.scss +279 -0
- package/styles/components/switch/_mixins.scss +0 -0
- package/styles/components/switch/_vars.scss +61 -0
- package/styles/components/tag/_index.scss +316 -0
- package/styles/components/tag/_var.scss +85 -0
- package/styles/components/tag-input/_index.scss +163 -0
- package/styles/components/tag-input/_vars.scss +16 -0
- package/styles/globals.css +250 -0
- package/styles/mixins/_focus.scss +7 -0
- package/styles/mixins/_layout.scss +32 -0
- package/styles/mixins/_reset.scss +10 -0
- package/styles/mixins/_scrollbar.scss +31 -0
- package/styles/mixins/_text.scss +48 -0
- package/styles/rillple.css +16 -0
- package/styles/scrollbar.css +42 -0
- package/styles/themes/_dark.scss +191 -0
- package/styles/themes/_font.scss +79 -0
- package/styles/themes/_index.scss +5 -0
- package/styles/themes/_light.scss +190 -0
- package/styles/themes/_radius.scss +9 -0
- package/styles/themes/_size.scss +68 -0
- package/styles/themes.css +66 -0
- package/styles/utilities/_animation.scss +57 -0
- package/styles/utilities/_tips.scss +9 -0
- package/switch/Switch.tsx +120 -0
- package/switch/defaultProps.ts +3 -0
- package/switch/index.ts +7 -0
- package/switch/style/css.js +1 -0
- package/switch/style/index.js +1 -0
- package/switch/type.ts +46 -0
- package/tag/Tag.tsx +149 -0
- package/tag/defaultProps.ts +19 -0
- package/tag/index.ts +8 -0
- package/tag/style/css.js +1 -0
- package/tag/style/index.js +1 -0
- package/tag/type.ts +170 -0
- package/tag-input/TagInput.tsx +215 -0
- package/tag-input/defaultProps.ts +15 -0
- package/tag-input/hooks/useHover.ts +28 -0
- package/tag-input/hooks/useTagList.tsx +131 -0
- package/tag-input/hooks/useTagScroll.ts +105 -0
- package/tag-input/index.ts +9 -0
- package/tag-input/style/css.js +1 -0
- package/tag-input/style/index.js +1 -0
- package/tag-input/type.ts +224 -0
- package/tag-input/useTagList.tsx +131 -0
- package/utils/composeRefs.ts +14 -0
- package/utils/dom.ts +29 -0
- package/utils/forwardRefWithStatics.ts +12 -0
- package/utils/getScrollbarWidth.ts +11 -0
- package/utils/helper.ts +161 -0
- package/utils/isFragment.ts +22 -0
- package/utils/listener.ts +37 -0
- package/utils/noop.ts +3 -0
- package/utils/parentTNode.ts +38 -0
- package/utils/parseTNode.ts +38 -0
- package/utils/react-render.ts +108 -0
- package/utils/ref.ts +6 -0
- package/utils/refs.ts +81 -0
- package/utils/style.ts +60 -0
- package/utils/transition.ts +28 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
import useConfig from "../../hooks/useConfig";
|
|
4
|
+
import parseTNode from "../../utils/parseTNode";
|
|
5
|
+
import { ValidateStatus } from "../const";
|
|
6
|
+
|
|
7
|
+
export default function useFormItemStyle(props) {
|
|
8
|
+
const { classPrefix } = useConfig();
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
className,
|
|
12
|
+
help,
|
|
13
|
+
tips,
|
|
14
|
+
snakeName,
|
|
15
|
+
status,
|
|
16
|
+
successBorder,
|
|
17
|
+
errorList,
|
|
18
|
+
successList,
|
|
19
|
+
layout,
|
|
20
|
+
verifyStatus,
|
|
21
|
+
label,
|
|
22
|
+
labelWidth,
|
|
23
|
+
labelAlign,
|
|
24
|
+
requiredMark,
|
|
25
|
+
requiredMarkPosition,
|
|
26
|
+
showErrorMessage,
|
|
27
|
+
innerRules
|
|
28
|
+
} = props;
|
|
29
|
+
|
|
30
|
+
// 传入 status 时受控
|
|
31
|
+
const renderStatus = status || verifyStatus;
|
|
32
|
+
|
|
33
|
+
// help 文本
|
|
34
|
+
const helpNode = help && <div className={`${classPrefix}-input__help`}>{parseTNode(help)}</div>;
|
|
35
|
+
|
|
36
|
+
// 判断是否有星号
|
|
37
|
+
const needRequiredMark =
|
|
38
|
+
requiredMark || (requiredMark ?? innerRules.filter((rule: { required?: boolean }) => rule.required).length > 0);
|
|
39
|
+
|
|
40
|
+
// 提示文本
|
|
41
|
+
const extraNode = useMemo(() => {
|
|
42
|
+
let extra = tips ? (
|
|
43
|
+
<div className={`${classPrefix}-input__extra`} title={tips}>
|
|
44
|
+
{tips}
|
|
45
|
+
</div>
|
|
46
|
+
) : null;
|
|
47
|
+
|
|
48
|
+
if (showErrorMessage && errorList?.[0]?.message) {
|
|
49
|
+
extra = (
|
|
50
|
+
<div className={`${classPrefix}-input__extra`} title={errorList[0].message}>
|
|
51
|
+
{errorList[0].message}
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
} else if (successList.length) {
|
|
55
|
+
extra = (
|
|
56
|
+
<div className={`${classPrefix}-input__extra`} title={successList[0].message}>
|
|
57
|
+
{successList[0].message}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return extra;
|
|
63
|
+
}, [showErrorMessage, errorList, successList, tips, classPrefix]);
|
|
64
|
+
|
|
65
|
+
// snake 在dom上显示的名字改成下划线拼接
|
|
66
|
+
const formSnakeName = snakeName.split(",").join("_");
|
|
67
|
+
|
|
68
|
+
const formItemClass = classNames(`${classPrefix}-form__item`, className, {
|
|
69
|
+
[`${classPrefix}-form-item__${formSnakeName}`]: formSnakeName,
|
|
70
|
+
[`${classPrefix}-form__item-with-help`]: helpNode,
|
|
71
|
+
[`${classPrefix}-form__item-with-extra`]: extraNode
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const formItemLabelClass = classNames(`${classPrefix}-form__label`, {
|
|
75
|
+
[`${classPrefix}-form__label--required`]: needRequiredMark,
|
|
76
|
+
[`${classPrefix}-form__label--required-right`]: needRequiredMark && requiredMarkPosition === "right",
|
|
77
|
+
[`${classPrefix}-form__label--top`]: labelAlign === "top" || !labelWidth,
|
|
78
|
+
[`${classPrefix}-form__label--left`]: labelAlign === "left" && labelWidth,
|
|
79
|
+
[`${classPrefix}-form__label--right`]: labelAlign === "right" && labelWidth
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const contentClass = () => {
|
|
83
|
+
const controlCls = `${classPrefix}-form__controls`;
|
|
84
|
+
if (!showErrorMessage) return controlCls;
|
|
85
|
+
|
|
86
|
+
const isSuccess = renderStatus === ValidateStatus.SUCCESS;
|
|
87
|
+
if (isSuccess) {
|
|
88
|
+
return classNames(controlCls, `${classPrefix}-is-success`, {
|
|
89
|
+
[`${classPrefix}-form--success-border`]: successBorder
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return classNames(controlCls, {
|
|
94
|
+
[`${classPrefix}-is-warning`]: renderStatus === "warning",
|
|
95
|
+
[`${classPrefix}-is-error`]: ["fail", "error"].includes(renderStatus),
|
|
96
|
+
[`${classPrefix}-form--has-error`]:
|
|
97
|
+
renderStatus === ValidateStatus.ERROR || renderStatus === ValidateStatus.WARNING
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
let labelStyle = {};
|
|
102
|
+
let contentStyle = {};
|
|
103
|
+
if (label && labelWidth && labelAlign !== "top") {
|
|
104
|
+
if (typeof labelWidth === "number") {
|
|
105
|
+
labelStyle = { width: `${labelWidth}px` };
|
|
106
|
+
contentStyle = { marginLeft: layout !== "inline" ? `${labelWidth}px` : "" };
|
|
107
|
+
} else {
|
|
108
|
+
labelStyle = { width: labelWidth };
|
|
109
|
+
contentStyle = { marginLeft: layout !== "inline" ? labelWidth : "" };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
formItemClass,
|
|
115
|
+
formItemLabelClass,
|
|
116
|
+
contentClass,
|
|
117
|
+
labelStyle,
|
|
118
|
+
contentStyle,
|
|
119
|
+
helpNode,
|
|
120
|
+
extraNode
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { isEmpty, isFunction, isEqual, merge, get, set } from "lodash-es";
|
|
2
|
+
// import log from '@tdesign/common-js/log/index';
|
|
3
|
+
import type {
|
|
4
|
+
TdFormProps,
|
|
5
|
+
FormValidateResult,
|
|
6
|
+
FormResetParams,
|
|
7
|
+
FormValidateMessage,
|
|
8
|
+
AllValidateResult,
|
|
9
|
+
NamePath
|
|
10
|
+
} from "../type";
|
|
11
|
+
import type { FormItemInstance } from "../FormItem";
|
|
12
|
+
import useConfig from "../../hooks/useConfig";
|
|
13
|
+
import { getMapValue, objectToArray, travelMapFromObject, calcFieldValue } from "../utils";
|
|
14
|
+
|
|
15
|
+
// 检测是否需要校验 默认全量校验
|
|
16
|
+
function needValidate(name: NamePath, fields: string[]) {
|
|
17
|
+
if (!fields || !Array.isArray(fields)) return true;
|
|
18
|
+
return fields.some((item) => String(item) === String(name));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 整理校验结果
|
|
22
|
+
function formatValidateResult(validateResultList) {
|
|
23
|
+
const result = validateResultList.reduce((r, err) => Object.assign(r || {}, err), {});
|
|
24
|
+
Object.keys(result).forEach((key) => {
|
|
25
|
+
if (result[key] === true) {
|
|
26
|
+
delete result[key];
|
|
27
|
+
} else {
|
|
28
|
+
result[key] = result[key].filter((fr: AllValidateResult) => fr.result === false);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 整理嵌套数据
|
|
32
|
+
if (result[key] && key.includes(",")) {
|
|
33
|
+
const keyList = key.split(",");
|
|
34
|
+
const fieldValue = calcFieldValue(keyList, result[key]);
|
|
35
|
+
merge(result, fieldValue);
|
|
36
|
+
delete result[key];
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return isEmpty(result) ? true : result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default function useInstance(
|
|
43
|
+
props: TdFormProps,
|
|
44
|
+
formRef,
|
|
45
|
+
formMapRef: React.MutableRefObject<Map<NamePath, React.RefObject<FormItemInstance>>>,
|
|
46
|
+
floatingFormDataRef: React.RefObject<Record<string, unknown>>
|
|
47
|
+
) {
|
|
48
|
+
const { classPrefix } = useConfig();
|
|
49
|
+
|
|
50
|
+
const { scrollToFirstError, preventSubmitDefault = true, onSubmit, onReset } = props;
|
|
51
|
+
|
|
52
|
+
// 获取第一个错误表单
|
|
53
|
+
function getFirstError(r: FormValidateResult<FormData>) {
|
|
54
|
+
if (r === true) return;
|
|
55
|
+
const [firstKey] = Object.keys(r);
|
|
56
|
+
if (scrollToFirstError) {
|
|
57
|
+
scrollTo(`.${classPrefix}-form--has-error`);
|
|
58
|
+
}
|
|
59
|
+
return r[firstKey][0]?.message;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 校验不通过时,滚动到第一个错误表单
|
|
63
|
+
function scrollTo(selector: string) {
|
|
64
|
+
const dom = formRef.current.querySelector?.(selector);
|
|
65
|
+
const behavior = scrollToFirstError as ScrollBehavior;
|
|
66
|
+
if (dom) {
|
|
67
|
+
dom.scrollIntoView({ behavior });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 对外方法 手动提交表单
|
|
72
|
+
function submit(e?: React.FormEvent<HTMLFormElement>) {
|
|
73
|
+
if (preventSubmitDefault) {
|
|
74
|
+
e?.preventDefault?.();
|
|
75
|
+
e?.stopPropagation?.();
|
|
76
|
+
}
|
|
77
|
+
validate().then((r) => {
|
|
78
|
+
const firstError = getFirstError(r);
|
|
79
|
+
const fields = getFieldsValue(true);
|
|
80
|
+
onSubmit?.({ validateResult: r, firstError, e, fields });
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 对外方法,该方法会触发全部表单组件错误信息显示
|
|
85
|
+
async function validate(param?: Record<string, unknown>): Promise<FormValidateResult<FormData>> {
|
|
86
|
+
const { fields, trigger = "all", showErrorMessage } = param || {};
|
|
87
|
+
const list = [...formMapRef.current.values()]
|
|
88
|
+
.filter(
|
|
89
|
+
(formItemRef) =>
|
|
90
|
+
isFunction(formItemRef?.current?.validate) && needValidate(formItemRef?.current?.name, fields as string[])
|
|
91
|
+
)
|
|
92
|
+
.map((formItemRef) => formItemRef?.current?.validate?.(trigger as string, showErrorMessage as boolean));
|
|
93
|
+
|
|
94
|
+
const validateList = await Promise.all(list);
|
|
95
|
+
return formatValidateResult(validateList);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 对外方法,该方法只会校验不会触发信息提示
|
|
99
|
+
async function validateOnly(param?: Record<string, unknown>): Promise<FormValidateResult<FormData>> {
|
|
100
|
+
const { fields, trigger = "all" } = param || {};
|
|
101
|
+
const list = [...formMapRef.current.values()]
|
|
102
|
+
.filter(
|
|
103
|
+
(formItemRef) =>
|
|
104
|
+
isFunction(formItemRef?.current?.validateOnly) && needValidate(formItemRef?.current?.name, fields as string[])
|
|
105
|
+
)
|
|
106
|
+
.map((formItemRef) => formItemRef?.current?.validateOnly?.(trigger as string));
|
|
107
|
+
|
|
108
|
+
const validateList = await Promise.all(list);
|
|
109
|
+
return formatValidateResult(validateList);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 对外方法,获取对应 formItem 的值
|
|
113
|
+
function getFieldValue(name: NamePath) {
|
|
114
|
+
if (!name) return null;
|
|
115
|
+
|
|
116
|
+
const formItemRef = getMapValue(name, formMapRef) as React.RefObject<FormItemInstance> | undefined;
|
|
117
|
+
return formItemRef?.current?.getValue?.();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 对外方法,获取一组字段名对应的值,当调用 getFieldsValue(true) 时返回所有值
|
|
121
|
+
function getFieldsValue(nameList: string[] | boolean) {
|
|
122
|
+
const fieldsValue = {};
|
|
123
|
+
|
|
124
|
+
if (nameList === true) {
|
|
125
|
+
// 嵌套数组子节点先添加导致外层数据覆盖因而需要倒序遍历
|
|
126
|
+
for (const [name, formItemRef] of [...formMapRef.current.entries()].reverse()) {
|
|
127
|
+
let fieldValue = null;
|
|
128
|
+
const currentRef = formItemRef as React.RefObject<FormItemInstance>;
|
|
129
|
+
if (currentRef?.current?.isFormList) {
|
|
130
|
+
fieldValue = calcFieldValue(name, currentRef?.current?.getValue?.());
|
|
131
|
+
} else {
|
|
132
|
+
fieldValue = calcFieldValue(name, currentRef?.current?.getValue?.(), !props.supportNumberKey);
|
|
133
|
+
}
|
|
134
|
+
merge(fieldsValue, fieldValue);
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
if (!Array.isArray(nameList)) {
|
|
138
|
+
console.error("Form", "`getFieldsValue` 参数需要 Array 类型");
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
nameList.forEach((name) => {
|
|
143
|
+
const formItemRef = getMapValue(name, formMapRef) as React.RefObject<FormItemInstance> | undefined;
|
|
144
|
+
if (!formItemRef) return;
|
|
145
|
+
|
|
146
|
+
const fieldValue = calcFieldValue(name, formItemRef?.current.getValue?.());
|
|
147
|
+
merge(fieldsValue, fieldValue);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return fieldsValue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 对外方法,设置对应 formItem 的值
|
|
154
|
+
function setFieldsValue(fields = {}) {
|
|
155
|
+
const nameLists = objectToArray(fields);
|
|
156
|
+
|
|
157
|
+
nameLists.forEach((nameList) => {
|
|
158
|
+
const fieldValue = get(fields, nameList);
|
|
159
|
+
|
|
160
|
+
let formItemRef;
|
|
161
|
+
if (nameList.length > 1) {
|
|
162
|
+
// 如果是数组,由于内存地址不一致,不能直接使用 Map.get 获取到 formItemRef
|
|
163
|
+
for (const [mapNameList, _formItemRef] of formMapRef.current.entries()) {
|
|
164
|
+
if (isEqual(nameList, mapNameList)) {
|
|
165
|
+
formItemRef = _formItemRef;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
formItemRef = formMapRef.current.get(nameList[0]);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (formItemRef?.current) {
|
|
174
|
+
formItemRef?.current?.setValue?.(fieldValue, fields);
|
|
175
|
+
} else {
|
|
176
|
+
set(floatingFormDataRef.current, nameList, fieldValue);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 对外方法,设置对应 formItem 的数据
|
|
182
|
+
function setFields(fields = []) {
|
|
183
|
+
if (!Array.isArray(fields)) throw new Error("setFields 参数需要 Array 类型");
|
|
184
|
+
|
|
185
|
+
fields.forEach((field) => {
|
|
186
|
+
const { name, ...restFields } = field;
|
|
187
|
+
const formItemRef = getMapValue(name, formMapRef) as React.RefObject<FormItemInstance> | undefined;
|
|
188
|
+
|
|
189
|
+
formItemRef?.current?.setField(restFields, field);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 对外方法,重置对应 formItem 的数据
|
|
194
|
+
function reset(params: FormResetParams<FormData>) {
|
|
195
|
+
// reset all
|
|
196
|
+
if (typeof params === "undefined") {
|
|
197
|
+
[...formMapRef.current.values()].forEach((formItemRef) => {
|
|
198
|
+
formItemRef?.current?.resetField?.();
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
201
|
+
const { type = "initial", fields = [] } = params;
|
|
202
|
+
|
|
203
|
+
fields.forEach((name) => {
|
|
204
|
+
const formItemRef = getMapValue(name, formMapRef) as React.RefObject<FormItemInstance> | undefined;
|
|
205
|
+
formItemRef?.current?.resetField(type);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
onReset?.({});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 对外方法,重置对应 formItem 的状态
|
|
212
|
+
function clearValidate(fields?: Array<keyof FormData>) {
|
|
213
|
+
// reset all
|
|
214
|
+
if (typeof fields === "undefined") {
|
|
215
|
+
[...formMapRef.current.values()].forEach((formItemRef) => {
|
|
216
|
+
formItemRef?.current?.resetValidate();
|
|
217
|
+
});
|
|
218
|
+
} else {
|
|
219
|
+
if (!Array.isArray(fields)) throw new Error("clearValidate 参数需要 Array 类型");
|
|
220
|
+
|
|
221
|
+
fields.forEach((name) => {
|
|
222
|
+
const formItemRef = getMapValue(name, formMapRef) as React.RefObject<FormItemInstance> | undefined;
|
|
223
|
+
formItemRef?.current?.resetValidate();
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 对外方法,设置 formItem 的错误信息
|
|
229
|
+
function setValidateMessage(message: FormValidateMessage<FormData>) {
|
|
230
|
+
travelMapFromObject(message, formMapRef, (formItemRef, fieldValue) => {
|
|
231
|
+
(formItemRef as React.RefObject<FormItemInstance>)?.current?.setValidateMessage?.(fieldValue);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// 对外方法,获取 formItem 的错误信息
|
|
236
|
+
function getValidateMessage(fields?: Array<keyof FormData>) {
|
|
237
|
+
const message = {};
|
|
238
|
+
|
|
239
|
+
if (typeof fields === "undefined") {
|
|
240
|
+
[...formMapRef.current.values()].forEach((formItemRef) => {
|
|
241
|
+
const item = formItemRef?.current?.getValidateMessage?.();
|
|
242
|
+
if (isEmpty(item)) return;
|
|
243
|
+
message[String(formItemRef?.current?.name)] = item;
|
|
244
|
+
});
|
|
245
|
+
} else {
|
|
246
|
+
if (!Array.isArray(fields)) throw new Error("getValidateMessage 参数需要 Array 类型");
|
|
247
|
+
|
|
248
|
+
fields.forEach((name) => {
|
|
249
|
+
const formItemRef = getMapValue(name, formMapRef) as React.RefObject<FormItemInstance> | undefined;
|
|
250
|
+
const item = formItemRef?.current?.getValidateMessage?.();
|
|
251
|
+
if (isEmpty(item)) return;
|
|
252
|
+
message[String(formItemRef?.current?.name)] = item;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (isEmpty(message)) return;
|
|
257
|
+
|
|
258
|
+
return message;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
submit,
|
|
263
|
+
reset,
|
|
264
|
+
validate,
|
|
265
|
+
validateOnly,
|
|
266
|
+
clearValidate,
|
|
267
|
+
setFields,
|
|
268
|
+
setFieldsValue,
|
|
269
|
+
setValidateMessage,
|
|
270
|
+
getValidateMessage,
|
|
271
|
+
getFieldValue,
|
|
272
|
+
getFieldsValue,
|
|
273
|
+
getCurrentElement: () => formRef.current
|
|
274
|
+
};
|
|
275
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { get, isUndefined } from "lodash-es";
|
|
3
|
+
import type { NamePath } from "../type";
|
|
4
|
+
import type { InternalFormInstance } from "./interface";
|
|
5
|
+
import { HOOK_MARK } from "./useForm";
|
|
6
|
+
import noop from "../../utils/noop";
|
|
7
|
+
|
|
8
|
+
export default function useWatch(name: NamePath, form: InternalFormInstance) {
|
|
9
|
+
const [value, setValue] = useState<string>();
|
|
10
|
+
const valueStr = useMemo(() => JSON.stringify(value), [value]);
|
|
11
|
+
const valueStrRef = useRef(valueStr);
|
|
12
|
+
|
|
13
|
+
const isValidForm = form && form._init;
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!isValidForm) return;
|
|
17
|
+
|
|
18
|
+
const internalHooks = form.getInternalHooks?.(HOOK_MARK);
|
|
19
|
+
const { registerWatch = noop } = internalHooks || {};
|
|
20
|
+
|
|
21
|
+
const cancelRegister = registerWatch(() => {
|
|
22
|
+
const allFieldsValue = form.getFieldsValue?.(true);
|
|
23
|
+
const newValue = get(allFieldsValue, name);
|
|
24
|
+
const nextValueStr = JSON.stringify(newValue);
|
|
25
|
+
|
|
26
|
+
// Compare stringify in case it's nest object
|
|
27
|
+
if (valueStrRef.current !== nextValueStr) {
|
|
28
|
+
valueStrRef.current = nextValueStr;
|
|
29
|
+
setValue(nextValueStr);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const allFieldsValue = form.getFieldsValue?.(true);
|
|
34
|
+
const initialValue = get(allFieldsValue, name);
|
|
35
|
+
setValue(JSON.stringify(initialValue));
|
|
36
|
+
|
|
37
|
+
return cancelRegister;
|
|
38
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
return isUndefined(value) ? value : JSON.parse(value);
|
|
42
|
+
}
|
package/form/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import _Form from "./Form";
|
|
2
|
+
|
|
3
|
+
import "./style/index.js";
|
|
4
|
+
|
|
5
|
+
export type { FormProps } from "./Form";
|
|
6
|
+
export type { FormItemProps } from "./FormItem";
|
|
7
|
+
export * from "./type";
|
|
8
|
+
export * from "./hooks/interface";
|
|
9
|
+
|
|
10
|
+
export const Form = _Form;
|
|
11
|
+
export default Form;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./index.css";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "../../styles/components/form/_index.scss";
|