Caveat: This is going to be deep in the realm of opinion... I ended up going with Downshift for my customization npm install downshift
This code is bit dirty (out of my dev branch), but it does a customized dropdown that you can edit
import React from 'react'
import {render} from 'react-dom'
import Downshift from 'downshift'
import {
MenuItem,
Paper,
TextField,
} from '@material-ui/core'
import {
withStyles
} from '@material-ui/core/styles'
const items = [
'apple',
'pear',
'orange',
'grape',
'banana',
]
class DownshiftWrapper extends React.Component {
constructor(props) {
super(props)
this.state = {
value: props.value || '',
backup: props.value || '',
onChange: v => {console.log('changed', v)}
}
}
_renderMenuItem(args) {
const { key, index, itemProps, current, highlightedIndex, selectedItem, ...rest } = args
const isSelected = key == current
return (
<MenuItem
{...rest}
key = { key }
selected = { isSelected }
component='div'
style={{
fontWeight: isSelected ? 500 : 400,
padding: '2px 16px 2px 16px',
borderBottom: '1px solid rgba(128,128,128,0.5)',
}}
>
{ key }
</MenuItem>
)
}
render() {
const { classes, style } = this.props
const _override = (incoming) => {
console.log('override:', incoming)
this.setState({
...this.state,
value: incoming
})
if(this.props.onChange) {
this.props.onChange(incoming)
} else {
console.log(`Downshift::onChange the onchange handler is missing. New value:${incoming}`)
}
}
return (
<Downshift
ref = { x => this.downshift = x}
onSelect = { (selected) => {
if(selected) {
console.log('::onSelect', selected)
_override(selected)
}
} }
onInputValueChange= { (inputValue, stateAndHelpers) => {
console.log('::onInputValueChange', {
...stateAndHelpers,
_val: inputValue,
})
} }
// onStateChange={( state ) => {
// //return input.onChange(inputValue);
// let value = state.inputValue
// this.state.onChange(state.inputValue)
// console.log('old:state', state)
// console.log('value:', value)
// _override( state.inputValue )
// }}
onChange={ selection => { console.log(selection) }}
itemToString={ item => {
return item || ''
} }
//selectedItem={this.props.input.value}
>
{({
getInputProps,
getItemProps,
getLabelProps,
getMenuProps,
isOpen,
inputValue,
highlightedIndex,
selectedItem,
}) => {
const inputProps = getInputProps()
let value = inputProps.value
//FIXME add filtering options
let filtered = this.props.items || items//.filter(item => !inputValue || item.includes(inputValue))
return (
<div className={classes.container}>
<TextField
{ ...inputProps }
style={
style
}
label={this.props.label}
placeholder={this.props.placeholder}
value = {
this.state.value
}
onFocus = { e => {
this.downshift.openMenu()
e.target.select()
}}
onBlur={ e => {
console.log(inputValue)
e.preventDefault()
this.downshift.closeMenu()
} }
onChange={ e => {
inputProps.onChange(e)//pass to the logic
_override(e.target.value)
}}
onKeyDown= { (e) => {
const key = e.which || e.keyCode
if(key == 27){
e.preventDefault()
e.target.blur()
//reset to default
_override(this.state.backup || '')
} else if (key == 13){
e.preventDefault()
e.target.blur()
_override(e.target.value)
}
}}
/>
{isOpen
? (
<Paper
className={classes.paper}
// style={{
// backgroundColor: 'white',
// }}
square>
{ filtered
.map( (item, index) => {
const _props = {
...getItemProps({ item: item }),
index: index,
key: item,
item: item,
current: this.state.value,
}
return this._renderMenuItem(_props)
} )
}
</Paper>
)
: null}
{/* <div style={{color: 'red'}}>{this.state.value || 'null'}</div> */}
</div>
)
} }
</Downshift>
)
}
}
class Integrated extends React.Component {
}
//Material UI Examples -> https://material-ui.com/demos/autocomplete/
const styles = theme => ({
root: {
flexGrow: 1,
height: 250,
},
container: {
flexGrow: 1,
position: 'relative',
},
paper: {
position: 'absolute',
zIndex: 1,
marginTop: theme.spacing.unit,
left: 0,
right: 0,
},
chip: {
margin: `${theme.spacing.unit / 2}px ${theme.spacing.unit / 4}px`,
},
inputRoot: {
flexWrap: 'wrap',
},
})
export default withStyles(styles)(DownshiftWrapper)
in use (EditableSelect is the export name in my project):
return (
<EditableSelect
//onFocus={e => this.onFocus(e) }
//multiLine={true}
//onKeyDown={ e=> this.keyHandler(e) }
items={ options }
value={ cue.spots[index][field] }
hintText={T.get('spot' + field + 'Hint')}
placeholder={ T.get('spot' + field + 'Hint') }
ref={x => this[id] = x }
style={{width: '90%' }}
onChange={ val => this.updateSpotExplicit(val, index, field) }
/>
)
I'm not sure what you're after, but I found the Autocomplete
problematic when I tried to customize it. There's a bunch of cleanup that needs to happen in this code, but I can verify it is working in our production environment. This was the best solution I found ~18 months ago and we're still using it.
"@material-ui/core": "^4.11.2",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.57",
"@material-ui/styles": "^4.11.2",
"downshift": "^2.0.10",