/// /** * ToggleButton Component - Lynx 版 MUI ToggleButton * 100% 一比一复刻 MUI ToggleButton * * 切换按钮,支持选中/未选中状态 * * 对应 MUI: packages/mui-material/src/ToggleButton/ToggleButton.js */ import './ToggleButton.css' import toggleButtonClasses, { getToggleButtonUtilityClass } from './toggleButtonClasses' export { toggleButtonClasses, getToggleButtonUtilityClass } // ============================================= // 类型定义 // ============================================= export type ToggleButtonColor = 'standard' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' export type ToggleButtonSize = 'small' | 'medium' | 'large' export interface ToggleButtonProps { /** 子元素 */ children?: any /** 自定义类名 */ className?: string /** 样式类覆盖 */ classes?: Partial /** 颜色 */ color?: ToggleButtonColor /** 是否禁用 */ disabled?: boolean /** 是否禁用涟漪 */ disableRipple?: boolean /** 是否全宽 */ fullWidth?: boolean /** 是否选中 */ selected?: boolean /** 默认是否选中 */ defaultSelected?: boolean /** 尺寸 */ size?: ToggleButtonSize /** 值 */ value?: string /** 内联样式 */ style?: Record /** sx 属性 */ sx?: Record /** 点击事件 */ bindtap?: (event?: any) => void onChange?: (event: any, selected: boolean) => void } // ============================================= // 辅助函数 // ============================================= 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 // ============================================= interface OwnerState extends ToggleButtonProps {} function useUtilityClasses(ownerState: OwnerState) { const { classes, color = 'standard', disabled, fullWidth, selected, size = 'medium' } = ownerState const slots = { root: [ 'root', selected && 'selected', disabled && 'disabled', fullWidth && 'fullWidth', `size${capitalize(size)}`, color, ], } return composeClasses(slots, getToggleButtonUtilityClass, classes) } // ============================================= // ToggleButton 组件 // ============================================= export function ToggleButton(props: ToggleButtonProps) { const { children, className, classes: classesProp, color = 'standard', disabled = false, disableRipple = false, fullWidth = false, selected: selectedProp, defaultSelected = false, size = 'medium', value, style, sx, bindtap, onChange, ...other } = props // 在 Lynx 环境下,组件必须是受控的 // 如果没有提供 selected 属性,使用默认值 const selected = selectedProp !== undefined ? selectedProp : defaultSelected const ownerState: OwnerState = { ...props, color, disabled, fullWidth, selected, size, } const classes = useUtilityClasses(ownerState) // 处理点击 const handleTap = (event?: any) => { if (disabled) return const newSelected = !selected // 在 Lynx 环境下,组件是受控的,不需要内部状态更新 // 状态由父组件管理 // 触发回调 if (bindtap) bindtap(event) if (onChange) onChange(event, newSelected) } // 构建类名 const rootClasses = [ classes.root, toggleButtonClasses.root, className, selected && toggleButtonClasses.selected, disabled && toggleButtonClasses.disabled, fullWidth && toggleButtonClasses.fullWidth, toggleButtonClasses[`size${capitalize(size)}` as keyof typeof toggleButtonClasses], toggleButtonClasses[`color${capitalize(color)}` as keyof typeof toggleButtonClasses], ].filter(Boolean).join(' ') return ( {children} ) } export default ToggleButton