UNPKG

wix-style-react

Version:
389 lines (318 loc) • 13.8 kB
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _class, _temp; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* eslint-disable no-console */ import React from 'react'; import PropTypes from 'prop-types'; import castArray from 'lodash/castArray'; import Input from '../Input'; import InputWithOptions from '../InputWithOptions'; import { google2address, includes, trySetStreetNumberIfNotReceived } from './google2address'; import styles from './GoogleAddressInput.scss'; export var GoogleAddressInputHandler = { geocode: 'geocode', places: 'places' }; /** * Address input box (using Google Maps) */ var GoogleAddressInput = (_temp = _class = function (_React$Component) { _inherits(GoogleAddressInput, _React$Component); function GoogleAddressInput(props) { _classCallCheck(this, GoogleAddressInput); var _this = _possibleConstructorReturn(this, (GoogleAddressInput.__proto__ || Object.getPrototypeOf(GoogleAddressInput)).call(this, 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); return _this; } _createClass(GoogleAddressInput, [{ key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { var _this2 = this; if (nextProps.value !== this.props.value) { this._getSuggestions(nextProps.value).then(function (suggestions) { _this2.setState({ suggestions: suggestions }); }).catch(function () { // Nothing really to do... _this2.setState({ suggestions: [] }); }); } } }, { key: 'render', value: function render() { var _this3 = this; var _state = this.state, suggestions = _state.suggestions, value = _state.value; var options = [].concat(_toConsumableArray(suggestions.map(function (_ref) { var description = _ref.description, id = _ref.id; return { id: id, value: description }; })), _toConsumableArray(this.props.footer ? [_extends({ id: suggestions.length, value: this.props.footer }, this.props.footerOptions)] : [])); return React.createElement( 'div', null, React.createElement(InputWithOptions, _extends({ ref: function ref(autocomplete) { return _this3.autocomplete = autocomplete; } }, this.props, { onInput: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onSelect: function onSelect(option) { return _this3.onSet(option.value); }, onManuallyInput: this.onManuallyInput, value: value, options: options, fixedFooter: suggestions.length && this.props.poweredByGoogle ? GoogleAddressInput.getGoogleFooter() : null, selectedHighlight: false })) ); } }, { key: 'focus', value: function focus() { this.autocomplete.focus(); } }, { key: 'select', value: function select() { this.autocomplete.select(); } }, { key: 'onChange', value: function onChange(e) { var _this4 = this; var 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(function (suggestions) { _this4.setState({ suggestions: suggestions }); }).catch(function () { // Nothing really to do... _this4.setState({ suggestions: [] }); }); } }, { key: 'onBlur', value: function onBlur() { var _this5 = this; this.props.onBlur && this.props.onBlur(); if (this.props.clearSuggestionsOnBlur) { this.timer = setTimeout(function () { _this5.setState({ suggestions: [] }); }, 250); } } }, { key: 'onFocus', value: function onFocus() { this.props.onFocus && this.props.onFocus(); } }, { key: 'onSet', value: function onSet(value) { var _this6 = this; var _props = this.props, countryCode = _props.countryCode, handler = _props.handler; var suggestion = this.state.suggestions.find(function (s) { return s.description === value; }); this.setState({ suggestions: [], value: this.props.value || value }); var requestId = ++this.geocodeRequestId; var handlerCall = void 0; if (handler === GoogleAddressInputHandler.places && suggestion && suggestion.place_id) { handlerCall = this.client.placeDetails({ request: { placeId: suggestion.place_id } }); } else { handlerCall = this.client.geocode({ request: _defineProperty({ region: countryCode }, suggestion ? 'placeId' : 'address', suggestion ? suggestion.place_id : value) }); } handlerCall.then(function (results) { results = castArray(results).filter(Boolean); if (requestId !== _this6.geocodeRequestId) { return; } if (results.length === 0) { console.error('[GoogleAddressInput] handler (' + handler + ') returned no results on', value); _this6.props.onSet && _this6.props.onSet(null); // This shouldn't happen since we're running geocode on exactly the same // value returned by suggestions list return; } var firstResult = trySetStreetNumberIfNotReceived(results[0], _this6.state.value); var result = { originValue: value, googleResult: firstResult, address: google2address(firstResult) }; _this6.props.onSet && _this6.props.onSet(result); }).catch(function (e) { console.error('[GoogleAddressInput] handler (' + handler + ') failed on', value, e.message); _this6.props.onSet && _this6.props.onSet(null); }); } }, { key: 'onManuallyInput', value: function onManuallyInput(inputValue) { var _this7 = this; var _props2 = this.props, value = _props2.value, fallbackToManual = _props2.fallbackToManual, onSet = _props2.onSet; if (fallbackToManual) { this._getSuggestions(inputValue, typeof value !== 'undefined').then(function (suggestions) { if (suggestions.length === 0) { // No suggestion to the text entered if (inputValue) { _this7.onSet(inputValue); } else { onSet && onSet(null); } } }); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { if (this.timer) { clearTimeout(this.timer); } } }, { key: '_getSuggestions', value: function _getSuggestions(value, skipSetState) { var _this8 = this; var _props3 = this.props, _props3$valuePrefix = _props3.valuePrefix, valuePrefix = _props3$valuePrefix === undefined ? '' : _props3$valuePrefix, countryCode = _props3.countryCode, types = _props3.types, filterTypes = _props3.filterTypes; var requestId = ++this.autoCompleteRequestId; return new Promise(function (resolve) { if (skipSetState) { // Controlled mode resolve(); return; } _this8.setState({ value: value }, function () { return resolve(); }); }).then(function () { if (value === '') { return Promise.resolve([]); } var request = { types: types, componentRestrictions: { country: countryCode }, input: valuePrefix + value }; return _this8.client.autocomplete({ request: request }); }).then(function (results) { if (results.length === 0) { return Promise.resolve([]); } if (requestId !== _this8.autoCompleteRequestId) { return Promise.resolve([]); } if (filterTypes) { results = results.filter(function (result) { return includes(result.types, filterTypes); }); } return Promise.resolve(results); }); } }]); return GoogleAddressInput; }(React.Component), _class.getGoogleFooter = function () { return React.createElement('div', { className: styles.googleFooter, 'data-hook': 'google-footer' }); }, _temp); GoogleAddressInput.displayName = 'GoogleAddressInput'; GoogleAddressInput.defaultProps = { magnifyingGlass: true, theme: Input.defaultProps.theme, autoSelect: true, footerOptions: {}, clearSuggestionsOnBlur: true, fallbackToManual: false, poweredByGoogle: false, handler: GoogleAddressInputHandler.geocode }; GoogleAddressInput.propTypes = { /** Placeholder for the input box */ placeholder: PropTypes.string, /** Value to place before every search term (normally should not be used) */ valuePrefix: PropTypes.string, /** Country code used to help with suggestions and geocoding */ countryCode: PropTypes.string, /** Controlled mode - value to display */ value: PropTypes.string, /** Limit the autocomplete to specific types (see [here](https://developers.google.com/places/supported_types#table3) for list) */ types: PropTypes.array, /** Lower level filtering of autocomplete result types (see [here](https://developers.google.com/places/supported_types) for list) */ filterTypes: PropTypes.array, /** Should display error marker */ error: PropTypes.bool, onChange: PropTypes.func, onBlur: PropTypes.func, onFocus: PropTypes.func, onKeyDown: PropTypes.func, /** Callback for results. Will return an object containing: originValue (value in the search), googleResult (google geocode result for the search), address (which will include: formatted (google formatted address), country, countryCode, street, number, postalCode, latLng (lat, lng)) */ onSet: PropTypes.func, /** Google map client implementation (should implement autocomplete and geocode functions). Normally you would use wix-style-react/clients/GoogleMapsClient */ Client: PropTypes.func.isRequired, /** Show or hide magnifying glass icon */ magnifyingGlass: PropTypes.bool, theme: Input.propTypes.theme, /** Sets the input to readOnly */ readOnly: PropTypes.bool, autoSelect: PropTypes.bool, /** Display a footer as the last suggestion in the list */ footer: PropTypes.any, /** Set the footer's options (e.g. disabled, overrideStyles, etc. ) */ footerOptions: PropTypes.object, /** Clear the suggestions list upon input blur */ clearSuggestionsOnBlur: PropTypes.bool, /** If set to `true`, we will attempt to get a Google location from the input's text if there are no suggestions. This is useful when looking for locations for which google does not give suggestions - for example: Apartment/Apt */ fallbackToManual: PropTypes.bool, /** Shows the Powered By Google credit in a fixed footer */ poweredByGoogle: PropTypes.bool, /** Sets how to get more details for a place (e.g. geocode, places, etc) */ handler: PropTypes.oneOf([GoogleAddressInputHandler.geocode, GoogleAddressInputHandler.places]) }; export default GoogleAddressInput;