///
/**
* 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