import Checkbox, { CheckboxProps } from '@material-ui/core/Checkbox'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import FormGroup from '@material-ui/core/FormGroup'; import FormLabel from '@material-ui/core/FormLabel'; import MenuItem from '@material-ui/core/MenuItem'; import Radio from '@material-ui/core/Radio'; import RadioGroup from '@material-ui/core/RadioGroup'; import { SelectProps as MaterialSelectProps } from '@material-ui/core/Select'; import Switch, { SwitchProps } from '@material-ui/core/Switch'; import TextField, { TextFieldProps } from '@material-ui/core/TextField'; import useTheme from '@material-ui/core/styles/useTheme'; import omit from 'lodash/omit'; import xor from 'lodash/xor'; import React, { Ref } from 'react'; import { FieldProps, connectField, filterDOMProps, Override } from 'uniforms'; import type { Option } from './types'; import wrapField from './wrapField'; type SelectFieldCommonProps = { appearance?: 'checkbox' | 'switch'; inputRef?: Ref; required?: boolean; variant?: 'standard' | 'outlined' | 'filled'; options?: Option[]; }; type CheckboxesProps = FieldProps< string | string[], CheckboxProps | SwitchProps, SelectFieldCommonProps & { checkboxes: true; legend?: string; } >; type SelectProps = FieldProps< string | string[], Override< MaterialSelectProps & TextFieldProps, { margin?: 'normal' | 'dense' | 'none' } >, SelectFieldCommonProps & { checkboxes?: false; labelProps?: object; native?: boolean; textFieldProps?: Omit; } >; export type SelectFieldProps = CheckboxesProps | SelectProps; const base64: (string: string) => string = typeof btoa === 'undefined' ? /* istanbul ignore next */ x => Buffer.from(x).toString('base64') : btoa; const escape = (x: string) => base64(encodeURIComponent(x)).replace(/=+$/, ''); // eslint-disable-next-line complexity function Select(props: SelectFieldProps) { const theme = useTheme(); const formControlThemeProps = theme.props?.MuiFormControl; const value = props.value ?? ''; if (props.checkboxes) { const { options, disabled, fieldType, id, inputRef, label, legend, name, onChange, readOnly, } = props; const appearance = props.appearance ?? 'checkbox'; const SelectionControl = appearance === 'checkbox' ? Checkbox : Switch; const filteredProps = omit(filterDOMProps(props), [ 'checkboxes' as never, 'disableItem' as never, 'id', 'inputRef', ]); const children = fieldType !== Array ? ( disabled || readOnly || onChange(event.target.value) } ref={inputRef} value={value ?? ''} > {options?.map(option => ( } disabled={option.disabled || disabled} key={option.key ?? option.value} label={option.label ?? option.value} value={option.value} /> ))} ) : ( {options?.map(option => ( disabled || readOnly || onChange(xor([option.value], value)) } ref={inputRef} value={name} {...filteredProps} /> } disabled={option.disabled || disabled} key={option.key ?? option.value} label={option.label ?? option.value} /> ))} ); return wrapField( { ...formControlThemeProps, ...props, component: 'fieldset' }, (legend || label) && ( {legend || label} ), children, ); } const textFieldThemeProps = theme.props?.MuiTextField; const { options, disabled, error, errorMessage, fieldType, fullWidth = textFieldThemeProps?.fullWidth ?? true, helperText, id, InputLabelProps, inputProps, label, labelProps, margin = textFieldThemeProps?.margin ?? 'dense', name, native, onChange, placeholder, readOnly, required, showInlineError, variant, textFieldProps, } = props; const Item = native ? 'option' : MenuItem; const hasPlaceholder = !!placeholder; const hasValue = value !== '' && value !== undefined; /* Never was added because these 2 variables cause omit to fall into an unpleasant overload. So we need to use a trick to reduce the confusion of handling types */ const filteredProps = omit(filterDOMProps(props), [ 'checkboxes' as never, 'disableItem' as never, 'fullWidth', 'helperText', 'margin', 'textFieldProps', 'variant', ]); return ( disabled || readOnly || onChange(event.target.value !== '' ? event.target.value : undefined) } required={required} select SelectProps={{ displayEmpty: hasPlaceholder, inputProps: { name, id, ...inputProps }, multiple: fieldType === Array || undefined, native, ...filteredProps, }} value={native && !value ? '' : value} variant={variant} {...textFieldProps} > {(hasPlaceholder || !required || !hasValue) && ( {placeholder || label} )} {options?.map(option => ( {option.label ?? option.value} ))} ); } export default connectField(Select, { kind: 'leaf' });