import cns from 'classnames' import React, { CSSProperties, ReactNode } from 'react' import { ComponentSize, usePrefix } from '../ConfigProvider' import { concat } from '../utils' import { ValidateMessage, validateResultCtx, ValidateStatus, ValidateResult, defaultValidateStatus, Layout, LayoutType, defaultLabelAlign, useFormSize, useFormLayout, sizeCtx, layoutCtx, useLabelColorMap, defaultLabelColor, LabelOptions, useFormLabelOptions, defaultLabelVerticalAlign, defaultLabelWidth, defaultLabelGapForHorizontal, defaultLabelGapForInline, useFormVerticalGap } from './defs' export interface Props extends LabelOptions { /** 内容尺寸 */ size?: ComponentSize /** 内容布局 */ layout?: LayoutType | Layout /** 表单项名称 */ label?: ReactNode /** 输入提示信息 */ tip?: ReactNode /** 校验状态 */ validateStatus?: ValidateStatus /** 校验结果信息 */ validateMessage?: ValidateMessage /** 是否必填(会在 label 处展示对应的标识) */ required?: boolean /** 自定义样式名 */ className?: string /** 自定义样式 */ style?: CSSProperties /** 表单项内容 */ children?: ReactNode } export default function FormItem(props: Props) { const prefix = usePrefix('form-item') const size = useFormSize(props.size) const layout = useFormLayout(props.layout) const labelOptions = useFormLabelOptions({ labelWidth: props.labelWidth, labelGap: props.labelGap, labelAlign: props.labelAlign, labelColor: props.labelColor, labelVerticalAlign: props.labelVerticalAlign }) const verticalGap = useFormVerticalGap(undefined) const labelColorMap = useLabelColorMap() const validateStatus = props.validateStatus ?? defaultValidateStatus const className = cns( prefix, props.className, concat(prefix, 'size', size), concat(prefix, 'layout', layout.type), concat(prefix, 'validate-status', validateStatus) ) const style: CSSProperties = { marginBottom: verticalGap, ...props.style } const labelClassName = cns( concat(prefix, 'label'), props.required && concat(prefix, 'label', 'required'), layout.type === 'horizontal' && concat(prefix, 'label', 'align', labelOptions.labelAlign ?? defaultLabelAlign), layout.type === 'horizontal' && concat(prefix, 'label', 'vertical-align', labelOptions.labelVerticalAlign ?? defaultLabelVerticalAlign) ) const labelMarginRight = (() => { if (layout.type === 'horizontal') return labelOptions.labelGap ?? defaultLabelGapForHorizontal if (layout.type === 'inline') return labelOptions.labelGap ?? defaultLabelGapForInline })() const labelStyle: CSSProperties = { flexBasis: layout.type === 'horizontal' ? (labelOptions.labelWidth ?? defaultLabelWidth) : undefined, color: labelColorMap[labelOptions.labelColor ?? defaultLabelColor], marginRight: labelMarginRight } const validateResult: ValidateResult = { status: validateStatus, message: props.validateMessage } const tipView = props.tip != null && {props.tip} return (
{props.label != null &&
{props.label}
}
{props.children}
{props.validateMessage}
{tipView}
) } export interface TipProps { /** 提示内容 */ children?: ReactNode } export function Tip({ children }: TipProps) { const prefix = usePrefix('form-item') return
{children}
}