1 | import { __rest } from "tslib";
|
2 | import { QuestionCircleOutline } from 'antd-mobile-icons';
|
3 | import classNames from 'classnames';
|
4 | import { Field } from 'rc-field-form';
|
5 | import FieldContext from 'rc-field-form/lib/FieldContext';
|
6 | import React, { useCallback, useContext, useRef, useState } from 'react';
|
7 | import { devWarning } from '../../utils/dev-log';
|
8 | import { withNativeProps } from '../../utils/native-props';
|
9 | import { undefinedFallback } from '../../utils/undefined-fallback';
|
10 | import { mergeProps } from '../../utils/with-default-props';
|
11 | import { useConfig } from '../config-provider';
|
12 | import List from '../list';
|
13 | import Popover from '../popover';
|
14 | import { FormContext, NoStyleItemContext } from './context';
|
15 | import { isSafeSetRefComponent, toArray } from './utils';
|
16 | const NAME_SPLIT = '__SPLIT__';
|
17 | const classPrefix = `adm-form-item`;
|
18 | const MemoInput = React.memo(({
|
19 | children
|
20 | }) => children, (prev, next) => prev.value === next.value && prev.update === next.update);
|
21 | const 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 | };
|
109 | export const FormItem = props => {
|
110 | const {
|
111 |
|
112 | style,
|
113 |
|
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 |
|
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 |
|
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 |
|
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 |