Skip to content

Instantly share code, notes, and snippets.

@oscaroceguera
Last active September 1, 2019 07:45
Show Gist options
  • Save oscaroceguera/621a08f64e1b391059d5a0fc6bff1ccd to your computer and use it in GitHub Desktop.
Save oscaroceguera/621a08f64e1b391059d5a0fc6bff1ccd to your computer and use it in GitHub Desktop.
Autocomplete material-ui component
import React from 'react'
import PropTypes from 'prop-types'
import deburr from 'lodash/deburr'
import Downshift from 'downshift'
import { withStyles } from '@material-ui/core/styles'
import { TextField, Paper, MenuItem, Chip } from '@material-ui/core'
function renderInput(inputProps) {
const { InputProps, classes, ref, ...other } = inputProps
return (
<TextField
InputProps={{
inputRef: ref,
classes: {
root: classes.inputRoot
},
...InputProps
}}
{...other}
/>
)
}
function renderSuggestion({ suggestion, index, itemProps, highlightedIndex, selectedItem }) {
const isHighlighted = highlightedIndex === index
const isSelected = (selectedItem || '').indexOf(suggestion.label) > -1
return (
<MenuItem
{...itemProps}
key={suggestion.label}
selected={isHighlighted}
component='div'
style={{
fontWeight: isSelected ? 500 : 400
}}
>
{suggestion.label}
</MenuItem>
)
}
renderSuggestion.propTypes = {
highlightedIndex: PropTypes.number,
index: PropTypes.number,
itemProps: PropTypes.object,
selectedItem: PropTypes.string,
suggestion: PropTypes.shape({ label: PropTypes.string }).isRequired
}
function getSuggestions(value, data) {
const inputValue = deburr(value.trim()).toLowerCase()
const inputLength = inputValue.length
let count = 0
return inputLength === 0
? []
: data.filter(suggestion => {
const keep =
count < 5 && suggestion.label.slice(0, inputLength).toLowerCase() === inputValue
if (keep) {
count += 1
}
return keep
})
}
const styles = theme => ({
root: {
flexGrow: 1
},
container: {
flexGrow: 1,
position: 'relative'
},
paper: {
position: 'absolute',
zIndex: 1,
marginTop: theme.spacing(1),
left: 0,
right: 0
},
chip: {
margin: theme.spacing(0.5, 0.25)
},
inputRoot: {
flexWrap: 'wrap'
},
divider: {
height: theme.spacing(2)
}
})
const Autocomplete = ({
classes,
data,
placeholder,
label,
inputValue,
selectedItem,
handleKeyDown,
handleInputChange,
handleChange,
handleDelete
}) => {
return (
<Downshift
id='downshift-multiple'
inputValue={inputValue}
onChange={handleChange}
selectedItem={selectedItem}
>
{({
getInputProps,
getItemProps,
isOpen,
inputValue: inputValue2,
selectedItem: selectedItem2,
highlightedIndex
}) => (
<div className={classes.container}>
{renderInput({
fullWidth: true,
classes,
InputProps: getInputProps({
startAdornment: selectedItem.map(item => (
<Chip
key={item}
tabIndex={-1}
label={item}
className={classes.chip}
onDelete={handleDelete(item)}
/>
)),
onChange: handleInputChange,
onKeyDown: handleKeyDown,
placeholder: placeholder
}),
label: label
})}
{isOpen ? (
<Paper className={classes.paper} square>
{getSuggestions(inputValue2, data).map((suggestion, index) =>
renderSuggestion({
suggestion,
index,
itemProps: getItemProps({ item: suggestion.label }),
highlightedIndex,
selectedItem: selectedItem2
})
)}
</Paper>
) : null}
</div>
)}
</Downshift>
)
}
Autocomplete.propTypes = {
classes: PropTypes.object.isRequired,
data: PropTypes.array.isRequired,
placeholder: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
inputValue: PropTypes.string.isRequired,
selectedItem: PropTypes.array.isRequired,
handleKeyDown: PropTypes.func.isRequired,
handleInputChange: PropTypes.func.isRequired,
handleChange: PropTypes.func.isRequired,
handleDelete: PropTypes.func.isRequired
}
export default withStyles(styles)(Autocomplete)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment