├── .eslintrc ├── .github └── workflows │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .prettierrc ├── CONTRIBUTING.md ├── CONVENTIONS.md ├── LICENSE.md ├── README.md ├── examples ├── .env.example ├── .prettierignore ├── LICENSE.md ├── README.md ├── basic-google-map │ ├── README.md │ ├── app.tsx │ ├── components │ │ └── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── constants.ts ├── directions-service │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── directions-service │ │ │ └── directions-service.tsx │ │ └── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── distance-matrix-service │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── distance-matrix-service │ │ │ ├── distance-matrix-service.tsx │ │ │ └── distance-matrix.module.css │ │ ├── icons │ │ │ └── origin-marker.tsx │ │ └── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ ├── index.html │ ├── index.tsx │ ├── libs │ │ └── marker-icon-helpers.tsx │ └── main.module.css ├── elevation-service │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── elevation-service │ │ │ └── elevation-service.tsx │ │ └── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── geocoding-service │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── geocoding-service │ │ │ └── geocoding-service.tsx │ │ └── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── google-map-with-markers │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ │ └── map-markers │ │ │ └── map-markers.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── index.d.ts ├── max-zoom-service │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ │ └── max-zoom-service │ │ │ └── max-zoom-service.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── multiple-google-maps │ ├── README.md │ ├── app.tsx │ ├── components │ │ └── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── package.json ├── places-autocomplete-service │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ │ └── places-autocomplete-service │ │ │ ├── places-autocomplete-service.module.css │ │ │ └── places-autocomplete-service.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── places-autocomplete-widget │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ │ └── places-autocomplete-widget │ │ │ ├── places-autocomplete-widget.module.css │ │ │ └── places-autocomplete-widget.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── places-service-with-element │ ├── README.md │ ├── app.tsx │ ├── components │ │ └── places-service-with-element │ │ │ ├── places-service-with-element.module.css │ │ │ └── places-service-with-element.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── places-service │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ │ └── places-service │ │ │ └── places-service.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── street-view-panorama-map │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ │ └── street-view-panorama-map │ │ │ └── street-view-panorama-map.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css ├── street-view-panorama-with-element │ ├── README.md │ ├── app.tsx │ ├── components │ │ ├── map-canvas │ │ │ ├── map-canvas.module.css │ │ │ └── map-canvas.tsx │ │ └── street-view-panorama-with-element │ │ │ ├── street-view-element.module.css │ │ │ └── street-view-element.tsx │ ├── index.html │ ├── index.tsx │ └── main.module.css └── tsconfig.json ├── library ├── .npmignore ├── .prettierignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── docs │ ├── GoogleMapsProvider.md │ ├── useAutocomplete.md │ ├── useAutocompleteService.md │ ├── useDirectionsService.md │ ├── useDistanceMatrixService.md │ ├── useElevationService.md │ ├── useGeocodingService.md │ ├── useGoogleMap.md │ ├── useMaxZoomService.md │ ├── usePlacesService.md │ └── useStreetViewPanorama.md ├── package.json ├── src │ ├── google-maps-provider.tsx │ ├── hooks │ │ ├── autocomplete-service.ts │ │ ├── autocomplete.ts │ │ ├── directions-service.ts │ │ ├── distance-matrix-service.ts │ │ ├── elevation-service.ts │ │ ├── geocoding-service.ts │ │ ├── map-instance.ts │ │ ├── max-zoom-service.ts │ │ ├── places-service.ts │ │ └── street-view-panorama.ts │ ├── index.ts │ └── types │ │ └── index.d.ts └── tsconfig.json ├── package-lock.json ├── package.json ├── start-example.sh └── tsconfig.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "react", 4 | "import", 5 | "react-hooks", 6 | "@typescript-eslint", 7 | "codegen" 8 | ], 9 | "extends": ["prettier", "plugin:@typescript-eslint/recommended"], 10 | "parser": "@typescript-eslint/parser", 11 | "env": { 12 | "node": true, 13 | "browser": true, 14 | "es6": true 15 | }, 16 | "globals": { 17 | "google": true 18 | }, 19 | "overrides": [ 20 | { 21 | "files": ["*.ts", "*.mts", "*.cts", "*.tsx"], 22 | "rules": { 23 | "no-undef": "off" 24 | } 25 | } 26 | ], 27 | "parserOptions": { 28 | "ecmaVersion": 6, 29 | "sourceType": "module", 30 | "ecmaFeatures": { 31 | "arrowFunctions": true, 32 | "binaryLiterals": true, 33 | "blockBindings": true, 34 | "classes": true, 35 | "defaultParams": true, 36 | "destructuring": true, 37 | "forOf": true, 38 | "generators": true, 39 | "modules": true, 40 | "objectLiteralComputedProperties": true, 41 | "objectLiteralDuplicateProperties": true, 42 | "objectLiteralShorthandMethods": true, 43 | "objectLiteralShorthandProperties": true, 44 | "octalLiterals": true, 45 | "regexUFlag": true, 46 | "regexYFlag": true, 47 | "restParams": true, 48 | "spread": true, 49 | "superInFunctions": true, 50 | "templateStrings": true, 51 | "unicodeCodePointEscapes": true, 52 | "globalReturn": true 53 | } 54 | }, 55 | "rules": { 56 | "comma-dangle": 2, 57 | "no-cond-assign": [2, "except-parens"], 58 | "no-console": [ 59 | 1, 60 | { 61 | "allow": ["info", "warn", "error"] 62 | } 63 | ], 64 | "no-constant-condition": 2, 65 | "no-control-regex": 2, 66 | "no-debugger": 2, 67 | "no-dupe-keys": 2, 68 | "no-duplicate-case": 2, 69 | "no-empty": 2, 70 | "no-empty-character-class": 2, 71 | "no-ex-assign": 2, 72 | "no-extra-boolean-cast": 2, 73 | "no-extra-parens": 0, 74 | "no-extra-semi": 2, 75 | "no-func-assign": 2, 76 | "no-inner-declarations": 2, 77 | "no-invalid-regexp": 2, 78 | "no-irregular-whitespace": 2, 79 | "no-negated-in-lhs": 2, 80 | "no-obj-calls": 2, 81 | "no-prototype-builtins": 1, 82 | "no-regex-spaces": 1, 83 | "no-sparse-arrays": 2, 84 | "no-unexpected-multiline": 2, 85 | "no-unreachable": 2, 86 | "no-unsafe-finally": 2, 87 | "use-isnan": 2, 88 | "valid-typeof": 2, 89 | "accessor-pairs": 0, 90 | "array-callback-return": 2, 91 | "no-var": 2, 92 | "block-scoped-var": 2, 93 | "complexity": [1, 7], 94 | "consistent-return": 2, 95 | "curly": [2, "all"], 96 | "default-case": 0, 97 | "dot-location": [2, "property"], 98 | "dot-notation": [ 99 | 1, 100 | { 101 | "allowKeywords": true 102 | } 103 | ], 104 | "eqeqeq": [2, "smart"], 105 | "guard-for-in": 1, 106 | "no-alert": 2, 107 | "no-caller": 2, 108 | "no-case-declarations": 2, 109 | "no-div-regex": 1, 110 | "no-else-return": 2, 111 | "no-eq-null": 2, 112 | "no-eval": 2, 113 | "no-extend-native": 2, 114 | "no-extra-bind": 2, 115 | "no-fallthrough": 2, 116 | "no-floating-decimal": 2, 117 | "no-implicit-coercion": [ 118 | 2, 119 | { 120 | "boolean": true, 121 | "number": true, 122 | "string": true 123 | } 124 | ], 125 | "no-implied-eval": 2, 126 | "no-invalid-this": 0, 127 | "no-iterator": 2, 128 | "no-labels": 2, 129 | "no-lone-blocks": 2, 130 | "no-loop-func": 2, 131 | "no-multi-spaces": 2, 132 | "no-multi-str": 2, 133 | "no-native-reassign": 2, 134 | "no-new": 0, 135 | "no-new-func": 2, 136 | "no-new-wrappers": 2, 137 | "no-octal": 2, 138 | "no-octal-escape": 2, 139 | "no-process-env": 0, 140 | "no-proto": 2, 141 | "no-redeclare": 2, 142 | "no-return-assign": 2, 143 | "no-script-url": 2, 144 | "no-self-compare": 2, 145 | "no-sequences": 2, 146 | "no-unused-expressions": [ 147 | "error", 148 | {"allowShortCircuit": true, "allowTernary": true} 149 | ], 150 | "no-useless-call": 2, 151 | "no-void": 2, 152 | "no-warning-comments": 1, 153 | "no-with": 2, 154 | "radix": 2, 155 | "vars-on-top": 0, 156 | "wrap-iife": [2, "inside"], 157 | "yoda": [2, "never"], 158 | "strict": [0, "never"], 159 | "init-declarations": [0, "never"], 160 | "no-catch-shadow": 2, 161 | "no-delete-var": 2, 162 | "no-label-var": 2, 163 | "no-shadow": 2, 164 | "no-shadow-restricted-names": 2, 165 | "no-undef": 2, 166 | "no-undef-init": 2, 167 | "no-undefined": 0, 168 | "no-unused-vars": 0, 169 | "no-use-before-define": 0, 170 | "callback-return": 2, 171 | "handle-callback-err": 1, 172 | "no-mixed-requires": 0, 173 | "no-new-require": 2, 174 | "no-path-concat": 2, 175 | "no-process-exit": 1, 176 | "no-restricted-modules": 0, 177 | "no-sync": 0, 178 | "brace-style": [ 179 | 2, 180 | "1tbs", 181 | { 182 | "allowSingleLine": true 183 | } 184 | ], 185 | "camelcase": 2, 186 | "comma-spacing": [ 187 | 2, 188 | { 189 | "before": false, 190 | "after": true 191 | } 192 | ], 193 | "comma-style": [2, "last"], 194 | "consistent-this": [2, "none"], 195 | "eol-last": 1, 196 | "func-names": 0, 197 | "func-style": 0, 198 | "id-length": [0], 199 | "id-match": [0], 200 | "key-spacing": [ 201 | 0, 202 | { 203 | "beforeColon": false, 204 | "afterColon": true 205 | } 206 | ], 207 | "max-nested-callbacks": [2, 3], 208 | "new-cap": 2, 209 | "new-parens": 2, 210 | "no-array-constructor": 2, 211 | "no-inline-comments": 0, 212 | "no-lonely-if": 2, 213 | "no-mixed-spaces-and-tabs": 2, 214 | "no-multiple-empty-lines": [ 215 | 1, 216 | { 217 | "max": 1 218 | } 219 | ], 220 | "no-nested-ternary": 2, 221 | "no-new-object": 2, 222 | "no-spaced-func": 2, 223 | "no-ternary": 0, 224 | "no-trailing-spaces": 2, 225 | "no-underscore-dangle": 0, 226 | "one-var": 0, 227 | "one-var-declaration-per-line": [2, "always"], 228 | "operator-assignment": 0, 229 | "padded-blocks": [1, "never"], 230 | "quote-props": [ 231 | 0, 232 | "as-needed", 233 | { 234 | "keywords": false 235 | } 236 | ], 237 | "quotes": [2, "single", "avoid-escape"], 238 | "semi": [2, "always"], 239 | "semi-spacing": [ 240 | 2, 241 | { 242 | "before": false, 243 | "after": true 244 | } 245 | ], 246 | "sort-vars": 0, 247 | "space-before-function-paren": [ 248 | "error", 249 | { 250 | "anonymous": "never", 251 | "named": "never", 252 | "asyncArrow": "always" 253 | } 254 | ], 255 | "keyword-spacing": [ 256 | "error", 257 | { 258 | "after": true 259 | } 260 | ], 261 | "space-before-blocks": [2, "always"], 262 | "space-in-parens": [2, "never"], 263 | "space-infix-ops": 2, 264 | "space-unary-ops": [ 265 | 2, 266 | { 267 | "words": true, 268 | "nonwords": false 269 | } 270 | ], 271 | "spaced-comment": [2, "always"], 272 | "object-curly-spacing": [1, "never"], 273 | "array-bracket-spacing": 0, 274 | "wrap-regex": 0, 275 | "generator-star-spacing": [ 276 | 2, 277 | { 278 | "before": true, 279 | "after": false 280 | } 281 | ], 282 | "max-depth": [1, 3], 283 | "max-params": 0, 284 | "max-statements": 0, 285 | "no-bitwise": 2, 286 | "no-plusplus": 0, 287 | "arrow-spacing": [ 288 | 2, 289 | { 290 | "before": true, 291 | "after": true 292 | } 293 | ], 294 | "no-class-assign": 2, 295 | "no-const-assign": 2, 296 | "arrow-body-style": [1, "as-needed"], 297 | "arrow-parens": 0, 298 | "constructor-super": 2, 299 | "no-confusing-arrow": 0, 300 | "no-dupe-class-members": 2, 301 | "no-duplicate-imports": 1, 302 | "no-new-symbol": 2, 303 | "no-this-before-super": 2, 304 | "no-useless-computed-key": 2, 305 | "no-useless-constructor": 2, 306 | "object-shorthand": 2, 307 | "prefer-arrow-callback": [ 308 | 1, 309 | { 310 | "allowUnboundThis": false 311 | } 312 | ], 313 | "prefer-const": [ 314 | 2, 315 | { 316 | "destructuring": "all" 317 | } 318 | ], 319 | "prefer-reflect": 0, 320 | "prefer-rest-params": 1, 321 | "prefer-spread": 1, 322 | "prefer-template": 1, 323 | "require-yield": 1, 324 | "sort-imports": 0, 325 | "template-curly-spacing": [2, "never"], 326 | "yield-star-spacing": 1, 327 | "react/jsx-uses-vars": 2, 328 | "react/jsx-uses-react": 2, 329 | "react/react-in-jsx-scope": 2, 330 | "import/no-duplicates": 2, 331 | "react-hooks/rules-of-hooks": 2, 332 | "react-hooks/exhaustive-deps": 0, 333 | "@typescript-eslint/ban-ts-comment": 0, 334 | "@typescript-eslint/explicit-function-return-type": 0, 335 | "@typescript-eslint/no-empty-function": 0, 336 | "@typescript-eslint/no-unused-vars": ["error"], 337 | "@typescript-eslint/no-use-before-define": ["error"], 338 | "@typescript-eslint/no-non-null-assertion": 0, 339 | "codegen/codegen": "error" 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish package 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 7 | 8 | jobs: 9 | create-release: 10 | name: Create Release 11 | runs-on: ubuntu-latest 12 | outputs: 13 | upload_url: ${{ steps.create_release.outputs.upload_url }} 14 | steps: 15 | - name: Create Release 16 | id: create_release 17 | uses: actions/create-release@v1 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 20 | with: 21 | tag_name: ${{ github.ref }} 22 | release_name: Release ${{ github.ref }} 23 | draft: false 24 | prerelease: false 25 | 26 | publish-npm-package: 27 | name: Publish to NPM 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout repository from GitHub 31 | uses: actions/checkout@v3 32 | - name: Setup npm 33 | uses: actions/setup-node@v3 34 | with: 35 | node-version: '16' 36 | registry-url: 'https://registry.npmjs.org' 37 | # npm cache folder is in ~/, not within the working directory 38 | - name: Cache npm directory 39 | uses: actions/cache@v3 40 | with: 41 | path: ~/.npm 42 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 43 | restore-keys: | 44 | ${{ runner.os }}-node- 45 | - name: Install dependencies 46 | run: npm install 47 | - name: Build library for examples 48 | run: npm run build -w library 49 | - name: Run tests 50 | run: npm run test 51 | - name: Publish 52 | run: npm publish -w library --access public 53 | env: 54 | NODE_AUTH_TOKEN: ${{ secrets.NPM_BOT_ACCESS_TOKEN }} 55 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: push 4 | 5 | jobs: 6 | create-release: 7 | name: Test 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 8 10 | steps: 11 | - name: Checkout repository from GitHub 12 | uses: actions/checkout@v3 13 | - name: Setup npm 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: '16' 17 | # npm cache folder is in ~/, not within the working directory 18 | - name: Cache npm directory 19 | uses: actions/cache@v3 20 | with: 21 | path: ~/.npm 22 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 23 | restore-keys: | 24 | ${{ runner.os }}-node- 25 | - name: Install dependencies 26 | run: npm install 27 | - name: Build library for examples 28 | run: npm run build -w library 29 | - name: Run tests 30 | run: npm run test 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .vscode 4 | .env 5 | .parcel-cache 6 | public 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "bracketSameLine": true, 10 | "arrowParens": "avoid" 11 | } 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Ubilabs Google Maps React Hooks 2 | 3 | We are happy you came here and thank you for contributing! 🎉 4 | 5 | To get started there are some things that need to be set up. 6 | 7 | --- 8 | 9 | #### Table of contents 10 | 11 | - [Setting Up Dev Environment](#setting-up-dev-environment) 12 | - [Fork the repo and start building](#fork-the-repo-and-start-building) 13 | - [Issue Tracker](#issue-tracker) 14 | - [Pull Requests / Merge Requests](#pull-requests--merge-requests) 15 | - [Code Style and Code Quality](#code-style-and-code-quality) 16 | - [Adding An Example](#adding-an-example) 17 | - [Run examples](#run-examples) 18 | - [Develop examples](#develop-examples) 19 | - [Publish library on npm](#publish-library-on-npm) 20 | 21 | --- 22 | 23 | ## Setting Up Dev Environment 24 | 25 | We will work on the `develop` branch. 26 | 27 | We use [npm](https://www.npmjs.com/) to manage the dependencies. 28 | 29 | ### Fork the repo and start building 30 | 31 | All new features and bug-fixes should be branched off and merged into the `develop` branch. 32 | 33 | #### Follow these steps to get started: 34 | 35 | 1. Fork the repo (click the Fork button at the top right of [Google Maps React Hooks](https://github.com/ubilabs/google-maps-react-hooks)). 36 | 37 | 2. Clone your fork locally 38 | 39 | ```bash 40 | # In a terminal, cd to parent directory where you want your clone to be, then 41 | git clone https://github.com//google-maps-react-hooks.git 42 | 43 | # cd to the google-maps-react-hooks directory 44 | cd google-maps-react-hooks 45 | 46 | # Checkout the develop branch 47 | git checkout develop 48 | ``` 49 | 50 | 3. At the root directory of the project on the develop branch, install all dependencies and build. 51 | 52 | ```bash 53 | # Install dependencies 54 | npm install 55 | 56 | # Start the project locally before changing code 57 | npm run start:library 58 | ``` 59 | 60 | 4. Create a new branch where to make code changes. 61 | 62 | ```bash 63 | # Make sure you are on the develop branch 64 | git checkout develop 65 | 66 | # Create a new branch 67 | git checkout -b feat/my-new-feature-branch 68 | ``` 69 | 70 | 5. To see example cases of the Google Maps react hooks, checkout the [Examples Folder](./examples). To start an example: 71 | 72 | ```bash 73 | # If not done already: Go to 'google-maps-react-hooks' directory and run: 74 | npm install 75 | 76 | # Run an example (look at the different examples for their start scripts): 77 | npm run start:sample-map 78 | ``` 79 | 80 | Then open [`localhost:1234`](http://localhost:1234) in a browser. 81 | 82 | --- 83 | 84 | ## Issue Tracker 85 | 86 | - before submitting a new issue, please: 87 | 88 | - check for existing related issues 89 | 90 | - check the issue tracker for a specific upstream project that may be more appropriate 91 | 92 | - check against supported versions of this project (i.e. the latest) 93 | 94 | - please keep discussions on-topic, and respect the opinions of others 95 | 96 | - please contact us privately to discuss security vulnerabilities 97 | 98 | --- 99 | 100 | ## Pull Requests / Merge Requests 101 | 102 | - **IMPORTANT**: by submitting a patch, you agree to allow the project owners to license your work under this [LICENSE.md](LICENSE.md) 103 | 104 | - please provide test cases for all features and bug fixes 105 | 106 | - provide documentation for all public API methods 107 | 108 | - commit messages should follow the format outlined in [CONVENTIONS.md](CONVENTIONS.md) 109 | 110 | ### Code Style and Code Quality 111 | 112 | - **Testing** 113 | 114 | - [ESLint](https://eslint.org/) configuration files are provided 115 | - [TypeScript](https://www.typescriptlang.org/) check for types and TypeScript setup 116 | - [Prettier](https://prettier.io/) code formatter 117 | 118 | Run `npm run test` before submitting a PR to ensure that your code uses correct style and passes all tests 119 | 120 | --- 121 | 122 | ## Adding An Example 123 | 124 | Each hook should have an example in the examples folder. 125 | 126 | ### Run examples 127 | 128 | To develop one of the examples, you have to create a `.env` file in the `/examples` directory first and add your [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) to it in the following format: 129 | 130 | ``` 131 | GOOGLE_MAPS_API_KEY="" 132 | ``` 133 | 134 | An example can be found in `/examples/.env.example`. 135 | 136 | Start the example locally with the appropriate task, e.g. `npm run start:map-example`. You can find the right task in the README of the example you want to start. 137 | 138 | The example runs on [localhost:1234](http://localhost:1234). 139 | 140 | ### Develop examples 141 | 142 | If you want to provide an example for a hook, please follow these steps: 143 | 144 | 1. Create a new folder in the [examples folder](./examples) with the new example's name. 145 | 146 | 2. In the folder, create a runnable React App showing the usage of the new hook in a common use case. Checkout the current examples in the [examples folder](./examples) to see how the setup should looks like. The [basic google map example](./examples/basic-google-map/) is always a good starting point. 147 | 148 | 3. Add a npm task in the [examples workspace package.json](./examples/package.json) with a naming convention like this: 149 | 150 | ```json 151 | "start:example-folder-name": "EXAMPLE_ENTRY=./example-folder-name/index.html npm run start:example" 152 | ``` 153 | 154 | Please compare to the other example start tasks. 155 | 156 | 4. Add another npm task in the root [package.json](./package.json) to start the example, with a naming convention like this: 157 | 158 | ```json 159 | "start:example-folder-name-example": "EXAMPLE=example-folder-name run-p start:library start:example" 160 | ``` 161 | 162 | Please compare to the other example start tasks. 163 | 164 | 5. Add a README to each example with an explanation of what the example does, a code snippet and an image of the example app in a ratio of 2:1. 165 | 166 | 6. Link the example in the [root README](./README.md) and the [README of the library workspace](./library/README.md) in the **Examples** overview of the **Table of contents** section. 167 | 168 | --- 169 | 170 | ## Publish library on npm 171 | 172 | A new library version is automatically published by Github Actions as soon as a new version tag is available. 173 | To trigger a new release, run: 174 | 175 | ```sh 176 | npm version [ | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git] -w library 177 | ``` 178 | 179 | **NOTE**: Make sure to not forget setting the context to the library workspace with `-w library` when running the command from project root. 180 | -------------------------------------------------------------------------------- /CONVENTIONS.md: -------------------------------------------------------------------------------- 1 | ## Git Commit Guidelines 2 | 3 | These rules are adopted from [the AngularJS commit conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/) and the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). 4 | 5 | ### Commit Message Format 6 | 7 | Each commit message starts with a **type**, a **scope**, and a **subject**. 8 | 9 | Below that, the commit message has a **body**. 10 | 11 | - **type**: what type of change this commit contains. 12 | - **scope**: what item of code this commit is changing. 13 | - **subject**: a short description of the changes. 14 | - **body** (optional): a more in-depth description of the changes 15 | 16 | ``` 17 | (): 18 | 19 | 20 | ``` 21 | 22 | Examples: 23 | 24 | ``` 25 | feat(ruler): add inches as well as centimeters 26 | ``` 27 | 28 | ``` 29 | fix(protractor): fix 90 degrees counting as 91 degrees 30 | ``` 31 | 32 | ``` 33 | refactor(pencil): use graphite instead of lead 34 | 35 | Closes #640. 36 | 37 | Graphite is a much more available resource than lead, so we use it to lower the price. 38 | ``` 39 | 40 | ``` 41 | fix(pen): use blue ink instead of red ink 42 | 43 | BREAKING CHANGE: Pen now uses blue ink instead of red. 44 | 45 | To migrate, change your code from the following: 46 | 47 | `pen.draw('blue')` 48 | 49 | To: 50 | 51 | `pen.draw('red')` 52 | ``` 53 | 54 | Any line of the commit message should not be longer 100 characters. This allows the message to be easier 55 | to read on github as well as in various git tools. 56 | 57 | ### Type 58 | 59 | Is recommended to be one of the below items. Only **feat** and **fix** show up in the changelog, in addition to breaking changes (see breaking changes section at bottom). 60 | 61 | - **feat**: A new feature 62 | - **fix**: A bug fix 63 | - **docs**: Documentation only changes 64 | - **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing 65 | semi-colons, etc) 66 | - **refactor**: A code change that neither fixes a bug or adds a feature 67 | - **test**: Adding missing tests 68 | - **chore**: Changes to the build process or auxiliary tools and libraries such as documentation 69 | generation 70 | - **build**: Changes that affect the build system or external dependencies 71 | - **ci**: Changes to our CI configuration files and scripts 72 | - **perf**: A code change that improves performance 73 | 74 | ### Scope 75 | 76 | The scope could be anything specifying place of the commit change. For example `$location`, 77 | `$browser`, `$compile`, `$rootScope`, `ngHref`, `ngClick`, `ngView`, etc... 78 | 79 | ### Subject 80 | 81 | The subject contains succinct description of the change: 82 | 83 | - use the imperative, present tense: "change" not "changed" nor "changes" 84 | - don't capitalize first letter 85 | - no dot (.) at the end 86 | 87 | ### Breaking Changes 88 | 89 | Put any breaking changes with migration instructions in the commit body. 90 | 91 | If there is a breaking change, put **BREAKING CHANGE:** in your commit body, and it will show up in the changelog. 92 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ubilabs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google Maps React Hooks 2 | 3 | [![npm version](https://img.shields.io/npm/v/@ubilabs/google-maps-react-hooks)](https://www.npmjs.com/package/@ubilabs/google-maps-react-hooks) [![GitHub license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) 4 | 5 | ## Description 6 | 7 | This is a JavaScript library to easily implement a Google Maps map into your React application. It comes with a collection of React hooks to access the Google Maps map instance all over your components and to use some of the Google Maps [Services](https://developers.google.com/maps/documentation/javascript#services) or [Libraries](https://developers.google.com/maps/documentation/javascript#libraries). 8 | 9 | #### Table of contents 10 | 11 | - [Requirements](#requirements) 12 | - [Installation](#installation) 13 | - [Library](#library) 14 | - [Basic Google Map Setup](#basic-google-map-setup) 15 | - [Hooks](#hooks) 16 | - [Hooks Overview](#hooks-overview) 17 | - [Hooks Example Setup](#hooks-example-setup) 18 | - [Examples](#examples) 19 | - [Examples Overview](#examples-overview) 20 | - [Development](#development-only-for-maintainers) 21 | - [Contribution](#contribution) 22 | - [Quick Start](#quick-start) 23 | 24 | ## Requirements 25 | 26 | You need to have React [16.8.0](https://reactjs.org/blog/2019/02/06/react-v16.8.0.html) or later installed to use the Hooks API. 27 | 28 | ## Installation 29 | 30 | ```sh 31 | npm install @ubilabs/google-maps-react-hooks -D 32 | ``` 33 | 34 | **NOTE FOR WINDOWS USERS**: 35 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 36 | 37 | ## Library 38 | 39 | The full Google Maps React Hooks library can be found in the [library directory](./library). 40 | 41 | ## Basic Google Map Setup 42 | 43 | Import the `GoogleMapsProvider` and wrap it around your components. 44 | Make sure all components that should have access to the Google Maps map instance are nested inside the `GoogleMapsProvider`. 45 | 46 | If you still can't see a map on your page, make sure that your map container has a `height` CSS property (by default it usually has no height) and that a `center` and `zoom` was set for your map. 47 | 48 | ```tsx 49 | import React, {useState, useCallback, forwardRef} from 'react'; 50 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 51 | 52 | function App() { 53 | const [mapContainer, setMapContainer] = useState(null); 54 | const mapRef = useCallback(node => { 55 | node && setMapContainer(node); 56 | }, []); 57 | 58 | const mapOptions = { 59 | // Add your map options here 60 | // `center` and `zoom` are required for every map to be displayed 61 | center: {lat: 53.5582447, lng: 9.647645}, 62 | zoom: 6 63 | }; 64 | 65 | return ( 66 | 70 | 71 |
72 | 73 | 74 | ); 75 | } 76 | 77 | export default App; 78 | ``` 79 | 80 | The `GoogleMapsProvider` makes the Google Maps map instance available to any nested components with the `useGoogleMap` hook. 81 | 82 | ```tsx 83 | import React from 'react'; 84 | import {useGoogleMap} from '@ubilabs/google-maps-react-hooks'; 85 | 86 | const MyComponent = () => { 87 | const map = useGoogleMap(); 88 | 89 | // Do something with the Google Maps map instance 90 | 91 | return (...); 92 | }; 93 | ``` 94 | 95 | ## Hooks 96 | 97 | All hooks can be found [here](./library/src/hooks/). Please checkout the [documentation](./library/docs) for each hook and have a look at the [examples directory](./examples) to see how each hook can be implemented. 98 | 99 | ### Hooks Overview 100 | 101 | - [useGoogleMap](./library/docs/useGoogleMap.md) 102 | - [useDirectionsService](./library/docs/useDirectionsService.md) 103 | - [useDistanceMatrixService](./library/docs/useDistanceMatrixService.md) 104 | - [useElevationService](./library/docs/useElevationService.md) 105 | - [useGeocodingService](./library/docs/useGeocodingService.md) 106 | - [useMaxZoomService](./library/docs/useMaxZoomService.md) 107 | - [usePlacesService](./library/docs/usePlacesService.md) 108 | - [useAutocomplete](./library/docs/useAutocomplete.md) 109 | - [useAutocompleteService](./library/docs/useAutocompleteService.md) 110 | 111 | ### Hooks Example Setup 112 | 113 | **useGeocodingService** 114 | 115 | ```tsx 116 | import React from 'react'; 117 | import {useGeocodingService} from '@ubilabs/google-maps-react-hooks'; 118 | 119 | const MyComponent = () => { 120 | const geocoder = useGeocodingService(); 121 | 122 | // Do something with the geocoder 123 | 124 | return (...); 125 | }; 126 | ``` 127 | 128 | **useAutocomplete** 129 | 130 | ```tsx 131 | import React, {useRef, useState} from 'react'; 132 | import {useAutocomplete} from '@ubilabs/google-maps-react-hooks'; 133 | 134 | const MyComponent = () => { 135 | const inputRef = useRef(null); 136 | const [inputValue, setInputValue] = useState(''); 137 | 138 | const onPlaceChanged = place => { 139 | if (place) { 140 | setInputValue(place.formatted_address || place.name); 141 | } 142 | 143 | // Keep focus on input element 144 | inputRef.current && inputRef.current.focus(); 145 | }; 146 | 147 | useAutocomplete({ 148 | inputField: inputRef && inputRef.current, 149 | onPlaceChanged 150 | }); 151 | 152 | const handleInputChange = event => { 153 | setInputValue(event.target.value); 154 | }; 155 | 156 | return ( 157 | 158 | ); 159 | }; 160 | ``` 161 | 162 | ## Examples 163 | 164 | Explore our [examples directory on GitHub](./examples) for full implementation examples. 165 | 166 | ### Examples Overview 167 | 168 | - [Basic Google Map](./examples/basic-google-map) 169 | - [Google Map with Markers](./examples/google-map-with-markers) 170 | - [Multiple Google Maps](./examples/multiple-google-maps) 171 | - [Directions Service](./examples/directions-service) 172 | - [Distance Matrix Service](./examples/distance-matrix-service) 173 | - [Elevation Service](./examples/elevation-service) 174 | - [Geocoding Service](./examples/geocoding-service) 175 | - [Maximum Zoom Imagery Service](./examples/max-zoom-service) 176 | - [Places Autocomplete Service](./examples/places-autocomplete-service) 177 | - [Places Autocomplete Widget](./examples/places-autocomplete-widget) 178 | - [Places Service](./examples/places-service) 179 | - [Places Service With Element](./examples/places-service-with-element) 180 | - [Street View Panorama Map](./examples/street-view-panorama-map) 181 | - [Street View Panorama With Element](./examples/street-view-panorama-with-element) 182 | 183 | ## Development (only for Maintainers) 184 | 185 | ### Contribution 186 | 187 | We are happy about your contribution. Please checkout the following guide to get started: 188 | [Contribution Guide](./CONTRIBUTING.md). 189 | 190 | Also, make sure to follow our [Coding Conventions](./CONVENTIONS.md) when making commits. 191 | 192 | ### Quick Start 193 | 194 | Clone the repository and run 195 | 196 | ```sh 197 | npm install 198 | ``` 199 | 200 | in the project root to install all dependencies. 201 | 202 | To develop the Google Maps React Hooks library, start the project locally with 203 | 204 | ```sh 205 | npm run start:library 206 | ``` 207 | -------------------------------------------------------------------------------- /examples/.env.example: -------------------------------------------------------------------------------- 1 | GOOGLE_MAPS_API_KEY="..." -------------------------------------------------------------------------------- /examples/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public 3 | .env 4 | .env.example 5 | -------------------------------------------------------------------------------- /examples/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ubilabs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Google Maps React Hooks Examples 2 | 3 | Here you can find some implementation examples for the [Google Maps React Hooks library](../library). 4 | 5 | - [Basic Google Map](./basic-google-map) 6 | - [Google Map with Markers](./google-map-with-markers) 7 | - [Multiple Google Maps](./multiple-google-maps) 8 | - [Directions Service](./directions-service) 9 | - [Distance Matrix Service](./distance-matrix-service) 10 | - [Elevation Service](./elevation-service) 11 | - [Geocoding Service](./geocoding-service) 12 | - [Maximum Zoom Imagery Service](./max-zoom-service) 13 | - [Places Autocomplete Service](./places-autocomplete-service) 14 | - [Places Autocomplete Widget](./places-autocomplete-widget) 15 | - [Places Service](./places-service) 16 | - [Places Service With Element](./places-service-with-element) 17 | - [Street View Panorama Map](./street-view-panorama-map) 18 | - [Street View Panorama With Element](./street-view-panorama-with-element) 19 | -------------------------------------------------------------------------------- /examples/basic-google-map/README.md: -------------------------------------------------------------------------------- 1 | # Basic Google Maps Setup Example 2 | 3 | This is an example setup to show a **basic Google Map** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:map-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show a basic Google Map. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/194873046-3e731074-c7f5-4ff7-8b7d-69f7895ce50a.png) 40 | -------------------------------------------------------------------------------- /examples/basic-google-map/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | 6 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 7 | 8 | import './main.module.css'; 9 | 10 | const mapOptions = { 11 | center: {lat: 53.5582447, lng: 9.647645}, 12 | zoom: 6, 13 | disableDefaultUI: true, 14 | zoomControl: true, 15 | zoomControlOptions: { 16 | position: 3 // Right top 17 | } 18 | }; 19 | 20 | const App: FunctionComponent> = () => { 21 | const [mapContainer, setMapContainer] = useState(null); 22 | 23 | const mapRef = useCallback( 24 | (node: React.SetStateAction) => { 25 | node && setMapContainer(node); 26 | }, 27 | [] 28 | ); 29 | 30 | return ( 31 | 35 | 36 |
37 | 38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /examples/basic-google-map/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/basic-google-map/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/basic-google-map/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | Basic Google Maps setup with the Google Maps React Hooks. 9 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/basic-google-map/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/basic-google-map/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/constants.ts: -------------------------------------------------------------------------------- 1 | if (!process.env.GOOGLE_MAPS_API_KEY) { 2 | throw new Error('No GOOGLE_MAPS_API_KEY provided'); 3 | } 4 | 5 | export const GOOGLE_MAPS_API_KEY = process.env.GOOGLE_MAPS_API_KEY as string; 6 | -------------------------------------------------------------------------------- /examples/directions-service/README.md: -------------------------------------------------------------------------------- 1 | # `useDirectionsService` Hook Setup Example 2 | 3 | This is an example setup to show the usage of the **useDirectionsService hook with findAndRenderRoute** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | Go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:directions-service-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show a Google Map with a route from Berlin to Munich, retrieved from directions. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/196410364-de14d9a0-5ecf-430d-846e-bc04e405b889.png) 40 | -------------------------------------------------------------------------------- /examples/directions-service/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import DirectionsService from './components/directions-service/directions-service'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 51.08998021141488, lng: 10.627828045134935}, 13 | zoom: 6, 14 | disableDefaultUI: true, 15 | zoomControl: false, 16 | clickableIcons: false 17 | }; 18 | 19 | const App: FunctionComponent> = () => { 20 | const [mapContainer, setMapContainer] = useState(null); 21 | 22 | const mapRef = useCallback( 23 | (node: React.SetStateAction) => { 24 | node && setMapContainer(node); 25 | }, 26 | [] 27 | ); 28 | 29 | return ( 30 | 34 | 35 |
36 | 37 | 38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /examples/directions-service/components/directions-service/directions-service.tsx: -------------------------------------------------------------------------------- 1 | import {useEffect} from 'react'; 2 | import {useDirectionsService} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | const DirectionsService = () => { 5 | const directionsOptions = { 6 | renderOnMap: true, 7 | renderOptions: { 8 | suppressMarkers: true, 9 | polylineOptions: {strokeColor: '#FB2576', strokeWeight: 4} 10 | } 11 | }; 12 | 13 | // Use findAndRenderRoute to get directions and render a route to the map 14 | const {findAndRenderRoute, directionsRenderer} = 15 | useDirectionsService(directionsOptions); 16 | 17 | useEffect(() => { 18 | if (!findAndRenderRoute) { 19 | return () => {}; 20 | } 21 | 22 | // Form Request to pass to findAndRenderRoute 23 | // https://developers.google.com/maps/documentation/javascript/directions#DirectionsRequests 24 | const request = { 25 | travelMode: google.maps.TravelMode.DRIVING, 26 | origin: 'Berlin', 27 | destination: 'München', 28 | drivingOptions: { 29 | departureTime: new Date(), 30 | trafficModel: google.maps.TrafficModel.BEST_GUESS 31 | } 32 | }; 33 | 34 | findAndRenderRoute(request) 35 | .then((result: google.maps.DirectionsResult) => { 36 | // eslint-disable-next-line no-console 37 | console.log(result); 38 | }) 39 | .catch((errorStatus: google.maps.DirectionsStatus) => { 40 | console.error(errorStatus); 41 | }); 42 | 43 | return () => { 44 | if (directionsRenderer) { 45 | directionsRenderer.setMap(null); 46 | } 47 | }; 48 | }, [findAndRenderRoute]); 49 | 50 | return null; 51 | }; 52 | 53 | export default DirectionsService; 54 | -------------------------------------------------------------------------------- /examples/directions-service/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/directions-service/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/directions-service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Example of the usage of the useDirectionsService hook with the Google Maps 10 | React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/directions-service/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/directions-service/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/README.md: -------------------------------------------------------------------------------- 1 | # `useDistanceMatrixService` Hook Setup Example 2 | 3 | This is an example setup to show the usage of the **useDistanceMatrixService hook** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run: 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:distance-matrix-service-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show Google Map with a origin and different destinations. A side panel will show the different durations and distances to the destinations. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/197476221-68f06a3c-f76d-40b5-ab81-f9dbd8d4fbe5.png) 40 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import DistanceMatrixService from './components/distance-matrix-service/distance-matrix-service'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 48.8589466, lng: 2.2769956}, 13 | zoom: 6, 14 | disableDefaultUI: true, 15 | zoomControl: true 16 | }; 17 | 18 | const App: FunctionComponent> = () => { 19 | const [mapContainer, setMapContainer] = useState(null); 20 | 21 | const mapRef = useCallback( 22 | (node: React.SetStateAction) => { 23 | node && setMapContainer(node); 24 | }, 25 | [] 26 | ); 27 | 28 | return ( 29 | 34 | 35 |
36 | 37 | 38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/components/distance-matrix-service/distance-matrix-service.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | 3 | import { 4 | useDistanceMatrixService, 5 | useGeocodingService, 6 | useGoogleMap 7 | } from '@ubilabs/google-maps-react-hooks'; 8 | 9 | import {getOriginMarkerIcon} from '../../libs/marker-icon-helpers'; 10 | 11 | import styles from './distance-matrix.module.css'; 12 | 13 | const DistanceMatrixService = () => { 14 | const map = useGoogleMap(); 15 | const geocoder = useGeocodingService(); 16 | 17 | // Get the service from the useDistanceMatrix hook 18 | const service = useDistanceMatrixService(); 19 | 20 | const [elements, setElements] = useState< 21 | google.maps.DistanceMatrixResponseElement[] 22 | >([]); 23 | const [destinationList, setDestinationList] = useState(['']); 24 | 25 | useEffect(() => { 26 | if (!map || !geocoder || !service) { 27 | return () => {}; 28 | } 29 | 30 | const markers: Array = []; 31 | 32 | const bounds = new google.maps.LatLngBounds(); 33 | 34 | // Distance Matrix request 35 | // https://developers.google.com/maps/documentation/distance-matrix/distance-matrix#required-parameters 36 | const request = { 37 | origins: ['Invalides'], 38 | destinations: [ 39 | 'Palais Garnier', 40 | {lat: 48.87486838960709, lng: 2.293475122446004}, // Arc de Triomphe 41 | {lat: 48.85680070130583, lng: 2.2917585087353705}, // Eiffel Tower 42 | 'Louvre Museum', 43 | {lat: 48.846182892343855, lng: 2.3604230571607023} // Jardin de Plantes 44 | ], 45 | travelMode: google.maps.TravelMode.DRIVING, 46 | unitSystem: google.maps.UnitSystem.METRIC, 47 | avoidHighways: false, 48 | avoidTolls: false 49 | }; 50 | 51 | // Function to create a marker 52 | const createMarker = ( 53 | position: google.maps.LatLng | google.maps.LatLngLiteral, 54 | icon?: google.maps.Icon 55 | ) => { 56 | const marker = new google.maps.Marker({ 57 | map, 58 | position, 59 | icon 60 | }); 61 | markers.push(marker); 62 | }; 63 | 64 | // Get distance matrix response 65 | service.getDistanceMatrix(request, response => { 66 | if (!response) { 67 | return; 68 | } 69 | 70 | const origins: Array = response.originAddresses; 71 | const destinations: Array = response.destinationAddresses; 72 | const responseElements = response.rows[0].elements; 73 | 74 | setDestinationList(destinations); 75 | setElements(responseElements); 76 | 77 | // Geocode the response to set a marker at the positions of the origin and the destinations 78 | geocoder.geocode({address: origins[0]}, results => { 79 | if (!results) { 80 | return; 81 | } 82 | 83 | const position = results[0]?.geometry.location; 84 | 85 | // Add another marker icon for the origin 86 | createMarker(position, getOriginMarkerIcon()); 87 | 88 | map.fitBounds(bounds.extend(position)); 89 | }); 90 | 91 | destinations.forEach(destination => { 92 | geocoder.geocode({address: destination}, results => { 93 | if (!results) { 94 | return; 95 | } 96 | 97 | const position = results[0]?.geometry.location; 98 | 99 | createMarker(position); 100 | 101 | map.fitBounds(bounds.extend(position)); 102 | }); 103 | }); 104 | }); 105 | 106 | // Clean up markers 107 | return () => { 108 | markers.forEach(marker => marker.setMap(null)); 109 | }; 110 | }, [map, Boolean(geocoder), Boolean(service)]); 111 | 112 | return ( 113 |
114 | {destinationList.map((destination, index) => { 115 | const distance = elements[index]?.distance?.text; 116 | const duration = elements[index]?.duration?.text; 117 | 118 | return ( 119 |
120 |

{destination}

121 |

{distance}

122 |

{duration}

123 |
124 | ); 125 | })} 126 |
127 | ); 128 | }; 129 | 130 | export default DistanceMatrixService; 131 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/components/distance-matrix-service/distance-matrix.module.css: -------------------------------------------------------------------------------- 1 | .info { 2 | font-family: sans-serif; 3 | width: 250px; 4 | padding: 1.5rem 2rem; 5 | background: #fff; 6 | border-radius: 20px; 7 | position: absolute; 8 | top: 20px; 9 | right: 20px; 10 | } 11 | 12 | .info p { 13 | margin: 0.5rem 0; 14 | } 15 | 16 | .info h3 { 17 | margin-bottom: 0.5rem; 18 | } 19 | 20 | .info h3:first-child { 21 | margin: 0.5rem 0; 22 | } 23 | 24 | .info p:last-child { 25 | margin-bottom: 1rem; 26 | } 27 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/components/icons/origin-marker.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const OriginMarkerIcon = () => ( 4 | 5 | 12 | 13 | 20 | 21 | ); 22 | 23 | export default OriginMarkerIcon; 24 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Google Maps with Distance Matrix Service API example with the Google Maps 10 | React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/libs/marker-icon-helpers.tsx: -------------------------------------------------------------------------------- 1 | import React, {ReactElement} from 'react'; 2 | import * as ReactDOMServer from 'react-dom/server'; 3 | 4 | import OriginMarkerIcon from '../components/icons/origin-marker'; 5 | 6 | const size: [number, number] = [30, 30]; // [width, height] 7 | 8 | /** 9 | * Returns an image url string for the svg element 10 | */ 11 | export const encodeSvgToDataUrl = (svgElement: ReactElement): string => 12 | `data:image/svg+xml;charset=utf-8,${encodeURIComponent( 13 | ReactDOMServer.renderToStaticMarkup(svgElement) 14 | )}`; 15 | 16 | /** 17 | * Gets the origin marker icon svg to use for the google.maps.Marker 18 | * */ 19 | export const getOriginMarkerIcon = () => ({ 20 | url: encodeSvgToDataUrl(), 21 | scaledSize: new google.maps.Size(...size) 22 | }); 23 | -------------------------------------------------------------------------------- /examples/distance-matrix-service/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/elevation-service/README.md: -------------------------------------------------------------------------------- 1 | # `useElevationService` Hook Setup Example 2 | 3 | This is an example setup to show the usage of the **useElevationService hook** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:elevation-service-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | `GOOGLE_MAPS_API_KEY=""` 27 | 28 | An example can be found in `.env.example`. 29 | 30 | **NOTE FOR WINDOWS USERS**: 31 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 32 | 33 | ## Output 34 | 35 | The project will start at [localhost:1234](http://localhost:1234) and show an infowindow on a map asking to click somewhere on the map to see the elevation of that position. 36 | 37 | ![image](https://user-images.githubusercontent.com/39244966/197743973-de8d7a95-0e8e-4564-b9de-68913e17d6ca.png) 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/197744010-bd381b26-432e-497f-a0c5-0fd27f107f81.png) 40 | -------------------------------------------------------------------------------- /examples/elevation-service/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import ElevationService from './components/elevation-service/elevation-service'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 53.5582447, lng: 9.647645}, 13 | zoom: 8, 14 | disableDefaultUI: true, 15 | zoomControl: false 16 | }; 17 | 18 | const App: FunctionComponent> = () => { 19 | const [mapContainer, setMapContainer] = useState(null); 20 | 21 | const mapRef = useCallback( 22 | (node: React.SetStateAction) => { 23 | node && setMapContainer(node); 24 | }, 25 | [] 26 | ); 27 | 28 | return ( 29 | 33 | 34 |
35 | 36 | 37 |
38 |
39 |
40 | ); 41 | }; 42 | 43 | export default App; 44 | -------------------------------------------------------------------------------- /examples/elevation-service/components/elevation-service/elevation-service.tsx: -------------------------------------------------------------------------------- 1 | import {useEffect} from 'react'; 2 | import { 3 | useElevationService, 4 | useGoogleMap 5 | } from '@ubilabs/google-maps-react-hooks'; 6 | 7 | const ElevationService = () => { 8 | const map = useGoogleMap(); 9 | 10 | // Get the elevator from the useElevationService hook 11 | const elevator = useElevationService(); 12 | 13 | useEffect(() => { 14 | if (!map || !elevator) { 15 | return () => {}; 16 | } 17 | 18 | const initialPosition = {lat: 51.08998021141488, lng: 10.627828045134935}; 19 | 20 | // Create a new InfoWindow 21 | const infoWindow = new google.maps.InfoWindow({ 22 | content: 'Click somewhere on the map to see the elevation', 23 | position: initialPosition 24 | }); 25 | 26 | map.setCenter(initialPosition); 27 | 28 | infoWindow.open(map); 29 | 30 | // Click on the map and open an infowindow with the elevation. 31 | const clickListener = map.addListener( 32 | 'click', 33 | (mapsMouseEvent: google.maps.MapMouseEvent) => { 34 | const {latLng} = mapsMouseEvent; 35 | 36 | if (!latLng) { 37 | return; 38 | } 39 | 40 | // Update infowindow with new position and elevation info 41 | infoWindow.setPosition(latLng); 42 | 43 | // Retrieve elevation info from elevator 44 | elevator.getElevationForLocations( 45 | {locations: [latLng]}, 46 | ( 47 | results: google.maps.ElevationResult[] | null, 48 | status: google.maps.ElevationStatus 49 | ) => { 50 | if (status !== google.maps.ElevationStatus.OK || !results) { 51 | console.error(status); 52 | 53 | return; 54 | } 55 | 56 | const {location, elevation} = results[0]; 57 | 58 | if (!location || !elevation) { 59 | return; 60 | } 61 | 62 | // eslint-disable-next-line no-console 63 | console.log(results); 64 | 65 | map.setCenter(location); 66 | 67 | infoWindow.setPosition(location); 68 | infoWindow.setContent(`Elevation: ${elevation}`); 69 | } 70 | ); 71 | } 72 | ); 73 | 74 | // Clean up click listener and remove the infowindow from the map 75 | return () => { 76 | google.maps.event.removeListener(clickListener); 77 | infoWindow.close(); 78 | }; 79 | }, [map, elevator]); 80 | 81 | return null; 82 | }; 83 | 84 | export default ElevationService; 85 | -------------------------------------------------------------------------------- /examples/elevation-service/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/elevation-service/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/elevation-service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Example of the usage of the useElevationService hook with the Google Maps 10 | React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/elevation-service/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/elevation-service/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/geocoding-service/README.md: -------------------------------------------------------------------------------- 1 | # `useGeocodingService` Hook Setup Example 2 | 3 | This is an example setup to show the usage of the **useGeocodingService hook** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:geocoding-service-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show a Google Map where the user can click on the map and the coordinates will be reversed geocoded into a human readable address. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/196205574-b0c318a8-1e55-4c52-a18f-43fc13d2d903.png) 40 | -------------------------------------------------------------------------------- /examples/geocoding-service/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import GeocodingService from './components/geocoding-service/geocoding-service'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 51.08998021141488, lng: 10.627828045134935}, 13 | zoom: 8, 14 | disableDefaultUI: true, 15 | zoomControl: false, 16 | clickableIcons: false 17 | }; 18 | 19 | const App: FunctionComponent> = () => { 20 | const [mapContainer, setMapContainer] = useState(null); 21 | 22 | const mapRef = useCallback( 23 | (node: React.SetStateAction) => { 24 | node && setMapContainer(node); 25 | }, 26 | [] 27 | ); 28 | 29 | return ( 30 | 34 | 35 |
36 | 37 | 38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /examples/geocoding-service/components/geocoding-service/geocoding-service.tsx: -------------------------------------------------------------------------------- 1 | import {useEffect, useState} from 'react'; 2 | import { 3 | useGeocodingService, 4 | useGoogleMap 5 | } from '@ubilabs/google-maps-react-hooks'; 6 | 7 | const initialPosition = {lat: 51.08998021141488, lng: 10.627828045134935}; 8 | 9 | const GeocodingService = () => { 10 | const map = useGoogleMap(); 11 | 12 | // Get the geocoder from the useGeocoder hook 13 | const geocoder = useGeocodingService(); 14 | 15 | const [marker, setMarker] = useState(null); 16 | const [infoWindow, setInfoWindow] = useState( 17 | null 18 | ); 19 | 20 | // Add marker and info window to the map 21 | useEffect(() => { 22 | if (!map) { 23 | return () => {}; 24 | } 25 | 26 | // Add a marker 27 | const newMarker = new google.maps.Marker({ 28 | map, 29 | position: initialPosition 30 | }); 31 | 32 | setMarker(newMarker); 33 | 34 | // Add an infowindow 35 | const newInfoWindow = new google.maps.InfoWindow({ 36 | content: 37 | 'Click somewhere on the map to reverse geocode the position to an address.', 38 | position: initialPosition 39 | }); 40 | 41 | setInfoWindow(newInfoWindow); 42 | newInfoWindow.open(map, newMarker); 43 | 44 | // Remove infowindow and marker from the map 45 | return () => { 46 | newInfoWindow?.close(); 47 | newMarker?.setMap(null); 48 | }; 49 | }, [map]); 50 | 51 | // Run geocoder on click on the map 52 | useEffect(() => { 53 | if (!map || !marker || !infoWindow || !geocoder) { 54 | return () => {}; 55 | } 56 | 57 | // Click on the map and open an infowindow with the reversed geocoded address. 58 | const clickListener = map.addListener( 59 | 'click', 60 | (mapsMouseEvent: google.maps.MapMouseEvent) => { 61 | // Use the geocoder to reverse geocode the position from the map 62 | // and add the address as content of the infowindow 63 | geocoder?.geocode( 64 | {location: mapsMouseEvent.latLng}, 65 | ( 66 | results: google.maps.GeocoderResult[] | null, 67 | status: google.maps.GeocoderStatus 68 | ) => { 69 | if (status !== 'OK' || !results) { 70 | console.error( 71 | `Geocoding was not successful for the following reason: ${status}` 72 | ); 73 | 74 | return; 75 | } 76 | 77 | const position = results[0].geometry.location; 78 | const formattedAddress = results[0].formatted_address; 79 | 80 | if (!position || !formattedAddress) { 81 | return; 82 | } 83 | 84 | marker.setPosition(position); 85 | 86 | infoWindow.setPosition(position); 87 | infoWindow.setContent(formattedAddress); 88 | 89 | map.setCenter(results[0].geometry.location); 90 | } 91 | ); 92 | } 93 | ); 94 | 95 | // Clean up click listener 96 | return () => { 97 | google.maps.event.removeListener(clickListener); 98 | }; 99 | }, [map, infoWindow, marker, geocoder]); 100 | 101 | return null; 102 | }; 103 | 104 | export default GeocodingService; 105 | -------------------------------------------------------------------------------- /examples/geocoding-service/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/geocoding-service/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/geocoding-service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Example of the usage of the useGeocodingService hook with the Google Maps 10 | React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/geocoding-service/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/geocoding-service/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/google-map-with-markers/README.md: -------------------------------------------------------------------------------- 1 | # Google Map With Markers Setup Example 2 | 3 | This is an example setup to show a **Google Map with markers** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:map-with-markers-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show a Google Map with markers. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/194924334-46e612a6-b312-4a98-a36b-473558ca7bf9.png) 40 | -------------------------------------------------------------------------------- /examples/google-map-with-markers/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import MapMarkers from './components/map-markers/map-markers'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 53.5582447, lng: 9.647645}, 13 | zoom: 6, 14 | disableDefaultUI: true, 15 | zoomControl: true, 16 | zoomControlOptions: { 17 | position: 3 // Right top 18 | } 19 | }; 20 | 21 | const App: FunctionComponent> = () => { 22 | const [mapContainer, setMapContainer] = useState(null); 23 | 24 | const mapRef = useCallback( 25 | (node: React.SetStateAction) => { 26 | node && setMapContainer(node); 27 | }, 28 | [] 29 | ); 30 | 31 | return ( 32 | 36 | 37 |
38 | 39 | 40 |
41 |
42 |
43 | ); 44 | }; 45 | 46 | export default App; 47 | -------------------------------------------------------------------------------- /examples/google-map-with-markers/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/google-map-with-markers/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/google-map-with-markers/components/map-markers/map-markers.tsx: -------------------------------------------------------------------------------- 1 | import {FunctionComponent, useState, useEffect} from 'react'; 2 | import {useGoogleMap} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | interface MuseumData { 5 | name: string; 6 | position: google.maps.LatLngLiteral; 7 | } 8 | 9 | const museums: MuseumData[] = [ 10 | { 11 | name: 'Hamburger Kunsthalle', 12 | position: {lat: 53.55674745629424, lng: 10.002396545723593} 13 | }, 14 | { 15 | name: 'German Historical Museum', 16 | position: {lat: 52.51823022299806, lng: 13.39694989857281} 17 | }, 18 | { 19 | name: 'The British Museum', 20 | position: {lat: 52.633392284804785, lng: -0.12003770548773834} 21 | }, 22 | { 23 | name: 'Louvre Museum', 24 | position: {lat: 48.86297399085735, lng: 2.3373843681422697} 25 | }, 26 | { 27 | name: 'Van Gough Museum', 28 | position: {lat: 52.37016007435205, lng: 4.901699141751193} 29 | }, 30 | { 31 | name: 'Museo Nacional del Prado', 32 | position: {lat: 40.414953005436075, lng: -3.6919729648241835} 33 | }, 34 | { 35 | name: 'National Archaeological Museum', 36 | position: {lat: 37.99043330407338, lng: 23.732574205404468} 37 | }, 38 | { 39 | name: 'Milan Natural History Museum', 40 | position: {lat: 45.47412946601384, lng: 9.20229385023787} 41 | }, 42 | { 43 | name: 'Museum National History', 44 | position: {lat: 48.206734688772926, lng: 16.359912051473867} 45 | } 46 | ]; 47 | 48 | /** 49 | * Component to render all map markers 50 | */ 51 | const MapMarkers: FunctionComponent> = () => { 52 | // Get the global map instance with the useGoogleMap hook 53 | const map = useGoogleMap(); 54 | 55 | const [, setMarkers] = useState>([]); 56 | 57 | // Add markers to the map 58 | useEffect(() => { 59 | if (!map) { 60 | return () => {}; 61 | } 62 | 63 | const initialBounds = new google.maps.LatLngBounds(); 64 | 65 | const museumMarkers: Array = museums.map(museum => { 66 | const {position, name} = museum; 67 | 68 | const markerOptions: google.maps.MarkerOptions = { 69 | map, 70 | position, 71 | title: name, 72 | clickable: false 73 | }; 74 | 75 | initialBounds.extend(position); 76 | 77 | return new google.maps.Marker(markerOptions); 78 | }); 79 | 80 | // Set the center of the map to fit markers 81 | map.setCenter(initialBounds.getCenter()); 82 | 83 | setMarkers(museumMarkers); 84 | 85 | // Clean up markers 86 | return () => { 87 | museumMarkers.forEach(marker => marker.setMap(null)); 88 | }; 89 | }, [map]); 90 | 91 | return null; 92 | }; 93 | 94 | export default MapMarkers; 95 | -------------------------------------------------------------------------------- /examples/google-map-with-markers/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Google Maps with markers setup with the Google Maps React Hooks. 10 | 11 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/google-map-with-markers/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/google-map-with-markers/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'google.maps'; 2 | 3 | /* eslint-disable init-declarations */ 4 | declare module '*.module.css' { 5 | const classes: {[key: string]: string}; 6 | export default classes; 7 | } 8 | -------------------------------------------------------------------------------- /examples/max-zoom-service/README.md: -------------------------------------------------------------------------------- 1 | # `useMaxZoomService` Hook Setup Example 2 | 3 | This is an example setup to show the usage of the **useMaxZoomService hook** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:max-zoom-service-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | `GOOGLE_MAPS_API_KEY=""` 27 | 28 | An example can be found in `.env.example`. 29 | 30 | **NOTE FOR WINDOWS USERS**: 31 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 32 | 33 | ## Output 34 | 35 | The project will start at [localhost:1234](http://localhost:1234) and show an infowindow on a map asking to click somewhere on the map to see the max zoom for map type imagery for a position. 36 | 37 | ![image](https://user-images.githubusercontent.com/39244966/197749024-734cf2e4-10c9-4433-92b7-f2e1ff88f028.png) 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/197749048-81987dd9-9cbd-49dd-a728-1fe5db2e1516.png) 40 | -------------------------------------------------------------------------------- /examples/max-zoom-service/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import MaxZoomService from './components/max-zoom-service/max-zoom-service'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 53.5582447, lng: 9.647645}, 13 | zoom: 6, 14 | disableDefaultUI: true, 15 | zoomControl: false, 16 | mapType: 'hybrid' 17 | }; 18 | 19 | const App: FunctionComponent> = () => { 20 | const [mapContainer, setMapContainer] = useState(null); 21 | 22 | const mapRef = useCallback( 23 | (node: React.SetStateAction) => { 24 | node && setMapContainer(node); 25 | }, 26 | [] 27 | ); 28 | 29 | return ( 30 | 34 | 35 |
36 | 37 | 38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /examples/max-zoom-service/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/max-zoom-service/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/max-zoom-service/components/max-zoom-service/max-zoom-service.tsx: -------------------------------------------------------------------------------- 1 | // Example from: https://developers.google.com/maps/documentation/javascript/examples/maxzoom-simple 2 | 3 | import {useEffect} from 'react'; 4 | import { 5 | useMaxZoomService, 6 | useGoogleMap 7 | } from '@ubilabs/google-maps-react-hooks'; 8 | 9 | const MaxZoomService = () => { 10 | const map = useGoogleMap(); 11 | 12 | // Get max zoom service from hook 13 | const maxZoomService = useMaxZoomService(); 14 | 15 | useEffect(() => { 16 | if (!map || !maxZoomService) { 17 | return () => {}; 18 | } 19 | 20 | // Create new infoWindow 21 | const initialPosition = {lat: 51.08998021141488, lng: 10.627828045134935}; 22 | const infoWindow = new google.maps.InfoWindow({ 23 | content: 24 | 'Click somewhere on the map to see the max zoom at the position for map type imagery.', 25 | position: initialPosition 26 | }); 27 | 28 | map.setCenter(initialPosition); 29 | 30 | infoWindow.open(map); 31 | 32 | // Function to show the maximum zoom level of a location 33 | const showMaxZoomLevel = (event: google.maps.MapMouseEvent) => { 34 | const {latLng} = event; 35 | 36 | if (!latLng) { 37 | return; 38 | } 39 | 40 | maxZoomService.getMaxZoomAtLatLng( 41 | latLng, 42 | (result: google.maps.MaxZoomResult) => { 43 | if (result.status !== 'OK') { 44 | // eslint-disable-next-line no-console 45 | console.error(result.status); 46 | } else { 47 | infoWindow.setContent( 48 | `The maximum zoom at this location is: ${result.zoom}` 49 | ); 50 | 51 | infoWindow.setPosition(event.latLng); 52 | } 53 | } 54 | ); 55 | }; 56 | 57 | map.addListener('click', showMaxZoomLevel); 58 | 59 | // Clean up infoWindow 60 | return () => { 61 | if (map) { 62 | google.maps.event.clearListeners(map, 'click'); 63 | infoWindow.close(); 64 | } 65 | }; 66 | }, [map, maxZoomService]); 67 | 68 | return null; 69 | }; 70 | 71 | export default MaxZoomService; 72 | -------------------------------------------------------------------------------- /examples/max-zoom-service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Example of the usage of the useMaxZoomService hook with the Google Maps 10 | React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/max-zoom-service/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/max-zoom-service/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/multiple-google-maps/README.md: -------------------------------------------------------------------------------- 1 | # Multiple Google Maps Setup Example 2 | 3 | This is an example setup to show the usage of **multiple Google Maps instances** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:multiple-maps-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show multiple Google Map instances. 38 | 39 | ![image](https://user-images.githubusercontent.com/12370310/199680106-a523d143-f3e4-43e3-b32f-9e7049c1ac0e.png) 40 | -------------------------------------------------------------------------------- /examples/multiple-google-maps/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | 6 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 7 | 8 | import './main.module.css'; 9 | 10 | const basicMapOptions = { 11 | zoom: 10, 12 | disableDefaultUI: true, 13 | zoomControl: true, 14 | zoomControlOptions: { 15 | position: 3 // Right top 16 | } 17 | }; 18 | 19 | // The Google Maps API parameters must be the same for all `GoogleMapsProvider` components! 20 | const googleMapsAPIParameters = { 21 | googleMapsAPIKey: GOOGLE_MAPS_API_KEY, 22 | language: 'it', 23 | region: 'IT' 24 | }; 25 | 26 | const App: FunctionComponent> = () => { 27 | const [hamburgMapContainer, setHamburgMapContainer] = 28 | useState(null); 29 | const hamburgMapRef = useCallback( 30 | (node: React.SetStateAction) => { 31 | node && setHamburgMapContainer(node); 32 | }, 33 | [] 34 | ); 35 | const hamburgMapOptions = { 36 | ...basicMapOptions, 37 | center: {lat: 53.551086, lng: 9.993682} 38 | }; 39 | 40 | const [munichMapContainer, setMunichMapContainer] = 41 | useState(null); 42 | const munichMapRef = useCallback( 43 | (node: React.SetStateAction) => { 44 | node && setMunichMapContainer(node); 45 | }, 46 | [] 47 | ); 48 | const munichMapOptions = { 49 | ...basicMapOptions, 50 | center: {lat: 48.137154, lng: 11.576124} 51 | }; 52 | 53 | const [sanFranciscoMapContainer, setSanFranciscoMapContainer] = 54 | useState(null); 55 | const sanFranciscoMapRef = useCallback( 56 | (node: React.SetStateAction) => { 57 | node && setSanFranciscoMapContainer(node); 58 | }, 59 | [] 60 | ); 61 | const sanFranciscoMapOptions = { 62 | ...basicMapOptions, 63 | center: {lat: 37.773972, lng: -122.431297} 64 | }; 65 | 66 | return ( 67 |
68 | 72 | 73 | 74 | {/** The `useGoogleMap()` hook called inside this provider will return the map showing Hamburg. */} 75 | 76 | 77 | 78 | 82 | 83 | 84 | {/** The `useGoogleMap()` hook called inside this provider will return the map showing Munich. */} 85 | 86 | 87 | 88 | 92 | 93 | 94 | {/** The `useGoogleMap()` hook called inside this provider will return the map showing San Francisco. */} 95 | 96 | 97 |
98 | ); 99 | }; 100 | 101 | export default App; 102 | -------------------------------------------------------------------------------- /examples/multiple-google-maps/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/multiple-google-maps/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/multiple-google-maps/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Multiple Google Maps instances setup with the Google Maps React Hooks. 10 | 11 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/multiple-google-maps/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/multiple-google-maps/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | 11 | :global(#grid) { 12 | display: grid; 13 | grid-template-columns: repeat(3, calc(100% / 3)); 14 | height: 100%; 15 | margin: 0; 16 | padding: 0; 17 | } 18 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ubilabs/google-maps-react-hooks-examples", 3 | "description": "Sample Google Map setups with the @ubilabs/google-maps-react-hooks library", 4 | "main": "index.js", 5 | "scripts": { 6 | "linter": "eslint './**/*.{ts,tsx}'", 7 | "typecheck": "tsc --project tsconfig.json --noEmit", 8 | "formatter": "prettier --check .", 9 | "test": "npm run linter && npm run typecheck && npm run formatter", 10 | "clean-examples": "rm -rf ./public ../.parcel-cache", 11 | "start:example": "npm run clean-examples && cross-env $(cat .env) PARCEL_AUTOINSTALL=false parcel serve $EXAMPLE_ENTRY --dist-dir public --port 1234 --no-cache", 12 | "start:map": "cross-env EXAMPLE_ENTRY=./basic-google-map/index.html npm run start:example", 13 | "start:map-with-markers": "cross-env EXAMPLE_ENTRY=./google-map-with-markers/index.html npm run start:example", 14 | "start:multiple-maps": "cross-env EXAMPLE_ENTRY=./multiple-google-maps/index.html npm run start:example", 15 | "start:geocoding-service": "cross-env EXAMPLE_ENTRY=./geocoding-service/index.html npm run start:example", 16 | "start:places-service": "cross-env EXAMPLE_ENTRY=./places-service/index.html npm run start:example", 17 | "start:places-service-with-element": "cross-env EXAMPLE_ENTRY=./places-service-with-element/index.html npm run start:example", 18 | "start:places-autocomplete-widget": "cross-env EXAMPLE_ENTRY=./places-autocomplete-widget/index.html npm run start:example", 19 | "start:directions-service": "cross-env EXAMPLE_ENTRY=./directions-service/index.html npm run start:example", 20 | "start:distance-matrix-service": "cross-env EXAMPLE_ENTRY=./distance-matrix-service/index.html npm run start:example", 21 | "start:elevation-service": "cross-env EXAMPLE_ENTRY=./elevation-service/index.html npm run start:example", 22 | "start:max-zoom-service": "cross-env EXAMPLE_ENTRY=./max-zoom-service/index.html npm run start:example", 23 | "start:places-autocomplete-service": "cross-env EXAMPLE_ENTRY=./places-autocomplete-service/index.html npm run start:example", 24 | "start:street-view-panorama-map": "cross-env EXAMPLE_ENTRY=./street-view-panorama-map/index.html npm run start:example", 25 | "start:street-view-panorama-element": "cross-env EXAMPLE_ENTRY=./street-view-panorama-with-element/index.html npm run start:example", 26 | "preversion": "echo \"To create a new library version run 'npm version -w library' in the repository root.\"" 27 | }, 28 | "license": "MIT", 29 | "dependencies": { 30 | "@ubilabs/google-maps-react-hooks": "*", 31 | "react": "^18.2.0", 32 | "react-dom": "^18.2.0" 33 | }, 34 | "devDependencies": { 35 | "@types/google.maps": "^3.50.5", 36 | "@types/react": "^18.0.21", 37 | "@types/react-dom": "^18.0.6", 38 | "cross-env": "^7.0.3", 39 | "eslint": "^8.25.0", 40 | "eslint-config-prettier": "^8.3.0", 41 | "eslint-plugin-codegen": "^0.16.1", 42 | "eslint-plugin-import": "^2.23.4", 43 | "eslint-plugin-react": "^7.24.0", 44 | "eslint-plugin-react-hooks": "^4.2.0", 45 | "parcel": "^2.7.0", 46 | "postcss": "^8.4.18", 47 | "postcss-custom-media": "^8.0.2", 48 | "postcss-modules": "^5.0.0", 49 | "process": "^0.11.10", 50 | "typescript": "^4.8.4" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/README.md: -------------------------------------------------------------------------------- 1 | # `useAutocompleteService` Hook Setup Example 2 | 3 | This is an example setup of a **Places Autocomplete Service** to show the usage of the **useAutocompleteService hook** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run: 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:places-autocomplete-service-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show a map with an input field and an autocomplete functionality. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/199686376-1814625f-ac1d-4955-bdd9-0fd75bf4550d.png) 40 | 41 | ![image](https://user-images.githubusercontent.com/39244966/199686400-24e98a25-3ff0-426d-b9dd-274dc36b1765.png) 42 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import PlacesAutocompleteService from './components/places-autocomplete-service/places-autocomplete-service'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 53.5582447, lng: 9.647645}, 13 | zoom: 6, 14 | disableDefaultUI: true, 15 | zoomControl: false 16 | }; 17 | 18 | const App: FunctionComponent> = () => { 19 | const [mapContainer, setMapContainer] = useState(null); 20 | 21 | const mapRef = useCallback( 22 | (node: React.SetStateAction) => { 23 | node && setMapContainer(node); 24 | }, 25 | [] 26 | ); 27 | 28 | return ( 29 | 35 | 36 |
37 | 38 | 39 |
40 |
41 |
42 | ); 43 | }; 44 | 45 | export default App; 46 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/components/places-autocomplete-service/places-autocomplete-service.module.css: -------------------------------------------------------------------------------- 1 | .searchInput { 2 | position: absolute; 3 | top: 1rem; 4 | right: 1rem; 5 | height: 3rem; 6 | width: 20rem; 7 | margin: 0; 8 | padding: 0; 9 | padding-left: 0.5rem; 10 | font-family: sans-serif; 11 | border: 1px solid #f0f0f0; 12 | } 13 | 14 | /* Hide label visually. To use for A11y only. */ 15 | label { 16 | z-index: -1000; 17 | } 18 | 19 | .suggestions { 20 | position: absolute; 21 | top: 4.25rem; 22 | right: 1rem; 23 | width: 20.5rem; 24 | margin: 0; 25 | padding: 0; 26 | font-family: sans-serif; 27 | overflow: auto; 28 | background-color: white; 29 | font-size: 0.875rem; 30 | list-style: none; 31 | } 32 | 33 | .suggestions > li { 34 | display: flex; 35 | align-items: center; 36 | height: 2.125rem; 37 | padding: 0.5rem 0.5rem 0.5rem 1rem; 38 | outline: none; 39 | cursor: default; 40 | } 41 | 42 | .suggestions > li:hover, 43 | .suggestions > li:focus-visible { 44 | background-color: #f0f0f0; 45 | } 46 | 47 | .suggestions > li > span { 48 | overflow: hidden; 49 | white-space: nowrap; 50 | text-overflow: ellipsis; 51 | } 52 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/components/places-autocomplete-service/places-autocomplete-service.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | FunctionComponent, 3 | ChangeEvent, 4 | useState, 5 | useRef, 6 | useEffect 7 | } from 'react'; 8 | import { 9 | useAutocompleteService, 10 | useGoogleMap, 11 | usePlacesService 12 | } from '@ubilabs/google-maps-react-hooks'; 13 | 14 | import styles from './places-autocomplete-service.module.css'; 15 | 16 | export interface PlacesAutocompleteServiceSuggestion { 17 | id: string; 18 | label: string; 19 | } 20 | 21 | const maxNumberOfSuggestions = 5; 22 | 23 | const PlacesAutocompleteService: FunctionComponent< 24 | Record 25 | > = () => { 26 | const inputRef = useRef(null); 27 | const timeout = useRef(null); 28 | 29 | const [inputValue, setInputValue] = useState(''); 30 | const [suggestions, setSuggestions] = useState< 31 | Array 32 | >([]); 33 | const [suggestionsAreVisible, setSuggestionsAreVisible] = 34 | useState(false); 35 | 36 | const map = useGoogleMap(); 37 | const autocompleteService = useAutocompleteService(); 38 | const placesService = usePlacesService(); 39 | 40 | // Update the user input value 41 | const handleInputChange = (event: ChangeEvent) => { 42 | setInputValue(event.target.value); 43 | 44 | if (timeout.current) { 45 | clearTimeout(timeout.current); 46 | } 47 | 48 | // Show dropdown with a little delay 49 | timeout.current = setTimeout(() => { 50 | setSuggestionsAreVisible(true); 51 | }, 300); 52 | }; 53 | 54 | // Handle suggestion selection 55 | const selectSuggestion = ( 56 | suggestion: PlacesAutocompleteServiceSuggestion 57 | ) => { 58 | inputRef.current?.focus(); 59 | setInputValue(suggestion.label); 60 | 61 | // Close dropdown 62 | setSuggestionsAreVisible(false); 63 | 64 | // Get the location from Places Service of the selected place and zoom to it 65 | placesService?.getDetails( 66 | {placeId: suggestion.id}, 67 | ( 68 | placeResult: google.maps.places.PlaceResult | null, 69 | status: google.maps.places.PlacesServiceStatus 70 | ) => { 71 | if ( 72 | status !== google.maps.places.PlacesServiceStatus.OK || 73 | !placeResult 74 | ) { 75 | return; 76 | } 77 | 78 | // Get position of the suggestion to move map 79 | const position = placeResult.geometry?.location; 80 | 81 | if (map && position) { 82 | map.setZoom(14); 83 | map.panTo(position); 84 | } 85 | } 86 | ); 87 | }; 88 | 89 | // Update suggestions and get autocomplete place suggestions 90 | useEffect(() => { 91 | if (inputValue.length >= 2) { 92 | autocompleteService?.getPlacePredictions( 93 | { 94 | input: inputValue 95 | }, 96 | ( 97 | predictions: google.maps.places.AutocompletePrediction[] | null, 98 | status: google.maps.places.PlacesServiceStatus 99 | ) => { 100 | if ( 101 | status !== google.maps.places.PlacesServiceStatus.OK || 102 | !predictions 103 | ) { 104 | return; 105 | } 106 | 107 | const autocompleteSuggestions = predictions 108 | .slice(0, maxNumberOfSuggestions) 109 | .map(prediction => ({ 110 | id: prediction.place_id, 111 | label: prediction.description 112 | })); 113 | 114 | // Update suggestions for dropdown suggestions list 115 | setSuggestions(autocompleteSuggestions); 116 | } 117 | ); 118 | } else { 119 | setSuggestions([]); 120 | } 121 | }, [inputValue]); 122 | 123 | return ( 124 | <> 125 | 126 | 138 | 139 | {suggestionsAreVisible && ( 140 |
    145 | {suggestions.map(suggestion => ( 146 |
  • selectSuggestion(suggestion)} 149 | id={suggestion.id} 150 | role="option"> 151 | {suggestion.label} 152 |
  • 153 | ))} 154 |
155 | )} 156 | 157 | ); 158 | }; 159 | 160 | export default PlacesAutocompleteService; 161 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Example Google Map with a Places Autocomplete Search to show the usage of 10 | the useAutocompleteService hook of the Google Maps React Hooks. 11 | 12 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/places-autocomplete-service/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/README.md: -------------------------------------------------------------------------------- 1 | # `useAutocomplete` Hook Setup Example 2 | 3 | This is an example setup of a **Places Autocomplete Widget** to show the usage of the **useAutocomplete hook** with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run: 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:places-autocomplete-widget-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show a map with an input field and an autocomplete functionality. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/196221028-34d47ae1-b612-4886-bd51-f736ffb77197.png) 40 | 41 | ![image](https://user-images.githubusercontent.com/39244966/196220079-22405c58-c364-48bf-b70c-dc828e4bd635.png) 42 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import PlacesAutocompleteWidget from './components/places-autocomplete-widget/places-autocomplete-widget'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 53.5582447, lng: 9.647645}, 13 | zoom: 6, 14 | disableDefaultUI: true, 15 | zoomControl: true 16 | }; 17 | 18 | const App: FunctionComponent> = () => { 19 | const [mapContainer, setMapContainer] = useState(null); 20 | 21 | const mapRef = useCallback( 22 | (node: React.SetStateAction) => { 23 | node && setMapContainer(node); 24 | }, 25 | [] 26 | ); 27 | 28 | return ( 29 | 35 | 36 |
37 | 38 | 39 |
40 |
41 |
42 | ); 43 | }; 44 | 45 | export default App; 46 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/components/places-autocomplete-widget/places-autocomplete-widget.module.css: -------------------------------------------------------------------------------- 1 | .searchInput { 2 | position: absolute; 3 | top: 1rem; 4 | right: 1rem; 5 | width: 20rem; 6 | height: 2rem; 7 | padding-left: 0.5rem; 8 | } 9 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/components/places-autocomplete-widget/places-autocomplete-widget.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useRef, useState} from 'react'; 2 | import {useAutocomplete, useGoogleMap} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import styles from './places-autocomplete-widget.module.css'; 5 | 6 | const PlacesAutocompleteWidget = () => { 7 | const map = useGoogleMap(); 8 | 9 | // Use the input ref to pass an input field to the useAutocomplete hook below 10 | const inputRef = useRef(null); 11 | 12 | const [inputValue, setInputValue] = useState(''); 13 | const [selectedPlace, setSelectedPlace] = 14 | useState(null); 15 | 16 | const onPlaceChanged = (place: google.maps.places.PlaceResult) => { 17 | if (place) { 18 | setSelectedPlace(place); 19 | 20 | const formattedAddress = place.formatted_address; 21 | const {name} = place; 22 | 23 | if (!formattedAddress || !name) { 24 | return; 25 | } 26 | 27 | setInputValue(formattedAddress || name); 28 | 29 | // Keep focus on input element 30 | inputRef.current?.focus(); 31 | } 32 | }; 33 | 34 | // Use the useAutocomplete hook and pass the input field ref and the onPlaceChanged function to it 35 | useAutocomplete({ 36 | inputField: inputRef && inputRef.current, 37 | onPlaceChanged 38 | }); 39 | 40 | const handleInputChange = (event: React.ChangeEvent) => { 41 | setInputValue(event.target.value); 42 | }; 43 | 44 | // Add a marker whenever a place was selected 45 | useEffect(() => { 46 | if (!map || !selectedPlace) { 47 | return () => {}; 48 | } 49 | 50 | const markerOptions: google.maps.MarkerOptions = { 51 | map, 52 | position: selectedPlace.geometry?.location, 53 | title: selectedPlace.name, 54 | clickable: false 55 | }; 56 | 57 | const marker = new google.maps.Marker(markerOptions); 58 | 59 | // Clean up marker 60 | return () => { 61 | marker.setMap(null); 62 | }; 63 | }, [map, selectedPlace]); 64 | 65 | return ( 66 | 72 | ); 73 | }; 74 | 75 | export default PlacesAutocompleteWidget; 76 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Example Google Map with a Places Autocomplete Widget to show the usage of 10 | the useAutocomplete hook of the Google Maps React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/places-autocomplete-widget/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/places-service-with-element/README.md: -------------------------------------------------------------------------------- 1 | # `usePlacesService` Hook Setup Example 2 | 3 | This is an example setup to show a **Google Map with places in a div element** using the usePlacesService hook with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | Go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:places-service-with-element-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show a list of restaurants and their ratings close to Istanbul. Information is retrieved from Places Service. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/199743076-46093bd8-5da7-4377-80ba-ce492019fa42.png) 40 | -------------------------------------------------------------------------------- /examples/places-service-with-element/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import PlacesServiceElement from './components/places-service-with-element/places-service-with-element'; 5 | 6 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 7 | 8 | import './main.module.css'; 9 | 10 | const App: FunctionComponent> = () => ( 11 | 15 | 16 |
17 | 18 |
19 |
20 |
21 | ); 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /examples/places-service-with-element/components/places-service-with-element/places-service-with-element.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | font-family: sans-serif; 5 | overflow: scroll; 6 | height: 100%; 7 | } 8 | 9 | h1 { 10 | margin-left: 1.875rem; 11 | } 12 | 13 | .restaurantList { 14 | display: flex; 15 | flex-wrap: wrap; 16 | list-style-type: none; 17 | padding: 0; 18 | margin: 0; 19 | } 20 | 21 | .restaurantList li { 22 | border: 2px solid turquoise; 23 | width: 12.5rem; 24 | height: 9.375rem; 25 | padding: 0.625rem; 26 | margin: 1.25rem; 27 | } 28 | -------------------------------------------------------------------------------- /examples/places-service-with-element/components/places-service-with-element/places-service-with-element.tsx: -------------------------------------------------------------------------------- 1 | import React, {useCallback, useEffect, useState} from 'react'; 2 | 3 | import {usePlacesService} from '@ubilabs/google-maps-react-hooks'; 4 | 5 | import styles from './places-service-with-element.module.css'; 6 | 7 | const PlacesServiceElement = () => { 8 | const [divContainer, setDivContainer] = useState(null); 9 | 10 | const divRef = useCallback( 11 | (node: React.SetStateAction) => { 12 | node && setDivContainer(node); 13 | }, 14 | [] 15 | ); 16 | 17 | const [placeResults, setPlaceResults] = useState< 18 | google.maps.places.PlaceResult[] 19 | >([]); 20 | 21 | // Get the places service from the usePlacesService hook 22 | const service = usePlacesService({divElement: divContainer}); 23 | 24 | useEffect(() => { 25 | if (!service) { 26 | return; 27 | } 28 | 29 | const request = { 30 | location: {lat: 41.0864626164749, lng: 28.934162036450086}, 31 | radius: 800, 32 | type: 'cafe' 33 | }; 34 | 35 | function callback( 36 | results: google.maps.places.PlaceResult[] | null, 37 | status: google.maps.places.PlacesServiceStatus 38 | ) { 39 | if (status !== google.maps.places.PlacesServiceStatus.OK || !results) { 40 | console.error(status); 41 | 42 | return; 43 | } 44 | 45 | setPlaceResults(results); 46 | } 47 | 48 | service.nearbySearch(request, callback); 49 | }, [Boolean(service)]); 50 | 51 | return ( 52 | <> 53 |
54 |

Amazing restaurants in Istanbul

55 |
    56 | {placeResults.map((place, index) => { 57 | const name = place.name || 'N/A'; 58 | const rating = place.rating || 'N/A'; 59 | 60 | return ( 61 |
  • 62 |

    {name}

    63 |

    Rating: {rating}

    64 |
  • 65 | ); 66 | })} 67 |
68 |
69 | {/* Add div container for the places service */} 70 |
71 | 72 | ); 73 | }; 74 | 75 | export default PlacesServiceElement; 76 | -------------------------------------------------------------------------------- /examples/places-service-with-element/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Google Maps with places with a div element, using the usePlacesService 10 | hook with the Google Maps React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/places-service-with-element/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/places-service-with-element/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/places-service/README.md: -------------------------------------------------------------------------------- 1 | # `usePlacesService` Hook Setup Example 2 | 3 | This is an example setup to show a **Google Map with places** using the usePlacesService hook with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | Go to the root of the repository and run 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:places-service-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | ``` 27 | GOOGLE_MAPS_API_KEY="" 28 | ``` 29 | 30 | An example can be found in `.env.example`. 31 | 32 | **NOTE FOR WINDOWS USERS**: 33 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 34 | 35 | ## Output 36 | 37 | The project will start at [localhost:1234](http://localhost:1234) and show a Google Map that highlights all nearby Cafes to the central station in Hamburg, and their opening hours. Information is retrieved from Places Service. 38 | 39 | ![image](https://user-images.githubusercontent.com/39244966/212645480-54cb939d-5caa-4c57-9f26-bcfbe6f652c0.png) 40 | -------------------------------------------------------------------------------- /examples/places-service/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import PlacesService from './components/places-service/places-service'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 53.5582447, lng: 9.647645}, 13 | zoom: 20, 14 | disableDefaultUI: true, 15 | zoomControl: false, 16 | clickableIcons: false 17 | }; 18 | 19 | const App: FunctionComponent> = () => { 20 | const [mapContainer, setMapContainer] = useState(null); 21 | 22 | const mapRef = useCallback( 23 | (node: React.SetStateAction) => { 24 | node && setMapContainer(node); 25 | }, 26 | [] 27 | ); 28 | 29 | return ( 30 | 36 | 37 |
38 | 39 | 40 |
41 |
42 |
43 | ); 44 | }; 45 | 46 | export default App; 47 | -------------------------------------------------------------------------------- /examples/places-service/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/places-service/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/places-service/components/places-service/places-service.tsx: -------------------------------------------------------------------------------- 1 | import {useEffect} from 'react'; 2 | 3 | import {useGoogleMap, usePlacesService} from '@ubilabs/google-maps-react-hooks'; 4 | 5 | const PlacesService = () => { 6 | const map = useGoogleMap(); 7 | 8 | // Get the places service from the usePlacesService hook 9 | const service = usePlacesService(); 10 | 11 | useEffect(() => { 12 | if (!map || !service) { 13 | return () => {}; 14 | } 15 | 16 | const markers: Array = []; 17 | 18 | const bounds = new google.maps.LatLngBounds(); 19 | 20 | const request = { 21 | location: {lat: 53.550481787761306, lng: 9.992336490896136}, 22 | radius: 500, 23 | type: 'cafe' 24 | }; 25 | 26 | function callback( 27 | results: google.maps.places.PlaceResult[] | null, 28 | status: google.maps.places.PlacesServiceStatus 29 | ) { 30 | if (status !== google.maps.places.PlacesServiceStatus.OK || !results) { 31 | console.error(status); 32 | 33 | return; 34 | } 35 | 36 | for (let index = 0; index < results.length; index++) { 37 | const name = results[index].name; 38 | const position = results[index].geometry?.location; 39 | const openingHours = results[index].opening_hours; 40 | 41 | const isOpenStatus = openingHours ? 'open' : 'closed'; 42 | 43 | if (!map || !position) { 44 | return; 45 | } 46 | 47 | const marker = new google.maps.Marker({ 48 | map, 49 | position 50 | }); 51 | 52 | markers.push(marker); 53 | 54 | map.fitBounds(bounds.extend(position)); 55 | 56 | const infowindow = new google.maps.InfoWindow({ 57 | position, 58 | content: `${name} is ${isOpenStatus}` 59 | }); 60 | 61 | infowindow.open(map, marker); 62 | } 63 | } 64 | 65 | service.nearbySearch(request, callback); 66 | 67 | // Clean up markers 68 | return () => { 69 | markers.forEach(marker => marker.setMap(null)); 70 | }; 71 | }, [map, Boolean(service)]); 72 | 73 | return null; 74 | }; 75 | 76 | export default PlacesService; 77 | -------------------------------------------------------------------------------- /examples/places-service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Google Maps with places, using the usePlacesService hook with the Google 10 | Maps React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/places-service/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/places-service/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/street-view-panorama-map/README.md: -------------------------------------------------------------------------------- 1 | # `useStreetViewPanorama` Hook Setup Example 2 | 3 | This is an example setup of **Street View Panorama** to show the usage of the **useStreetViewPanorama hook** to enable Street View in an existing map with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run: 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:street-view-panorama-map-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | `GOOGLE_MAPS_API_KEY=""` 27 | 28 | An example can be found in `.env.example`. 29 | 30 | **NOTE FOR WINDOWS USERS**: 31 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 32 | 33 | ## Output 34 | 35 | The project will start at [localhost:1234](http://localhost:1234) and show a map with street view. 36 | -------------------------------------------------------------------------------- /examples/street-view-panorama-map/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import StreetViewPanoramaMap from './components/street-view-panorama-map/street-view-panorama-map'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 53.5582447, lng: 9.647645}, 13 | zoom: 12, 14 | disableDefaultUI: true, 15 | zoomControl: true, 16 | streetViewControl: true 17 | }; 18 | 19 | const App: FunctionComponent> = () => { 20 | const [mapContainer, setMapContainer] = useState(null); 21 | 22 | const mapRef = useCallback( 23 | (node: React.SetStateAction) => { 24 | node && setMapContainer(node); 25 | }, 26 | [] 27 | ); 28 | 29 | return ( 30 | 34 | 35 |
36 | 37 | 38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /examples/street-view-panorama-map/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/street-view-panorama-map/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/street-view-panorama-map/components/street-view-panorama-map/street-view-panorama-map.tsx: -------------------------------------------------------------------------------- 1 | import {useStreetViewPanorama} from '@ubilabs/google-maps-react-hooks'; 2 | 3 | const StreetViewPanoramaMap = () => { 4 | const position = {lat: 53.55150164023877, lng: 9.986843327204179}; 5 | const pov = {heading: 165, pitch: 20}; 6 | 7 | useStreetViewPanorama({ 8 | position, 9 | pov, 10 | zoom: 1 11 | }); 12 | 13 | return null; 14 | }; 15 | 16 | export default StreetViewPanoramaMap; 17 | -------------------------------------------------------------------------------- /examples/street-view-panorama-map/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Street View Panorama Map with the useStreetViewPanorama hook of the Google 10 | Maps React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/street-view-panorama-map/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/street-view-panorama-map/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/README.md: -------------------------------------------------------------------------------- 1 | # `useStreetViewPanorama` Hook Setup Example 2 | 3 | This is an example setup of a **Street View Panorama in a div element** to show the usage of the **useStreetViewPanorama hook** when passing a div element with the Google Maps React Hooks library. 4 | 5 | ## Instructions 6 | 7 | To run this project, clone the Google Maps React Hooks repository locally. 8 | 9 | First go to the root of the repository and run: 10 | 11 | ```shell 12 | npm install 13 | ``` 14 | 15 | once to install all dependencies. 16 | 17 | Then start this example locally with 18 | 19 | ```shell 20 | npm run start:street-view-panorama-element-example 21 | ``` 22 | 23 | **NOTE**: 24 | To see the examples it is needed to add an `.env` file with a [Google Maps API key](https://developers.google.com/maps/documentation/embed/get-api-key#:~:text=Go%20to%20the%20Google%20Maps%20Platform%20%3E%20Credentials%20page.&text=On%20the%20Credentials%20page%2C%20click,Click%20Close.) in the following format: 25 | 26 | `GOOGLE_MAPS_API_KEY=""` 27 | 28 | An example can be found in `.env.example`. 29 | 30 | **NOTE FOR WINDOWS USERS**: 31 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 32 | 33 | ## Output 34 | 35 | The project will start at [localhost:1234](http://localhost:1234) and show a map with a street view element at the right bottom. 36 | 37 | - Add image here 38 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/app.tsx: -------------------------------------------------------------------------------- 1 | import React, {FunctionComponent, useState, useCallback} from 'react'; 2 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 3 | 4 | import MapCanvas from './components/map-canvas/map-canvas'; 5 | import StreetViewPanoramaElement from './components/street-view-panorama-with-element/street-view-element'; 6 | 7 | import {GOOGLE_MAPS_API_KEY} from '../constants'; 8 | 9 | import './main.module.css'; 10 | 11 | const mapOptions = { 12 | center: {lat: 53.5582447, lng: 9.647645}, 13 | zoom: 12, 14 | disableDefaultUI: true, 15 | zoomControl: true, 16 | streetViewControl: true 17 | }; 18 | 19 | const App: FunctionComponent> = () => { 20 | const [mapContainer, setMapContainer] = useState(null); 21 | 22 | const mapRef = useCallback( 23 | (node: React.SetStateAction) => { 24 | node && setMapContainer(node); 25 | }, 26 | [] 27 | ); 28 | 29 | return ( 30 | 34 | 35 |
36 | 37 | 38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/components/map-canvas/map-canvas.module.css: -------------------------------------------------------------------------------- 1 | .map { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/components/map-canvas/map-canvas.tsx: -------------------------------------------------------------------------------- 1 | import React, {forwardRef} from 'react'; 2 | 3 | import styles from './map-canvas.module.css'; 4 | 5 | const MapCanvas = forwardRef>( 6 | (_, ref) =>
7 | ); 8 | 9 | export default MapCanvas; 10 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/components/street-view-panorama-with-element/street-view-element.module.css: -------------------------------------------------------------------------------- 1 | .pano { 2 | height: 400px; 3 | width: 400px; 4 | position: absolute; 5 | bottom: 20px; 6 | right: 20px; 7 | background-color: rgb(229, 227, 223); 8 | overflow: hidden; 9 | } 10 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/components/street-view-panorama-with-element/street-view-element.tsx: -------------------------------------------------------------------------------- 1 | import React, {useCallback, useEffect, useState} from 'react'; 2 | 3 | import { 4 | useGoogleMap, 5 | useStreetViewPanorama 6 | } from '@ubilabs/google-maps-react-hooks'; 7 | 8 | import styles from './street-view-element.module.css'; 9 | 10 | const StreetViewPanoramaElement = () => { 11 | const [divContainer, setDivContainer] = useState(null); 12 | 13 | const divRef = useCallback( 14 | (node: React.SetStateAction) => { 15 | node && setDivContainer(node); 16 | }, 17 | [] 18 | ); 19 | 20 | const map = useGoogleMap(); 21 | 22 | const panorama = useStreetViewPanorama({ 23 | divElement: divContainer, 24 | position: {lat: 53.55150164023877, lng: 9.986843327204179}, 25 | pov: {heading: 165, pitch: 0}, 26 | zoom: 1 27 | }); 28 | 29 | useEffect(() => { 30 | if (map) { 31 | map.setStreetView(panorama); 32 | } 33 | }, [map, panorama]); 34 | 35 | // Clean up map when component unmounts 36 | useEffect( 37 | () => () => { 38 | if (map) { 39 | map.setStreetView(null); 40 | } 41 | }, 42 | [] 43 | ); 44 | 45 | return
; 46 | }; 47 | 48 | export default StreetViewPanoramaElement; 49 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Street View Panorama in a div element with the useStreetViewPanorama hook 10 | of the Google Maps React Hooks. 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createRoot} from 'react-dom/client'; 3 | 4 | import App from './app'; 5 | 6 | const root = createRoot(document.getElementById('app')!); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /examples/street-view-panorama-with-element/main.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | :global(#app), 4 | :global(#container) { 5 | height: 100vh; 6 | overflow: hidden; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "exclude": ["public"] 4 | } 5 | -------------------------------------------------------------------------------- /library/.npmignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | dist 4 | docs 5 | src 6 | tsconfig.json 7 | -------------------------------------------------------------------------------- /library/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .npmignore 4 | CHANGELOG.md 5 | -------------------------------------------------------------------------------- /library/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ubilabs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /library/README.md: -------------------------------------------------------------------------------- 1 | # Google Maps React Hooks 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/ubilabs/google-maps-react-hooks/tree/main/LICENSE) 4 | 5 | ## Description 6 | 7 | This is a JavaScript library to easily implement a Google Maps map into your React application. It comes with a collection of React hooks to access the Google Maps map instance all over your components and to use some of the Google Maps [Services](https://developers.google.com/maps/documentation/javascript#services) or [Libraries](https://developers.google.com/maps/documentation/javascript#libraries). 8 | 9 | #### Table of contents 10 | 11 | - [Requirements](#requirements) 12 | - [Installation](#installation) 13 | - [Map Usage](#map-usage) 14 | - Documentation 15 | - [GoogleMapsProvider](./docs/GoogleMapsProvider.md) 16 | - Hooks 17 | - [useGoogleMap](./docs/useGoogleMap.md) 18 | - [useDirectionsService](./docs/useDirectionsService.md) 19 | - [useDistanceMatrixService](./docs/useDistanceMatrixService.md) 20 | - [useElevationService](./docs/useElevationService.md) 21 | - [useGeocodingService](./docs/useGeocodingService.md) 22 | - [useMaxZoomService](./docs/useMaxZoomService.md) 23 | - [usePlacesService](./docs/usePlacesService.md) 24 | - [useAutocomplete](./docs/useAutocomplete.md) 25 | - [useAutocompleteService](./docs/useAutocompleteService.md) 26 | - [Examples](https://github.com/ubilabs/google-maps-react-hooks/tree/main/examples) 27 | - [Basic Google Map](../examples/basic-google-map) 28 | - [Google Map with Markers](../examples/google-map-with-markers) 29 | - [Multiple Google Maps](../examples/multiple-google-maps) 30 | - [Directions Service](../examples/directions-service) 31 | - [Distance Matrix Service](../examples/distance-matrix-service) 32 | - [Elevation Service](../examples/elevation-service) 33 | - [Geocoding Service](../examples/geocoding-service) 34 | - [Maximum Zoom Imagery Service](../examples/max-zoom-service) 35 | - [Places Autocomplete Service](../examples/places-autocomplete-service) 36 | - [Places Autocomplete Widget](../examples/places-autocomplete-widget) 37 | - [Places Service](../examples/places-service) 38 | - [Places Service With Element](../examples/places-service-with-element) 39 | - [Street View Panorama Map](../examples/street-view-panorama-map) 40 | - [Street View Panorama With Element](../examples/street-view-panorama-with-element) 41 | 42 | ## Requirements 43 | 44 | You need to have React [16.8.0](https://reactjs.org/blog/2019/02/06/react-v16.8.0.html) or later installed to use the Hooks API. 45 | 46 | ## Installation 47 | 48 | ```sh 49 | npm install @ubilabs/google-maps-react-hooks -D 50 | ``` 51 | 52 | **NOTE FOR WINDOWS USERS**: 53 | We are using [cross-env](https://github.com/kentcdodds/cross-env) for environment variables to work on all platforms. There is an issue that `npm` uses `cmd` by default. The workaround is to add `script-shell` to `powershell` in your `.npmrc`. Please follow [this setup](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729) to make it work. 54 | 55 | ## Map Usage 56 | 57 | Import the `GoogleMapsProvider` and wrap it around your components. 58 | Make sure all components that should have access to the Google Maps map instance are nested inside the `GoogleMapsProvider`. 59 | 60 | If you still can't see a map on your page, make sure that your map container has a `height` CSS property (by default it usually has no height) and that a `center` and `zoom` was set for your map. 61 | 62 | ```tsx 63 | import React, {useState, useCallback, forwardRef} from 'react'; 64 | import {GoogleMapsProvider} from '@ubilabs/google-maps-react-hooks'; 65 | 66 | function App() { 67 | const [mapContainer, setMapContainer] = useState(null); 68 | const mapRef = useCallback(node => { 69 | node && setMapContainer(node); 70 | }, []); 71 | 72 | const mapOptions = { 73 | // Add your map options here 74 | // `center` and `zoom` are required for every map to be displayed 75 | center: {lat: 53.5582447, lng: 9.647645}, 76 | zoom: 6 77 | }; 78 | 79 | return ( 80 | 84 | 85 |
86 | 87 | 88 | ); 89 | } 90 | 91 | export default App; 92 | ``` 93 | 94 | The `GoogleMapsProvider` makes the Google Maps map instance available to any nested components with the `useGoogleMap` hook. 95 | 96 | ```tsx 97 | import React from 'react'; 98 | import {useGoogleMap} from '@ubilabs/google-maps-react-hooks'; 99 | 100 | const MyComponent = () => { 101 | const map = useGoogleMap(); 102 | 103 | // Do something with the Google Maps map instance 104 | 105 | return (...); 106 | }; 107 | ``` 108 | 109 | ## Examples 110 | 111 | Explore our [examples directory on GitHub](https://github.com/ubilabs/google-maps-react-hooks/tree/main/examples) for full implementation examples. 112 | -------------------------------------------------------------------------------- /library/docs/GoogleMapsProvider.md: -------------------------------------------------------------------------------- 1 | # `GoogleMapsProvider` Component 2 | 3 | The `GoogleMapsProvider` is a component to wrap around the code where the map should be available. 4 | 5 | ```tsx 6 | 7 | {children} 8 | 9 | ``` 10 | 11 | ## Properties 12 | 13 | Properties that can be passed to the `GoogleMapsProvider` that are either the container to hold the map instance or [Maps JavaScript API URL Parameters](https://developers.google.com/maps/documentation/javascript/url-params). 14 | 15 | ```TypeScript 16 | interface GoogleMapsProviderProps { 17 | googleMapsAPIKey: string; 18 | mapContainer?: HTMLElement | null; 19 | mapOptions?: google.maps.MapOptions; 20 | libraries?: string[]; 21 | language?: string; 22 | region?: string; 23 | version?: string; 24 | authReferrerPolicy?: string; 25 | onLoadScript?: () => void; 26 | onLoadMap?: (map: google.maps.Map) => void; 27 | } 28 | ``` 29 | 30 | **NOTE**: 31 | If you want to implement multiple maps in your application you can use multiple `GoogleMapsProvider` components to do so, but you have to pass the same Google Maps API parameters (`googleMapsAPIKey`, `libraries`, `language`, `region`, `version` and `authReferrerPolicy`) to all `GoogleMapsProvider` components. 32 | 33 | --- 34 | 35 | **googleMapsAPIKey** (_compulsory property_) 36 | 37 | The Google Maps JavaScript API Key. 38 | 39 | ```Typescript 40 | googleMapsAPIKey: string; 41 | ``` 42 | 43 | See: [Use API Key](https://developers.google.com/maps/documentation/embed/get-api-key) 44 | 45 | --- 46 | 47 | **mapContainer** (_optional property_) 48 | 49 | A reference to the HTML element that displays the map. 50 | Usually we do this by adding a `div` element. 51 | Without the `mapContainer` provided, no visual map will be displayed. 52 | 53 | ```Typescript 54 | mapContainer?: HTMLElement | null; 55 | ``` 56 | 57 | _Example:_ 58 | 59 | The `mapContainer` will be passed to the `GoogleMapsProvider` in the following way: 60 | 61 | ```tsx 62 | function App() { 63 | const [mapContainer, setMapContainer] = useState(null); 64 | const mapRef = useCallback(node => { 65 | node && setMapContainer(node); 66 | }, []); 67 | 68 | return ( 69 | 72 | 73 |
74 | 75 | 76 | ); 77 | } 78 | ``` 79 | 80 | See: [Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/overview) 81 | 82 | **NOTE**: Make sure to give your element a height (by default divs usually have no height), otherwise you won't see the map displayed. 83 | 84 | --- 85 | 86 | **mapOptions** (_optional property_) 87 | 88 | The Google Maps MapOptions. 89 | 90 | ```Typescript 91 | mapOptions?: google.maps.MapOptions; 92 | ``` 93 | 94 | _Example:_ 95 | 96 | ```Typescript 97 | const mapOptions = { 98 | center: {lat: 53.5582447, lng: 9.647645}, 99 | zoom: 6 100 | }; 101 | ``` 102 | 103 | See: [MapOptions](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions) 104 | 105 | **NOTE**: If the `center` and `zoom` options are not provided here, the map will not be displayed until they are set with `map.setCenter(latLng)` and `map.setZoom(zoom)`. 106 | 107 | _Example:_ 108 | 109 | MapOptions can also be set or changed later in another component in the following way: 110 | 111 | ```Typescript 112 | import {useGoogleMap} from '@ubilabs/google-maps-react-hooks'; 113 | 114 | const mapOptions = { 115 | center: {lat: 53.5582447, lng: 9.647645}, 116 | zoom: 6, 117 | disableDefaultUI: true, 118 | zoomControl: true, 119 | zoomControlOptions: { 120 | position: 3 // Right top 121 | } 122 | }; 123 | 124 | const map = useGoogleMap(); 125 | 126 | map?.setOptions(mapOptions); 127 | ``` 128 | 129 | --- 130 | 131 | **libraries** (_optional property_) 132 | 133 | Additional Google Maps libraries to load ('drawing', 'geometry', 'places' or 'visualization'). 134 | 135 | ```Typescript 136 | libraries?: string[]; 137 | ``` 138 | 139 | See: [Libraries](https://developers.google.com/maps/documentation/javascript/libraries) 140 | 141 | --- 142 | 143 | **language** (_optional property_) 144 | 145 | By default Google Maps will use the preferred language from the browser setting. This is the property to set it manually. 146 | 147 | ```Typescript 148 | language?: string; 149 | ``` 150 | 151 | See: [Localization](https://developers.google.com/maps/documentation/javascript/localization) 152 | 153 | --- 154 | 155 | **region** (_optional property_) 156 | 157 | By default Google Maps will use the preferred region from the browser setting. This is the property to set it manually. 158 | 159 | ```Typescript 160 | region?: string; 161 | ``` 162 | 163 | See: [Localization](https://developers.google.com/maps/documentation/javascript/localization) 164 | 165 | --- 166 | 167 | **version** (_optional property_) 168 | 169 | Use this parameter to specify a Google Maps JavaScript API version. 170 | 171 | ```Typescript 172 | version?: string; 173 | ``` 174 | 175 | See: [Versions](https://developers.google.com/maps/documentation/javascript/versions) 176 | 177 | --- 178 | 179 | **authReferrerPolicy** (_optional property_) 180 | 181 | Use this parameter to set auth_referrer_policy=origin when an URL on the same origin uses the API Key, to limit the amount of data sent when authorizing requests. 182 | 183 | ```Typescript 184 | authReferrerPolicy?: string; 185 | ``` 186 | 187 | See: [auth_referrer_policy](https://developers.google.com/maps/documentation/javascript/url-params) 188 | 189 | --- 190 | 191 | **onLoadScript** (_optional property_) 192 | 193 | A callback function that is called, when the Google Maps API is loaded. 194 | 195 | ```Typescript 196 | onLoadScript?: () => void; 197 | ``` 198 | 199 | _Example:_ 200 | 201 | ```tsx 202 | { 205 | console.log(google.maps); 206 | }}> 207 | ... 208 | 209 | ``` 210 | 211 | **onLoadMap** (_optional property_) 212 | 213 | A callback function that is called, when the Google Map map is loaded. 214 | 215 | ```Typescript 216 | onLoadMap?: (map: google.maps.Map) => void; 217 | ``` 218 | 219 | _Example:_ 220 | 221 | ```tsx 222 | map.setZoom(4)}> 225 | ... 226 | 227 | ``` 228 | -------------------------------------------------------------------------------- /library/docs/useAutocomplete.md: -------------------------------------------------------------------------------- 1 | # `useAutocomplete` Hook 2 | 3 | React hook to use the [Google Maps Places Autocomplete Widget](https://developers.google.com/maps/documentation/javascript/reference/places-widget) in any component. 4 | 5 | ## Usage 6 | 7 | When initializing the ``, include the places library like this: `libraries={['places']}`. 8 | 9 | ```tsx 10 | import React, {useRef, useState} from 'react'; 11 | import {useAutocomplete} from '@ubilabs/google-maps-react-hooks'; 12 | 13 | const MyComponent = () => { 14 | const inputRef = useRef(null); 15 | const [inputValue, setInputValue] = useState(''); 16 | 17 | const onPlaceChanged = place => { 18 | if (place) { 19 | setInputValue(place.formatted_address || place.name); 20 | } 21 | 22 | // Keep focus on input element 23 | inputRef.current && inputRef.current.focus(); 24 | }; 25 | 26 | useAutocomplete({ 27 | inputField: inputRef && inputRef.current, 28 | onPlaceChanged 29 | }); 30 | 31 | const handleInputChange = event => { 32 | setInputValue(event.target.value); 33 | }; 34 | 35 | return ( 36 | 37 | ); 38 | }; 39 | ``` 40 | 41 | ## Parameters 42 | 43 | ### AutocompleteProps 44 | 45 | Needs a reference to an Input field, some optional [AutocompleteOptions](https://developers.google.com/maps/documentation/javascript/reference/places-widget#AutocompleteOptions) and a callback for when a place got changed. 46 | 47 | ```TypeScript 48 | interface AutocompleteProps { 49 | inputField: HTMLInputElement | null; 50 | options?: google.maps.places.AutocompleteOptions; 51 | onPlaceChanged: (place: google.maps.places.PlaceResult) => void; 52 | } 53 | ``` 54 | 55 | ## Return value 56 | 57 | Returns an [`Autocomplete Places Widget`](https://developers.google.com/maps/documentation/javascript/reference/places-widget) instance to use directly. 58 | 59 | ```TypeScript 60 | google.maps.places.Autocomplete 61 | ``` 62 | -------------------------------------------------------------------------------- /library/docs/useAutocompleteService.md: -------------------------------------------------------------------------------- 1 | # `useAutocompleteService` Hook 2 | 3 | React hook to use the [Google Maps Places Autocomplete Service](https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service) in any component. 4 | 5 | ## Usage 6 | 7 | When initializing the ``, include the places library like this: `libraries={['places']}`. 8 | 9 | ```tsx 10 | const autocompleteService = useAutocompleteService(); 11 | 12 | const request = {input: inputValue}; // google.maps.places.AutocompletionRequest 13 | 14 | autocompleteService?.getPlacePredictions( 15 | request, 16 | ( 17 | predictions: google.maps.places.AutocompletePrediction[] | null, 18 | status: google.maps.places.PlacesServiceStatus 19 | ) => { 20 | if (status !== google.maps.places.PlacesServiceStatus.OK || !predictions) { 21 | return; 22 | } 23 | // Do something with predictions 24 | } 25 | ); 26 | ``` 27 | 28 | ## Parameters 29 | 30 | ### AutocompleteProps 31 | 32 | Needs a reference to an Input field, and has some optional properties. Check: [AutocompletionRequest interface](https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service#AutocompletionRequest) or [QueryAutocompletionRequest interface](https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service#QueryAutocompletionRequest). 33 | 34 | ## Return value 35 | 36 | Returns an [`Autocomplete Service`](https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service) instance to use directly. 37 | 38 | ```TypeScript 39 | google.maps.places.AutocompleteService 40 | ``` 41 | -------------------------------------------------------------------------------- /library/docs/useDirectionsService.md: -------------------------------------------------------------------------------- 1 | # `useDirectionsService` Hook 2 | 3 | React hook to use the [Google Maps Directions Service](https://developers.google.com/maps/documentation/javascript/reference/directions) in any component. 4 | 5 | ## Usage 6 | 7 | ```tsx 8 | import React from 'react'; 9 | import {useDirectionsService} from '@ubilabs/google-maps-react-hooks'; 10 | 11 | const MyComponent = () => { 12 | const { 13 | directionsService, 14 | directionsRenderer, 15 | findAndRenderRoute, 16 | setRouteIndex 17 | } = useDirectionsService(directionsOptions); 18 | 19 | // Do something with the directions 20 | 21 | return (...); 22 | }; 23 | ``` 24 | 25 | ## Parameters 26 | 27 | ### DirectionsProps 28 | 29 | Pass in whether to render on a Google Maps map or not and the [DirectionsRendererOptions](https://developers.google.com/maps/documentation/javascript/reference/directions#DirectionsRendererOptions). 30 | 31 | ```TypeScript 32 | interface DirectionsServiceProps { 33 | renderOnMap?: boolean; 34 | renderOptions?: google.maps.DirectionsRendererOptions; 35 | } 36 | ``` 37 | 38 | ## Return value 39 | 40 | Returns an object with the following elements: 41 | 42 | - [`directionsService`](https://developers.google.com/maps/documentation/javascript/reference/directions#DirectionsService) instance 43 | - [`directionsRenderer`](https://developers.google.com/maps/documentation/javascript/reference/directions#DirectionsRenderer) instance 44 | - `findRoute` function, which returns a route 45 | - `findAndRenderRoute` function, which also renders the route on the map 46 | - `renderRouteOfIndex` function, which can be used to render a specific route of `google.maps.DirectionsResult` returned by `findRoute` or `findAndRenderRoute` 47 | 48 | ```TypeScript 49 | interface DirectionsServiceHookReturns { 50 | directionsService: google.maps.DirectionsService | null; 51 | directionsRenderer: google.maps.DirectionsRenderer | null; 52 | findRoute: ((request: google.maps.DirectionsRequest) => Promise) | null; 53 | findAndRenderRoute: ((request: google.maps.DirectionsRequest) => Promise) | null; 54 | renderRouteOfIndex: (index: number) => void; 55 | } 56 | ``` 57 | 58 | **NOTE**: 59 | When using `findAndRenderRoute`, the `renderOnMap` property must be set to `true`. 60 | -------------------------------------------------------------------------------- /library/docs/useDistanceMatrixService.md: -------------------------------------------------------------------------------- 1 | # `useDistanceMatrixService` Hook 2 | 3 | React hook to use the [Google Maps Distance Matrix Service](https://developers.google.com/maps/documentation/javascript/distancematrix) in any component. 4 | 5 | ## Usage 6 | 7 | ```tsx 8 | import React from 'react'; 9 | import {useDistanceMatrixService} from '@ubilabs/google-maps-react-hooks'; 10 | 11 | const MyComponent = () => { 12 | const service = useDistanceMatrixService(); 13 | 14 | service.getDistanceMatrix(request, response => { 15 | // Do something with the response 16 | } 17 | 18 | return (...); 19 | }; 20 | ``` 21 | 22 | ## Return value 23 | 24 | Returns a [`Distance Matrix Service`](https://developers.google.com/maps/documentation/javascript/distancematrix) instance to use directly. 25 | 26 | ```TypeScript 27 | google.maps.DistanceMatrixService 28 | ``` 29 | -------------------------------------------------------------------------------- /library/docs/useElevationService.md: -------------------------------------------------------------------------------- 1 | # `useElevationService` Hook 2 | 3 | React hook to use the [Elevation Service](https://developers.google.com/maps/documentation/javascript/elevation) in any component. 4 | 5 | ## Usage 6 | 7 | ```tsx 8 | import React, {useEffect} from 'react'; 9 | import {useElevationService} from '@ubilabs/google-maps-react-hooks'; 10 | 11 | const MyComponent = () => { 12 | const elevator = useElevationService(); 13 | const location = /** google.maps.LatLng */; 14 | 15 | useEffect(() => { 16 | elevator?.getElevationForLocations( 17 | {locations: [location]}, 18 | (results: google.maps.ElevationResult[]) => { 19 | // Do something with results 20 | } 21 | ); 22 | }, [location]); 23 | 24 | return (...); 25 | }; 26 | ``` 27 | 28 | ## Return value 29 | 30 | Returns a [`Elevation Service`](https://developers.google.com/maps/documentation/javascript/elevation) instance to use directly. 31 | 32 | ```TypeScript 33 | google.maps.ElevationService 34 | ``` 35 | -------------------------------------------------------------------------------- /library/docs/useGeocodingService.md: -------------------------------------------------------------------------------- 1 | # `useGeocodingService` Hook 2 | 3 | React hook to use the [Google Maps Geocoding Service](https://developers.google.com/maps/documentation/javascript/geocoding) in any component. 4 | 5 | ## Usage 6 | 7 | ```tsx 8 | import React from 'react'; 9 | import {useGeocodingService} from '@ubilabs/google-maps-react-hooks'; 10 | 11 | const MyComponent = () => { 12 | const geocoder = useGeocodingService(); 13 | 14 | // Do something with the geocoder 15 | 16 | return (...); 17 | }; 18 | ``` 19 | 20 | ## Return value 21 | 22 | Returns a [`Geocoder`](https://developers.google.com/maps/documentation/javascript/reference/geocoder) instance to use directly. 23 | 24 | ```TypeScript 25 | google.maps.Geocoder 26 | ``` 27 | -------------------------------------------------------------------------------- /library/docs/useGoogleMap.md: -------------------------------------------------------------------------------- 1 | # `useGoogleMap` Hook 2 | 3 | React hook to get the [Google Maps map](https://developers.google.com/maps/documentation/javascript/reference/map#Map) instance. 4 | 5 | ## Usage 6 | 7 | ```tsx 8 | import React from 'react'; 9 | import {useGoogleMap} from '@ubilabs/google-maps-react-hooks'; 10 | 11 | const MyComponent = () => { 12 | const map = useGoogleMap(); 13 | 14 | // Do something with the Google Maps map instance 15 | 16 | return (...); 17 | }; 18 | ``` 19 | 20 | ## Return value 21 | 22 | Returns a [Google Maps map instance](https://developers.google.com/maps/documentation/javascript/reference/map#Map) to use directly. 23 | Type: `GoogleMapContextType`: 24 | 25 | ```TypeScript 26 | google.maps.Map 27 | ``` 28 | -------------------------------------------------------------------------------- /library/docs/useMaxZoomService.md: -------------------------------------------------------------------------------- 1 | # `useMaxZoomService` Hook 2 | 3 | React hook to use the [Maximum Zoom Imagery Service](https://developers.google.com/maps/documentation/javascript/maxzoom) in any component. 4 | 5 | ## Usage 6 | 7 | ```tsx 8 | import React, {useEffect} from 'react'; 9 | import {useMaxZoomService} from '@ubilabs/google-maps-react-hooks'; 10 | 11 | const MyComponent = () => { 12 | const maxZoomService = useMaxZoomService(); 13 | const location = /** google.maps.LatLng */; 14 | 15 | useEffect(() => { 16 | maxZoomService?.getMaxZoomAtLatLng( 17 | location, 18 | (result: google.maps.MaxZoomResult) => { 19 | // Do something with result 20 | } 21 | ); 22 | }, [location]); 23 | 24 | return (...); 25 | }; 26 | ``` 27 | 28 | ## Return value 29 | 30 | Returns a [`Max Zoom Service`](https://developers.google.com/maps/documentation/javascript/maxzoom) instance to use directly. 31 | 32 | ```TypeScript 33 | google.maps.MaxZoomService 34 | ``` 35 | -------------------------------------------------------------------------------- /library/docs/usePlacesService.md: -------------------------------------------------------------------------------- 1 | # `usePlacesService` Hook 2 | 3 | React hook to use the [Google Maps Places Service](https://developers.google.com/maps/documentation/javascript/reference/places-service) in any component. 4 | 5 | ## Usage 6 | 7 | When initializing the ``, include the places library like this: `libraries={['places']}`. 8 | 9 | The Places Service renders attributions in a specified container. 10 | 11 | This container can either be a **map**, which is implemented in the following way with the `usePlacesService` Hook: 12 | 13 | ```tsx 14 | import React from 'react'; 15 | import {usePlacesService} from '@ubilabs/google-maps-react-hooks'; 16 | 17 | const MyComponent = () => { 18 | const placesService = usePlacesService(); 19 | 20 | // Do something with the Places Service 21 | 22 | return (...); 23 | }; 24 | ``` 25 | 26 | or the container is a **div element**, that needs to be passed to the `usePlacesService` Hook in the following way: 27 | 28 | ```tsx 29 | import React from 'react'; 30 | import {usePlacesService} from '@ubilabs/google-maps-react-hooks'; 31 | 32 | const MyComponent = () => { 33 | const [divContainer, setDivContainer] = useState(null); 34 | 35 | const divRef = useCallback( 36 | (node: React.SetStateAction) => { 37 | node && setDivContainer(node); 38 | }, 39 | [] 40 | ); 41 | 42 | const service = usePlacesService({divElement: divContainer}); 43 | 44 | // Do something with the places Service 45 | 46 | return (...); 47 | }; 48 | ``` 49 | 50 | ## Return value 51 | 52 | Returns a [`Places Service`](https://developers.google.com/maps/documentation/javascript/reference/places-service) instance to use directly. 53 | 54 | ```TypeScript 55 | google.maps.places.PlacesService 56 | ``` 57 | -------------------------------------------------------------------------------- /library/docs/useStreetViewPanorama.md: -------------------------------------------------------------------------------- 1 | # `useStreetViewPanorama` Hook 2 | 3 | React hook to use the [Street View Panorama](https://developers.google.com/maps/documentation/javascript/streetview) in any component. 4 | 5 | ## Usage 6 | 7 | The `StreetViewPanorama` can either be used within a DOM element, like a `
` element: 8 | 9 | ```tsx 10 | import React, {useEffect} from 'react'; 11 | import { 12 | useGoogleMap, 13 | useStreetViewPanorama 14 | } from '@ubilabs/google-maps-react-hooks'; 15 | 16 | const MyComponent = () => { 17 | const [divContainer, setDivContainer] = useState(null); 18 | 19 | const divRef = useCallback( 20 | (node: React.SetStateAction) => { 21 | node && setDivContainer(node); 22 | }, 23 | [] 24 | ); 25 | 26 | const map = useGoogleMap(); 27 | 28 | const position = /** google.maps.LatLng */; 29 | const pov = /** google.maps.StreetViewPov */; 30 | 31 | const panorama = useStreetViewPanorama({ 32 | divElement: divContainer, 33 | position, 34 | pov 35 | }); 36 | 37 | return
; 38 | }; 39 | ``` 40 | 41 | or be created on its own to be used by the map: 42 | 43 | ```tsx 44 | import React, {useEffect} from 'react'; 45 | import { 46 | useGoogleMap, 47 | useStreetViewPanorama 48 | } from '@ubilabs/google-maps-react-hooks'; 49 | 50 | const MyComponent = () => { 51 | const position = /** google.maps.LatLng */; 52 | const pov = /** google.maps.StreetViewPov */; 53 | 54 | useStreetViewPanorama({ 55 | position, 56 | pov 57 | }); 58 | 59 | return null; 60 | }; 61 | ``` 62 | 63 | **NOTE**: 64 | The map instance is only created and can be used with the `useStreetViewPanorama` hook when the `mapContainer` is passed to the `GoogleMapsProvider`. 65 | 66 | ## Return value 67 | 68 | Returns a [`StreetViewPanorama`](google.maps.StreetViewPanorama) instance to use directly. 69 | 70 | ```TypeScript 71 | google.maps.StreetViewPanorama 72 | ``` 73 | 74 | ## Parameters 75 | 76 | ### StreetViewPanoramaProps 77 | 78 | Optional options that can be passed to display a street view location: [Street View Locations and Point-of-View (POV)](https://developers.google.com/maps/documentation/javascript/streetview#StreetViewLocation). 79 | 80 | ```TypeScript 81 | interface StreetViewPanoramaProps { 82 | divElement?: HTMLElement | null; 83 | position?: google.maps.LatLng | google.maps.LatLngLiteral; 84 | pov?: google.maps.StreetViewPov; 85 | zoom?: number; 86 | } 87 | ``` 88 | -------------------------------------------------------------------------------- /library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ubilabs/google-maps-react-hooks", 3 | "version": "2.0.2", 4 | "description": "React hooks and map context provider for Google Maps", 5 | "source": "src/index.ts", 6 | "main": "dist/index.umd.js", 7 | "module": "dist/index.modern.mjs", 8 | "types": "dist/index.d.ts", 9 | "homepage": "https://github.com/ubilabs/google-maps-react-hooks", 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/ubilabs/google-maps-react-hooks.git" 13 | }, 14 | "scripts": { 15 | "build": "npm run barrel && npm run microbundle:build", 16 | "start": "run-p barrel:watch microbundle:dev", 17 | "microbundle:build": "rm -rf dist/* && microbundle -o dist/index.js -f modern,umd --external react=React --sourcemap false --jsx React.createElement --no-compress --tsconfig ./tsconfig.json", 18 | "microbundle:dev": "rm -rf dist/* && microbundle watch -o dist/index.js -f modern,umd --external react=React --sourcemap false --jsx React.createElement --no-compress --tsconfig ./tsconfig.json", 19 | "linter": "eslint './src/**/*.{ts,tsx}'", 20 | "typecheck": "tsc --project tsconfig.json --noEmit", 21 | "formatter": "prettier --check .", 22 | "test": "npm run linter && npm run typecheck && npm run formatter", 23 | "barrel": "eslint --fix src/index.ts", 24 | "barrel:watch": "nodemon --delay 1 -e ts,tsx --watch src/hooks -x 'npm run barrel --silent || exit 1'", 25 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0", 26 | "preversion": "npm run test", 27 | "version": "npm run changelog && git checkout -b chore/release-${npm_package_version} && git add .", 28 | "postversion": "git push -u origin chore/release-${npm_package_version} && git push --tags --no-verify", 29 | "prepublishOnly": "npm run test && npm run build" 30 | }, 31 | "keywords": [ 32 | "React hooks", 33 | "Google Maps" 34 | ], 35 | "peerDependencies": { 36 | "react": ">=16.8.0" 37 | }, 38 | "devDependencies": { 39 | "@types/google.maps": "^3.50.5", 40 | "@types/react": "^18.0.21", 41 | "@typescript-eslint/eslint-plugin": "^5.40.0", 42 | "@typescript-eslint/parser": "^5.40.0", 43 | "conventional-changelog-cli": "^2.1.1", 44 | "eslint": "^8.25.0", 45 | "eslint-config-prettier": "^8.3.0", 46 | "eslint-plugin-codegen": "^0.16.1", 47 | "eslint-plugin-import": "^2.23.4", 48 | "eslint-plugin-react": "^7.24.0", 49 | "eslint-plugin-react-hooks": "^4.2.0", 50 | "microbundle": "^0.15.1", 51 | "nodemon": "^2.0.20", 52 | "npm-run-all": "^4.1.5", 53 | "prettier": "^2.3.2", 54 | "typescript": "^4.3.5", 55 | "typescript-plugin-css-modules": "^3.4.0" 56 | }, 57 | "bugs": { 58 | "url": "https://github.com/ubilabs/google-maps-react-hooks/issues" 59 | }, 60 | "license": "MIT" 61 | } 62 | -------------------------------------------------------------------------------- /library/src/google-maps-provider.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect, PropsWithChildren} from 'react'; 2 | 3 | const GOOGLE_MAPS_API_URL = 'https://maps.googleapis.com/maps/api/js'; 4 | 5 | // https://developers.google.com/maps/documentation/javascript/url-params 6 | export interface GoogleMapsAPIUrlParameters { 7 | googleMapsAPIKey: string; 8 | libraries?: string[]; 9 | language?: string; 10 | region?: string; 11 | version?: string; 12 | authReferrerPolicy?: string; 13 | } 14 | 15 | export interface GoogleMapsConfiguration { 16 | mapContainer?: HTMLElement | null; 17 | mapOptions?: google.maps.MapOptions; 18 | } 19 | 20 | export interface GoogleMapsProviderProps 21 | extends GoogleMapsAPIUrlParameters, 22 | GoogleMapsConfiguration { 23 | onLoadScript?: () => void; 24 | onLoadMap?: (map: google.maps.Map) => void; 25 | } 26 | 27 | export interface GoogleMapsContextType { 28 | googleMapsAPIIsLoaded: boolean; 29 | map?: google.maps.Map; 30 | } 31 | 32 | // Declare global maps callback function 33 | declare global { 34 | interface Window { 35 | mapsCallback: () => void; 36 | } 37 | } 38 | 39 | /** 40 | * The Google Maps context 41 | */ 42 | export const GoogleMapsContext = React.createContext({ 43 | googleMapsAPIIsLoaded: false 44 | }); 45 | 46 | /** 47 | * The global Google Maps provider 48 | */ 49 | export const GoogleMapsProvider: React.FunctionComponent< 50 | PropsWithChildren 51 | > = props => { 52 | const { 53 | children, 54 | googleMapsAPIKey, 55 | mapContainer, 56 | mapOptions, 57 | libraries, 58 | language, 59 | region, 60 | version, 61 | authReferrerPolicy, 62 | onLoadScript, 63 | onLoadMap 64 | } = props; 65 | 66 | const [isLoadingAPI, setIsLoadingAPI] = useState(true); 67 | const [map, setMap] = useState(); 68 | 69 | // Handle Google Maps API loading 70 | // eslint-disable-next-line complexity 71 | useEffect(() => { 72 | const apiLoadingFinished = () => { 73 | setIsLoadingAPI(false); 74 | onLoadScript && onLoadScript(); 75 | }; 76 | 77 | const defaultLanguage = navigator.language.slice(0, 2); 78 | const defaultRegion = navigator.language.slice(3, 5); 79 | 80 | /* eslint-disable camelcase */ 81 | const params = new URLSearchParams({ 82 | key: googleMapsAPIKey, 83 | language: language || defaultLanguage, 84 | region: region || defaultRegion, 85 | ...(libraries?.length && {libraries: libraries.join(',')}), 86 | ...(version && {v: version}), 87 | ...(authReferrerPolicy && {auth_referrer_policy: authReferrerPolicy}) 88 | }); 89 | /* eslint-enable camelcase */ 90 | 91 | const existingScriptTag: HTMLScriptElement | null = document.querySelector( 92 | `script[src^="${GOOGLE_MAPS_API_URL}"]` 93 | ); 94 | 95 | // Check if Google Maps API was loaded with the passed parameters 96 | if (existingScriptTag) { 97 | const loadedURL = new URL(existingScriptTag.src); 98 | const loadedParams = loadedURL.searchParams.toString(); 99 | const passedParams = params.toString(); 100 | 101 | if (loadedParams !== passedParams) { 102 | console.error( 103 | 'The Google Maps API Parameters passed to the `GoogleMapsProvider` components do not match. The Google Maps API can only be loaded once. Please make sure to pass the same API parameters to all of your `GoogleMapsProvider` components.', 104 | '\n\nExpected parameters:', 105 | Object.fromEntries(loadedURL.searchParams), 106 | '\n\nReceived parameters:', 107 | Object.fromEntries(params) 108 | ); 109 | } 110 | } 111 | 112 | if (typeof google === 'object' && typeof google.maps === 'object') { 113 | // Google Maps API is already loaded 114 | apiLoadingFinished(); 115 | } else if (existingScriptTag) { 116 | // Google Maps API is already loading 117 | setIsLoadingAPI(true); 118 | 119 | const onload = existingScriptTag.onload; 120 | existingScriptTag.onload = event => { 121 | onload?.call(existingScriptTag, event); 122 | apiLoadingFinished(); 123 | }; 124 | } else { 125 | // Load Google Maps API 126 | setIsLoadingAPI(true); 127 | 128 | // Add google maps callback 129 | window.mapsCallback = () => { 130 | apiLoadingFinished(); 131 | }; 132 | 133 | params.set('callback', 'mapsCallback'); 134 | 135 | const scriptTag = document.createElement('script'); 136 | scriptTag.type = 'text/javascript'; 137 | scriptTag.src = `${GOOGLE_MAPS_API_URL}?${params.toString()}`; 138 | document.getElementsByTagName('head')[0].appendChild(scriptTag); 139 | } 140 | 141 | // Clean up Google Maps API 142 | return () => { 143 | // Remove all loaded Google Maps API scripts 144 | document 145 | .querySelectorAll('script[src^="https://maps.googleapis.com"]') 146 | .forEach(script => { 147 | script.remove(); 148 | }); 149 | 150 | // Remove google.maps global 151 | if (typeof google === 'object' && typeof google.maps === 'object') { 152 | // @ts-ignore: The operand of a 'delete' operator must be optional. 153 | delete google.maps; 154 | } 155 | }; 156 | }, [ 157 | googleMapsAPIKey, 158 | JSON.stringify(libraries), 159 | language, 160 | region, 161 | version, 162 | authReferrerPolicy 163 | ]); 164 | 165 | // Handle Google Maps map instance 166 | useEffect(() => { 167 | // Check for google.maps is needed because of Hot Module Replacement 168 | if ( 169 | isLoadingAPI || 170 | !mapContainer || 171 | !(typeof google === 'object' && typeof google.maps === 'object') 172 | ) { 173 | return () => {}; 174 | } 175 | 176 | const newMap = new google.maps.Map(mapContainer, mapOptions); 177 | 178 | google.maps.event.addListenerOnce(newMap, 'idle', () => { 179 | if (onLoadMap && newMap) { 180 | onLoadMap(newMap); 181 | } 182 | }); 183 | 184 | setMap(newMap); 185 | 186 | // Remove all map related event listeners 187 | return () => { 188 | if ( 189 | newMap && 190 | typeof google === 'object' && 191 | typeof google.maps === 'object' 192 | ) { 193 | google.maps.event.clearInstanceListeners(newMap); 194 | } 195 | }; 196 | }, [isLoadingAPI, mapContainer]); 197 | 198 | return ( 199 | 201 | {children} 202 | 203 | ); 204 | }; 205 | -------------------------------------------------------------------------------- /library/src/hooks/autocomplete-service.ts: -------------------------------------------------------------------------------- 1 | import {useContext, useMemo} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | /** 6 | * Hook to get Google Maps Autocomplete Service instance 7 | */ 8 | export const useAutocompleteService = 9 | (): google.maps.places.AutocompleteService | null => { 10 | const {googleMapsAPIIsLoaded} = useContext(GoogleMapsContext); 11 | 12 | // Creates an Autocomplete Service instance 13 | const autocompleteService = 14 | useMemo(() => { 15 | // Wait for Google Maps API to be loaded 16 | if (!googleMapsAPIIsLoaded) { 17 | return null; 18 | } 19 | 20 | if (!google.maps.places) { 21 | throw Error( 22 | "Places library missing. Add 'places' to the libraries array of GoogleMapsProvider." 23 | ); 24 | } 25 | 26 | return new google.maps.places.AutocompleteService(); 27 | }, [googleMapsAPIIsLoaded]); 28 | 29 | return autocompleteService; 30 | }; 31 | -------------------------------------------------------------------------------- /library/src/hooks/autocomplete.ts: -------------------------------------------------------------------------------- 1 | import {useContext, useState, useRef, useEffect} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | export interface AutocompleteProps { 6 | inputField: HTMLInputElement | null; 7 | options?: google.maps.places.AutocompleteOptions; 8 | onPlaceChanged: (place: google.maps.places.PlaceResult) => void; 9 | } 10 | 11 | /** 12 | * Hook to get a Google Maps Places Autocomplete instance 13 | * monitoring an input field 14 | */ 15 | export const useAutocomplete = ( 16 | props: AutocompleteProps 17 | ): google.maps.places.Autocomplete | null => { 18 | const {inputField, options, onPlaceChanged} = props; 19 | const placeChangedHandler = useRef(onPlaceChanged); 20 | const {googleMapsAPIIsLoaded} = useContext(GoogleMapsContext); 21 | 22 | const [autocomplete, setAutocomplete] = 23 | useState(null); 24 | 25 | // Initializes the Google Maps Places Autocomplete 26 | useEffect(() => { 27 | // Wait for the Google Maps API and input element to be initialized 28 | if (!googleMapsAPIIsLoaded || !inputField) { 29 | return (): void => {}; 30 | } 31 | 32 | if (!google.maps.places) { 33 | throw Error( 34 | "Autocomplete library missing. Add 'places' to the libraries array of GoogleMapsProvider." 35 | ); 36 | } 37 | 38 | // Create Autocomplete instance 39 | const autocompleteInstance = new google.maps.places.Autocomplete( 40 | inputField, 41 | options 42 | ); 43 | setAutocomplete(autocompleteInstance); 44 | 45 | // Add places change listener to Autocomplete 46 | autocompleteInstance.addListener('place_changed', () => { 47 | const place = autocompleteInstance.getPlace(); 48 | placeChangedHandler.current && placeChangedHandler.current(place); 49 | }); 50 | 51 | // Clear listeners on unmount 52 | return (): void => { 53 | autocompleteInstance && 54 | google.maps.event.clearInstanceListeners(autocompleteInstance); 55 | }; 56 | }, [googleMapsAPIIsLoaded, inputField, options]); 57 | 58 | return autocomplete; 59 | }; 60 | -------------------------------------------------------------------------------- /library/src/hooks/directions-service.ts: -------------------------------------------------------------------------------- 1 | import {useContext, useMemo, useEffect, useCallback} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | export interface DirectionsServiceProps { 6 | renderOnMap?: boolean; 7 | renderOptions?: google.maps.DirectionsRendererOptions; 8 | } 9 | 10 | interface DirectionsServiceHookReturns { 11 | directionsService: google.maps.DirectionsService | null; 12 | directionsRenderer: google.maps.DirectionsRenderer | null; 13 | findRoute: 14 | | (( 15 | request: google.maps.DirectionsRequest 16 | ) => Promise) 17 | | null; 18 | findAndRenderRoute: 19 | | (( 20 | request: google.maps.DirectionsRequest 21 | ) => Promise) 22 | | null; 23 | renderRouteOfIndex: (index: number) => void; 24 | } 25 | 26 | /** 27 | * Hook to get Google Maps Places Directions Service instance 28 | */ 29 | export const useDirectionsService = ( 30 | props: DirectionsServiceProps = {} 31 | ): DirectionsServiceHookReturns => { 32 | const {renderOnMap, renderOptions} = props; 33 | const {googleMapsAPIIsLoaded, map} = useContext(GoogleMapsContext); 34 | 35 | // Creates a Directions Service instance 36 | const directionsService = 37 | useMemo(() => { 38 | // Wait for Google Maps API to be loaded 39 | if (!googleMapsAPIIsLoaded) { 40 | return null; 41 | } 42 | 43 | return new google.maps.DirectionsService(); 44 | }, [googleMapsAPIIsLoaded]); 45 | 46 | // Creates a Directions Renderer instance 47 | const directionsRenderer = 48 | useMemo(() => { 49 | // Wait for map to be initialized 50 | if (!map || !renderOnMap) { 51 | return null; 52 | } 53 | 54 | const renderer = new google.maps.DirectionsRenderer(renderOptions); 55 | renderer.setMap(map); 56 | 57 | return renderer; 58 | }, [map, renderOnMap]); 59 | 60 | // Updates the directions renderer options 61 | useEffect(() => { 62 | if (!directionsRenderer) { 63 | return; 64 | } 65 | 66 | directionsRenderer.setOptions(renderOptions || {}); 67 | }, [renderOptions]); 68 | 69 | // Custom Directions route request 70 | const findRoute = useCallback( 71 | ( 72 | request: google.maps.DirectionsRequest 73 | ): Promise => 74 | new Promise((resolve, reject) => { 75 | if (directionsService) { 76 | directionsService.route( 77 | request, 78 | ( 79 | result: google.maps.DirectionsResult | null, 80 | status: google.maps.DirectionsStatus 81 | ): void => { 82 | if (status !== google.maps.DirectionsStatus.OK || !result) { 83 | reject(status); 84 | } else { 85 | resolve(result); 86 | } 87 | } 88 | ); 89 | } 90 | }), 91 | [directionsService] 92 | ); 93 | 94 | // Custom Directions route request followed by directions rendering 95 | const findAndRenderRoute = useCallback( 96 | ( 97 | request: google.maps.DirectionsRequest 98 | ): Promise => 99 | new Promise((resolve, reject) => { 100 | if (directionsService) { 101 | directionsService.route( 102 | request, 103 | ( 104 | result: google.maps.DirectionsResult | null, 105 | status: google.maps.DirectionsStatus 106 | ): void => { 107 | if (status !== google.maps.DirectionsStatus.OK || !result) { 108 | reject(status); 109 | } else { 110 | if (directionsRenderer) { 111 | directionsRenderer.setDirections(result); 112 | } 113 | 114 | resolve(result); 115 | } 116 | } 117 | ); 118 | } 119 | }), 120 | [directionsService, directionsRenderer] 121 | ); 122 | 123 | // Renders directions route of given index 124 | const renderRouteOfIndex = (index: number) => { 125 | if (directionsRenderer) { 126 | directionsRenderer.setRouteIndex(index); 127 | } 128 | }; 129 | 130 | return { 131 | directionsService, 132 | directionsRenderer, 133 | findRoute: directionsService && findRoute, 134 | findAndRenderRoute: 135 | directionsService && directionsRenderer && findAndRenderRoute, 136 | renderRouteOfIndex 137 | }; 138 | }; 139 | -------------------------------------------------------------------------------- /library/src/hooks/distance-matrix-service.ts: -------------------------------------------------------------------------------- 1 | import {useContext, useMemo} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | /** 6 | * Hook to get Distance Matrix Service instance 7 | */ 8 | export const useDistanceMatrixService = 9 | (): google.maps.DistanceMatrixService | null => { 10 | const {googleMapsAPIIsLoaded} = useContext(GoogleMapsContext); 11 | 12 | // Creates a Distance Matrix Service instance 13 | const distanceMatrixService = 14 | useMemo(() => { 15 | // Wait for Google Maps API to be loaded 16 | if (!googleMapsAPIIsLoaded) { 17 | return null; 18 | } 19 | 20 | if (!google.maps.DistanceMatrixService) { 21 | throw Error('Distance Matrix library missing.'); 22 | } 23 | 24 | return new google.maps.DistanceMatrixService(); 25 | }, [googleMapsAPIIsLoaded]); 26 | 27 | return distanceMatrixService; 28 | }; 29 | -------------------------------------------------------------------------------- /library/src/hooks/elevation-service.ts: -------------------------------------------------------------------------------- 1 | import {useContext, useMemo} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | /** 6 | * Hook to get Elevation Service instance 7 | */ 8 | export const useElevationService = (): google.maps.ElevationService | null => { 9 | const {googleMapsAPIIsLoaded} = useContext(GoogleMapsContext); 10 | 11 | // Creates an Elevation Service instance 12 | const elevationService = useMemo(() => { 13 | // Wait for Google Maps API to be loaded 14 | if (!googleMapsAPIIsLoaded) { 15 | return null; 16 | } 17 | 18 | return new google.maps.ElevationService(); 19 | }, [googleMapsAPIIsLoaded]); 20 | 21 | return elevationService; 22 | }; 23 | -------------------------------------------------------------------------------- /library/src/hooks/geocoding-service.ts: -------------------------------------------------------------------------------- 1 | import {useContext, useMemo} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | /** 6 | * Hook to get Google Maps Geocoder instance 7 | */ 8 | export const useGeocodingService = (): google.maps.Geocoder | null => { 9 | const {googleMapsAPIIsLoaded} = useContext(GoogleMapsContext); 10 | 11 | // Creates a Geocoder instance 12 | const geocoder = useMemo(() => { 13 | // Wait for Google Maps API to be loaded 14 | if (!googleMapsAPIIsLoaded) { 15 | return null; 16 | } 17 | 18 | return new google.maps.Geocoder(); 19 | }, [googleMapsAPIIsLoaded]); 20 | 21 | return geocoder; 22 | }; 23 | -------------------------------------------------------------------------------- /library/src/hooks/map-instance.ts: -------------------------------------------------------------------------------- 1 | import {useContext} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | /** 6 | * Hook to get global map instance 7 | */ 8 | export const useGoogleMap = (): google.maps.Map | undefined => 9 | useContext(GoogleMapsContext).map; 10 | -------------------------------------------------------------------------------- /library/src/hooks/max-zoom-service.ts: -------------------------------------------------------------------------------- 1 | import {useContext, useMemo} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | /** 6 | * Hook to get Max Zoom Service instance 7 | */ 8 | export const useMaxZoomService = (): google.maps.MaxZoomService | null => { 9 | const {googleMapsAPIIsLoaded} = useContext(GoogleMapsContext); 10 | 11 | // Creates a Max Zoom Service instance 12 | const maxZoomService = useMemo(() => { 13 | // Wait for Google Maps API to be loaded 14 | if (!googleMapsAPIIsLoaded) { 15 | return null; 16 | } 17 | 18 | return new google.maps.MaxZoomService(); 19 | }, [googleMapsAPIIsLoaded]); 20 | 21 | return maxZoomService; 22 | }; 23 | -------------------------------------------------------------------------------- /library/src/hooks/places-service.ts: -------------------------------------------------------------------------------- 1 | import {useContext, useEffect, useState} from 'react'; 2 | 3 | import {GoogleMapsContext} from '../google-maps-provider'; 4 | 5 | export interface PlacesServiceProps { 6 | divElement?: HTMLDivElement | null; 7 | } 8 | 9 | /** 10 | * Hook to get Google Maps Places Service instance 11 | */ 12 | export const usePlacesService = ( 13 | props?: PlacesServiceProps 14 | ): google.maps.places.PlacesService | null => { 15 | const {map, googleMapsAPIIsLoaded} = useContext(GoogleMapsContext); 16 | 17 | const [placesService, setPlacesService] = 18 | useState(null); 19 | 20 | // Creates a Places Service instance 21 | useEffect(() => { 22 | if (!googleMapsAPIIsLoaded) { 23 | return; 24 | } 25 | 26 | if (!google.maps.places) { 27 | throw Error( 28 | "Places library missing. Add 'places' to the libraries array of GoogleMapsProvider." 29 | ); 30 | } 31 | 32 | // Create places service which renders attributions in the map container 33 | if (props?.divElement === undefined) { 34 | // Wait for map to be initialized 35 | if (!map) { 36 | return; 37 | } 38 | 39 | const serviceMap = new google.maps.places.PlacesService(map); 40 | setPlacesService(serviceMap); 41 | 42 | return; 43 | } 44 | 45 | // Create places service which renders attributions in the passed div element 46 | // Wait for div element to be available 47 | if (!props?.divElement) { 48 | return; 49 | } 50 | 51 | const serviceElement = new google.maps.places.PlacesService( 52 | props?.divElement 53 | ); 54 | setPlacesService(serviceElement); 55 | }, [googleMapsAPIIsLoaded, map, props?.divElement]); 56 | 57 | return placesService; 58 | }; 59 | -------------------------------------------------------------------------------- /library/src/hooks/street-view-panorama.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable complexity */ 2 | import {useContext, useEffect, useState} from 'react'; 3 | 4 | import {GoogleMapsContext} from '../google-maps-provider'; 5 | 6 | export interface StreetViewPanoramaProps { 7 | divElement?: HTMLElement | null; 8 | position?: google.maps.LatLng | google.maps.LatLngLiteral; 9 | pov?: google.maps.StreetViewPov; 10 | zoom?: number; 11 | } 12 | 13 | /** 14 | * Hook to get Street View Panorama 15 | */ 16 | export const useStreetViewPanorama = ( 17 | props: StreetViewPanoramaProps 18 | ): google.maps.StreetViewPanorama | null => { 19 | const {divElement, position, pov, zoom} = props; 20 | 21 | const {googleMapsAPIIsLoaded, map} = useContext(GoogleMapsContext); 22 | 23 | const [streetViewPanorama, setStreetViewPanorama] = 24 | useState(null); 25 | 26 | // Creates a Street View instance 27 | useEffect(() => { 28 | // If no div element is passed, initialize a map with Street View Panorama 29 | if (!divElement) { 30 | // Wait for Google Maps map instance 31 | if (!map) { 32 | return (): void => {}; 33 | } 34 | 35 | const newPanorama = map.getStreetView(); 36 | 37 | if (pov) { 38 | newPanorama.setPov(pov); 39 | } 40 | 41 | if (position) { 42 | newPanorama.setPosition(position); 43 | } 44 | 45 | // eslint-disable-next-line no-eq-null 46 | if (zoom != null) { 47 | newPanorama.setZoom(zoom); 48 | } 49 | 50 | setStreetViewPanorama(newPanorama); 51 | } else { 52 | // Wait for Google Maps API 53 | if (!googleMapsAPIIsLoaded) { 54 | return (): void => {}; 55 | } 56 | 57 | // If a div element is passed, initialize street view in the element 58 | const newPanorama = new google.maps.StreetViewPanorama(divElement, { 59 | position, 60 | pov, 61 | zoom 62 | }); 63 | 64 | setStreetViewPanorama(newPanorama); 65 | } 66 | 67 | return (): void => { 68 | if (!divElement && map) { 69 | map.setStreetView(null); 70 | } 71 | }; 72 | }, [map, divElement]); 73 | 74 | return streetViewPanorama; 75 | }; 76 | -------------------------------------------------------------------------------- /library/src/index.ts: -------------------------------------------------------------------------------- 1 | // codegen:start {preset: barrel, include: ./**/*, exclude: [./index.ts, ./types/*]} 2 | export * from './google-maps-provider'; 3 | export * from './hooks/autocomplete-service'; 4 | export * from './hooks/autocomplete'; 5 | export * from './hooks/directions-service'; 6 | export * from './hooks/distance-matrix-service'; 7 | export * from './hooks/elevation-service'; 8 | export * from './hooks/geocoding-service'; 9 | export * from './hooks/map-instance'; 10 | export * from './hooks/max-zoom-service'; 11 | export * from './hooks/places-service'; 12 | export * from './hooks/street-view-panorama'; 13 | // codegen:end 14 | -------------------------------------------------------------------------------- /library/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'google.maps'; 2 | 3 | /* eslint-disable init-declarations */ 4 | declare module '*.module.css' { 5 | const classes: {[key: string]: string}; 6 | export default classes; 7 | } 8 | -------------------------------------------------------------------------------- /library/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "typeRoots": ["./src/types"] 5 | }, 6 | "exclude": ["dist"] 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ubilabs/google-maps-react-hooks-library-and-examples", 3 | "description": "React hooks and map context provider for Google Maps", 4 | "homepage": "https://github.com/ubilabs/google-maps-react-hooks", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/ubilabs/google-maps-react-hooks.git" 8 | }, 9 | "scripts": { 10 | "test": "npm run test -ws --if-present", 11 | "start:library": "npm start -w library", 12 | "start:example": "./start-example.sh", 13 | "start:map-example": "cross-env EXAMPLE=map run-p start:library start:example", 14 | "start:map-with-markers-example": "cross-env EXAMPLE=map-with-markers run-p start:library start:example", 15 | "start:multiple-maps-example": "cross-env EXAMPLE=multiple-maps run-p start:library start:example", 16 | "start:geocoding-service-example": "cross-env EXAMPLE=geocoding-service run-p start:library start:example", 17 | "start:places-service-example": "cross-env EXAMPLE=places-service run-p start:library start:example", 18 | "start:places-service-with-element-example": "cross-env EXAMPLE=places-service-with-element run-p start:library start:example", 19 | "start:places-autocomplete-widget-example": "cross-env EXAMPLE=places-autocomplete-widget run-p start:library start:example", 20 | "start:directions-service-example": "cross-env EXAMPLE=directions-service run-p start:library start:example", 21 | "start:distance-matrix-service-example": "cross-env EXAMPLE=distance-matrix-service run-p start:library start:example", 22 | "start:elevation-service-example": "cross-env EXAMPLE=elevation-service run-p start:library start:example", 23 | "start:max-zoom-service-example": "cross-env EXAMPLE=max-zoom-service run-p start:library start:example", 24 | "start:places-autocomplete-service-example": "cross-env EXAMPLE=places-autocomplete-service run-p start:library start:example", 25 | "start:street-view-panorama-map-example": "cross-env EXAMPLE=street-view-panorama-map run-p start:library start:example", 26 | "start:street-view-panorama-element-example": "cross-env EXAMPLE=street-view-panorama-element run-p start:library start:example", 27 | "preversion": "echo \"To create a new library version run 'npm version -w library' in the repository root.\"" 28 | }, 29 | "keywords": [ 30 | "React hooks", 31 | "Google Maps" 32 | ], 33 | "bugs": { 34 | "url": "https://github.com/ubilabs/google-maps-react-hooks/issues" 35 | }, 36 | "license": "MIT", 37 | "workspaces": [ 38 | "examples", 39 | "library" 40 | ], 41 | "devDependencies": { 42 | "cross-env": "^7.0.3", 43 | "npm-run-all": "^4.1.5" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /start-example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [ -z ${EXAMPLE+x} ]; then 3 | echo Check out the examples README for how to start a specific example. 4 | exit 1 5 | else 6 | npm run start:$EXAMPLE -w examples 7 | fi -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "ES2015", 7 | "target": "es5", 8 | "lib": ["es2015", "dom"], 9 | "jsx": "react", 10 | "esModuleInterop": true, 11 | "types": ["node", "google.maps"], 12 | "moduleResolution": "node", 13 | "plugins": [{"name": "typescript-plugin-css-modules"}] 14 | }, 15 | "exclude": ["node_modules"] 16 | } 17 | --------------------------------------------------------------------------------