/// /** * NativeSelect Component - Lynx 版 MUI NativeSelect * 100% 一比一复刻 MUI NativeSelect * * 原生下拉选择组件,使用原生 select 元素 * * 对应 MUI: packages/mui-material/src/NativeSelect/NativeSelect.js */ import './NativeSelect.css' import nativeSelectClasses, { getNativeSelectUtilityClass } from './nativeSelectClasses' import { useFormControl, formControlState } from '../FormControl' export { nativeSelectClasses, getNativeSelectUtilityClass } // ============================================= // 类型定义 // ============================================= export type NativeSelectVariant = 'standard' | 'outlined' | 'filled' export interface NativeSelectProps { /** 子元素 - option 元素 */ children?: any /** 自定义类名 */ className?: string /** 样式类覆盖 */ classes?: Partial /** 默认值 */ defaultValue?: string | number /** 是否禁用 */ disabled?: boolean /** 图标组件 */ IconComponent?: any /** input 组件 */ input?: any /** input 属性 */ inputProps?: Record /** 变体 */ variant?: NativeSelectVariant /** 是否多选 */ multiple?: boolean /** 当前值 */ value?: string | number | string[] /** 内联样式 */ style?: Record /** sx 属性 */ sx?: Record /** 变化事件 */ onChange?: (event: any, value: any) => 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 NativeSelectProps {} function useUtilityClasses(ownerState: OwnerState) { const { classes } = ownerState const slots = { root: ['root'], } return composeClasses(slots, getNativeSelectUtilityClass, classes) } // ============================================= // NativeSelect 组件 // ============================================= export function NativeSelect(props: NativeSelectProps) { const { children, className, classes: classesProp, defaultValue, disabled = false, IconComponent, input, inputProps, variant, multiple = false, value: valueProp, style, sx, onChange, ...other } = props const muiFormControl = useFormControl() const fcs = formControlState({ props, muiFormControl, states: ['variant', 'error'], }) const ownerState: OwnerState = { ...props, disabled, variant: variant || fcs.variant, multiple, } const classes = useUtilityClasses(ownerState) // 在 Lynx 环境下,简化实现,直接渲染 select 元素 // 不使用 Input 组件包装 // 处理变化事件 const handleChange = (event: any) => { if (onChange) { onChange(event, event.target.value) } } // 构建类名 const rootClasses = [ classes.root, nativeSelectClasses.root, className, nativeSelectClasses.select, nativeSelectClasses[`variant${capitalize(variant || fcs.variant || 'standard')}` as keyof typeof nativeSelectClasses], disabled && nativeSelectClasses.disabled, multiple && nativeSelectClasses.multiple, fcs.error && 'Mui-error', ].filter(Boolean).join(' ') // 构建 select 属性 const selectProps = { ...inputProps, ...other, value: valueProp !== undefined ? valueProp : defaultValue, disabled, multiple, onChange: handleChange, } return ( {IconComponent && ( )} ) } export default NativeSelect