import * as React from 'react' ; import { useCallback } from 'react' ; import PropTypes from 'prop-types' ; import MenuItem from '@material-ui/core/MenuItem' ; import { TextFieldProps } from '@material-ui/core/TextField' ; import { makeStyles } from '@material-ui/core/styles' ; import { useInput , FieldTitle , useTranslate , ChoicesInputProps , useChoices , warning , } from 'ra-core' ; import ResettableTextField , { resettableStyles } from './ResettableTextField' ; import InputHelperText from './InputHelperText' ; import sanitizeInputRestProps from './sanitizeInputRestProps' ; import Labeled from './Labeled' ; import { LinearProgress } from '../layout' ; import { useSupportCreateSuggestion , SupportCreateSuggestionOptions , } from './useSupportCreateSuggestion' ; import { ClassesOverride } from '../types' ; example example example example example example example example export const SelectInput = ( props ) => { const { allowEmpty , choices = [ ] , classes : classesOverride , className , create , createLabel , createValue , disableValue , emptyText , emptyValue , format , helperText , label , loaded , loading , margin = 'dense' , onBlur , onChange , onCreate , onFocus , options , optionText , optionValue , parse , resource , source , translateChoice , validate , ... rest } = props ; const translate = useTranslate ( ) ; const classes = useStyles ( props ) ; warning ( source === undefined , `If you're not wrapping the SelectInput inside a ReferenceInput, you must provide the source prop` ) ; warning ( choices === undefined , `If you're not wrapping the SelectInput inside a ReferenceInput, you must provide the choices prop` ) ; const { getChoiceText , getChoiceValue , getDisableValue } = useChoices ( { optionText , optionValue , disableValue , translateChoice , } ) ; const { id , input , isRequired , meta } = useInput ( { format , onBlur , onChange , onFocus , parse , resource , source , validate , ... rest , } ) ; const { touched , error , submitError } = meta ; const renderEmptyItemOption = useCallback ( ( ) => { return React . isValidElement ( emptyText ) ? React . cloneElement ( emptyText ) : emptyText === '' ? ' ' : translate ( emptyText , { _ : emptyText } ) ; } , [ emptyText , translate ] ) ; const renderMenuItemOption = useCallback ( choice => getChoiceText ( choice ) , [ getChoiceText , ] ) ; const handleChange = useCallback ( async ( event , newItem ) => { if ( newItem ) { const value = getChoiceValue ( newItem ) ; input . onChange ( value ) ; return ; } input . onChange ( event ) ; } , [ input , getChoiceValue ] ) ; const { getCreateItem , handleChange : handleChangeWithCreateSupport , createElement , } = useSupportCreateSuggestion ( { create , createLabel , createValue , handleChange , onCreate , optionText , } ) ; const createItem = create || onCreate ? getCreateItem ( ) : null ; const finalChoices = create || onCreate ? [ ... choices , createItem ] : choices ; const renderMenuItem = useCallback ( choice => { return choice ? ( < MenuItem key = { getChoiceValue ( choice ) } value = { getChoiceValue ( choice ) } disabled = { getDisableValue ( choice ) } > { renderMenuItemOption ( ! ! createItem && choice ?. id === createItem . id ? createItem : choice ) } < /MenuItem>) : null; } , [ getChoiceValue , getDisableValue , renderMenuItemOption , createItem ] ) ; if ( loading ) { return ( < Labeled id = { id } label = { label } source = { source } resource = { resource } className = { className } isRequired = { isRequired } meta = { meta } input = { input } margin = { margin } > < LinearProgress /> < /Labeled>); } return ( < > < ResettableTextField id = { id } { ... input } onChange = { handleChangeWithCreateSupport } select label = { label !== '' && label !== false && ( < FieldTitle label = { label } source = { source } resource = { resource } isRequired = { isRequired } / > ) } className = { `${ classes . input } ${ className }` } clearAlwaysVisible error = { ! ! ( touched && ( error || submitError ) ) } helperText = { < InputHelperText touched = { touched } error = { error || submitError } helperText = { helperText } / > } margin = { margin } { ... options } { ... sanitizeRestProps ( rest ) } > { allowEmpty ? ( < MenuItem value = { emptyValue } key = "null" aria - label = { translate ( 'ra.action.clear_input_value' ) } title = { translate ( 'ra.action.clear_input_value' ) } > { renderEmptyItemOption ( ) } < /MenuItem>) : null} { finalChoices . map ( renderMenuItem ) } < /ResettableTextField> { createElement } < />); } ; SelectInput . propTypes = { allowEmpty : PropTypes . bool , emptyText : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . element ] ) , emptyValue : PropTypes . any , choices : PropTypes . arrayOf ( PropTypes . object ) , classes : PropTypes . object , className : PropTypes . string , label : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . bool ] ) , options : PropTypes . object , optionText : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . func , PropTypes . element , ] ) . isRequired , optionValue : PropTypes . string . isRequired , disableValue : PropTypes . string , resettable : PropTypes . bool , resource : PropTypes . string , source : PropTypes . string , translateChoice : PropTypes . bool , } ; SelectInput . defaultProps = { emptyText : '' , emptyValue : '' , options : { } , optionText : 'name' , optionValue : 'id' , translateChoice : true , disableValue : 'disabled' , } ; const sanitizeRestProps = ( { addLabel , afterSubmit , allowNull , beforeSubmit , choices , className , crudGetMatching , crudGetOne , data , filter , filterToQuery , formatOnBlur , isEqual , limitChoicesToValue , multiple , name , pagination , perPage , ref , reference , refetch , render , setFilter , setPagination , setSort , sort , subscription , type , validateFields , validation , value , ... rest } ) => sanitizeInputRestProps ( rest ) ; const useStyles = makeStyles ( theme => ( { ... resettableStyles , input : { minWidth : theme . spacing ( 20 ) , } , } ) , { name : 'RaSelectInput' } ) ; export interface SelectInputProps extends ChoicesInputProps < TextFieldProps > , Omit < SupportCreateSuggestionOptions , 'handleChange' > , Omit < TextFieldProps , 'label' | 'helperText' | 'classes' > { classes ? : ClassesOverride < typeof useStyles > ; }