UNPKG

@wix/design-system

Version:

@wix/design-system

232 lines 8.87 kB
import React from 'react'; import castArray from 'lodash/castArray'; import { Search } from '@wix/wix-ui-icons-common'; import Input from '../Input'; import InputWithOptions from '../InputWithOptions'; import { google2address, includes, trySetStreetNumberIfNotReceived, } from './google2address'; import { classes } from './GoogleAddressInput.st.css.js'; import { IconThemeContext } from '../WixDesignSystemIconThemeProvider/IconThemeContext'; export const GoogleAddressInputHandler = { geocode: 'geocode', places: 'places', }; /** * Address input box (using Google Maps) */ class GoogleAddressInput extends React.Component { constructor(props) { super(props); this.state = { suggestions: [], value: props.value || '', }; this.autoCompleteRequestId = 0; this.geocodeRequestId = 0; this.client = new props.Client(); this.onChange = this.onChange.bind(this); this.onBlur = this.onBlur.bind(this); this.onFocus = this.onFocus.bind(this); this.onSet = this.onSet.bind(this); this.onManuallyInput = this.onManuallyInput.bind(this); } UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.value !== this.props.value) { this._getSuggestions(nextProps.value) .then(suggestions => { this.setState({ suggestions }); }) .catch(() => { // Nothing really to do... this.setState({ suggestions: [] }); }); } } render() { const { suggestions, value } = this.state; const { magnifyingGlass } = this.props; const options = [ ...suggestions.map((suggestion, index) => { const { place_id, description } = suggestion; return { id: place_id || index, value: description }; }), ...(this.props.footer ? [ { id: suggestions.length, value: this.props.footer, ...this.props.footerOptions, }, ] : []), ]; return (React.createElement(IconThemeContext.Consumer, null, ({ icons = {} }) => { const SearchIcon = icons.GoogleAddressInput?.Search || Search; const suffix = magnifyingGlass ? (React.createElement(Input.IconAffix, null, React.createElement(SearchIcon, { "data-hook": "search-icon" }))) : undefined; return (React.createElement("div", null, React.createElement(InputWithOptions, { ref: autocomplete => { this.autocomplete = autocomplete; }, ...this.props, onInput: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onSelect: option => this.onSet(option.value), onManuallyInput: this.onManuallyInput, value: value, options: options, fixedFooter: suggestions.length && this.props.poweredByGoogle ? GoogleAddressInput.getGoogleFooter() : null, suffix: suffix, selectedHighlight: false, menuArrow: false }))); })); } focus() { this.autocomplete.focus(); } select() { this.autocomplete.select(); } onChange(e) { const value = e.target.value; this.props.onChange && this.props.onChange(e); this.props.onSet && this.props.onSet(null); if (typeof this.props.value !== 'undefined') { // Controlled mode return; } this._getSuggestions(value) .then(suggestions => { this.setState({ suggestions }); }) .catch(() => { // Nothing really to do... this.setState({ suggestions: [] }); }); } onBlur() { this.props.onBlur && this.props.onBlur(); if (this.props.clearSuggestionsOnBlur) { this.timer = setTimeout(() => { this.setState({ suggestions: [] }); }, 250); } } onFocus() { this.props.onFocus && this.props.onFocus(); } onSet(value) { const { countryCode, handler } = this.props; const suggestion = this.state.suggestions.find(s => s.description === value); this.setState({ suggestions: [], value: this.props.value || value }); const requestId = ++this.geocodeRequestId; let handlerCall; if (handler === GoogleAddressInputHandler.places && suggestion && suggestion.place_id) { const request = { request: { placeId: suggestion.place_id, }, }; if (this.props.placeDetailsFields) { request.request.fields = this.props.placeDetailsFields; } handlerCall = this.client.placeDetails(request); } else { handlerCall = this.client.geocode({ request: { region: countryCode, [suggestion ? 'placeId' : 'address']: suggestion ? suggestion.place_id : value, }, }); } handlerCall .then(results => { results = castArray(results).filter(Boolean); if (requestId !== this.geocodeRequestId) { return; } if (results.length === 0) { console.error(`[GoogleAddressInput] handler (${handler}) returned no results on`, value); this.props.onSet && this.props.onSet(null); // This shouldn't happen since we're running geocode on exactly the same // value returned by suggestions list return; } const firstResult = trySetStreetNumberIfNotReceived(results[0], this.state.value); const result = { originValue: value, googleResult: firstResult, address: google2address(firstResult), }; this.props.onSet && this.props.onSet(result); }) .catch(e => { console.error(`[GoogleAddressInput] handler (${handler}) failed on`, value, e.message); this.props.onSet && this.props.onSet(null); }); } onManuallyInput(inputValue) { const { value, fallbackToManual, onSet } = this.props; if (fallbackToManual) { this._getSuggestions(inputValue, typeof value !== 'undefined').then(suggestions => { if (suggestions.length === 0) { // No suggestion to the text entered if (inputValue) { this.onSet(inputValue); } else { onSet && onSet(null); } } }); } } componentWillUnmount() { if (this.timer) { clearTimeout(this.timer); } } _getSuggestions(value, skipSetState) { const { valuePrefix = '', countryCode, types, filterTypes } = this.props; const requestId = ++this.autoCompleteRequestId; return new Promise(resolve => { if (skipSetState) { // Controlled mode resolve(); return; } this.setState({ value }, () => resolve()); }) .then(() => { if (value === '') { return Promise.resolve([]); } const request = { types, componentRestrictions: { country: countryCode }, input: valuePrefix + value, }; return this.client.autocomplete({ request }); }) .then(results => { if (results.length === 0) { return Promise.resolve([]); } if (requestId !== this.autoCompleteRequestId) { return Promise.resolve([]); } if (filterTypes) { results = results.filter(result => includes(result.types, filterTypes)); } return Promise.resolve(results); }); } } GoogleAddressInput.getGoogleFooter = () => (React.createElement("div", { className: classes.googleFooter, "data-hook": "google-footer" })); GoogleAddressInput.displayName = 'GoogleAddressInput'; GoogleAddressInput.defaultProps = { magnifyingGlass: true, autoSelect: true, footerOptions: {}, clearSuggestionsOnBlur: true, fallbackToManual: false, poweredByGoogle: false, handler: GoogleAddressInputHandler.geocode, }; export default GoogleAddressInput; //# sourceMappingURL=GoogleAddressInput.js.map