/// /** * FormControl Component - Lynx 版 MUI FormControl * 100% 一比一复刻 MUI FormControl * * 为表单输入组件提供上下文(filled/focused/error/required 等状态) * 用于:FormLabel, FormHelperText, Input, InputLabel * * 对应 MUI: packages/mui-material/src/FormControl/FormControl.js */ import './FormControl.css' import formControlClasses, { getFormControlUtilityClass } from './formControlClasses' import { setFormControlContext, FormControlState } from './FormControlContext' export { formControlClasses, getFormControlUtilityClass } export { useFormControl, formControlState } from './FormControlContext' export type { FormControlState } from './FormControlContext' // ============================================= // 类型定义 // ============================================= export type FormControlColor = 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' export type FormControlMargin = 'none' | 'dense' | 'normal' export type FormControlSize = 'small' | 'medium' export type FormControlVariant = 'standard' | 'outlined' | 'filled' export interface FormControlProps { /** 子元素 */ children?: any /** 自定义类名 */ className?: string /** 样式类覆盖 */ classes?: Partial /** 颜色 */ color?: FormControlColor /** 是否禁用 */ disabled?: boolean /** 是否错误状态 */ error?: boolean /** 是否聚焦 */ focused?: boolean /** 是否全宽 */ fullWidth?: boolean /** 是否隐藏标签 */ hiddenLabel?: boolean /** 边距 */ margin?: FormControlMargin /** 是否必填 */ required?: boolean /** 尺寸 */ size?: FormControlSize /** 内联样式 */ style?: Record /** sx 属性 */ sx?: Record /** 变体 */ variant?: FormControlVariant } // ============================================= // 辅助函数 // ============================================= function capitalize(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1) } function composeClasses( slots: Record, getUtilityClass: (slot: string) => string, classes?: Record ): Record { const output: Record = {} Object.keys(slots).forEach((slot) => { output[slot] = slots[slot] .filter(Boolean) .map((key) => { if (classes && classes[key as string]) { return `${getUtilityClass(key as string)} ${classes[key as string]}` } return getUtilityClass(key as string) }) .join(' ') }) return output } // ============================================= // useUtilityClasses // ============================================= function useUtilityClasses(ownerState: FormControlProps) { const { classes, margin = 'none', fullWidth } = ownerState const slots = { root: [ 'root', margin !== 'none' && `margin${capitalize(margin)}`, fullWidth && 'fullWidth', ], } return composeClasses(slots, getFormControlUtilityClass, classes) } // ============================================= // FormControl 组件 // ============================================= export function FormControl(props: FormControlProps) { const { children, className, classes: classesProp, color = 'primary', disabled = false, error = false, focused: visuallyFocused, fullWidth = false, hiddenLabel = false, margin = 'none', required = false, size = 'medium', style, sx, variant = 'outlined', ...other } = props const ownerState = { ...props, color, disabled, error, fullWidth, hiddenLabel, margin, required, size, variant, } const classes = useUtilityClasses(ownerState) // 内部状态 let filled = false let focused = visuallyFocused !== undefined && !disabled ? visuallyFocused : false // 创建上下文值 const contextValue: FormControlState = { color, disabled, error, filled, focused, fullWidth, hiddenLabel, size, required, variant, onBlur: () => { focused = false }, onFocus: () => { focused = true }, onEmpty: () => { filled = false }, onFilled: () => { filled = true }, } // 设置全局上下文(简化实现) setFormControlContext(contextValue) // 构建类名 const rootClasses = [ classes.root, className, ].filter(Boolean).join(' ') // 清理上下文的效果(组件卸载时) // 注意:在实际 React 中这应该用 useEffect return ( {children} ) } export default FormControl