///
/**
* FormControlLabel Component - Lynx 版 MUI FormControlLabel
* 100% 一比一复刻 MUI FormControlLabel
*
* 将表单控件(Checkbox、Radio、Switch)与标签组合在一起
*
* 对应 MUI: packages/mui-material/src/FormControlLabel/FormControlLabel.js
*/
import './FormControlLabel.css'
import formControlLabelClasses, { getFormControlLabelUtilityClass } from './formControlLabelClasses'
import { useFormControl, formControlState } from '../FormControl'
export { formControlLabelClasses, getFormControlLabelUtilityClass }
// =============================================
// 类型定义
// =============================================
export type FormControlLabelLabelPlacement = 'end' | 'start' | 'top' | 'bottom'
export interface FormControlLabelProps {
/** 表单控件 */
control: any
/** 标签文本 */
label: any
/** 自定义类名 */
className?: string
/** 样式类覆盖 */
classes?: Partial
/** 是否选中 - 传递给 control */
checked?: boolean
/** 是否禁用 */
disabled?: boolean
/** 是否禁用排版 */
disableTypography?: boolean
/** input 元素的 ref */
inputRef?: any
/** 标签位置 */
labelPlacement?: FormControlLabelLabelPlacement
/** name 属性 */
name?: string
/** 变化回调 */
onChange?: (event: any, checked: boolean) => void
/** 是否必填 */
required?: boolean
/** 内联样式 */
style?: Record
/** sx 属性 */
sx?: Record
/** 值 */
value?: string
}
// =============================================
// 辅助函数
// =============================================
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
// =============================================
interface OwnerState extends FormControlLabelProps {}
function useUtilityClasses(ownerState: OwnerState & { error?: boolean }) {
const {
classes,
disabled,
error,
required,
labelPlacement = 'end'
} = ownerState
const slots = {
root: [
'root',
disabled && 'disabled',
error && 'error',
required && 'required',
labelPlacement !== 'end' && `labelPlacement${labelPlacement.charAt(0).toUpperCase() + labelPlacement.slice(1)}`,
],
label: ['label', disabled && 'disabled'],
asterisk: ['asterisk', error && 'error'],
}
return composeClasses(slots, getFormControlLabelUtilityClass, classes)
}
// =============================================
// FormControlLabel 组件
// =============================================
export function FormControlLabel(props: FormControlLabelProps) {
const {
control,
label,
className,
classes: classesProp,
checked,
disabled: disabledProp,
disableTypography = false,
inputRef,
labelPlacement = 'end',
name,
onChange,
required: requiredProp,
style,
sx,
value,
...other
} = props
const muiFormControl = useFormControl()
const fcs = formControlState({
props,
muiFormControl,
states: ['disabled', 'error', 'required'],
})
const disabled = disabledProp ?? control?.props?.disabled ?? fcs.disabled
const required = requiredProp ?? control?.props?.required ?? fcs.required
const ownerState: OwnerState & { error?: boolean } = {
...props,
disabled,
labelPlacement,
required,
error: fcs.error,
}
const classes = useUtilityClasses(ownerState)
// 增强控件,注入必要的 props
const controlProps: Record = {
disabled,
required,
}
// 传递可选 props
if (checked !== undefined && control?.props?.checked === undefined) {
controlProps.checked = checked
}
if (name !== undefined && control?.props?.name === undefined) {
controlProps.name = name
}
if (onChange !== undefined && control?.props?.onChange === undefined) {
controlProps.onChange = onChange
}
if (value !== undefined && control?.props?.value === undefined) {
controlProps.value = value
}
if (inputRef !== undefined && control?.props?.inputRef === undefined) {
controlProps.inputRef = inputRef
}
const enhancedControl = control ? {
...control,
props: {
...control.props,
...controlProps,
}
} : null
// 构建类名
const rootClasses = [
classes.root,
formControlLabelClasses.root,
className,
disabled && formControlLabelClasses.disabled,
fcs.error && formControlLabelClasses.error,
labelPlacement !== 'end' && formControlLabelClasses[`labelPlacement${labelPlacement.charAt(0).toUpperCase() + labelPlacement.slice(1)}` as keyof typeof formControlLabelClasses],
].filter(Boolean).join(' ')
const labelClasses = [
classes.label,
formControlLabelClasses.label,
disabled && formControlLabelClasses.disabled,
].filter(Boolean).join(' ')
// 根据标签位置渲染不同布局
const renderContent = () => {
switch (labelPlacement) {
case 'start':
return (
<>
{label}
{enhancedControl}
>
)
case 'top':
return (
{label}
{enhancedControl}
)
case 'bottom':
return (
{enhancedControl}
{label}
)
case 'end':
default:
return (
<>
{enhancedControl}
{label}
>
)
}
}
return (
{renderContent()}
)
}
export default FormControlLabel