UNPKG

11.4 kBJavaScriptView Raw
1import { __rest } from "tslib";
2import { QuestionCircleOutline } from 'antd-mobile-icons';
3import classNames from 'classnames';
4import { Field } from 'rc-field-form';
5import FieldContext from 'rc-field-form/lib/FieldContext';
6import React, { useCallback, useContext, useRef, useState } from 'react';
7import { devWarning } from '../../utils/dev-log';
8import { withNativeProps } from '../../utils/native-props';
9import { undefinedFallback } from '../../utils/undefined-fallback';
10import { mergeProps } from '../../utils/with-default-props';
11import { useConfig } from '../config-provider';
12import List from '../list';
13import Popover from '../popover';
14import { FormContext, NoStyleItemContext } from './context';
15import { isSafeSetRefComponent, toArray } from './utils';
16const NAME_SPLIT = '__SPLIT__';
17const classPrefix = `adm-form-item`;
18const MemoInput = React.memo(({
19 children
20}) => children, (prev, next) => prev.value === next.value && prev.update === next.update);
21const FormItemLayout = props => {
22 var _a;
23 const {
24 locale,
25 form: componentConfig = {}
26 } = useConfig();
27 const {
28 style,
29 extra,
30 label,
31 help,
32 helpIcon,
33 required,
34 children,
35 htmlFor,
36 hidden,
37 arrow,
38 arrowIcon,
39 childElementPosition = 'normal'
40 } = mergeProps(componentConfig, props);
41 const context = useContext(FormContext);
42 const hasFeedback = props.hasFeedback !== undefined ? props.hasFeedback : context.hasFeedback;
43 const layout = props.layout || context.layout;
44 const disabled = (_a = props.disabled) !== null && _a !== void 0 ? _a : context.disabled;
45 const requiredMark = (() => {
46 const {
47 requiredMarkStyle
48 } = context;
49 switch (requiredMarkStyle) {
50 case 'asterisk':
51 return required && React.createElement("span", {
52 className: `${classPrefix}-required-asterisk`
53 }, "*");
54 case 'text-required':
55 return required && React.createElement("span", {
56 className: `${classPrefix}-required-text`
57 }, "(", locale.Form.required, ")");
58 case 'text-optional':
59 return !required && React.createElement("span", {
60 className: `${classPrefix}-required-text`
61 }, "(", locale.Form.optional, ")");
62 case 'none':
63 return null;
64 default:
65 return null;
66 }
67 })();
68 const labelElement = !!label && React.createElement("label", {
69 className: `${classPrefix}-label`,
70 htmlFor: htmlFor
71 }, label, requiredMark, help && React.createElement(Popover, {
72 content: help,
73 mode: 'dark',
74 trigger: 'click'
75 }, React.createElement("span", {
76 className: `${classPrefix}-label-help`,
77 onClick: e => {
78 e.stopPropagation();
79 e.preventDefault();
80 }
81 }, helpIcon || React.createElement(QuestionCircleOutline, null))));
82 const description = (!!props.description || hasFeedback) && React.createElement(React.Fragment, null, props.description, hasFeedback && React.createElement(React.Fragment, null, props.errors.map((error, index) => React.createElement("div", {
83 key: `error-${index}`,
84 className: `${classPrefix}-feedback-error`
85 }, error)), props.warnings.map((warning, index) => React.createElement("div", {
86 key: `warning-${index}`,
87 className: `${classPrefix}-feedback-warning`
88 }, warning))));
89 return withNativeProps(props, React.createElement(List.Item, {
90 style: style,
91 title: layout === 'vertical' && labelElement,
92 prefix: layout === 'horizontal' && labelElement,
93 extra: extra,
94 description: description,
95 className: classNames(classPrefix, `${classPrefix}-${layout}`, {
96 [`${classPrefix}-hidden`]: hidden,
97 [`${classPrefix}-has-error`]: props.errors.length
98 }),
99 disabled: disabled,
100 onClick: props.onClick,
101 clickable: props.clickable,
102 arrowIcon: arrowIcon || arrow
103 }, React.createElement("div", {
104 className: classNames(`${classPrefix}-child`, `${classPrefix}-child-position-${childElementPosition}`)
105 }, React.createElement("div", {
106 className: classNames(`${classPrefix}-child-inner`)
107 }, children))));
108};
109export const FormItem = props => {
110 const {
111 // 样式相关
112 style,
113 // FormItem 相关
114 label,
115 help,
116 helpIcon,
117 extra,
118 hasFeedback,
119 name,
120 required,
121 noStyle,
122 hidden,
123 layout,
124 childElementPosition,
125 description,
126 // Field 相关
127 disabled,
128 rules,
129 children,
130 messageVariables,
131 trigger = 'onChange',
132 validateTrigger = trigger,
133 onClick,
134 shouldUpdate,
135 dependencies,
136 clickable,
137 arrow,
138 arrowIcon
139 } = props,
140 fieldProps = __rest(props, ["style", "label", "help", "helpIcon", "extra", "hasFeedback", "name", "required", "noStyle", "hidden", "layout", "childElementPosition", "description", "disabled", "rules", "children", "messageVariables", "trigger", "validateTrigger", "onClick", "shouldUpdate", "dependencies", "clickable", "arrow", "arrowIcon"]);
141 const {
142 name: formName
143 } = useContext(FormContext);
144 const {
145 validateTrigger: contextValidateTrigger
146 } = useContext(FieldContext);
147 const mergedValidateTrigger = undefinedFallback(validateTrigger, contextValidateTrigger, trigger);
148 const widgetRef = useRef(null);
149 const updateRef = useRef(0);
150 updateRef.current += 1;
151 const [subMetas, setSubMetas] = useState({});
152 const onSubMetaChange = useCallback((subMeta, namePath) => {
153 setSubMetas(prevSubMetas => {
154 const nextSubMetas = Object.assign({}, prevSubMetas);
155 const nameKey = namePath.join(NAME_SPLIT);
156 if (subMeta.destroy) {
157 delete nextSubMetas[nameKey];
158 } else {
159 nextSubMetas[nameKey] = subMeta;
160 }
161 return nextSubMetas;
162 });
163 }, [setSubMetas]);
164 function renderLayout(baseChildren, fieldId, meta, isRequired) {
165 var _a, _b;
166 if (noStyle && !hidden) {
167 return baseChildren;
168 }
169 const curErrors = (_a = meta === null || meta === void 0 ? void 0 : meta.errors) !== null && _a !== void 0 ? _a : [];
170 const errors = Object.keys(subMetas).reduce((subErrors, key) => {
171 var _a, _b;
172 const errors = (_b = (_a = subMetas[key]) === null || _a === void 0 ? void 0 : _a.errors) !== null && _b !== void 0 ? _b : [];
173 if (errors.length) {
174 subErrors = [...subErrors, ...errors];
175 }
176 return subErrors;
177 }, curErrors);
178 const curWarnings = (_b = meta === null || meta === void 0 ? void 0 : meta.warnings) !== null && _b !== void 0 ? _b : [];
179 const warnings = Object.keys(subMetas).reduce((subWarnings, key) => {
180 var _a, _b;
181 const warnings = (_b = (_a = subMetas[key]) === null || _a === void 0 ? void 0 : _a.warnings) !== null && _b !== void 0 ? _b : [];
182 if (warnings.length) {
183 subWarnings = [...subWarnings, ...warnings];
184 }
185 return subWarnings;
186 }, curWarnings);
187 return withNativeProps(props, React.createElement(FormItemLayout, {
188 style: style,
189 label: label,
190 extra: extra,
191 help: help,
192 helpIcon: helpIcon,
193 description: description,
194 required: isRequired,
195 disabled: disabled,
196 hasFeedback: hasFeedback,
197 htmlFor: fieldId,
198 errors: errors,
199 warnings: warnings,
200 onClick: onClick && (e => onClick(e, widgetRef)),
201 hidden: hidden,
202 layout: layout,
203 childElementPosition: childElementPosition,
204 clickable: clickable,
205 arrow: arrow,
206 arrowIcon: arrowIcon
207 }, React.createElement(NoStyleItemContext.Provider, {
208 value: onSubMetaChange
209 }, baseChildren)));
210 }
211 const isRenderProps = typeof children === 'function';
212 if (!name && !isRenderProps && !props.dependencies) {
213 return renderLayout(children);
214 }
215 let Variables = {};
216 Variables.label = typeof label === 'string' ? label : '';
217 if (messageVariables) {
218 Variables = Object.assign(Object.assign({}, Variables), messageVariables);
219 }
220 const notifyParentMetaChange = useContext(NoStyleItemContext);
221 const onMetaChange = meta => {
222 if (noStyle && notifyParentMetaChange) {
223 const namePath = meta.name;
224 notifyParentMetaChange(meta, namePath);
225 }
226 };
227 return React.createElement(Field, Object.assign({}, fieldProps, {
228 name: name,
229 shouldUpdate: shouldUpdate,
230 dependencies: dependencies,
231 rules: rules,
232 trigger: trigger,
233 validateTrigger: mergedValidateTrigger,
234 onMetaChange: onMetaChange,
235 messageVariables: Variables
236 }), (control, meta, context) => {
237 let childNode = null;
238 const isRequired = required !== undefined ? required : rules && rules.some(rule => !!(rule && typeof rule === 'object' && rule.required));
239 const nameList = toArray(name).length && meta ? meta.name : [];
240 const fieldId = (nameList.length > 0 && formName ? [formName, ...nameList] : nameList).join('_');
241 if (shouldUpdate && dependencies) {
242 devWarning('Form.Item', "`shouldUpdate` and `dependencies` shouldn't be used together.");
243 }
244 if (isRenderProps) {
245 if ((shouldUpdate || dependencies) && !name) {
246 childNode = children(context);
247 } else {
248 if (!(shouldUpdate || dependencies)) {
249 devWarning('Form.Item', '`children` of render props only work with `shouldUpdate` or `dependencies`.');
250 }
251 if (name) {
252 devWarning('Form.Item', "Do not use `name` with `children` of render props since it's not a field.");
253 }
254 }
255 // not render props
256 } else if (dependencies && !name) {
257 devWarning('Form.Item', 'Must set `name` or use render props when `dependencies` is set.');
258 } else if (React.isValidElement(children)) {
259 if (children.props.defaultValue) {
260 devWarning('Form.Item', '`defaultValue` will not work on controlled Field. You should use `initialValues` of Form instead.');
261 }
262 const childProps = Object.assign(Object.assign({}, children.props), control);
263 if (isSafeSetRefComponent(children)) {
264 childProps.ref = instance => {
265 const originRef = children.ref;
266 if (originRef) {
267 if (typeof originRef === 'function') {
268 originRef(instance);
269 }
270 if ('current' in originRef) {
271 originRef.current = instance;
272 }
273 }
274 widgetRef.current = instance;
275 };
276 }
277 if (!childProps.id) {
278 childProps.id = fieldId;
279 }
280 // We should keep user origin event handler
281 const triggers = new Set([...toArray(trigger), ...toArray(mergedValidateTrigger)]);
282 triggers.forEach(eventName => {
283 childProps[eventName] = (...args) => {
284 var _a, _b, _c;
285 (_a = control[eventName]) === null || _a === void 0 ? void 0 : _a.call(control, ...args);
286 (_c = (_b = children.props)[eventName]) === null || _c === void 0 ? void 0 : _c.call(_b, ...args);
287 };
288 });
289 childNode = React.createElement(MemoInput, {
290 value: control[props.valuePropName || 'value'],
291 update: updateRef.current
292 }, React.cloneElement(children, childProps));
293 } else {
294 if (name) {
295 devWarning('Form.Item', '`name` is only used for validate React element. If you are using Form.Item as layout display, please remove `name` instead.');
296 }
297 childNode = children;
298 }
299 return renderLayout(childNode, fieldId, meta, isRequired);
300 });
301};
\No newline at end of file