gmaps-places-autocomplete
Version:
A React autosuggest input for the Google Maps Places API.
189 lines (158 loc) • 4.59 kB
JavaScript
'use strict';
import React from 'react';
import SuggestionItem from './item';
import google from 'google';
class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
isSuggestsHidden: true,
userInput: '',
activeSuggest: null,
suggests: [],
geocoder: new google.maps.Geocoder(),
autocompleteService: new google.maps.places.AutocompleteService(),
};
}
updateAutocomplete() {
if (!this.state.userInput.length) {
return this.updateSuggests();
}
this.state.autocompleteService.getPlacePredictions({
input: this.state.userInput,
location: this.props.location || new google.maps.LatLng(0, 0),
radius: this.props.radius || 0,
}, (suggestions) => this.updateSuggests(suggestions));
}
handleInputChange = (e) => {
this.setState({userInput: e.target.value}, () => this.updateAutocomplete());
}
handleInputFocus = () => {
this.updateSuggests();
this.setState({isSuggestsHidden: false});
}
handleInputBlur = () => {
setTimeout(() => {
this.setState({isSuggestsHidden: true});
}, 100);
}
handleInputKeyDown = (event) => {
switch (event.which) {
case 40: // DOWN
event.preventDefault();
this.activateSuggest('next');
break;
case 38: // UP
event.preventDefault();
this.activateSuggest('prev');
break;
case 13: // ENTER
this.selectSuggest(this.state.activeSuggest);
break;
case 9: // TAB
this.selectSuggest(this.state.activeSuggest);
break;
case 27: // ESC
this.handleInputBlur();
break;
}
}
handleSuggestSelect = (suggest) => {
if (!suggest) {
suggest = {
label: this.state.userInput,
};
}
this.setState({
isSuggestsHidden: true,
userInput: suggest.label,
});
if (suggest.location) {
this.props.onSuggestSelect(suggest);
return;
}
this.geocodeSuggest(suggest);
}
updateSuggests(suggestsGoogle) {
let suggests = [];
let regex = new RegExp(this.state.userInput, 'gim');
(this.props.fixtures || []).forEach((suggest) => {
if (suggest.label.match(regex)) {
suggest.placeId = suggest.label;
suggests.push(suggest);
}
});
(suggestsGoogle || []).forEach((suggest) => {
suggests.push({
label: suggest.description,
placeId: suggest.place_id,
});
});
this.setState({suggests});
}
activateSuggest(direction) {
let suggestsCount = this.state.suggests.length - 1;
let next = (direction === 'next');
let newActiveSuggest = null;
let newIndex = 0;
for (let i = 0; i <= suggestsCount; i++) {
if (this.state.suggests[i] === this.state.activeSuggest) {
newIndex = next ? i + 1 : i - 1;
}
}
if (!this.state.activeSuggest) {
newIndex = next ? 0 : suggestsCount;
}
if (newIndex >= 0 && newIndex <= suggestsCount) {
newActiveSuggest = this.state.suggests[newIndex];
}
this.setState({activeSuggest: newActiveSuggest});
}
geocodeSuggest(suggest) {
this.state.geocoder.geocode({
address: suggest.label,
}, (results, status) => {
if (status !== google.maps.GeocoderStatus.OK) { return; }
let location = results[0].geometry.location;
suggest.location = {
lat: location.lat(),
lng: location.lng(),
};
this.handleSuggestSelect(suggest);
});
}
getSuggestItems(){
return this.state.suggests.map((suggest) => {
let isActive = (this.state.activeSuggest && suggest.placeId === this.state.activeSuggest.placeId);
return (
<SuggestionItem
key={suggest.placeId}
suggest={suggest}
isActive={isActive}
onSuggestSelect={this.handleSuggestSelect}
/>
);
});
}
render() {
return (
<div className="geosuggest">
<input
className="geosuggest__input"
ref="geosuggestInput"
type="text"
value={this.state.userInput}
placeholder={this.props.placeholder || ''}
onKeyDown={this.handleInputKeyDown}
onChange={this.handleInputChange}
onFocus={this.handleInputFocus}
onBlur={this.handleInputBlur}
/>
<ul className={'geosuggest__suggests' + (this.state.isSuggestsHidden ? ' geosuggest__suggests--hidden' : '')}>
{this.getSuggestItems()}
</ul>
</div>
);
}
}
export default Input;