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