├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── build-utils
└── postBuild.js
├── docs
├── .gitignore
├── README.md
├── babel.config.js
├── docs
│ ├── basic-usage.mdx
│ ├── exposed-methods.md
│ ├── geocode-by-address.md
│ ├── geocode-by-lat-long.md
│ ├── geocode-by-place-id.md
│ ├── get-lat-long.md
│ ├── how-to-contribute.md
│ ├── props.md
│ ├── recipes.md
│ └── v2-to-v3.md
├── docusaurus.config.js
├── package.json
├── sidebars.js
├── src
│ ├── css
│ │ └── custom.css
│ └── pages
│ │ ├── index.js
│ │ └── styles.module.css
├── static
│ ├── .nojekyll
│ └── img
│ │ ├── customizable.svg
│ │ ├── easy-to-use.svg
│ │ ├── favicon.ico
│ │ └── logo.svg
└── yarn.lock
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── google-places-autocomplete.tsx
├── helpers
│ └── autocompletion-request-builder.ts
├── hooks
│ ├── use-fetch-suggestions.ts
│ └── use-places-service.ts
├── index.ts
├── types.ts
└── utils
│ ├── geocode-by-address.ts
│ ├── geocode-by-lat-lng.ts
│ ├── geocode-by-place-id.ts
│ └── get-lat-lng.ts
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "plugins": [
5 | "@typescript-eslint"
6 | ],
7 | "extends": [
8 | "eslint:recommended",
9 | "plugin:react/recommended",
10 | "plugin:@typescript-eslint/eslint-recommended",
11 | "plugin:@typescript-eslint/recommended"
12 | ]
13 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Dependency directories
9 | node_modules/
10 | jspm_packages/
11 |
12 | # TypeScript v1 declaration files
13 | typings/
14 |
15 | # Optional npm cache directory
16 | .npm
17 |
18 | # Optional eslint cache
19 | .eslintcache
20 |
21 | # Optional REPL history
22 | .node_repl_history
23 |
24 | # Output of 'npm pack'
25 | *.tgz
26 |
27 | # Yarn Integrity file
28 | .yarn-integrity
29 |
30 | # dotenv environment variables file
31 | .env
32 |
33 | # Ignore build
34 | build
35 |
36 | .DS_Store
37 |
38 | # Ignore vim files
39 | *.swp
40 | *~
41 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | docs
3 | build-utils
4 | .eslintignore
5 | .eslintrc
6 | .travis.yml
7 | rollup.config.js
8 | tsconfig.json
9 | .yalc
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # .travis.yml
2 | language: node_js
3 | node_js:
4 | - '12'
5 | branches:
6 | only:
7 | - master
8 | cache:
9 | yarn: true
10 | script:
11 | - git config --global user.name "${GH_NAME}"
12 | - git config --global user.email "${GH_EMAIL}"
13 | - echo "machine github.com login ${GH_NAME} password ${GH_TOKEN}" > ~/.netrc
14 | - cd docs && yarn && GIT_USER="${GH_NAME}" yarn deploy
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 | -----------
3 |
4 | Copyright (c) 2020 Nicolás Tinte (https://www.tintef.dev)
5 |
6 | Permission is hereby granted, free of charge, to any person
7 | obtaining a copy of this software and associated documentation
8 | files (the "Software"), to deal in the Software without
9 | restriction, including without limitation the rights to use,
10 | copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the
12 | Software is furnished to do so, subject to the following
13 | conditions:
14 |
15 | The above copyright notice and this permission notice shall be
16 | included in all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | OTHER DEALINGS IN THE SOFTWARE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | # React Google Places Autocomplete
25 |
26 | React component for easily use Google Places Autocomplete
27 |
28 |
29 | ## Getting started
30 |
31 | Install the latest version:
32 | ```sh
33 | npm install --save react-google-places-autocomplete
34 | or
35 | yarn add react-google-places-autocomplete
36 | ```
37 |
38 | Use the component!
39 | ```js
40 | import React from 'react';
41 | import GooglePlacesAutocomplete from 'react-google-places-autocomplete';
42 |
43 | const Component = () => (
44 |
45 |
48 |
49 | );
50 |
51 | export default Component;
52 | ```
53 |
54 | **Coming from v2? Check the [migration guide](https://tintef.github.io/react-google-places-autocomplete/docs/v2-to-v3)**
55 |
56 | ## Documentation
57 |
58 | [**Read The Docs**](https://tintef.github.io/react-google-places-autocomplete)
59 |
60 | ## How to contribute?
61 |
62 | 1. Fork this repo
63 | 2. Clone your fork
64 | 3. Code 🤓
65 | 4. Test your changes
66 |
67 | For this, I like to use [yalc](https://github.com/whitecolor/yalc), as it allows to emulate the process of using npm/yarn.
68 |
69 | 1. Install [yalc](https://github.com/whitecolor/yalc)
70 | 2. Build project with `yarn build` or `npm run build`
71 | 3. Publish the package with yalc: `yalc publish`
72 | 4. Add the package to your test project `yalc add react-google-places-automocomplete`
73 | 5. If needed, to update the package on your test project: `yalc update react-google-places-autocomplete`
74 |
75 |
76 | 5. Submit a PR!
77 |
78 |
79 |
80 |
81 |
82 | Icons made by Freepik from www.flaticon.com
83 |
--------------------------------------------------------------------------------
/build-utils/postBuild.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const pkg = require('../package.json');
3 |
4 | (async () => {
5 | fs.copyFileSync('LICENSE', './build/LICENSE');
6 | fs.copyFileSync('README.md', './build/README.md');
7 | fs.writeFileSync('./build/package.json', JSON.stringify({ ...pkg }, null, 2));
8 | })();
9 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ yarn
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ yarn start
15 | ```
16 |
17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | ```
30 | $ GIT_USER= USE_SSH=true yarn deploy
31 | ```
32 |
33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
34 |
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/docs/basic-usage.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | id: basic-usage
3 | title: Basics
4 | sidebar_label: Basics
5 | ---
6 |
7 | ## Install
8 |
9 | ```bash
10 | npm install --save react-google-places-autocomplete
11 | ```
12 | or
13 | ```
14 | yarn add react-google-places-autocomplete
15 | ```
16 |
17 | ## Basic Usage
18 |
19 | ### Load Google Maps JavaScript API
20 |
21 | First, generate an `apiKey` in order to use it to load [Google Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/). Then, use it to load it in your HTML file, adding a script tag:
22 |
23 | ```html
24 |
28 | ```
29 |
30 | **Note:** if you pass down the `apiKey` prop to the component, you can skip loading the [Google Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/), as the component will inject that script on the page.
31 |
32 | ### Use the component
33 |
34 | ```js
35 | import React from 'react';
36 | import GooglePlacesAutocomplete from 'react-google-places-autocomplete';
37 |
38 | const Component = () => (
39 |
40 |
41 |
42 | );
43 |
44 | export default Component;
45 | ```
46 |
47 | ### Save the selected result
48 |
49 | ```js
50 | import React, { useState } from 'react';
51 | import GooglePlacesAutocomplete from 'react-google-places-autocomplete';
52 |
53 | const Component = () => (
54 | const [value, setValue] = useState(null);
55 |
56 | return (
57 |
58 |
64 |
65 | );
66 | }
67 | ```
68 |
--------------------------------------------------------------------------------
/docs/docs/exposed-methods.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: exposed-methods
3 | title: Exposed Methods
4 | sidebar_label: Exposed Methods
5 | ---
6 |
7 | ```tsx
8 | type GooglePlacesAutocompleteHandle = {
9 | getSessionToken: () => google.maps.places.AutocompleteSessionToken | undefined,
10 | refreshSessionToken: () => void,
11 | }
12 | ```
13 |
14 | ## Usage
15 |
16 | In order to access the exposed methods you need to create a ref to the component:
17 |
18 | ```js
19 | import React, { useRef } from 'react';
20 | import GooglePlacesAutocomplete from 'react-google-places-autocomplete';
21 |
22 | const Component = () => {
23 | const rgpa = useRef(null);
24 | const refresh = () => {
25 | if (rgpa && rgpa.current) {
26 | rgpa.current.refreshSessionToken();
27 | }
28 | }
29 |
30 | return (
31 |
32 |
33 |
37 | Refresh session token
38 |
39 |
40 | );
41 | }
42 |
43 | export default Component;
44 | ```
45 |
46 | ## `getSessionToken`
47 |
48 | This function retrieves the current `sessionToken` being used.
49 |
50 |
51 | ## `refreshSessionToken`
52 |
53 | This function allows you to refresh the `sessionToken` being used.
54 |
55 |
56 | **Note:** the componente does not refresh the `sessionToken`, so you will need to handle that yourself.
57 |
--------------------------------------------------------------------------------
/docs/docs/geocode-by-address.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: geocode-by-address
3 | title: Geocode by Address
4 | sidebar_label: Geocode by Address
5 | ---
6 |
7 | This functions allows to get results by an address using [Google Maps Geocoder](https://developers.google.com/maps/documentation/javascript/geocoding).
8 |
9 | ## Firm
10 |
11 | ```tsx
12 | const geocodeByAddress = (address: string): Promise;
13 | ```
14 |
15 | ## Usage
16 |
17 | ```js
18 | import { geocodeByAddress } from 'react-google-places-autocomplete';
19 |
20 | geocodeByAddress('Montevideo, Uruguay')
21 | .then(results => console.log(results))
22 | .catch(error => console.error(error));
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/docs/geocode-by-lat-long.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: geocode-by-lat-lng
3 | title: Geocode by Latitude and Longitude
4 | sidebar_label: Geocode by Latitude and Longitude
5 | ---
6 |
7 | This functions allows to get results by it's coordinates (latitude and longitude) using [Google Maps Geocoder](https://developers.google.com/maps/documentation/javascript/geocoding).
8 |
9 | ## Firm
10 |
11 | ```tsx
12 | const geocodeByLatLng= (latLng: LatLng): Promise => {
13 | ```
14 |
15 | ## Usage
16 |
17 | ```js
18 | import { geocodeByLatLng } from 'react-google-places-autocomplete';
19 |
20 | geocodeByLatLng({ lat: -34.9011, lng: -56.1645 })
21 | .then(results => console.log(results))
22 | .catch(error => console.error(error));
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/docs/geocode-by-place-id.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: geocode-by-place-id
3 | title: Geocode by Place ID
4 | sidebar_label: Geocode by Place ID
5 | ---
6 |
7 | This functions allows to get result by a place id using [Google Maps Geocoder](https://developers.google.com/maps/documentation/javascript/geocoding).
8 |
9 | ## Firm
10 |
11 | ```tsx
12 | const geocodeByPlaceId = (placeId: string): Promise;
13 | ```
14 |
15 | ## Usage
16 |
17 | ```js
18 | import { geocodeByPlaceId } from 'react-google-places-autocomplete';
19 |
20 | geocodeByPlaceId('ChIJH_imbZDuDzkR2AjlbPGYKVE')
21 | .then(results => console.log(results))
22 | .catch(error => console.error(error));
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/docs/get-lat-long.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: get-lat-lng
3 | title: Get Latitude and Longitude
4 | sidebar_label: Get Latitude and Longitude
5 | ---
6 |
7 | This functions allows to get the latitude and longitude of a geocoder result.
8 |
9 | ## Firm
10 |
11 | ```tsx
12 | const getLatLng = (result: google.maps.GeocoderResult): Promise;
13 | ```
14 |
15 | ## Usage
16 |
17 | ```js
18 | import { geocodeByAddress, getLatLng } from 'react-google-places-autocomplete';
19 |
20 | geocodeByAddress('Montevideo, Uruguay')
21 | .then(results => getLatLng(results[0]))
22 | .then(({ lat, lng }) =>
23 | console.log('Successfully got latitude and longitude', { lat, lng })
24 | );
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/docs/how-to-contribute.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: how-to-contribute
3 | title: How to contribute?
4 | sidebar_label: How to contribute?
5 | ---
6 |
7 | 1. Fork this repo
8 | 2. Clone your fork
9 | 3. Code 🤓
10 | 4. Test your changes
11 |
12 | For this, I like to use [yalc](https://github.com/whitecolor/yalc), as it allows to emulate the process of using npm/yarn.
13 |
14 | 1. Install [yalc](https://github.com/whitecolor/yalc)
15 | 2. Build project with `yarn build` or `npm run build`
16 | 3. Publish the package with yalc: `yalc publish`
17 | 4. Add the package to your test project `yalc add react-google-places-automocomplete`
18 | 5. If needed, to update the package on your test project: `yalc update react-google-places-autocomplete`
19 |
20 |
21 | 5. Submit a PR!
22 |
--------------------------------------------------------------------------------
/docs/docs/props.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: props
3 | title: Props
4 | sidebar_label: Props
5 | ---
6 |
7 | ```tsx
8 | interface GooglePlacesAutocompleteProps {
9 | apiKey?: string; // default: ''
10 | apiOptions?: ApiOptions; // default: { }
11 | autocompletionRequest?: AutocompletionRequest; // default: { }
12 | debounce?: number; // default: 300
13 | minLengthAutocomplete?: number; // default: 0
14 | onLoadFailed?: (error: Error) => void; // default: console.error
15 | selectProps?: SelectProps; // default: { }
16 | withSessionToken?: boolean; // default: false
17 | }
18 | ```
19 |
20 | Where `SelectProps` are the ones accepted by [react-select](https://react-select.com/props).
21 |
22 |
23 | ## `apiKey`
24 |
25 | If this parameter is passed, the component will inject the [Google Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/) usign this `apiKey`. So there's no need to manually add the `script` tag to yout HTML document.
26 |
27 |
28 | ## `apiOptions`
29 |
30 | Object to configure the google script to inject. Let's see the shape this prop can take:
31 |
32 | ```tsx
33 | export interface ApiOptions {
34 | language?: string;
35 | region?: string;
36 | }
37 | ```
38 |
39 | Here's an example on how to use it:
40 |
41 | ```jsx
42 |
46 | ```
47 |
48 | **Note:** for more information check [localization documentation](https://developers.google.com/maps/documentation/javascript/localization).
49 |
50 |
51 | ## `autocompletionRequest`
52 |
53 | Autocompletion request object to add restrictions to the search. Let's see the shape this prop can take:
54 |
55 | ```tsx
56 | interface LatLng {
57 | lat: number;
58 | lng: number;
59 | }
60 |
61 | interface AutocompletionRequest {
62 | bounds?: [LatLng, LatLng];
63 | componentRestrictions?: { country: string | string[] };
64 | location?: LatLng;
65 | offset?: number;
66 | radius?: number;
67 | types?: string[];
68 | }
69 | ```
70 |
71 | Here's an example on how to use it:
72 |
73 | ```jsx
74 |
85 | ```
86 |
87 | **Note:** for more information check [google documentation](https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service#AutocompletionRequest).
88 |
89 |
90 | ## `debounce`
91 |
92 | The number of milliseconds to delay before making a call to Google Maps API.
93 |
94 |
95 | ## `minLengthAutocomplete`
96 |
97 | Defines a minimum number of characters needed on the input in order to make requests to the Google's API.
98 |
99 |
100 | ## `onLoadFailed`
101 |
102 | Function to be called when the injection of the [Google Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/) fails due to network error.
103 |
104 | For example:
105 | ```jsx
106 | (
108 | console.error("Could not inject Google script", error)
109 | )}
110 | />
111 | ```
112 |
113 | ## `selectProps`
114 |
115 | As this component uses [react-select](https://react-select.com) under the hood, this prop accepts everything that's accepted by it. You can check [react-select props here](https://react-select.com/props).
116 |
117 | For example, a really common use would be to use it as a controlled input:
118 | ```jsx
119 | const [value, setValue] = useState(null);
120 |
121 |
127 | ```
128 |
129 | ## `withSessionToken`
130 |
131 | If this prop is set to `true`, the component will handle changing the `sessionToken` on every session. To learn more about how this works refer to [Google Places Session Token docs](https://developers.google.com/places/web-service/session-tokens).
132 |
--------------------------------------------------------------------------------
/docs/docs/recipes.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: examples
3 | title: Examples
4 | sidebar_label: Examples
5 | ---
6 |
7 | ## Automatic Google Maps Javascript API injection
8 |
9 | ```jsx
10 |
11 | ```
12 |
13 |
14 | ## Controlled input
15 |
16 | ```jsx
17 | const [value, setValue] = useState(null);
18 |
19 |
25 | ```
26 |
27 | ## Customization
28 |
29 | In order to customize the input and suggestions list, we need to use the `styles` property of [react-select](https://react-select.com/styles).
30 |
31 | ```jsx
32 | ({
36 | ...provided,
37 | color: 'blue',
38 | }),
39 | option: (provided) => ({
40 | ...provided,
41 | color: 'blue',
42 | }),
43 | singleValue: (provided) => ({
44 | ...provided,
45 | color: 'blue',
46 | }),
47 | },
48 | }}
49 | />
50 | ```
51 |
--------------------------------------------------------------------------------
/docs/docs/v2-to-v3.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: v2-to-v3
3 | title: Migrate to v3
4 | sidebar_label: Migrate to v3
5 | ---
6 |
7 | If you are coming from v2 and wish to start using v3, here's a comprehensive list of the deprecated props and how to replace them.
8 |
9 | ## `disabled`
10 |
11 | ```jsx
12 |
15 | ```
16 |
17 |
18 | ## `displayFromSuggestionSelected`
19 |
20 | ```jsx
21 | string
24 | }}
25 | />
26 | ```
27 |
28 |
29 | ## `idPrefix`
30 |
31 | This prop was used in order to allow multiple instances on the component rendered on the same page. Starting on v3, there's no more need for this prop, but if you need to pass an specific `id`, you can do:
32 |
33 | ```jsx
34 |
42 | ```
43 |
44 |
45 | ## `initialValue`
46 |
47 | [React-select](https://react-select.com) provides a handful of ways to handle this. Refer to [their docs](https://react-select.com/props#statemanager-props) to find the way that best suites your use case.
48 |
49 | ## `inputClassName`, `inputStyle`, `suggestionsClassNames` and `suggestionsStyles`
50 |
51 | [React-select](https://react-select.com) provides an easy way to customize the select, refer to [their docs](https://react-select.com/tylesrops#statemanager-props).
52 |
53 | ## `loader`, `renderInput` and `renderSuggestions`
54 |
55 | Again, [react-select](https://react-select.com) provides an easy way to use custom components, refer to [their docs](https://react-select.com/props#replacing-components).
56 |
57 | ## `onSelect`
58 |
59 | ```jsx
60 | undefined }}
62 | />
63 | ```
64 |
65 |
66 | ## `placeholder`
67 |
68 | ```jsx
69 |
74 | ```
75 |
--------------------------------------------------------------------------------
/docs/docusaurus.config.js:
--------------------------------------------------------------------------------
1 | const repo = 'https://github.com/tintef/react-google-places-autocomplete';
2 |
3 | module.exports = {
4 | title: 'React Google Places Autocomplete',
5 | tagline: 'React component for easily use Google Places Autocomplete',
6 | url: 'https://tintef.github.io/',
7 | baseUrl: '/react-google-places-autocomplete/',
8 | onBrokenLinks: 'throw',
9 | favicon: 'img/favicon.ico',
10 | organizationName: 'tintef', // Usually your GitHub org/user name.
11 | projectName: 'react-google-places-autocomplete', // Usually your repo name.
12 | themeConfig: {
13 | navbar: {
14 | title: 'React Google Places Autocomplete',
15 | logo: {
16 | alt: 'Logo',
17 | src: 'img/logo.svg',
18 | },
19 | items: [
20 | {
21 | to: 'docs/',
22 | activeBasePath: 'docs',
23 | label: 'Docs',
24 | position: 'right',
25 | },
26 | {
27 | href: repo,
28 | label: 'GitHub',
29 | position: 'right',
30 | },
31 | ],
32 | },
33 | footer: {
34 | style: 'dark',
35 | links: [],
36 | copyright: `
37 | Copyright © ${new Date().getFullYear()} React Google Places Autocomplete, Tintef
38 |
39 | `,
40 | },
41 | },
42 | presets: [
43 | [
44 | '@docusaurus/preset-classic',
45 | {
46 | docs: {
47 | // It is recommended to set document id as docs home page (`docs/` path).
48 | homePageId: 'basic-usage',
49 | sidebarPath: require.resolve('./sidebars.js'),
50 | // Please change this to your repo.
51 | editUrl:
52 | 'https://github.com/tintef/react-google-places-autocomplete/edit/master/docs/',
53 | },
54 | theme: {
55 | customCss: require.resolve('./src/css/custom.css'),
56 | },
57 | },
58 | ],
59 | ],
60 | };
61 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "serve": "docusaurus serve"
12 | },
13 | "dependencies": {
14 | "@docusaurus/core": "^2.0.0",
15 | "@docusaurus/preset-classic": "^2.0.0",
16 | "@mdx-js/react": "^1.6.22",
17 | "clsx": "^1.2.0",
18 | "react": "^16.14.0",
19 | "react-dom": "^16.14.0"
20 | },
21 | "browserslist": {
22 | "production": [
23 | ">0.2%",
24 | "not dead",
25 | "not op_mini all"
26 | ],
27 | "development": [
28 | "last 1 chrome version",
29 | "last 1 firefox version",
30 | "last 1 safari version"
31 | ]
32 | },
33 | "devDependencies": {
34 | "@docusaurus/module-type-aliases": "^2.0.0-alpha.51"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/docs/sidebars.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | docs: {
3 | 'React Google Places Autocomplete': [
4 | 'basic-usage',
5 | 'props',
6 | 'exposed-methods',
7 | 'examples',
8 | 'v2-to-v3',
9 | ],
10 | 'Utility functions': [
11 | 'geocode-by-place-id',
12 | 'geocode-by-address',
13 | 'geocode-by-lat-lng',
14 | 'get-lat-lng',
15 | ],
16 | 'How to contribute?': ['how-to-contribute'],
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/docs/src/css/custom.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable docusaurus/copyright-header */
2 | /**
3 | * Any CSS included here will be global. The classic template
4 | * bundles Infima by default. Infima is a CSS framework designed to
5 | * work well for content-centric websites.
6 | */
7 |
8 | /* You can override the default Infima variables here. */
9 | :root {
10 | --ifm-color-primary: #25c2a0;
11 | --ifm-color-primary-dark: rgb(33, 175, 144);
12 | --ifm-color-primary-darker: rgb(31, 165, 136);
13 | --ifm-color-primary-darkest: rgb(26, 136, 112);
14 | --ifm-color-primary-light: rgb(70, 203, 174);
15 | --ifm-color-primary-lighter: rgb(102, 212, 189);
16 | --ifm-color-primary-lightest: rgb(146, 224, 208);
17 | --ifm-code-font-size: 95%;
18 | --ifm-navbar-height: 100px;
19 | }
20 |
21 | .docusaurus-highlight-code-line {
22 | background-color: rgb(72, 77, 91);
23 | display: block;
24 | margin: 0 calc(-1 * var(--ifm-pre-padding));
25 | padding: 0 var(--ifm-pre-padding);
26 | }
27 |
28 | .navbar__brand {
29 | height: 4em;
30 | }
31 |
32 | .container .row {
33 | justify-content: center;
34 | }
35 |
--------------------------------------------------------------------------------
/docs/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import Layout from '@theme/Layout';
4 | import Link from '@docusaurus/Link';
5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
6 | import useBaseUrl from '@docusaurus/useBaseUrl';
7 | import styles from './styles.module.css';
8 |
9 | const features = [
10 | {
11 | title: <>Easy to Use>,
12 | imageUrl: 'img/easy-to-use.svg',
13 | description: (
14 | <>
15 | React Google Places Autocomplete was designed to be easily installed and
16 | used to get your website up and running quickly.
17 | >
18 | ),
19 | },
20 | ];
21 |
22 | function Feature({ imageUrl, title, description }) {
23 | const imgUrl = useBaseUrl(imageUrl);
24 |
25 | return (
26 |
27 | {imgUrl && (
28 |
29 |
30 |
31 | )}
32 |
{title}
33 |
{description}
34 |
35 | );
36 | }
37 |
38 | function Home() {
39 | const context = useDocusaurusContext();
40 | const { siteConfig = {} } = context;
41 |
42 | return (
43 |
47 |
73 |
74 | {features && features.length > 0 && (
75 |
76 |
77 |
78 | {features.map((props, idx) => (
79 |
80 | ))}
81 |
82 |
83 |
84 | )}
85 |
86 |
87 | );
88 | }
89 |
90 | export default Home;
91 |
--------------------------------------------------------------------------------
/docs/src/pages/styles.module.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable docusaurus/copyright-header */
2 |
3 | /**
4 | * CSS files with the .module.css suffix will be treated as CSS modules
5 | * and scoped locally.
6 | */
7 |
8 | .heroBanner {
9 | padding: 4rem 0;
10 | text-align: center;
11 | position: relative;
12 | overflow: hidden;
13 | }
14 |
15 | @media screen and (max-width: 966px) {
16 | .heroBanner {
17 | padding: 2rem;
18 | }
19 | }
20 |
21 | .buttons {
22 | display: flex;
23 | align-items: center;
24 | justify-content: center;
25 | }
26 |
27 | .features {
28 | display: flex;
29 | align-items: center;
30 | padding: 2rem 0;
31 | width: 100%;
32 | }
33 |
34 | .featureImage {
35 | height: 150px;
36 | width: 150px;
37 | padding: 25px 0;
38 | }
39 |
40 | .ghStars {
41 | border: none;
42 | margin-left: 24px;
43 | overflow: hidden;
44 | }
45 |
--------------------------------------------------------------------------------
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tintef/react-google-places-autocomplete/73ed2dc7f76ace92d40f445260bbb30d379a10f7/docs/static/.nojekyll
--------------------------------------------------------------------------------
/docs/static/img/customizable.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/static/img/easy-to-use.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tintef/react-google-places-autocomplete/73ed2dc7f76ace92d40f445260bbb30d379a10f7/docs/static/img/favicon.ico
--------------------------------------------------------------------------------
/docs/static/img/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-google-places-autocomplete",
3 | "version": "4.1.0",
4 | "description": "Google places autocomplete input for ReactJS.",
5 | "main": "build/index.js",
6 | "module": "build/index.es.js",
7 | "scripts": {
8 | "build": "rollup -c",
9 | "postbuild": "node ./build-utils/postBuild",
10 | "lint": "eslint . --ext ts,tsx ."
11 | },
12 | "husky": {
13 | "hooks": {
14 | "pre-commit": "yarn lint"
15 | }
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/Tintef/react-google-places-autocomplete.git"
20 | },
21 | "author": "Nicolás Tinte",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/Tintef/react-google-places-autocomplete/issues"
25 | },
26 | "homepage": "https://github.com/Tintef/react-google-places-autocomplete#readme",
27 | "keywords": [
28 | "react",
29 | "google-places",
30 | "places",
31 | "autocomplete"
32 | ],
33 | "devDependencies": {
34 | "@rollup/plugin-commonjs": "^15.0.0",
35 | "@rollup/plugin-node-resolve": "^9.0.0",
36 | "@types/react": "^16.9.46",
37 | "@types/react-dom": "^16.9.8",
38 | "@typescript-eslint/eslint-plugin": "^3.9.1",
39 | "@typescript-eslint/parser": "^3.9.1",
40 | "eslint": "^7.7.0",
41 | "eslint-plugin-react": "^7.20.6",
42 | "husky": "^4.2.5",
43 | "react": "^16.8.0",
44 | "react-dom": "^16.8.0",
45 | "rollup": "^2.26.4",
46 | "rollup-plugin-bundle-size": "^1.0.3",
47 | "rollup-plugin-peer-deps-external": "^2.2.3",
48 | "rollup-plugin-terser": "^7.0.2",
49 | "rollup-plugin-typescript2": "^0.27.2",
50 | "typescript": "^3.9.7"
51 | },
52 | "dependencies": {
53 | "@googlemaps/js-api-loader": "^1.14.3",
54 | "@types/google.maps": "^3.50.4",
55 | "react-select": "^5.7.4",
56 | "use-debounce": "^3.4.3"
57 | },
58 | "peerDependencies": {
59 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
60 | "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import peerDepsExternal from "rollup-plugin-peer-deps-external";
2 | import resolve from "@rollup/plugin-node-resolve";
3 | import commonjs from "@rollup/plugin-commonjs";
4 | import typescript from "rollup-plugin-typescript2";
5 | import bundleSize from "rollup-plugin-bundle-size";
6 | import { terser } from "rollup-plugin-terser";
7 |
8 | import pkg from "./package.json";
9 |
10 | export default {
11 | input: "src/index.ts",
12 | output: [
13 | {
14 | file: pkg.main,
15 | format: "cjs",
16 | sourcemap: true
17 | },
18 | {
19 | file: pkg.module,
20 | format: "esm",
21 | sourcemap: true
22 | }
23 | ],
24 | plugins: [
25 | peerDepsExternal(),
26 | resolve(),
27 | commonjs(),
28 | typescript({
29 | clean: true,
30 | abortOnError: true,
31 | }),
32 | terser({}),
33 | bundleSize(),
34 | ],
35 | };
36 |
--------------------------------------------------------------------------------
/src/google-places-autocomplete.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef, useImperativeHandle } from 'react';
2 | import AsyncSelect from 'react-select/async';
3 |
4 | import GooglePlacesAutocompleteProps, { GooglePlacesAutocompleteHandle } from './types';
5 | import usePlacesService from './hooks/use-places-service';
6 | import useFetchSuggestions from './hooks/use-fetch-suggestions';
7 |
8 | const GooglePlacesAutocomplete: React.ForwardRefRenderFunction = (
9 | args: GooglePlacesAutocompleteProps,
10 | ref,
11 | ) : React.ReactElement => {
12 |
13 | const { placesService, sessionToken, setSessionToken } = usePlacesService({
14 | apiKey: args.apiKey ?? '',
15 | apiOptions: args.apiOptions ?? {},
16 | onLoadFailed: args.onLoadFailed ?? console.error,
17 | });
18 | const fetchSuggestions = useFetchSuggestions({
19 | autocompletionRequest: args.autocompletionRequest ?? {},
20 | debounce: args.debounce ?? 300,
21 | minLengthAutocomplete: args.minLengthAutocomplete ?? 0,
22 | placesService,
23 | sessionToken,
24 | withSessionToken: args.withSessionToken ?? false,
25 | });
26 |
27 | useImperativeHandle(ref, () => ({
28 | getSessionToken: () => {
29 | return sessionToken;
30 | },
31 | refreshSessionToken: () => {
32 | setSessionToken(new google.maps.places.AutocompleteSessionToken());
33 | }
34 | }), [sessionToken]);
35 |
36 | return (
37 | value.place_id}
41 | />
42 | );
43 | };
44 |
45 | export default forwardRef(GooglePlacesAutocomplete);
46 |
--------------------------------------------------------------------------------
/src/helpers/autocompletion-request-builder.ts:
--------------------------------------------------------------------------------
1 | import { AutocompletionRequest } from '../types';
2 |
3 | export default (
4 | autocompletionRequest: AutocompletionRequest,
5 | input: string,
6 | sessionToken?: google.maps.places.AutocompleteSessionToken,
7 | ): google.maps.places.AutocompletionRequest => {
8 | const { bounds, location, ...rest } = autocompletionRequest;
9 |
10 | const res: google.maps.places.AutocompletionRequest= {
11 | input,
12 | ...rest,
13 | };
14 |
15 | if (sessionToken) {
16 | res.sessionToken = sessionToken;
17 | }
18 |
19 | if (bounds) {
20 | res.bounds = new google.maps.LatLngBounds(...bounds);
21 | }
22 |
23 | if (location) {
24 | res.location = new google.maps.LatLng(location);
25 | }
26 |
27 | return res;
28 | };
29 |
--------------------------------------------------------------------------------
/src/hooks/use-fetch-suggestions.ts:
--------------------------------------------------------------------------------
1 | import { useDebouncedCallback } from 'use-debounce';
2 | import { Options } from 'react-select';
3 |
4 | import { AutocompletionRequest } from '../types';
5 | import autocompletionRequestBuilder from '../helpers/autocompletion-request-builder';
6 |
7 | type CBType = (options: Options) => void;
8 | type UseFetchSuggestionsArg = {
9 | autocompletionRequest: AutocompletionRequest;
10 | debounce: number;
11 | minLengthAutocomplete: number;
12 | placesService?: google.maps.places.AutocompleteService;
13 | sessionToken?: google.maps.places.AutocompleteSessionToken;
14 | withSessionToken: boolean;
15 | }
16 |
17 | const useFetchSuggestions = (arg: UseFetchSuggestionsArg): ((value: string, cb: CBType) => void) => {
18 | const {
19 | autocompletionRequest,
20 | debounce,
21 | minLengthAutocomplete,
22 | placesService,
23 | sessionToken,
24 | withSessionToken,
25 | } = arg;
26 |
27 | const [fetchSuggestions] = useDebouncedCallback((value: string, cb: CBType): void => {
28 | if (!placesService) return cb([]);
29 | if (value.length < minLengthAutocomplete) return cb([]);
30 |
31 | const autocompletionReq: AutocompletionRequest = { ...autocompletionRequest };
32 |
33 | placesService.getPlacePredictions(
34 | autocompletionRequestBuilder(
35 | autocompletionReq,
36 | value,
37 | withSessionToken && sessionToken,
38 | ), (suggestions) => {
39 | cb((suggestions || []).map(suggestion => ({ label: suggestion.description, value: suggestion })));
40 | },
41 | );
42 | }, debounce);
43 |
44 | return fetchSuggestions;
45 | }
46 |
47 | export default useFetchSuggestions;
48 |
--------------------------------------------------------------------------------
/src/hooks/use-places-service.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { Loader, LoaderOptions } from '@googlemaps/js-api-loader';
3 |
4 | type UsePlacesServiceArgs = {
5 | apiKey?: string;
6 | apiOptions?: Partial;
7 | onLoadFailed?: (error: Error) => void;
8 | }
9 |
10 | type UsePlacesServiceRes = {
11 | placesService?: google.maps.places.AutocompleteService;
12 | sessionToken?: google.maps.places.AutocompleteSessionToken;
13 | setSessionToken: React.Dispatch;
14 | }
15 |
16 | const usePlacesService = (args: UsePlacesServiceArgs): UsePlacesServiceRes => {
17 | const { apiKey, apiOptions, onLoadFailed } = args;
18 |
19 | const [placesService, setPlacesService] = useState(undefined);
20 | const [sessionToken, setSessionToken] = useState(undefined);
21 |
22 | const initializeService = () => {
23 | if (!window.google) throw new Error('[react-google-places-autocomplete]: Google script not loaded');
24 | if (!window.google.maps) throw new Error('[react-google-places-autocomplete]: Google maps script not loaded');
25 | if (!window.google.maps.places) throw new Error('[react-google-places-autocomplete]: Google maps places script not loaded');
26 |
27 | setPlacesService(new window.google.maps.places.AutocompleteService());
28 | setSessionToken(new google.maps.places.AutocompleteSessionToken());
29 | }
30 |
31 | useEffect(() => {
32 | const init = async () => {
33 | if (!apiKey) return;
34 |
35 | try {
36 | if ( !window.google || !window.google.maps || !window.google.maps.places ) {
37 | await new Loader({ apiKey, ...{ libraries: ['places'], ...apiOptions }}).load();
38 | }
39 | initializeService();
40 | } catch (error) {
41 | if (typeof onLoadFailed === 'function') onLoadFailed(error);
42 | }
43 | }
44 |
45 | if (apiKey) init();
46 | else initializeService();
47 | }, []);
48 |
49 |
50 | return { placesService, sessionToken, setSessionToken };
51 | }
52 |
53 | export default usePlacesService;
54 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import GooglePlacesAutocomplete from './google-places-autocomplete';
2 | import getLatLng from './utils/get-lat-lng';
3 | import geocodeByAddress from './utils/geocode-by-address';
4 | import geocodeByLatLng from './utils/geocode-by-lat-lng';
5 | import geocodeByPlaceId from './utils/geocode-by-place-id';
6 |
7 | export {
8 | getLatLng,
9 | geocodeByAddress,
10 | geocodeByLatLng,
11 | geocodeByPlaceId,
12 | };
13 | export default GooglePlacesAutocomplete;
14 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import { LoaderOptions } from '@googlemaps/js-api-loader';
2 | import { GroupBase } from 'react-select';
3 | import { AsyncProps } from 'react-select/async';
4 |
5 | export type GooglePlacesAutocompleteHandle = {
6 | getSessionToken: () => google.maps.places.AutocompleteSessionToken | undefined;
7 | refreshSessionToken: () => void;
8 | }
9 |
10 | export interface LatLng {
11 | lat: number;
12 | lng: number;
13 | }
14 |
15 | export interface AutocompletionRequest {
16 | bounds?: [LatLng, LatLng];
17 | componentRestrictions?: { country: string | string[] };
18 | location?: LatLng;
19 | offset?: number;
20 | radius?: number;
21 | types?: string[];
22 | }
23 |
24 | export type Option = {
25 | label: string;
26 | value: any;
27 | };
28 |
29 | export default interface GooglePlacesAutocompleteProps {
30 | apiKey?: string;
31 | apiOptions?: Partial;
32 | autocompletionRequest?: AutocompletionRequest;
33 | debounce?: number;
34 | minLengthAutocomplete?: number;
35 | onLoadFailed?: (error: Error) => void;
36 | selectProps?: AsyncProps>;
37 | withSessionToken?: boolean;
38 | }
39 |
--------------------------------------------------------------------------------
/src/utils/geocode-by-address.ts:
--------------------------------------------------------------------------------
1 | const geocodeByAddress = (address: string): Promise => {
2 | const geocoder = new window.google.maps.Geocoder();
3 | const { OK } = window.google.maps.GeocoderStatus;
4 |
5 | return new Promise((resolve, reject) => {
6 | geocoder.geocode(
7 | { address },
8 | (
9 | results: google.maps.GeocoderResult[],
10 | status: google.maps.GeocoderStatus,
11 | ) => {
12 | if (status !== OK) {
13 | return reject(status);
14 | }
15 |
16 | return resolve(results);
17 | });
18 | });
19 | };
20 |
21 | export default geocodeByAddress;
22 |
--------------------------------------------------------------------------------
/src/utils/geocode-by-lat-lng.ts:
--------------------------------------------------------------------------------
1 | import { LatLng } from '../types';
2 |
3 | const geocodeByLatLng = (latLng: LatLng): Promise => {
4 | const geocoder = new window.google.maps.Geocoder();
5 | const { OK } = window.google.maps.GeocoderStatus;
6 |
7 | return new Promise((resolve, reject) => {
8 | geocoder.geocode(
9 | { location: latLng },
10 | (
11 | results: google.maps.GeocoderResult[],
12 | status: google.maps.GeocoderStatus,
13 | ) => {
14 | if (status !== OK) return reject(status);
15 |
16 | return resolve(results);
17 | }
18 | );
19 | });
20 | };
21 |
22 | export default geocodeByLatLng;
23 |
--------------------------------------------------------------------------------
/src/utils/geocode-by-place-id.ts:
--------------------------------------------------------------------------------
1 | const geocodeByPlaceId = (placeId: string): Promise => {
2 | const geocoder = new window.google.maps.Geocoder();
3 | const { OK } = window.google.maps.GeocoderStatus;
4 |
5 | return new Promise((resolve, reject) => {
6 | geocoder.geocode(
7 | { placeId },
8 | (
9 | results: google.maps.GeocoderResult[],
10 | status: google.maps.GeocoderStatus,
11 | ) => {
12 | if (status !== OK) {
13 | return reject(status);
14 | }
15 | return resolve(results);
16 | });
17 | });
18 | };
19 |
20 | export default geocodeByPlaceId;
21 |
--------------------------------------------------------------------------------
/src/utils/get-lat-lng.ts:
--------------------------------------------------------------------------------
1 | import { LatLng } from '../types';
2 |
3 | const getLatLng = (result: google.maps.GeocoderResult): Promise => (
4 | new Promise((resolve, reject) => {
5 | try {
6 | const latLng = {
7 | lat: result.geometry.location.lat(),
8 | lng: result.geometry.location.lng(),
9 | };
10 | return resolve(latLng);
11 | } catch (e) {
12 | return reject(e);
13 | }
14 | })
15 | );
16 |
17 | export default getLatLng;
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "declaration": true,
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "jsx": "react",
8 | "lib": ["es6", "dom", "es2016", "es2017"],
9 | "module": "esnext",
10 | "moduleResolution": "node",
11 | "noImplicitAny": true,
12 | "noImplicitReturns": true,
13 | "noImplicitThis": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "outDir": "build",
17 | "sourceMap": true,
18 | "strictNullChecks": true,
19 | "suppressImplicitAnyIndexErrors": true,
20 | "target": "es5"
21 | },
22 | "include": ["src/**/*"],
23 | "exclude": [
24 | "node_modules",
25 | "build",
26 | "docs"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------