Skip to content

Instantly share code, notes, and snippets.

@remy-poirier
Created August 22, 2018 12:34
Show Gist options
  • Save remy-poirier/9acb4f50d70ffd8c0d49e781080e0e6a to your computer and use it in GitHub Desktop.
Save remy-poirier/9acb4f50d70ffd8c0d49e781080e0e6a to your computer and use it in GitHub Desktop.
React autosuggest with Popper
import React, { Component } from "react";
import TextField from "@material-ui/core/TextField";
import Popper from "@material-ui/core/Popper";
import { withStyles } from "@material-ui/core/styles";
import match from "autosuggest-highlight/match";
import parse from "autosuggest-highlight/parse";
import {MenuItem, Paper} from "@material-ui/core";
import Autosuggest from "react-autosuggest";
import T from "i18n-react";
function renderInputComponent(inputProps) {
const { classes, inputRef = () => {}, ref, ...other } = inputProps;
return (
<TextField
fullWidth
InputProps={{
inputRef: node => {
ref(node);
inputRef(node);
},
classes: {
input: classes.input
}
}}
{...other}
/>
)
}
function renderSuggestion(suggestion, {query, isHighlighted}) {
const matches = match(suggestion.label, query);
const parts = parse(suggestion.label, matches);
return (
<MenuItem selected={isHighlighted} component="div">
<div>
{parts.map((part, index) => {
return part.highlight ? (
<span key={String(index)} style={{fontWeight: 300}}>
{part.text}
</span>
) : (
<strong key={String(index)} style={{fontWeight: 700}}>
{part.text}
</strong>
)
})}
</div>
</MenuItem>
)
}
function getSuggestions(value) {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
let count = 0;
return inputLength === 0
? []
: suggestions.filter(suggestion => {
const keep =
count < 5 && suggestion.label.toLowerCase().slice(0, inputLength) === inputValue;
if (keep) {
count += 1;
}
return keep;
});
}
function getSuggestionValue(suggestion) {
return suggestion.label;
}
const styles = (theme) => ({
root: {
flexGrow: 1,
},
container: {
position: 'relative',
},
suggestionsContainerOpen: {
position: 'absolute',
zIndex: 9999,
marginTop: theme.spacing.unit,
left: 0,
top: 0,
background: 'white',
right: 0,
},
suggestion: {
display: 'block',
},
suggestionsList: {
margin: 0,
padding: 0,
listStyleType: 'none',
},
divider: {
height: theme.spacing.unit * 2,
},
});
class InputAutocomplete extends Component {
popperNode = null;
state = {
value: "",
popper: "",
suggestions: [],
hasBeenClicked: false
};
handleSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value),
});
};
handleSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
handleChange = (event, { newValue }) => {
this.setState({value: newValue});
};
render(){
const { classes } = this.props;
return (
<div className={classes.root}>
<Autosuggest
onSuggestionsFetchRequested={this.handleSuggestionsFetchRequested}
onSuggestionsClearRequested={this.handleSuggestionsClearRequested}
suggestions={this.state.suggestions}
renderInputComponent={renderInputComponent}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={{
classes,
placeholder: this.props.placeholder || T.translate("global.search"),
value: this.state.value,
onChange: this.handleChange,
inputRef: node => {
this.popperNode = node;
},
InputLabelProps: {
shrink: true
},
...this.props.id && {id: this.props.id}
}}
theme={{
suggestionsList: classes.suggestionsList,
suggestion: classes.suggestion,
}}
renderSuggestionsContainer={options => {
return <Popper anchorEl={this.popperNode} open={!!options.children}>
<Paper
{...options.containerProps}
square
style={{ width: this.popperNode ? this.popperNode.clientWidth : null }}
>
{options.children}
</Paper>
</Popper>
}}
{...this.props.onSelect && {onSuggestionSelected : this.onSelect}}
{...this.props.id && {id: this.props.id}}
/>
</div>
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment