import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled'; import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled'; import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled'; import LoadingOutlined from '@ant-design/icons/LoadingOutlined'; import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; import classNames from 'classnames'; import * as React from 'react'; import omit from 'rc-util/lib/omit'; import type { Meta } from 'rc-field-form/lib/interface'; import { Row } from '../../grid'; import FormItemLabel from '../FormItemLabel'; import FormItemInput from '../FormItemInput'; import type { FormItemStatusContextProps, ReportMetaChange } from '../context'; import { FormContext, FormItemInputContext, NoStyleItemContext } from '../context'; import type { FormItemProps, ValidateStatus } from '.'; import useDebounce from '../hooks/useDebounce'; const iconMap = { success: CheckCircleFilled, warning: ExclamationCircleFilled, error: CloseCircleFilled, validating: LoadingOutlined, }; export interface ItemHolderProps extends FormItemProps { prefixCls: string; className?: string; style?: React.CSSProperties; errors: React.ReactNode[]; warnings: React.ReactNode[]; meta: Meta; children?: React.ReactNode; fieldId?: string; isRequired?: boolean; onSubItemMetaChange: ReportMetaChange; } export default function ItemHolder(props: ItemHolderProps) { const { prefixCls, className, style, help, errors, warnings, validateStatus, meta, hasFeedback, hidden, children, fieldId, isRequired, onSubItemMetaChange, ...restProps } = props; const itemPrefixCls = `${prefixCls}-item`; const { requiredMark } = React.useContext(FormContext); // ======================== Margin ======================== const itemRef = React.useRef(null); const debounceErrors = useDebounce(errors); const debounceWarnings = useDebounce(warnings); const hasHelp = help !== undefined && help !== null; const hasError = !!(hasHelp || errors.length || warnings.length); const [marginBottom, setMarginBottom] = React.useState(null); useLayoutEffect(() => { if (hasError && itemRef.current) { const itemStyle = getComputedStyle(itemRef.current); setMarginBottom(parseInt(itemStyle.marginBottom, 10)); } }, [hasError]); const onErrorVisibleChanged = (nextVisible: boolean) => { if (!nextVisible) { setMarginBottom(null); } }; // ======================== Status ======================== let mergedValidateStatus: ValidateStatus = ''; if (validateStatus !== undefined) { mergedValidateStatus = validateStatus; } else if (meta.validating) { mergedValidateStatus = 'validating'; } else if (debounceErrors.length) { mergedValidateStatus = 'error'; } else if (debounceWarnings.length) { mergedValidateStatus = 'warning'; } else if (meta.touched) { mergedValidateStatus = 'success'; } const formItemStatusContext = React.useMemo(() => { let feedbackIcon: React.ReactNode; if (hasFeedback) { const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus]; feedbackIcon = IconNode ? ( ) : null; } return { status: mergedValidateStatus, hasFeedback, feedbackIcon, isFormItemInput: true, }; }, [mergedValidateStatus, hasFeedback]); // ======================== Render ======================== const itemClassName = { [itemPrefixCls]: true, [`${itemPrefixCls}-with-help`]: hasHelp || debounceErrors.length || debounceWarnings.length, [`${className}`]: !!className, // Status [`${itemPrefixCls}-has-feedback`]: mergedValidateStatus && hasFeedback, [`${itemPrefixCls}-has-success`]: mergedValidateStatus === 'success', [`${itemPrefixCls}-has-warning`]: mergedValidateStatus === 'warning', [`${itemPrefixCls}-has-error`]: mergedValidateStatus === 'error', [`${itemPrefixCls}-is-validating`]: mergedValidateStatus === 'validating', [`${itemPrefixCls}-hidden`]: hidden, }; return (
{/* Label */} {/* Input Group */} {children} {!!marginBottom && (
)}
); }