Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-google-autocomplete

Package Overview
Dependencies
Maintainers
1
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-google-autocomplete - npm Package Compare versions

Comparing version 1.3.0 to 2.0.0

examples/index.js

14

index.d.ts

@@ -0,1 +1,3 @@

import { HTMLProps } from "react";
export type OptionType = {

@@ -12,3 +14,6 @@ componentRestrictions?: {};

export interface ReactGoogleAutocomplete {
export interface ReactGoogleAutocomplete<
T = { current: null },
B = { current: null }
> extends HTMLProps<HTMLInputElement> {
onPlaceSelected?: (

@@ -18,10 +23,7 @@ places: Record<string, unknown>,

) => void;
types?: string[];
componentRestrictions?: {};
bounds?: {};
fields?: string[];
inputAutocompleteValue?: string;
options?: OptionType;
apiKey?: string;
style?: CSSStyleDeclaration;
ref?: T;
autocompleteRef?: B;
}

@@ -1,2 +0,2 @@

'use strict';
"use strict";

@@ -6,13 +6,12 @@ Object.defineProperty(exports, "__esModule", {

});
exports.ReactCustomGoogleAutocomplete = undefined;
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 _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _react = require('react');
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes = require("prop-types");

@@ -25,141 +24,152 @@ var _propTypes2 = _interopRequireDefault(_propTypes);

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var GOOGLE_MAP_SCRIPT_BASE_URL = "https://maps.googleapis.com/maps/api/js";
var isBrowser = typeof window !== "undefined" && window.document;
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 ReactGoogleAutocomplete(props) {
var onPlaceSelected = props.onPlaceSelected,
apiKey = props.apiKey,
_props$inputAutocompl = props.inputAutocompleteValue,
inputAutocompleteValue = _props$inputAutocompl === undefined ? "new-password" : _props$inputAutocompl,
_props$options = props.options;
_props$options = _props$options === undefined ? {} : _props$options;
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; }
var _props$options$types = _props$options.types,
types = _props$options$types === undefined ? ["(cities)"] : _props$options$types,
componentRestrictions = _props$options.componentRestrictions,
_props$options$fields = _props$options.fields,
fields = _props$options$fields === undefined ? ["address_components", "geometry.location", "place_id", "formatted_address"] : _props$options$fields,
bounds = _props$options.bounds,
options = _objectWithoutProperties(_props$options, ["types", "componentRestrictions", "fields", "bounds"]),
_props$googleMapsScri = props.googleMapsScriptBaseUrl,
googleMapsScriptBaseUrl = _props$googleMapsScri === undefined ? GOOGLE_MAP_SCRIPT_BASE_URL : _props$googleMapsScri,
refProp = props.refProp,
autocompleteRef = props.autocompleteRef,
rest = _objectWithoutProperties(props, ["onPlaceSelected", "apiKey", "inputAutocompleteValue", "options", "googleMapsScriptBaseUrl", "refProp", "autocompleteRef"]);
var ReactGoogleAutocomplete = function (_React$Component) {
_inherits(ReactGoogleAutocomplete, _React$Component);
var inputRef = (0, _react.useRef)(null);
var event = (0, _react.useRef)(null);
var autocomplete = (0, _react.useRef)(null);
var googleMapsScript = (0, _react.useRef)(null);
var observerHack = (0, _react.useRef)(null);
var googleMapsScriptUrl = googleMapsScriptBaseUrl + "?key=" + apiKey + "&libraries=places";
function ReactGoogleAutocomplete(props) {
_classCallCheck(this, ReactGoogleAutocomplete);
var handleLoadScript = (0, _react.useCallback)(function () {
if (!isBrowser) return Promise.resolve();
var _this = _possibleConstructorReturn(this, (ReactGoogleAutocomplete.__proto__ || Object.getPrototypeOf(ReactGoogleAutocomplete)).call(this, props));
var _document$querySelect = document.querySelectorAll("script[src*=\"" + googleMapsScriptBaseUrl + "\""),
_document$querySelect2 = _slicedToArray(_document$querySelect, 1),
scriptElement = _document$querySelect2[0];
_this.handleLoadScript = function () {
var googleMapsScriptUrl = 'https://maps.googleapis.com/maps/api/js?key=' + _this.props.apiKey + '&libraries=places';
// Check if script exists already
if (document.querySelectorAll('script[src="' + googleMapsScriptUrl + '"]').length > 0) {
return Promise.resolve();
}
_this.googleMapsScript = document.createElement('script');
_this.googleMapsScript.src = googleMapsScriptUrl;
document.body.appendChild(_this.googleMapsScript);
if (scriptElement) {
return new Promise(function (resolve) {
_this.googleMapsScript.addEventListener('load', function () {
scriptElement.addEventListener("load", function () {
return resolve();
});
});
};
}
_this.autocomplete = null;
_this.event = null;
return _this;
}
googleMapsScript.current = document.createElement("script");
googleMapsScript.current.src = googleMapsScriptUrl;
_createClass(ReactGoogleAutocomplete, [{
key: 'componentDidMount',
value: function componentDidMount() {
var _this2 = this;
document.body.appendChild(googleMapsScript.current);
// TODO: only take options as configuration object, remove config props from the components props.
var _props = this.props,
_props$types = _props.types,
types = _props$types === undefined ? ['(cities)'] : _props$types,
componentRestrictions = _props.componentRestrictions,
bounds = _props.bounds,
apiKey = _props.apiKey,
_props$fields = _props.fields,
fields = _props$fields === undefined ? ['address_components', 'geometry.location', 'place_id', 'formatted_address'] : _props$fields,
_props$options = _props.options,
options = _props$options === undefined ? {} : _props$options;
var config = _extends({}, options, {
types: types,
bounds: bounds,
fields: fields
return new Promise(function (resolve) {
googleMapsScript.current.addEventListener("load", function () {
return resolve();
});
});
}, [googleMapsScriptBaseUrl, googleMapsScriptUrl]);
if (componentRestrictions) {
config.componentRestrictions = componentRestrictions;
}
// Autofill workaround adapted from https://stackoverflow.com/questions/29931712/chrome-autofill-covers-autocomplete-for-google-maps-api-v3/49161445#49161445
(0, _react.useEffect)(function () {
if (isBrowser && window.MutationObserver && inputRef.current) {
observerHack.current = new MutationObserver(function () {
observerHack.current.disconnect();
this.disableAutofill();
inputRef.current.autocomplete = inputAutocompleteValue;
});
observerHack.current.observe(inputRef.current, {
attributes: true,
attributeFilter: ["autocomplete"]
});
}
}, [inputAutocompleteValue]);
var handleAutoComplete = function handleAutoComplete() {
_this2.autocomplete = new google.maps.places.Autocomplete(_this2.refs.input, config);
(0, _react.useEffect)(function () {
if (autocomplete.current) {
autocomplete.current.setFields(fields);
}
}, [fields]);
_this2.event = _this2.autocomplete.addListener('place_changed', _this2.onSelected.bind(_this2));
};
(0, _react.useEffect)(function () {
if (autocomplete.current) {
autocomplete.current.setBounds(bounds);
}
}, [bounds]);
if (apiKey) {
this.handleLoadScript().then(function () {
return handleAutoComplete();
});
} else {
handleAutoComplete();
}
(0, _react.useEffect)(function () {
if (autocomplete.current) {
autocomplete.current.setComponentRestrictions(componentRestrictions);
}
}, {
key: 'disableAutofill',
value: function disableAutofill() {
var _this3 = this;
}, [componentRestrictions]);
// Autofill workaround adapted from https://stackoverflow.com/questions/29931712/chrome-autofill-covers-autocomplete-for-google-maps-api-v3/49161445#49161445
if (window.MutationObserver) {
var observerHack = new MutationObserver(function () {
observerHack.disconnect();
if (_this3.refs && _this3.refs.input) {
_this3.refs.input.autocomplete = _this3.props.inputAutocompleteValue || 'new-password';
}
});
observerHack.observe(this.refs.input, {
attributes: true,
attributeFilter: ['autocomplete']
});
}
(0, _react.useEffect)(function () {
if (autocomplete.current) {
autocomplete.current.setOptions(options);
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
if (this.event) this.event.remove();
}, [options]);
(0, _react.useEffect)(function () {
var config = _extends({}, options, {
types: types,
bounds: bounds,
componentRestrictions: componentRestrictions
});
if (autocomplete.current) return;
var handleAutoComplete = function handleAutoComplete() {
// eslint-disable-next-line no-undef
autocomplete.current = new google.maps.places.Autocomplete(inputRef.current, config);
if (autocompleteRef) autocompleteRef.current = autocomplete.current;
event.current = autocomplete.current.addListener("place_changed", function () {
if (onPlaceSelected && autocomplete && autocomplete.current) {
onPlaceSelected(autocomplete.current.getPlace(), inputRef.current, autocomplete.current);
}
});
};
if (apiKey) {
handleLoadScript().then(function () {
return handleAutoComplete();
});
} else {
handleAutoComplete();
}
}, {
key: 'onSelected',
value: function onSelected() {
if (this.props.onPlaceSelected && this.autocomplete) {
this.props.onPlaceSelected(this.autocomplete.getPlace(), this.refs.input);
}
}
}, {
key: 'render',
value: function render() {
var _props2 = this.props,
onPlaceSelected = _props2.onPlaceSelected,
types = _props2.types,
componentRestrictions = _props2.componentRestrictions,
bounds = _props2.bounds,
options = _props2.options,
apiKey = _props2.apiKey,
inputAutocompleteValue = _props2.inputAutocompleteValue,
rest = _objectWithoutProperties(_props2, ['onPlaceSelected', 'types', 'componentRestrictions', 'bounds', 'options', 'apiKey', 'inputAutocompleteValue']);
return _react2.default.createElement('input', _extends({ ref: 'input' }, rest));
return function () {
return event.current ? event.current.remove() : undefined;
};
}, [types, options, fields, componentRestrictions, apiKey, onPlaceSelected, handleLoadScript, autocompleteRef, bounds]);
return _react2.default.createElement("input", _extends({
ref: function ref(el) {
inputRef.current = el;
if (refProp) refProp.current = el;
}
}]);
}, rest));
}
return ReactGoogleAutocomplete;
}(_react2.default.Component);
ReactGoogleAutocomplete.propTypes = {
apiKey: _propTypes2.default.string,
ref: _propTypes2.default.oneOfType([
// Either a function
_propTypes2.default.func,
// Or the instance of a DOM native element (see the note about SSR)
_propTypes2.default.shape({ current: _propTypes2.default.instanceOf(Element) })]),
autocompleteRef: _propTypes2.default.oneOfType([_propTypes2.default.func, _propTypes2.default.shape({ current: _propTypes2.default.instanceOf(Element) })]),
googleMapsScriptBaseUrl: _propTypes2.default.string,
onPlaceSelected: _propTypes2.default.func,
types: _propTypes2.default.arrayOf(_propTypes2.default.string),
componentRestrictions: _propTypes2.default.object,
bounds: _propTypes2.default.object,
fields: _propTypes2.default.array,
inputAutocompleteValue: _propTypes2.default.string,

@@ -175,80 +185,7 @@ options: _propTypes2.default.shape({

types: _propTypes2.default.arrayOf(_propTypes2.default.string)
}),
apiKey: _propTypes2.default.string
})
};
exports.default = ReactGoogleAutocomplete;
var ReactCustomGoogleAutocomplete = exports.ReactCustomGoogleAutocomplete = function (_React$Component2) {
_inherits(ReactCustomGoogleAutocomplete, _React$Component2);
function ReactCustomGoogleAutocomplete(props) {
_classCallCheck(this, ReactCustomGoogleAutocomplete);
var _this4 = _possibleConstructorReturn(this, (ReactCustomGoogleAutocomplete.__proto__ || Object.getPrototypeOf(ReactCustomGoogleAutocomplete)).call(this, props));
_this4.service = new google.maps.places.AutocompleteService();
return _this4;
}
_createClass(ReactCustomGoogleAutocomplete, [{
key: 'onChange',
value: function onChange(e) {
var _this5 = this;
var _props$types2 = this.props.types,
types = _props$types2 === undefined ? ['(cities)'] : _props$types2;
if (e.target.value) {
this.service.getPlacePredictions({ input: e.target.value, types: types }, function (predictions, status) {
if (status === 'OK' && predictions && predictions.length > 0) {
_this5.props.onOpen(predictions);
} else {
_this5.props.onClose();
}
});
} else {
this.props.onClose();
}
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var _this6 = this;
if (this.props.input.value) {
this.placeService = new google.maps.places.PlacesService(this.refs.div);
this.placeService.getDetails({ placeId: this.props.input.value }, function (e, status) {
if (status === 'OK') {
_this6.refs.input.value = e.formatted_address;
}
});
}
}
}, {
key: 'render',
value: function render() {
var _this7 = this;
return _react2.default.createElement(
'div',
null,
_react2.default.cloneElement(this.props.input, _extends({}, this.props, {
ref: 'input',
onChange: function onChange(e) {
_this7.onChange(e);
}
})),
_react2.default.createElement('div', { ref: 'div' })
);
}
}]);
return ReactCustomGoogleAutocomplete;
}(_react2.default.Component);
ReactCustomGoogleAutocomplete.propTypes = {
input: _propTypes2.default.node.isRequired,
onOpen: _propTypes2.default.func.isRequired,
onClose: _propTypes2.default.func.isRequired
};
exports.default = (0, _react.forwardRef)(function (props, ref) {
return _react2.default.createElement(ReactGoogleAutocomplete, _extends({}, props, { refProp: ref }));
});
{
"name": "react-google-autocomplete",
"version": "1.3.0",
"version": "2.0.0",
"description": "React component for google autocomplete.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -0,1 +1,5 @@

![](https://img.badgesize.io/ErrorPro/react-google-autocomplete/master/lib/index.js?compression=gzip&label=gzip)
![](https://img.badgesize.io/ErrorPro/react-google-autocomplete/master/lib/index.js?compression=brotli&label=brotli)
[![GitHub license](https://img.shields.io/github/license/Naereen/StrapDown.js.svg)](https://GitHub.com/ErrorPro/react-google-autocomplete/master/LICENSE)
## React google autocomplete

@@ -16,7 +20,7 @@

apiKey={YOUR_GOOGLE_MAPS_API_KEY}
onPlaceSelected={() => "do something on select"}
onPlaceSelected={(place) => console.log(place)}
/>
```
Alternatively if not passing the `apiKey` prop, you can include google autocomplete link api in your app. Somewhere in index.html or somewhere else.
Alternatively if not passing the `apiKey` prop, you can include google autocomplete link api in your app. Somewhere in index.html or somewhere else. More info [here](https://developers.google.com/maps/documentation/places/web-service/autocomplete)

@@ -30,4 +34,29 @@ ```html

## Example
## Props
- `apiKey`: pass to automatically load the Google maps scripts. The api key can be found in your [google cloud console.](https://developers.google.com/maps/documentation/javascript/get-api-key)
- `ref`: [React ref](https://reactjs.org/docs/hooks-reference.html#useref) to be assigned the underlying text input ref.
- `autocompleteRef`: [React ref](https://reactjs.org/docs/hooks-reference.html#useref) to be assigned the [google autocomplete instance](https://developers.google.com/maps/documentation/javascript/reference/places-widget#Autocomplete).
- `onPlaceSelected: (place: `[PlaceResult](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult)`, inputRef, autocompleteRef) => void`: The function gets invoked every time a user chooses location.
- `options`: [Google autocomplete options.](https://developers.google.com/maps/documentation/javascript/reference/places-widget#AutocompleteOptions)
- `options.types`: By default it uses (cities).
- `options.fields`: By default it uses `address_components`, `geometry.location`, `place_id`, `formatted_address`.
- `inputAutocompleteValue`: Autocomplete value to be set to the underlying input.
- `googleMapsScriptBaseUrl`: Provide custom google maps url. By default `https://maps.googleapis.com/maps/api/js`
- `defaultValue` prop is used for setting up the default value e.g `defaultValue={'Amsterdam'}`.
You can pass any prop specified for the hmtl [input tag](https://www.w3schools.com/tags/tag_input.asp). You can also set [options.fields](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult) prop if you need extra information, now it defaults to basic data in order to control expenses.
## Examples
### Simple autocomplete with options
```js

@@ -37,2 +66,3 @@ import Autocomplete from "react-google-autocomplete";

<Autocomplete
apiKey={YOUR_GOOGLE_MAPS_API_KEY}
style={{ width: "90%" }}

@@ -42,12 +72,53 @@ onPlaceSelected={(place) => {

}}
types={["(regions)"]}
componentRestrictions={{ country: "ru" }}
options={{
types: ["(regions)"],
componentRestrictions: { country: "ru" },
}}
defaultValue="Amsterdam"
/>;
```
## Typescript
### Passing refs
We are planning on adding a full support for TS and Flow in the later releases.
```js
import Autocomplete from "react-google-autocomplete";
const inputRef = useRef(null);
useEffect(() => {
// focus on mount
inputRef.current.focus()
}, [])
<Autocomplete
ref={inputRef}
onPlaceSelected={(place) => {
console.log(place);
}}
/>;
```
### Getting access to the google autocomplete instance
```js
import Autocomplete from "react-google-autocomplete";
const autocompleteRef = useRef(null);
<Autocomplete
autocompleteRef={autocompleteRef}
onPlaceSelected={(place, inputRef, theSameAutocompletRef) => {
console.log(place);
}}
/>;
<button onClick={() => autocompleteRef.current.getPlace()}>Read place</button>;
```
### Typescript
We are planning on adding full support for TS and Flow in the later releases.
```ts
import Autocomplete, {

@@ -59,14 +130,17 @@ ReactGoogleAutocomplete,

<AutocompleteTS key="123" />
<AutocompleteTS apiKey="123" />;
```
The component has one function called `onPlaceSelected`. The function gets invoked every time a user chooses location.
A `types` props means type of places in [google place API](https://developers.google.com/places/web-service/autocomplete#place_types). By default it uses (cities).
A [componentRestrictions](https://developers.google.com/maps/documentation/javascript/reference#ComponentRestrictions) prop by default is empty.
A [bounds](https://developers.google.com/maps/documentation/javascript/reference#AutocompleteOptions) prop by default is empty.
You also can pass any props you want to the final input. You can also set [fields](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult) prop if you need extra information, now it defaults to basic data in order to control expenses.
The `options`(optional) prop is the optional configuration to your Autocomplete instance. You can see full options [here](https://developers.google.com/maps/documentation/javascript/places-autocomplete#add_autocomplete)
### More examples(dynamic props, MaterialUI) how to use the lib could be found in `examples/index.js`
[Video of the example](https://api.monosnap.com/file/download?id=vIjRwTxVyMj0Sd2Gjhsfie2SPk1y4l)
### TODO
- Check that it fully works with SSR
- Add eslint config(base-airbnb)
- Rewrite the lib to TS and add flow support
## Contribution
If you would like to see something in this library please create an issue and I will implement it as soon as possible.

@@ -1,200 +0,187 @@

import React from 'react';
import PropTypes from 'prop-types';
import React, { useRef, forwardRef, useEffect, useCallback } from "react";
import PropTypes from "prop-types";
export default class ReactGoogleAutocomplete extends React.Component {
static propTypes = {
onPlaceSelected: PropTypes.func,
types: PropTypes.arrayOf(PropTypes.string),
componentRestrictions: PropTypes.object,
bounds: PropTypes.object,
fields: PropTypes.array,
inputAutocompleteValue: PropTypes.string,
options: PropTypes.shape({
componentRestrictions: PropTypes.object,
bounds: PropTypes.object,
location: PropTypes.object,
offset: PropTypes.number,
origin: PropTypes.object,
radius: PropTypes.number,
sessionToken: PropTypes.object,
types: PropTypes.arrayOf(PropTypes.string)
}),
apiKey: PropTypes.string
};
const GOOGLE_MAP_SCRIPT_BASE_URL = "https://maps.googleapis.com/maps/api/js";
const isBrowser = typeof window !== "undefined" && window.document;
constructor(props) {
super(props);
this.autocomplete = null;
this.event = null;
}
componentDidMount() {
// TODO: only take options as configuration object, remove config props from the components props.
const {
types = ['(cities)'],
function ReactGoogleAutocomplete(props) {
const {
onPlaceSelected,
apiKey,
inputAutocompleteValue = "new-password",
options: {
types = ["(cities)"],
componentRestrictions,
bounds,
apiKey,
fields = [
'address_components',
'geometry.location',
'place_id',
'formatted_address'
"address_components",
"geometry.location",
"place_id",
"formatted_address",
],
options = {}
} = this.props;
const config = {
...options,
types,
bounds,
fields
};
...options
} = {},
googleMapsScriptBaseUrl = GOOGLE_MAP_SCRIPT_BASE_URL,
refProp,
autocompleteRef,
...rest
} = props;
const inputRef = useRef(null);
const event = useRef(null);
const autocomplete = useRef(null);
const googleMapsScript = useRef(null);
const observerHack = useRef(null);
const googleMapsScriptUrl = `${googleMapsScriptBaseUrl}?key=${apiKey}&libraries=places`;
if (componentRestrictions) {
config.componentRestrictions = componentRestrictions;
const handleLoadScript = useCallback(() => {
if (!isBrowser) return Promise.resolve();
const [scriptElement] = document.querySelectorAll(
`script[src*="${googleMapsScriptBaseUrl}"`
);
if (scriptElement) {
return new Promise((resolve) => {
scriptElement.addEventListener("load", () => resolve());
});
}
this.disableAutofill();
googleMapsScript.current = document.createElement("script");
googleMapsScript.current.src = googleMapsScriptUrl;
const handleAutoComplete = () => {
this.autocomplete = new google.maps.places.Autocomplete(
this.refs.input,
config
);
document.body.appendChild(googleMapsScript.current);
this.event = this.autocomplete.addListener(
'place_changed',
this.onSelected.bind(this)
);
};
return new Promise((resolve) => {
googleMapsScript.current.addEventListener("load", () => resolve());
});
}, [googleMapsScriptBaseUrl, googleMapsScriptUrl]);
if (apiKey) {
this.handleLoadScript().then(() => handleAutoComplete());
} else {
handleAutoComplete();
}
}
// Autofill workaround adapted from https://stackoverflow.com/questions/29931712/chrome-autofill-covers-autocomplete-for-google-maps-api-v3/49161445#49161445
useEffect(() => {
if (isBrowser && window.MutationObserver && inputRef.current) {
observerHack.current = new MutationObserver(() => {
observerHack.current.disconnect();
disableAutofill() {
// Autofill workaround adapted from https://stackoverflow.com/questions/29931712/chrome-autofill-covers-autocomplete-for-google-maps-api-v3/49161445#49161445
if (window.MutationObserver) {
const observerHack = new MutationObserver(() => {
observerHack.disconnect();
if (this.refs && this.refs.input) {
this.refs.input.autocomplete = this.props.inputAutocompleteValue || 'new-password';
}
inputRef.current.autocomplete = inputAutocompleteValue;
});
observerHack.observe(this.refs.input, {
observerHack.current.observe(inputRef.current, {
attributes: true,
attributeFilter: ['autocomplete']
attributeFilter: ["autocomplete"],
});
}
}
}, [inputAutocompleteValue]);
componentWillUnmount() {
if (this.event) this.event.remove();
}
useEffect(() => {
if (autocomplete.current) {
autocomplete.current.setFields(fields);
}
}, [fields]);
onSelected() {
if (this.props.onPlaceSelected && this.autocomplete) {
this.props.onPlaceSelected(this.autocomplete.getPlace(), this.refs.input);
useEffect(() => {
if (autocomplete.current) {
autocomplete.current.setBounds(bounds);
}
}
}, [bounds]);
handleLoadScript = () => {
const googleMapsScriptUrl = `https://maps.googleapis.com/maps/api/js?key=${this.props.apiKey}&libraries=places`;
useEffect(() => {
if (autocomplete.current) {
autocomplete.current.setComponentRestrictions(componentRestrictions);
}
}, [componentRestrictions]);
// Check if script exists already
if (
document.querySelectorAll(`script[src="${googleMapsScriptUrl}"]`).length >
0
) {
return Promise.resolve();
useEffect(() => {
if (autocomplete.current) {
autocomplete.current.setOptions(options);
}
}, [options]);
this.googleMapsScript = document.createElement('script');
this.googleMapsScript.src = googleMapsScriptUrl;
document.body.appendChild(this.googleMapsScript);
return new Promise((resolve) => {
this.googleMapsScript.addEventListener('load', () => resolve());
});
};
render() {
const {
onPlaceSelected,
useEffect(() => {
const config = {
...options,
types,
bounds,
componentRestrictions,
bounds,
options,
apiKey,
inputAutocompleteValue,
...rest
} = this.props;
};
return <input ref="input" {...rest} />;
}
}
if (autocomplete.current) return;
export class ReactCustomGoogleAutocomplete extends React.Component {
static propTypes = {
input: PropTypes.node.isRequired,
onOpen: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired
};
const handleAutoComplete = () => {
// eslint-disable-next-line no-undef
autocomplete.current = new google.maps.places.Autocomplete(
inputRef.current,
config
);
constructor(props) {
super(props);
this.service = new google.maps.places.AutocompleteService();
}
if (autocompleteRef) autocompleteRef.current = autocomplete.current;
onChange(e) {
const { types = ['(cities)'] } = this.props;
event.current = autocomplete.current.addListener("place_changed", () => {
if (onPlaceSelected && autocomplete && autocomplete.current) {
onPlaceSelected(
autocomplete.current.getPlace(),
inputRef.current,
autocomplete.current
);
}
});
};
if (e.target.value) {
this.service.getPlacePredictions(
{ input: e.target.value, types },
(predictions, status) => {
if (status === 'OK' && predictions && predictions.length > 0) {
this.props.onOpen(predictions);
} else {
this.props.onClose();
}
}
);
if (apiKey) {
handleLoadScript().then(() => handleAutoComplete());
} else {
this.props.onClose();
handleAutoComplete();
}
}
componentDidMount() {
if (this.props.input.value) {
this.placeService = new google.maps.places.PlacesService(this.refs.div);
this.placeService.getDetails(
{ placeId: this.props.input.value },
(e, status) => {
if (status === 'OK') {
this.refs.input.value = e.formatted_address;
}
}
);
}
}
return () => (event.current ? event.current.remove() : undefined);
}, [
types,
options,
fields,
componentRestrictions,
apiKey,
onPlaceSelected,
handleLoadScript,
autocompleteRef,
bounds,
]);
render() {
return (
<div>
{React.cloneElement(this.props.input, {
...this.props,
ref: 'input',
onChange: e => {
this.onChange(e);
}
})}
<div ref="div" />
</div>
);
}
return (
<input
ref={(el) => {
inputRef.current = el;
if (refProp) refProp.current = el;
}}
{...rest}
/>
);
}
ReactGoogleAutocomplete.propTypes = {
apiKey: PropTypes.string,
ref: PropTypes.oneOfType([
// Either a function
PropTypes.func,
// Or the instance of a DOM native element
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
]),
autocompleteRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
]),
googleMapsScriptBaseUrl: PropTypes.string,
onPlaceSelected: PropTypes.func,
inputAutocompleteValue: PropTypes.string,
options: PropTypes.shape({
componentRestrictions: PropTypes.object,
bounds: PropTypes.object,
location: PropTypes.object,
offset: PropTypes.number,
origin: PropTypes.object,
radius: PropTypes.number,
sessionToken: PropTypes.object,
types: PropTypes.arrayOf(PropTypes.string),
}),
};
export default forwardRef((props, ref) => (
<ReactGoogleAutocomplete {...props} refProp={ref} />
));
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc