├── .browserslistrc ├── .editorconfig ├── .eslintrc.js ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── .prettierrc.js ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── build-copy.js ├── docs ├── .npmignore ├── package-lock.json ├── package.json └── src │ ├── .vuepress │ ├── components │ │ ├── Foo │ │ │ └── Bar.vue │ │ ├── OtherComponent.vue │ │ └── demo-component.vue │ ├── config.js │ ├── enhanceApp.js │ ├── public │ │ └── assets │ │ │ └── logo.jpg │ └── styles │ │ ├── index.styl │ │ └── palette.styl │ ├── components │ ├── README.md │ ├── autocomplete.md │ ├── circle.md │ ├── cluster.md │ ├── info-window.md │ ├── map.md │ ├── marker.md │ ├── polygon.md │ ├── polyline.md │ ├── rectangle.md │ └── using-vue.md │ ├── config │ └── README.md │ ├── docs │ └── README.md │ ├── examples │ ├── README.md │ ├── how-to-access-google-maps-object.md │ ├── how-to-add-a-custom-button-to-map.md │ ├── how-to-add-custom-controls.md │ ├── how-to-open-or-close-info-window-on-event.md │ ├── introduction.md │ ├── points-in-polygon.md │ └── using-custom-renderer-function-and-clustering-algorithm.md │ └── index.md ├── jsconfig.json ├── package-lock.json ├── package.json ├── service ├── commands │ └── build.js ├── config │ ├── base.js │ ├── css.js │ ├── dev.js │ ├── prod.js │ └── terserOptions.js ├── project.config.js └── utils │ ├── getLocalIP.js │ ├── logger.js │ ├── paths.js │ ├── resolveClientEnv.js │ └── spinner.js ├── src ├── components │ ├── autocomplete.vue │ ├── autocompleteImpl.js │ ├── build-component.js │ ├── circle.js │ ├── cluster.vue │ ├── heatmap.js │ ├── infoWindow.vue │ ├── map.vue │ ├── mapElementMixin.js │ ├── marker.vue │ ├── polygon.js │ ├── polyline.js │ └── rectangle.js ├── load-google-maps.js ├── main.js └── utils │ ├── TwoWayBindingWrapper.js │ ├── WatchPrimitiveProperties.js │ ├── bindEvents.js │ ├── bindProps.js │ ├── create-map-script.js │ ├── env.js │ ├── lazyValue.js │ ├── load-google-maps.js │ ├── mountableMixin.js │ ├── simulateArrowDown.js │ └── string.js ├── test ├── .eslintrc.json ├── basic.js ├── gmapApi-guard.js ├── maps-not-loaded.js ├── marker-with-infowindow.js ├── test-all-examples.js ├── test-pages │ ├── test-gmapApi.html │ ├── test-marker-with-infowindow.html │ ├── test-page-without-maps.html │ └── test-plain-map.html └── test-setup │ ├── babel-transform.js │ ├── compile-standalone.js │ └── test-common.js └── types └── index.d.ts /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset=utf-8 5 | end_of_line=lf 6 | insert_final_newline=true 7 | indent_style=space 8 | indent_size=2 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | node: true, 6 | }, 7 | "globals": { 8 | "google": true 9 | }, 10 | parser: 'vue-eslint-parser', 11 | parserOptions: { 12 | ecmaVersion: 2020, 13 | extraFileExtensions: ['.vue'], 14 | ecmaFeatures: { 15 | jsx: true, 16 | }, 17 | sourceType: 'module', 18 | }, 19 | 20 | extends: [ 21 | 'plugin:vue/vue3-essential', 22 | 'eslint:recommended', 23 | 'plugin:prettier/recommended', 24 | 'prettier', 25 | ], 26 | 27 | rules: { 28 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 29 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 30 | 'vue/multi-word-component-names': ['off'], 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "extends": "standard", 9 | "parserOptions": { 10 | "sourceType": "module" 11 | }, 12 | "rules": { 13 | "comma-dangle": 0 14 | }, 15 | "globals": { 16 | "google": true 17 | }, 18 | "plugins": [ 19 | "html" 20 | ], 21 | "settings": { 22 | "html/html-extensions": [".html", ".vue"] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a new bug 3 | about: Create a bug report to help our library improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Read before opening a new issue (I suggest you read in preview mode)** 10 | **Are you asking a question or need help?** 11 | Please use Github discussion, you will find better help there. 12 | Notice that if you create a question issue, we will close it and create a new discussion for you. 13 | 14 | **Having problems with an event?** 15 | You should trying enabling events before using them. 16 | More details here: https://vue-map.netlify.app/docs/#register-google-maps-events. 17 | 18 | **Playcode support** 19 | We strongly recommend you to use Playcode to reproduce your code! 20 | You can find a small template [here](https://playcode.io/1012771). 21 | Following the steps on the template will make things really, really easier! 22 | 23 | **Codepen support** 24 | You can use Codepen to report your bug as well! 25 | In your Codepen, go to Settings, then select JS. In Add Packages section, search for vue-google-maps-community-fork. 26 | TBH Codepen isn't a really good option because of the file structure, but feel free to use if you prefer it! 27 | 28 | **Lastly, remember to look if this issue isn't already created** 29 | 30 | **OK! You can delete everything until here!** 31 | **You can also delete whatever part you want in this issue, but please make it clear** 32 | 33 | **Describe what you're reporting** 34 | Don't be ashamed to show what you're doing. Give us a clear and concise description of what the bug is. 35 | 36 | **How can we reproduce it?** 37 | Show us how to reproduce the bug. 38 | 39 | Example: 40 | 41 | 1. Go to '...' 42 | 2. Click on '....' 43 | 3. Scroll down to '....' 44 | 4. See error 45 | 46 | **What were your expecting to happen?** 47 | A description of the expected behavior. 48 | 49 | **What happened?** 50 | A description of the actual behavior. 51 | 52 | **Screenshots** 53 | You can send screenshots to help express what you want to show us. 54 | 55 | **Device or enviroment** 56 | 57 | - In which OS are you seeing this bug? Examples: Windows 11, Ubuntu, iOS. 58 | - If it is relevant, what browser are you? Examples: Chrome, Safari, Opera. 59 | - What is your npm version? You can check using npm -v. 60 | - What is your vue version? You can check using npm list. 61 | - What is your vue-google-maps-community-fork version? You can check using npm list. 62 | 63 | **Additional context** 64 | You also can add more context for your situation here. Feel free to express whatever you think its important. 65 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest a new feature to make our library better 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Read before opening a new issue** 10 | **Are you asking a question or need help?** 11 | Please use Github discussion, you will find better help there. 12 | Notice that if you create a question issue, we will close it and create a new discussion for you. 13 | 14 | **Playcode support** 15 | We strongly recommend you to use Playcode to reproduce your code! 16 | You can find a small template [here](https://playcode.io/1012771). 17 | Following the steps on the template will make things really, really easier! 18 | 19 | **Codepen support** 20 | You can use Codepen to report your bug as well! 21 | In your Codepen, go to Settings, then select JS. In Add Packages section, search for vue-google-maps-community-fork. 22 | TBH Codepen isn't a really good option because of the file structure, but feel free to use if you prefer it! 23 | 24 | **Lastly, remember to look if this issue isn't already created** 25 | 26 | **OK! You can delete everything until here!** 27 | **You can also delete whatever part you want in this issue, but please make it clear** 28 | 29 | **Does your feature has anything related to a problem?** 30 | Please describe what the problem is related to. For example: I'm frustrated that I cannot do [...] easily. 31 | 32 | **What is the best solution you'd like to see?** 33 | Describe what you think should be happening to fix that situation. 34 | 35 | **What did you try? Did you have some alternative to consider?** 36 | Is there any other solution or feature it could help fixing this situation? 37 | 38 | **Additional context** 39 | You also can add more context for your situation here. Feel free to express whatever you think its important. 40 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | 2 | # To get started with Dependabot version updates, you'll need to specify which 3 | # package ecosystems to update and where the package manifests are located. 4 | # Please see the documentation for all configuration options: 5 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "npm" # See documentation for possible values 10 | directory: "/" # Location of package manifests 11 | schedule: 12 | interval: "daily" 13 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '40 20 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: npm-publish 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | npm-publish: 8 | name: npm-publish 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@master 13 | - name: Set up Node.js 14 | uses: actions/setup-node@master 15 | with: 16 | node-version: 10.0.0 17 | - name: Publish if version has been updated 18 | uses: houtianze/npm-publish-action@master 19 | with: 20 | tag_name: "v%s" 21 | tag_message: "v%s" 22 | commit_pattern: "^(?:Release|Version) (\\S+)" 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | 23 | # Git 24 | *.diff 25 | *.patch 26 | *.bak 27 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | service 3 | test 4 | .browserlist 5 | .editorconfig 6 | .eslintrc 7 | .gitignore 8 | .prettierrc.js 9 | babel.config.js 10 | build-copy.js 11 | CONTRIBUTING.md 12 | jsconfig.json 13 | docs 14 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | semi: false, 4 | singleQuote: true, 5 | } 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md or docs with details of changes if necessary. 13 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 14 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 15 | 16 | ## Code of Conduct 17 | 18 | ### Our Pledge 19 | 20 | In the interest of fostering an open and welcoming environment, we as 21 | contributors and maintainers pledge to making participation in our project and 22 | our community a harassment-free experience for everyone, regardless of age, body 23 | size, disability, ethnicity, gender identity and expression, level of experience, 24 | nationality, personal appearance, race, religion, or sexual identity and 25 | orientation. 26 | 27 | ### Our Standards 28 | 29 | Examples of behavior that contributes to creating a positive environment 30 | include: 31 | 32 | * Using welcoming and inclusive language 33 | * Being respectful of differing viewpoints and experiences 34 | * Gracefully accepting constructive criticism 35 | * Focusing on what is best for the community 36 | * Showing empathy towards other community members 37 | 38 | Examples of unacceptable behavior by participants include: 39 | 40 | * The use of sexualized language or imagery and unwelcome sexual attention or 41 | advances 42 | * Trolling, insulting/derogatory comments, and personal or political attacks 43 | * Public or private harassment 44 | * Publishing others' private information, such as a physical or electronic 45 | address, without explicit permission 46 | * Other conduct which could reasonably be considered inappropriate in a 47 | professional setting 48 | 49 | ### Our Responsibilities 50 | 51 | Project maintainers are responsible for clarifying the standards of acceptable 52 | behavior and are expected to take appropriate and fair corrective action in 53 | response to any instances of unacceptable behavior. 54 | 55 | Project maintainers have the right and responsibility to remove, edit, or 56 | reject comments, commits, code, wiki edits, issues, and other contributions 57 | that are not aligned to this Code of Conduct, or to ban temporarily or 58 | permanently any contributor for other behaviors that they deem inappropriate, 59 | threatening, offensive, or harmful. 60 | 61 | ### Scope 62 | 63 | This Code of Conduct applies both within project spaces and in public spaces 64 | when an individual is representing the project or its community. Examples of 65 | representing a project or community include using an official project e-mail 66 | address, posting via an official social media account, or acting as an appointed 67 | representative at an online or offline event. Representation of a project may be 68 | further defined and clarified by project maintainers. 69 | 70 | ### Enforcement 71 | 72 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 73 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 74 | complaints will be reviewed and investigated and will result in a response that 75 | is deemed necessary and appropriate to the circumstances. The project team is 76 | obligated to maintain confidentiality with regard to the reporter of an incident. 77 | Further details of specific enforcement policies may be posted separately. 78 | 79 | Project maintainers who do not follow or enforce the Code of Conduct in good 80 | faith may face temporary or permanent repercussions as determined by other 81 | members of the project's leadership. 82 | 83 | ### Attribution 84 | 85 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 86 | available at [http://contributor-covenant.org/version/1/4][version] 87 | 88 | [homepage]: http://contributor-covenant.org 89 | [version]: http://contributor-covenant.org/version/1/4/ 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jamie Yang 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 | ## Note for the community 2 | 3 | Hey guys, today is 18/12/2024 and I'm doing this update to address you on the current situation of this library. 4 | 5 | To be honest I cannot help it improve anymore because my life changed too much in the last year. That said, you probably should be using [vue3-google-map](https://www.npmjs.com/package/vue3-google-map) for now on because their job is incredible and they're doing a great project for Google Maps features. Sorry to let you guys down but I prefer to be honest than say something that I won't be able to handle. 6 | 7 | I'm not turning it into deprecated yet, but probably will do soon. This project is awesome and it worked like a charm for me but I believe it would need an entire refactor to TS to make it better and scalable. Fawmi's code was so good that even after two and half years of no updates it still had 2.5k weekly downloads. 8 | 9 | Anyway, thanks for everything and sorry again. Much love and luck. 10 | 11 | ## Welcome! 12 | 13 | Hi! Vue Google Maps Community Fork is a communitary repository. It is a set of the most used Google Maps components made for VueJS 3. 14 | 15 | ## About this repository 16 | 17 | This repository was forked in October/2022 after the original repository's community decide that it was the best for the project. In [the same discussion](https://github.com/NathanAP/vue-google-maps-community-fork/discussions/1) you will find every information you need about our motivation. 18 | 19 | We have an update! Fawmi answered us, please access the discussion to know more about it! 20 | 21 | ## Changelog 22 | 23 | You can follow the official changelog [here](https://github.com/NathanAP/vue-google-maps-community-fork/discussions/6)! 24 | 25 | ## Documentation 26 | 27 | You can find a detailed documentation [here](https://vue-google-maps-community-fork.netlify.app). It will show you how to use every component this library contains and demonstrate some examples for you. 28 | 29 | Also, you can enter in [this discussion](https://github.com/NathanAP/vue-google-maps-community-fork/discussions/9) to talk about or read the documentation changelog. 30 | 31 | It is in our plans to make it better and more detailed, but for now it might help you start using the library. 32 | 33 | ## Installation 34 | 35 | You can install this library using this command: 36 | 37 | ``` 38 | npm install vue-google-maps-community-fork 39 | ``` 40 | 41 | ## Basic usage 42 | 43 | To use this library you will need an API Key. You can learn how to get an API Key [here](https://developers.google.com/maps/documentation/javascript/get-api-key). 44 | 45 | ### Configure Vue to use the Components 46 | 47 | In your `main.js` 48 | 49 | ```js 50 | import { createApp } from 'vue' 51 | import VueGoogleMaps from 'vue-google-maps-community-fork' 52 | 53 | const app = createApp(App) 54 | app 55 | .use(VueGoogleMaps, { 56 | load: { 57 | key: 'YOUR_API_KEY_COMES_HERE', 58 | }, 59 | }) 60 | .mount('#app') 61 | ``` 62 | 63 | ### If you are configuring your project please notice 64 | 65 | You might be getting the following error: 66 | 67 | `Requested module 'fast-deep-equal' does not provide an export named 'default'` 68 | 69 | Some notes about this: 70 | 71 | - [We are aware of this problem](https://github.com/NathanAP/vue-google-maps-community-fork/issues/4). 72 | - It doesn't seems that we have control over this, so we hope that it will be fixed soon enough by the responsibles. 73 | - To avoid it right now, you need to do this configuration in your `vite.config.js`: 74 | 75 | ```js 76 | optimizeDeps: { 77 | include: [ 78 | "vue-google-maps-community-fork", 79 | "fast-deep-equal", 80 | ], 81 | }, 82 | ``` 83 | 84 | - This **IS NOT** a fix! This is just a hack we are using to avoid the problem. 85 | - We encourage you to subscribe to that issue to stay in touch with this problem 86 | 87 | ### Using our components anywhere 88 | 89 | ```vue 90 | 94 | 95 | 105 | ``` 106 | 107 | ## Components 108 | 109 | ### Markers 110 | 111 | If you need to add markers to the `Map`, add `GMapMarker` as child of `GMapMap` component. 112 | 113 | ```vue 114 | 119 | 138 | ``` 139 | 140 | ## Center markers in the middle of the map 141 | 142 | If you want to zoom in on the map as far as you can, but still see all markers, reference this code snippet. 143 | 144 | ```vue 145 | 186 | 187 | 196 | 197 | ``` 198 | 199 | ### Cluster 200 | 201 | If you have too many markers, it is helpful to cluster markers. You can easily cluster markers by wrapping your markers with `GMapCluster` component. 202 | 203 | Note: clusters were not working in the original version of this package. All of the clusters are here because of the community fixes. If you're having trouble with it please [try using the docs](https://vue-google-maps-community-fork.netlify.app/components/cluster.html). Also, feel free to open discussions or issues to get help. 204 | 205 | ```vue 206 | 220 | 238 | ``` 239 | 240 | ### Heatmap 241 | 242 | If you need to add heatmap layer to the Map, add visualization library in load config and add GMapHeatmap as child of GMapMap component. 243 | 244 | ```js 245 | import { createApp } from 'vue' 246 | import VueGoogleMaps from '@fawmi/vue-google-maps' 247 | 248 | const app = createApp(App) 249 | app 250 | .use(VueGoogleMaps, { 251 | load: { 252 | key: 'YOUR_API_KEY_COMES_HERE', 253 | libraries: 'visualization', 254 | }, 255 | }) 256 | .mount('#app') 257 | ``` 258 | 259 | ```vue 260 | 265 | 293 | ``` 294 | 295 | Checkout docs for more component 296 | 297 | ## Access map object 298 | 299 | If you want to access `google map` object, you can access it by getting ref of the map object. 300 | 301 | ```vue 302 | 305 | 312 | ``` 313 | 314 | ### Map options 315 | 316 | You can pass Map options using options property: 317 | 318 | See [MapOptions](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions) for a complete list of available options. 319 | 320 | ```vue 321 | 332 | 333 | ``` 334 | 335 | ## More components 336 | 337 | Many other components are also supported. Checkout [docs](https://vue-map.netlify.app) for more. 338 | 339 | ## Nuxt 3 usage 340 | 341 | Warning: this is part of the old documentation and I never used Nuxt, please let me know if it will work properly this way. 342 | 343 | In order to your Nuxt 3 project work properly with this library, you need to add `vue-google-maps-community-fork` to `build.transpile` property in your `nuxt.config.ts`. 344 | 345 | Also, as pointed [here](https://github.com/NathanAP/vue-google-maps-community-fork/issues/14), you will need to add `@googlemaps/markercluster` into it as well for your builded project work properly. 346 | 347 | Here is an example: 348 | 349 | ```ts 350 | export default defineNuxtConfig({ 351 | build: { 352 | transpile: ['vue-google-maps-community-fork', '@googlemaps/markercluster'], 353 | }, 354 | }) 355 | ``` 356 | 357 | After that, you need to configure your plugin `~/plugin/vueGoogleMaps.ts`. 358 | 359 | ```ts 360 | import { defineNuxtPlugin } from '#app' 361 | import VueGoogleMaps from 'vue-google-maps-community-fork' 362 | 363 | export default defineNuxtPlugin((nuxtApp) => { 364 | nuxtApp.vueApp.use(VueGoogleMaps, { 365 | load: { 366 | key: 'YOUR_API_KEY_COMES_HERE', 367 | }, 368 | }) 369 | }) 370 | ``` 371 | 372 | ## Sponsorship 373 | 374 | Please read [this discussion](https://github.com/NathanAP/vue-google-maps-community-fork/discussions/1) where we talk about sponsorships. 375 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | useBuiltIns: 'usage', // adds specific imports for polyfills when they are used in each file. 7 | modules: false, // preserve ES modules. 8 | corejs: { version: 3, proposals: true }, // enable polyfilling of every proposal supported by core-js. 9 | }, 10 | ], 11 | ], 12 | plugins: [ 13 | '@babel/plugin-transform-runtime', // enables the re-use of Babel's injected helper code to save on codesize. 14 | ], 15 | exclude: [/core-js/], 16 | } 17 | -------------------------------------------------------------------------------- /build-copy.js: -------------------------------------------------------------------------------- 1 | /* copies the components to the dist directory */ 2 | require('shelljs/global') 3 | 4 | cp('src/components/infoWindow.vue', 'dist/components/') 5 | cp('src/components/map.vue', 'dist/components/') 6 | cp('src/components/placeInput.vue', 'dist/components/') 7 | cp('src/components/autocomplete.vue', 'dist/components/') 8 | cp('src/components/streetViewPanorama.vue', 'dist/components/') 9 | -------------------------------------------------------------------------------- /docs/.npmignore: -------------------------------------------------------------------------------- 1 | pids 2 | logs 3 | node_modules 4 | npm-debug.log 5 | coverage/ 6 | run 7 | dist 8 | .DS_Store 9 | .nyc_output 10 | .basement 11 | config.local.js 12 | basement_dist 13 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-google-maps-community-fork-docs", 3 | "version": "0.2.0", 4 | "description": "Documentation for Vue Google Maps Community Fork", 5 | "main": "index.js", 6 | "authors": { 7 | "name": "Nathan A Paludo", 8 | "email": "natspaludo@gmail.com" 9 | }, 10 | "repository": "NathanAP/vue-google-maps-community-fork", 11 | "scripts": { 12 | "dev": "vuepress dev src", 13 | "build": "vuepress build src" 14 | }, 15 | "license": "MIT", 16 | "devDependencies": { 17 | "vuepress": "^1.9.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/src/.vuepress/components/Foo/Bar.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /docs/src/.vuepress/components/OtherComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /docs/src/.vuepress/components/demo-component.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /docs/src/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const { description } = require('../../package') 2 | 3 | module.exports = { 4 | /** 5 | * Ref:https://v1.vuepress.vuejs.org/config/#title 6 | */ 7 | title: 'Vue 3 Google Maps - Community Fork', 8 | /** 9 | * Ref:https://v1.vuepress.vuejs.org/config/#description 10 | */ 11 | description: description, 12 | 13 | /** 14 | * Extra tags to be injected to the page HTML `` 15 | * 16 | * ref:https://v1.vuepress.vuejs.org/config/#head 17 | */ 18 | head: [ 19 | ['meta', { name: 'theme-color', content: '#2c3e50' }], 20 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], 21 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], 22 | ], 23 | 24 | /** 25 | * Theme configuration, here is the default theme configuration for VuePress. 26 | * 27 | * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html 28 | */ 29 | themeConfig: { 30 | repo: '', 31 | editLinks: true, 32 | docsDir: '', 33 | editLinkText: '', 34 | logo: '/assets/logo.jpg', 35 | head: [['meta', { name: 'theme-color', content: '#2c3e50' }]], 36 | lastUpdated: false, 37 | nav: [ 38 | { 39 | text: 'Docs', 40 | link: '/docs/', 41 | }, 42 | { 43 | text: 'GitHub', 44 | link: 'https://github.com/NathanAP/vue-google-maps-community-fork', 45 | }, 46 | { 47 | text: 'npm', 48 | link: 'https://www.npmjs.com/package/vue-google-maps-community-fork', 49 | }, 50 | ], 51 | sidebar: [ 52 | { 53 | title: 'Getting started', 54 | path: '/docs/', 55 | collapsable: false, 56 | sidebarDepth: 0, 57 | }, 58 | { 59 | title: 'Components', 60 | collapsable: false, 61 | path: '/components/', 62 | sidebarDepth: 0, 63 | children: [ 64 | '/components/map', 65 | '/components/marker', 66 | '/components/info-window', 67 | '/components/cluster', 68 | '/components/circle', 69 | '/components/polygon', 70 | '/components/polyline', 71 | '/components/rectangle', 72 | '/components/autocomplete', 73 | ], 74 | }, 75 | { 76 | title: 'Examples', 77 | collapsable: false, 78 | sidebarDepth: 0, 79 | path: '/examples/', 80 | children: [ 81 | '/examples/how-to-add-a-custom-button-to-map', 82 | '/examples/points-in-polygon', 83 | '/examples/how-to-access-google-maps-object', 84 | '/examples/how-to-add-custom-controls', 85 | '/examples/how-to-open-or-close-info-window-on-event', 86 | '/examples/using-custom-renderer-function-and-clustering-algorithm', 87 | ], 88 | }, 89 | ], 90 | }, 91 | 92 | /** 93 | * Apply plugins,ref:https://v1.vuepress.vuejs.org/zh/plugin/ 94 | */ 95 | plugins: ['@vuepress/plugin-back-to-top', '@vuepress/plugin-medium-zoom'], 96 | } 97 | -------------------------------------------------------------------------------- /docs/src/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Client app enhancement file. 3 | * 4 | * https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements 5 | */ 6 | 7 | export default ({ 8 | Vue, // the version of Vue being used in the VuePress app 9 | options, // the options for the root Vue instance 10 | router, // the router instance for the app 11 | siteData // site metadata 12 | }) => { 13 | // ...apply enhancements for the site. 14 | } 15 | -------------------------------------------------------------------------------- /docs/src/.vuepress/public/assets/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NathanAP/vue-google-maps-community-fork/cb642738492a46b3c9e691900c05c863eb771816/docs/src/.vuepress/public/assets/logo.jpg -------------------------------------------------------------------------------- /docs/src/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom Styles here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/config/#index-styl 5 | */ 6 | 7 | .home .hero img 8 | max-width 450px!important 9 | -------------------------------------------------------------------------------- /docs/src/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom palette here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl 5 | */ 6 | 7 | $accentColor = #2c3e50 8 | $textColor = #2c3e50 9 | $borderColor = #eaecef 10 | $codeBgColor = #282c34 11 | -------------------------------------------------------------------------------- /docs/src/components/README.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | In this section you will find everything about the currently supported components: 4 | 5 | - [Map](./map.md) 6 | 7 | - [Marker](./marker.md) 8 | 9 | - [InfoWindow](./info-window.md) 10 | 11 | - [Cluster](./cluster.md) 12 | 13 | - [Circle](./circle.md) 14 | 15 | - [Polygon](./polygon.md) 16 | 17 | - [Polyline](./polyline.md) 18 | 19 | - [Rectangle](./rectangle.md) 20 | 21 | - [Autocomplete](./autocomplete.md) 22 | -------------------------------------------------------------------------------- /docs/src/components/autocomplete.md: -------------------------------------------------------------------------------- 1 | # Autocomplete 2 | 3 | Here you will find some uses for Google Maps Autocomplete component: 4 | 5 | [[toc]] 6 | 7 | ## Pre-requisite: load places library 8 | 9 | Before using Autocomplete, you need to load the places library: 10 | 11 | ```js 12 | createApp(App) 13 | .use(VueGoogleMaps, { 14 | load: { 15 | key: 'YOUR_API_KEY_COMES_HERE', 16 | libraries: 'places', 17 | }, 18 | }) 19 | .mount('#app') 20 | ``` 21 | 22 | ## Adding Autocomplete to your components 23 | 24 | You can add an Autocomplete to your `template` using `GMapAutocomplete` component: 25 | 26 | ```html 27 | 30 | 31 | 42 | ``` 43 | 44 | ## Adding a custom input for autocomplete 45 | 46 | You can customize input for autocomplete. 47 | 48 | ```html 49 | 61 | 62 | 75 | ``` 76 | 77 | ## Adding custom options 78 | 79 | You can pass Google Maps Autocomplete options using the prop `options`: 80 | 81 | ```html 82 | 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/src/components/circle.md: -------------------------------------------------------------------------------- 1 | # Circle 2 | 3 | Here you will find some uses for Google Maps Circle component: 4 | 5 | [[toc]] 6 | 7 | ## Adding Circle to your Maps 8 | 9 | You can add circles to your Maps using `GMapCircle` component inside of `GMapMap` component: 10 | 11 | ```html 12 | 22 | 23 | 59 | ``` 60 | 61 | ## Adding custom options 62 | 63 | You can pass Google Maps Circle options using the prop `options`: 64 | 65 | ```html 66 | 67 | 68 | 69 | 70 | 86 | ``` 87 | -------------------------------------------------------------------------------- /docs/src/components/cluster.md: -------------------------------------------------------------------------------- 1 | # Cluster 2 | 3 | Here you will find some uses for Google Maps Clusterers. 4 | 5 | [[toc]] 6 | 7 | ## Before everything 8 | 9 | Clustering your markers are only possible because of `@googlemaps/markerclusterer`! The community is thankful for your effort! 10 | 11 | ## Cluster your markers 12 | 13 | You can add clusters to your `GMapMarker` using `GMapCluster` component inside of `GMapMap` component: 14 | 15 | ```html 16 | 30 | 31 | 49 | ``` 50 | 51 | ## Props 52 | 53 | You can pass the following props to control behavior of clusters: 54 | 55 | ### minimumClusterSize 56 | 57 | Defines the minimum distance of markers to cluster them: 58 | 59 | ```js 60 | :minimumClusterSize="2" 61 | ``` 62 | 63 | ### styles 64 | 65 | Controls style and icon of clusters: 66 | 67 | ```js 68 | :styles="clusterIcon" 69 | ``` 70 | 71 | ### zoomOnClick 72 | 73 | Defines whether clusters should zoom in, when clicked: 74 | 75 | ```js 76 | :zoomOnClick="true" 77 | ``` 78 | -------------------------------------------------------------------------------- /docs/src/components/info-window.md: -------------------------------------------------------------------------------- 1 | # Info window 2 | 3 | Here you will find some uses for Google Maps Info Window component: 4 | 5 | [[toc]] 6 | 7 | ## Adding info window to your Google Maps components 8 | 9 | You can create info window by passing a custom HTML or Vue components as the child of `GMapInfoWindow` component: 10 | 11 | ```html 12 | 13 | 14 | 15 |
I am in info window
16 | 17 |
18 |
19 |
20 | ``` 21 | 22 | ## Opening or closing an Info window 23 | 24 | You can pass the `opened` prop to open or close a `GMapInfoWindow`: 25 | 26 | ```html 27 | 28 | 29 | 30 |
I am in info window
31 | 32 |
33 |
34 |
35 | ``` 36 | 37 | ## Adding custom options 38 | 39 | You can pass any Google Maps Info Window component using the prop `options`: 40 | 41 | ```html 42 | 43 | 44 | 54 |
I am in info window
55 | 56 |
57 |
58 |
59 | ``` 60 | -------------------------------------------------------------------------------- /docs/src/components/map.md: -------------------------------------------------------------------------------- 1 | # Map 2 | 3 | Here is where you start. Here you will find some uses for Google Maps component: 4 | 5 | [[toc]] 6 | 7 | ## My first Google Maps component 8 | 9 | You can create a Google Map component using `GMapMap`: 10 | 11 | ```html 12 | 13 | 14 | ``` 15 | 16 | Example on [Playcode](https://playcode.io/1041241) 17 | 18 | ## Styling your Google Maps component 19 | 20 | You can generate custom map styles at [https://mapstyle.withgoogle.com/](https://mapstyle.withgoogle.com/). 21 | 22 | You can provide custom styles by providing `style` property to the `options` prop: 23 | 24 | ```html 25 | 31 | 32 | 46 | ``` 47 | 48 | Example on [Playcode](https://playcode.io/1041245) 49 | 50 | ### Cloud-based styles with Map ID 51 | 52 | You can manage your cloud-based styles on [Google Maps Platform: Map Styles](https://console.cloud.google.com/google/maps-apis/studio/styles), and your map ids on [Google Maps Platform: Map Management](https://console.cloud.google.com/google/maps-apis/studio/maps) 53 | 54 | [Documentation: Maps JavaScript API - Using Cloud-based maps styling](https://developers.google.com/maps/documentation/javascript/cloud-based-map-styling) 55 | 56 | ```html 57 | 67 | 68 | 80 | ``` 81 | 82 | ## Disable UI elements 83 | 84 | You can disable all UI components at once: 85 | 86 | ```html 87 | 88 | ``` 89 | 90 | You can also disable specific UI components: 91 | 92 | ```html 93 | 105 | ``` 106 | 107 | ## Max and Min Zoom 108 | 109 | You can set the maximum and minimum zoom levels for the map: 110 | 111 | ```html 112 | 117 | ``` 118 | 119 | - Max zoom specifies how far the user can zoom in, 18-20 is normally the max. 120 | - Min zoom is how far they can zoom out, 0-3 is normally the min. 121 | 122 | 123 | ## Access Google Maps instance 124 | 125 | You can easily access the Map instance by accessing `ref` in your component: 126 | 127 | ```html 128 | 134 | ``` 135 | -------------------------------------------------------------------------------- /docs/src/components/marker.md: -------------------------------------------------------------------------------- 1 | # Marker 2 | 3 | Here you will find some uses for Google Maps Marker component: 4 | 5 | [[toc]] 6 | 7 | ## Adding marker to your components 8 | 9 | You can add markers to your Maps using `GMapMarker` component inside of `GMapMap` component: 10 | 11 | ```html 12 | 17 | 18 | 35 | ``` 36 | 37 | ## Enabling or disabling events 38 | 39 | You can enable or disable the Google Maps Marker events by passing `props`: 40 | 41 | ```html 42 | 53 | ``` 54 | 55 | ## Registering events 56 | 57 | You can register events (like click) in the same way as you do in your Vue components: 58 | 59 | ```html 60 | 71 | ``` 72 | 73 | ## Adding a custom icon 74 | 75 | You can use `:icon` prop to pass a custom icon to your `GMapMarker`. Also, you can pass a local resource or an image from internet: 76 | 77 | ```html 78 | 90 | ``` 91 | 92 | Local resources can be passed in using `require`, for example: `:icon="require('@/assets/images/place-icon.svg').default"`. 93 | 94 | The `icon` prop also can receive an object to define custom size and label origin: 95 | 96 | ```html 97 | 113 | ``` 114 | -------------------------------------------------------------------------------- /docs/src/components/polygon.md: -------------------------------------------------------------------------------- 1 | # Polygon 2 | 3 | Here you will find some uses for Google Maps Polygon component: 4 | 5 | [[toc]] 6 | 7 | ## Adding Polygon to your Maps 8 | 9 | You can add polygons to your Maps using `GMapPolygon` component inside of `GMapMap` component: 10 | 11 | ```html 12 | 17 | 32 | ``` 33 | 34 | ## Adding custom options 35 | 36 | You can pass Google Maps Polygon options using the prop `options`: 37 | 38 | ```html 39 | 44 | 45 | 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/src/components/polyline.md: -------------------------------------------------------------------------------- 1 | # Polyline 2 | 3 | Here you will find some uses for Google Maps Polyline component: 4 | 5 | [[toc]] 6 | 7 | ## Adding Polyline to your Maps 8 | 9 | You can add polygons to your Maps using `GMapPolyline` component inside of `GMapMap` component: 10 | 11 | ```html 12 | 15 | 16 | 30 | ``` 31 | 32 | ## Making polyline editable 33 | 34 | You can make your `GMapPolyline` component editable using `editable` prop: 35 | 36 | ```html 37 | 42 | 43 | 51 | ``` 52 | 53 | ## Adding custom options 54 | 55 | You can pass Google Maps Polyline options using the prop `options`: 56 | 57 | ```html 58 | 63 | 64 | 74 | ``` 75 | -------------------------------------------------------------------------------- /docs/src/components/rectangle.md: -------------------------------------------------------------------------------- 1 | # Rectangle 2 | 3 | Here you will find some uses for Google Maps Rectangle component: 4 | 5 | [[toc]] 6 | 7 | ## Adding Rectangle to your Maps 8 | 9 | You can add polygons to your Maps using `GMapRectangle` component inside of `GMapMap` component: 10 | 11 | ```html 12 | 17 | 18 | 34 | ``` 35 | 36 | ## Adding custom options 37 | 38 | You can pass Google Maps Rectangle options using the prop `options`: 39 | 40 | ```html 41 | 46 | 47 | 70 | ``` 71 | -------------------------------------------------------------------------------- /docs/src/components/using-vue.md: -------------------------------------------------------------------------------- 1 | # Using Vue in Markdown 2 | 3 | ## Browser API Access Restrictions 4 | 5 | Because VuePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the [universal code requirements](https://ssr.vuejs.org/en/universal.html). In short, make sure to only access Browser / DOM APIs in `beforeMount` or `mounted` hooks. 6 | 7 | If you are using or demoing components that are not SSR friendly (for example containing custom directives), you can wrap them inside the built-in `` component: 8 | 9 | ## 10 | -------------------------------------------------------------------------------- /docs/src/config/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: auto 3 | --- 4 | 5 | # Config 6 | 7 | ## foo 8 | 9 | - Type: `string` 10 | - Default: `/` 11 | 12 | ## bar 13 | 14 | - Type: `string` 15 | - Default: `/` 16 | -------------------------------------------------------------------------------- /docs/src/docs/README.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | Welcome! In `vue-google-maps-community-fork` will you find a set of VueJS 3 components wrapping the Google Maps API. 4 | 5 | ## Before starting 6 | 7 | It is important to notice that this repository was forked by the community to keep the library alive. You can get more infor about our decision [in this GitHub discussion](https://github.com/NathanAP/vue-google-maps-community-fork/discussions/1). 8 | 9 | Since this library is currently maintained by the community, every help is needed and appreciated! You can follow everything in our [GitHub repository](https://github.com/NathanAP/vue-google-maps-community-fork). 10 | 11 | ## Installation 12 | 13 | You can install this library using npm: 14 | 15 | ``` 16 | npm install vue-google-maps-community-fork 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | To use this library you will need an API Key. You can learn how to get one [here](https://developers.google.com/maps/documentation/javascript/get-api-key). 22 | 23 | ### Configuration for Nuxt 24 | 25 | Warning: this is part of the old documentation and I never used Nuxt, please let me know if it will work properly this way. 26 | 27 | In order to your Nuxt 3 project work properly with this library, you need to add `vue-google-maps-community-fork` to `build.transpile` property in your `nuxt.config.ts`. 28 | 29 | Also, as pointed [here](https://github.com/NathanAP/vue-google-maps-community-fork/issues/14), you will need to add `@googlemaps/markercluster` into it as well for your builded project work properly. 30 | 31 | ```ts 32 | export default defineNuxtConfig({ 33 | build: { 34 | transpile: ['vue-google-maps-community-fork', '@googlemaps/markercluster'], 35 | }, 36 | }) 37 | ``` 38 | 39 | ### Configure your enviroment 40 | 41 | Initialise the plugin in your `main.js`: 42 | 43 | ```js 44 | import { createApp } from 'vue' 45 | import VueGoogleMaps from 'vue-google-maps-community-fork' 46 | 47 | const app = createApp(App) 48 | app 49 | .use(VueGoogleMaps, { 50 | load: { 51 | key: 'YOUR_API_KEY_COMES_HERE', 52 | }, 53 | }) 54 | .mount('#app') 55 | ``` 56 | 57 | ### Great! Now you can use anywhere in your components 58 | 59 | Here are some examples: 60 | 61 | ```vue 62 | 71 | 72 | 82 | ``` 83 | 84 | ## Registering Google Maps events 85 | 86 | To use Google Maps events you have two options: 87 | 88 | - The first (and better) option is to activate them when you need. 89 | 90 | In this example, we enable `closeclick` event for `GMapInfoWindow` component and register the event. 91 | 92 | ```html 93 | 94 |
I am in info window {{ m.id }}
95 |
96 | ``` 97 | 98 | - The second option is to enable them globally. 99 | 100 | ```js 101 | import { createApp } from 'vue' 102 | import VueGoogleMaps from 'vue-google-maps-community-fork' 103 | 104 | const app = createApp(App) 105 | app 106 | .use(VueGoogleMaps, { 107 | load: { 108 | key: 'YOUR_API_KEY_COMES_HERE', 109 | }, 110 | autobindAllEvents: true, // Add this to enable the events 111 | }) 112 | .mount('#app') 113 | ``` 114 | -------------------------------------------------------------------------------- /docs/src/examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Here you will find some basic examples that might be useful for you. 4 | 5 | - [How to add a custom button to my map](./how-to-add-a-custom-button-to-map.md) 6 | 7 | - [How to get if a clicked area is within polygon in Google Maps](./points-in-polygon.md) 8 | 9 | - [How to access Google Maps object](./how-to-access-google-maps-object.md) 10 | 11 | - [How to add custom controls](./how-to-add-custom-controls.md) 12 | 13 | - [How to open or close an Info window on event](./how-to-open-or-close-info-window-on-event.md) 14 | 15 | - [Using custom renderer function and clustering algorithm](./using-custom-renderer-function-and-clustering-algorithm.md) 16 | -------------------------------------------------------------------------------- /docs/src/examples/how-to-access-google-maps-object.md: -------------------------------------------------------------------------------- 1 | # How to access Google Maps object 2 | 3 | [Interactive example on Playcode](https://playcode.io/1041251) 4 | 5 | To access Google maps object, or do something when map is loaded, use a ref on the `GMapMap` object. 6 | 7 | ## Example 8 | 9 | ```html 10 | 20 | 21 | 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/src/examples/how-to-add-a-custom-button-to-map.md: -------------------------------------------------------------------------------- 1 | # Adding a custom button to my map 2 | 3 | You can use the map instance to add custom buttons to your map: 4 | 5 | ```html 6 | 14 | 15 | 50 | ``` 51 | -------------------------------------------------------------------------------- /docs/src/examples/how-to-add-custom-controls.md: -------------------------------------------------------------------------------- 1 | # How to add custom controls 2 | 3 | [Interactive example on Playcode](https://playcode.io/1041257) 4 | 5 | To add custom controls, you can access google maps object and add controls to it, look at this example or follow the interactive example on CodeSandbox above. 6 | 7 | ## Example 8 | 9 | ```html 10 | 30 | 31 | 95 | ``` 96 | -------------------------------------------------------------------------------- /docs/src/examples/how-to-open-or-close-info-window-on-event.md: -------------------------------------------------------------------------------- 1 | # How to open or close an info window on event 2 | 3 | [Interactive example on Playcode](https://playcode.io/1041264). 4 | 5 | To open or close info windows after marker click, you can modify the `:opened` prop and maintain a state variable containing ID of the opened marker. 6 | 7 | ```html 8 | 34 | 35 | 55 | 56 | 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/src/examples/introduction.md: -------------------------------------------------------------------------------- 1 | # Advanced 2 | 3 | `@fawmi/vue-google-maps` provides a set of Vue.js 3 components wrapping the Google Maps API v3. 4 | 5 | Currently `Map`, `Marker`, `InfoWindow`, `Polyline`, `Polygon` and `Rectangle` are supported. 6 | 7 | Other components are still under development. 8 | 9 | Checkout getting started to read, how to install and use vue-google-maps 10 | -------------------------------------------------------------------------------- /docs/src/examples/points-in-polygon.md: -------------------------------------------------------------------------------- 1 | # How to get if a clicked area is within polygon in Google Maps 2 | 3 | [Interactive example on Playcode](https://playcode.io/1041268) 4 | 5 | ## Example 6 | 7 | ```html 8 | 9 | 29 | 30 | 72 | ``` 73 | -------------------------------------------------------------------------------- /docs/src/examples/using-custom-renderer-function-and-clustering-algorithm.md: -------------------------------------------------------------------------------- 1 | # Using custom renderer function and clustering algorithm 2 | 3 | In order to use custom renderer functions or clustering algorithms, you will need to do the following step before: 4 | 5 | ## Install `@googlemaps/markerclusterer` package into your own project 6 | 7 | Use `npm install @googlemaps/markerclusterer` to install the library into your project. 8 | 9 | Note: this is required because it was the best option for us to make Clustering viable. The original version of this library wasn't supporting the new MarkerClustering package version, so we had to make adjustments. You can read more about it in [this PR](https://github.com/NathanAP/vue-google-maps-community-fork/pull/19). 10 | 11 | ## Example 12 | 13 | Following this example will help you create your own render function and clustering algorithm. 14 | 15 | ```html 16 | 36 | 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: 'assets/logo.jpg' 4 | tagline: A library that contains Google Maps reactive components for VueJS 3 5 | actionText: Read Docs 6 | actionLink: /docs/ 7 | --- 8 | 9 | 16 | 17 | ## Before starting 18 | 19 | It is important to notice that this repository was forked by the community to keep the library alive. You can get more infor about our decision [in this GitHub discussion](https://github.com/NathanAP/vue-google-maps-community-fork/discussions/1). 20 | 21 | Since this library is currently maintained by the community, every help is needed and appreciated! You can follow everything in our [GitHub](https://github.com/NathanAP/vue-google-maps-community-fork). 22 | 23 | ## Installation 24 | 25 | You can install this library using npm: 26 | 27 | ``` 28 | npm install vue-google-maps-community-fork 29 | ``` 30 | 31 | ### Pre-requisites 32 | 33 | To use this library you will need an API Key. You can learn how to get one [here](https://developers.google.com/maps/documentation/javascript/get-api-key). 34 | 35 | ### Configure your enviroment 36 | 37 | In your `main.js` or inside a Nuxt plugin: 38 | 39 | ```js 40 | import { createApp } from 'vue' 41 | import VueGoogleMaps from 'vue-google-maps-community-fork' 42 | 43 | const app = createApp(App) 44 | app 45 | .use(VueGoogleMaps, { 46 | load: { 47 | key: 'YOUR_API_KEY_COMES_HERE', 48 | }, 49 | }) 50 | .mount('#app') 51 | ``` 52 | 53 | ### Configuration for Nuxt 54 | 55 | Warning: this is part of the old documentation and I never used Nuxt, please let me know if it will work properly this way. 56 | 57 | In order to your Nuxt 3 project work properly with this library, you need to add `vue-google-maps-community-fork` to `build.transpile` property in your `nuxt.config.ts`. 58 | 59 | Also, as pointed [here](https://github.com/NathanAP/vue-google-maps-community-fork/issues/14), you will need to add `@googlemaps/markercluster` into it as well for your builded project work properly. 60 | 61 | ```ts 62 | export default defineNuxtConfig({ 63 | build: { 64 | transpile: ['vue-google-maps-community-fork', '@googlemaps/markercluster'], 65 | }, 66 | }) 67 | ``` 68 | 69 | ### Great! Now you can use anywhere in your components 70 | 71 | Here are some examples: 72 | 73 | ```vue 74 | 88 | 106 | ``` 107 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["src/*"] 7 | } 8 | }, 9 | "exclude": ["node_modules", "dist"], 10 | "include": ["src/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-google-maps-community-fork", 3 | "description": "Google Maps components for VueJS 3 maintained by the community", 4 | "version": "0.3.1", 5 | "private": false, 6 | "main": "src/main.js", 7 | "keywords": [ 8 | "Vue", 9 | "Vue.js", 10 | "Google Maps", 11 | "Vue 3 Google maps" 12 | ], 13 | "bugs": { 14 | "url": "https://github.com/NathanAP/vue-google-maps-community-fork/issues" 15 | }, 16 | "scripts": { 17 | "build": "node service/commands/build.js", 18 | "lint": "eslint --ext .js,.vue src" 19 | }, 20 | "devDependencies": { 21 | "@babel/core": "^7.19.3", 22 | "@babel/plugin-transform-runtime": "^7.19.1", 23 | "@babel/preset-env": "^7.19.4", 24 | "@babel/runtime": "^7.19.4", 25 | "@vue/compiler-sfc": "^3.2.40", 26 | "babel-loader": "^8.2.5", 27 | "core-js": "^3.25.5", 28 | "css-loader": "^6.7.1", 29 | "eslint": "^8.25.0", 30 | "eslint-config-prettier": "^8.5.0", 31 | "eslint-formatter-friendly": "^7.0.0", 32 | "eslint-plugin-prettier": "^4.2.1", 33 | "eslint-plugin-vue": "^9.6.0", 34 | "eslint-webpack-plugin": "^3.2.0", 35 | "mini-css-extract-plugin": "^2.6.1", 36 | "prettier": "^2.8.1", 37 | "rimraf": "^3.0.2", 38 | "style-loader": "^3.3.1", 39 | "vue": "^3.2.40", 40 | "vue-eslint-parser": "^9.1.0", 41 | "vue-loader": "^17.0.0", 42 | "vue-style-loader": "^4.1.3", 43 | "webpack": "^5.74.0", 44 | "webpack-merge": "^5.8.0" 45 | }, 46 | "homepage": "https://vue-google-maps-community-fork.netlify.app", 47 | "license": "MIT", 48 | "repository": { 49 | "type": "git", 50 | "url": "git+https://github.com/NathanAP/vue-google-maps-community-fork.git" 51 | }, 52 | "types": "types/index.d.ts", 53 | "dependencies": { 54 | "@googlemaps/markerclusterer": "^2.0.3" 55 | }, 56 | "directories": { 57 | "doc": "docs", 58 | "test": "test" 59 | }, 60 | "author": "Nathan A Paludo" 61 | } 62 | -------------------------------------------------------------------------------- /service/commands/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const rm = require('rimraf') 4 | const webpack = require('webpack') 5 | 6 | const { error, done } = require('../utils/logger') 7 | const paths = require('../utils/paths') 8 | 9 | const webpackConfig = require('../config/prod') 10 | const config = require('../project.config') 11 | 12 | rm(paths.resolve(config.outputDir), (err) => { 13 | if (err) throw err 14 | 15 | webpack(webpackConfig, (err, stats) => { 16 | if (err) throw err 17 | 18 | process.stdout.write( 19 | stats.toString({ 20 | colors: true, 21 | modules: false, 22 | children: false, 23 | chunks: false, 24 | chunkModules: false, 25 | }) + '\n\n' 26 | ) 27 | 28 | if (stats.hasErrors()) { 29 | error('Build failed with errors.\n') 30 | process.exit(1) 31 | } 32 | 33 | done('Build complete.\n') 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /service/config/base.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { DefinePlugin } = require('webpack') 4 | const { VueLoaderPlugin } = require('vue-loader') 5 | const ESLintPlugin = require('eslint-webpack-plugin') 6 | 7 | const resolveClientEnv = require('../utils/resolveClientEnv') 8 | const paths = require('../utils/paths') 9 | 10 | const config = require('../project.config') 11 | 12 | const isProd = process.env.NODE_ENV === 'production' 13 | const outputFileName = paths.getAssetPath(`js/[name]${isProd ? '' : ''}.js`) 14 | 15 | module.exports = { 16 | context: process.cwd(), 17 | 18 | entry: { 19 | app: './src/main.js', 20 | }, 21 | 22 | output: { 23 | path: paths.resolve(config.outputDir), 24 | publicPath: config.dev.publicPath, 25 | filename: outputFileName, 26 | chunkFilename: outputFileName, 27 | }, 28 | 29 | resolve: { 30 | alias: { 31 | '@': paths.resolve('src'), 32 | }, 33 | extensions: ['.js', '.vue', '.json'], 34 | }, 35 | 36 | plugins: [ 37 | new ESLintPlugin({ 38 | emitError: true, 39 | emitWarning: true, 40 | extensions: ['.js', '.vue'], 41 | formatter: require('eslint-formatter-friendly'), 42 | }), 43 | new VueLoaderPlugin(), 44 | new DefinePlugin({ 45 | __VUE_OPTIONS_API__: 'true', 46 | __VUE_PROD_DEVTOOLS__: 'false', 47 | ...resolveClientEnv({ publicPath: config.dev.publicPath }), 48 | }), 49 | ], 50 | 51 | module: { 52 | noParse: /^(vue)$/, 53 | rules: [ 54 | { 55 | test: /\.vue$/, 56 | loader: 'vue-loader', 57 | }, 58 | ], 59 | }, 60 | } 61 | -------------------------------------------------------------------------------- /service/config/css.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 4 | 5 | const genStyleRules = () => { 6 | const isProd = process.env.NODE_ENV === 'production' 7 | 8 | const cssLoader = { 9 | loader: 'css-loader', 10 | options: { 11 | // how many loaders before css-loader should be applied to [@import]ed resources. 12 | // stylePostLoader injected by vue-loader + postcss-loader 13 | importLoaders: 1 + 1, 14 | esModule: false, // css-loader using ES Modules as default in v4, but vue-style-loader support cjs only. 15 | }, 16 | } 17 | const extractPluginLoader = { 18 | loader: MiniCssExtractPlugin.loader, 19 | } 20 | const vueStyleLoader = { 21 | loader: 'vue-style-loader', 22 | } 23 | 24 | function createCSSRule(test, loader, loaderOptions) { 25 | const loaders = [cssLoader] 26 | 27 | if (isProd) { 28 | loaders.unshift(extractPluginLoader) 29 | } else { 30 | loaders.unshift(vueStyleLoader) 31 | } 32 | 33 | if (loader) { 34 | loaders.push({ loader, options: loaderOptions }) 35 | } 36 | 37 | return { test, use: loaders } 38 | } 39 | 40 | return [createCSSRule(/\.css$/)] 41 | } 42 | 43 | module.exports = { 44 | module: { 45 | rules: genStyleRules(), 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /service/config/dev.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { merge } = require('webpack-merge') 4 | 5 | const baseWebpackConfig = require('./base') 6 | const cssWebpackConfig = require('./css') 7 | const config = require('../project.config') 8 | 9 | module.exports = merge(baseWebpackConfig, cssWebpackConfig, { 10 | mode: 'development', 11 | 12 | devtool: 'eval-cheap-module-source-map', 13 | 14 | devServer: { 15 | historyApiFallback: { 16 | rewrites: [{ from: /./, to: '/index.html' }], 17 | }, 18 | dev: { 19 | publicPath: config.dev.publicPath, 20 | }, 21 | overlay: { 22 | warnings: true, 23 | errors: true, 24 | }, 25 | open: false, 26 | host: '0.0.0.0', 27 | port: config.dev.port, 28 | liveReload: false, 29 | }, 30 | 31 | infrastructureLogging: { 32 | level: 'warn', 33 | }, 34 | }) 35 | -------------------------------------------------------------------------------- /service/config/prod.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { merge } = require('webpack-merge') 4 | const TerserPlugin = require('terser-webpack-plugin') 5 | 6 | const baseWebpackConfig = require('./base') 7 | const cssWebpackConfig = require('./css') 8 | const config = require('../project.config') 9 | const terserOptions = require('./terserOptions') 10 | 11 | module.exports = merge(baseWebpackConfig, cssWebpackConfig, { 12 | mode: 'production', 13 | 14 | output: { 15 | publicPath: config.build.publicPath, 16 | }, 17 | 18 | optimization: { 19 | minimize: true, 20 | minimizer: [new TerserPlugin(terserOptions())], 21 | moduleIds: 'deterministic', 22 | splitChunks: { 23 | cacheGroups: { 24 | defaultVendors: { 25 | name: `chunk-vendors`, 26 | test: /[\\/]node_modules[\\/]/, 27 | priority: -10, 28 | chunks: 'initial', 29 | }, 30 | common: { 31 | name: `chunk-common`, 32 | minChunks: 2, 33 | priority: -20, 34 | chunks: 'initial', 35 | reuseExistingChunk: true, 36 | }, 37 | }, 38 | }, 39 | }, 40 | }) 41 | -------------------------------------------------------------------------------- /service/config/terserOptions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = () => ({ 4 | terserOptions: { 5 | compress: { 6 | // turn off flags with small gains to speed up minification 7 | arrows: false, 8 | collapse_vars: false, // 0.3kb 9 | comparisons: false, 10 | computed_props: false, 11 | hoist_funs: false, 12 | hoist_props: false, 13 | hoist_vars: false, 14 | inline: false, 15 | loops: false, 16 | negate_iife: false, 17 | properties: false, 18 | reduce_funcs: false, 19 | reduce_vars: false, 20 | switches: false, 21 | toplevel: false, 22 | typeofs: false, 23 | 24 | // a few flags with noticable gains/speed ratio 25 | // numbers based on out of the box vendor bundle 26 | booleans: true, // 0.7kb 27 | if_return: true, // 0.4kb 28 | sequences: true, // 0.7kb 29 | unused: true, // 2.3kb 30 | 31 | // required features to drop conditional branches 32 | conditionals: true, 33 | dead_code: true, 34 | evaluate: true, 35 | }, 36 | mangle: { 37 | safari10: true, 38 | }, 39 | }, 40 | // parallel: options.parallel, 41 | extractComments: false, 42 | }) 43 | -------------------------------------------------------------------------------- /service/project.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | outputDir: 'dist', 5 | 6 | dev: { 7 | publicPath: '/', 8 | port: 8080, 9 | }, 10 | 11 | build: { 12 | publicPath: './', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /service/utils/getLocalIP.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const os = require('os') 4 | 5 | module.exports = function getLocalIP() { 6 | const interfaces = os.networkInterfaces() 7 | 8 | for (const devName in interfaces) { 9 | const iface = interfaces[devName] 10 | for (let i = 0; i < iface.length; i++) { 11 | const alias = iface[i] 12 | if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { 13 | return alias.address 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/utils/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const chalk = require('chalk') 4 | const stripAnsi = require('strip-ansi') 5 | const readline = require('readline') 6 | const EventEmitter = require('events') 7 | 8 | exports.events = new EventEmitter() 9 | 10 | function _log(type, tag, message) { 11 | if (process.env.VUE_CLI_API_MODE && message) { 12 | exports.events.emit('log', { 13 | message, 14 | type, 15 | tag, 16 | }) 17 | } 18 | } 19 | 20 | const format = (label, msg) => { 21 | return msg 22 | .split('\n') 23 | .map((line, i) => { 24 | return i === 0 ? `${label} ${line}` : line.padStart(stripAnsi(label).length) 25 | }) 26 | .join('\n') 27 | } 28 | 29 | const chalkTag = (msg) => chalk.bgBlackBright.white.dim(` ${msg} `) 30 | 31 | exports.log = (msg = '', tag = null) => { 32 | tag ? console.log(format(chalkTag(tag), msg)) : console.log(msg) 33 | _log('log', tag, msg) 34 | } 35 | 36 | exports.info = (msg, tag = null) => { 37 | console.log(format(chalk.bgBlue.black(' INFO ') + (tag ? chalkTag(tag) : ''), msg)) 38 | _log('info', tag, msg) 39 | } 40 | 41 | exports.done = (msg, tag = null) => { 42 | console.log(format(chalk.bgGreen.black(' DONE ') + (tag ? chalkTag(tag) : ''), msg)) 43 | _log('done', tag, msg) 44 | } 45 | 46 | exports.warn = (msg, tag = null) => { 47 | console.warn( 48 | format(chalk.bgYellow.black(' WARN ') + (tag ? chalkTag(tag) : ''), chalk.yellow(msg)) 49 | ) 50 | _log('warn', tag, msg) 51 | } 52 | 53 | exports.error = (msg, tag = null) => { 54 | console.error(format(chalk.bgRed(' ERROR ') + (tag ? chalkTag(tag) : ''), chalk.red(msg))) 55 | _log('error', tag, msg) 56 | if (msg instanceof Error) { 57 | console.error(msg.stack) 58 | _log('error', tag, msg.stack) 59 | } 60 | } 61 | 62 | exports.clearConsole = (title) => { 63 | if (process.stdout.isTTY) { 64 | const blank = '\n'.repeat(process.stdout.rows) 65 | console.log(blank) 66 | readline.cursorTo(process.stdout, 0, 0) 67 | readline.clearScreenDown(process.stdout) 68 | if (title) { 69 | console.log(title) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /service/utils/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | 5 | // gen static file path 6 | exports.getAssetPath = (...args) => path.posix.join('static', ...args) 7 | 8 | // gen absolute path 9 | exports.resolve = (...args) => path.posix.join(process.cwd(), ...args) 10 | -------------------------------------------------------------------------------- /service/utils/resolveClientEnv.js: -------------------------------------------------------------------------------- 1 | const prefixRE = /^VUE_APP_/ 2 | 3 | module.exports = function resolveClientEnv(options, raw) { 4 | const env = {} 5 | Object.keys(process.env).forEach((key) => { 6 | if (prefixRE.test(key) || key === 'NODE_ENV') { 7 | env[key] = process.env[key] 8 | } 9 | }) 10 | env.PUBLIC_PATH = options.publicPath 11 | 12 | if (raw) { 13 | return env 14 | } 15 | 16 | for (const key in env) { 17 | env[key] = JSON.stringify(env[key]) 18 | } 19 | return { 20 | 'process.env': env, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /service/utils/spinner.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ora = require('ora') 4 | const chalk = require('chalk') 5 | 6 | const spinner = ora() 7 | let lastMsg = null 8 | let isPaused = false 9 | 10 | exports.logWithSpinner = (symbol, msg) => { 11 | if (!msg) { 12 | msg = symbol 13 | symbol = chalk.green('✔') 14 | } 15 | if (lastMsg) { 16 | spinner.stopAndPersist({ 17 | symbol: lastMsg.symbol, 18 | text: lastMsg.text, 19 | }) 20 | } 21 | spinner.text = ' ' + msg 22 | lastMsg = { 23 | symbol: symbol + ' ', 24 | text: msg, 25 | } 26 | spinner.start() 27 | } 28 | 29 | exports.stopSpinner = (persist) => { 30 | if (lastMsg && persist !== false) { 31 | spinner.stopAndPersist({ 32 | symbol: lastMsg.symbol, 33 | text: lastMsg.text, 34 | }) 35 | } else { 36 | spinner.stop() 37 | } 38 | lastMsg = null 39 | } 40 | 41 | exports.pauseSpinner = () => { 42 | if (spinner.isSpinning) { 43 | spinner.stop() 44 | isPaused = true 45 | } 46 | } 47 | 48 | exports.resumeSpinner = () => { 49 | if (isPaused) { 50 | spinner.start() 51 | isPaused = false 52 | } 53 | } 54 | 55 | exports.failSpinner = (text) => { 56 | spinner.fail(text) 57 | } 58 | -------------------------------------------------------------------------------- /src/components/autocomplete.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 93 | -------------------------------------------------------------------------------- /src/components/autocompleteImpl.js: -------------------------------------------------------------------------------- 1 | import { bindProps, getPropsValues } from '../utils/bindProps.js' 2 | import downArrowSimulator from '../utils/simulateArrowDown.js' 3 | import { mappedPropsToVueProps } from './build-component' 4 | 5 | const mappedProps = { 6 | bounds: { 7 | type: Object, 8 | }, 9 | componentRestrictions: { 10 | type: Object, 11 | // Do not bind -- must check for undefined 12 | // in the property 13 | noBind: true, 14 | }, 15 | types: { 16 | type: Array, 17 | default: function () { 18 | return [] 19 | }, 20 | }, 21 | } 22 | 23 | const props = { 24 | selectFirstOnEnter: { 25 | required: false, 26 | type: Boolean, 27 | default: false, 28 | }, 29 | options: { 30 | type: Object, 31 | }, 32 | } 33 | 34 | export default { 35 | mounted() { 36 | this.$gmapApiPromiseLazy().then(() => { 37 | if (this.selectFirstOnEnter) { 38 | downArrowSimulator(this.$refs.input) 39 | } 40 | 41 | if (typeof google.maps.places.Autocomplete !== 'function') { 42 | throw new Error( 43 | "google.maps.places.Autocomplete is undefined. Did you add 'places' to libraries when loading Google Maps?" 44 | ) 45 | } 46 | 47 | /* eslint-disable no-unused-vars */ 48 | const finalOptions = { 49 | ...getPropsValues(this, mappedProps), 50 | ...this.options, 51 | } 52 | 53 | this.$autocomplete = new google.maps.places.Autocomplete(this.$refs.input, finalOptions) 54 | bindProps(this, this.$autocomplete, mappedProps) 55 | 56 | this.$watch('componentRestrictions', (v) => { 57 | if (v !== undefined) { 58 | this.$autocomplete.setComponentRestrictions(v) 59 | } 60 | }) 61 | 62 | // Not using `bindEvents` because we also want 63 | // to return the result of `getPlace()` 64 | this.$autocomplete.addListener('place_changed', () => { 65 | this.$emit('place_changed', this.$autocomplete.getPlace()) 66 | }) 67 | }) 68 | }, 69 | props: { 70 | ...mappedPropsToVueProps(mappedProps), 71 | ...props, 72 | }, 73 | } 74 | -------------------------------------------------------------------------------- /src/components/build-component.js: -------------------------------------------------------------------------------- 1 | import bindEvents from '../utils/bindEvents.js' 2 | import { bindProps, getPropsValues } from '../utils/bindProps.js' 3 | import MapElementMixin from './mapElementMixin' 4 | 5 | /** 6 | * 7 | * @param {Object} options 8 | * @param {Object} options.mappedProps - Definitions of props 9 | * @param {Object} options.mappedProps.PROP.type - Value type 10 | * @param {Boolean} options.mappedProps.PROP.twoWay 11 | * - Whether the prop has a corresponding PROP_changed 12 | * event 13 | * @param {Boolean} options.mappedProps.PROP.noBind 14 | * - If true, do not apply the default bindProps / bindEvents. 15 | * However it will still be added to the list of component props 16 | * @param {Object} options.props - Regular Vue-style props. 17 | * Note: must be in the Object form because it will be 18 | * merged with the `mappedProps` 19 | * 20 | * @param {Object} options.events - Google Maps API events 21 | * that are not bound to a corresponding prop 22 | * @param {String} options.name - e.g. `polyline` 23 | * @param {=> String} options.ctr - constructor, e.g. 24 | * `google.maps.Polyline`. However, since this is not 25 | * generally available during library load, this becomes 26 | * a function instead, e.g. () => google.maps.Polyline 27 | * which will be called only after the API has been loaded 28 | * @param {(MappedProps, OtherVueProps) => Array} options.ctrArgs - 29 | * If the constructor in `ctr` needs to be called with 30 | * arguments other than a single `options` object, e.g. for 31 | * GroundOverlay, we call `new GroundOverlay(url, bounds, options)` 32 | * then pass in a function that returns the argument list as an array 33 | * 34 | * Otherwise, the constructor will be called with an `options` object, 35 | * with property and values merged from: 36 | * 37 | * 1. the `options` property, if any 38 | * 2. a `map` property with the Google Maps 39 | * 3. all the properties passed to the component in `mappedProps` 40 | * @param {Object => Any} options.beforeCreate - 41 | * Hook to modify the options passed to the initializer 42 | * @param {(options.ctr, Object) => Any} options.afterCreate - 43 | * Hook called when 44 | * 45 | */ 46 | export default function (options) { 47 | const { mappedProps, name, ctr, ctrArgs, events, beforeCreate, afterCreate, props, ...rest } = 48 | options 49 | 50 | const promiseName = `$${name}Promise` 51 | const instanceName = `$${name}Object` 52 | 53 | assert(!(rest.props instanceof Array), '`props` should be an object, not Array') 54 | 55 | return { 56 | ...(typeof GENERATE_DOC !== 'undefined' ? { $vgmOptions: options } : {}), 57 | mixins: [MapElementMixin], 58 | props: { 59 | ...props, 60 | ...mappedPropsToVueProps(mappedProps), 61 | }, 62 | render() { 63 | return '' 64 | }, 65 | provide() { 66 | const promise = this.$mapPromise 67 | .then((map) => { 68 | // Infowindow needs this to be immediately available 69 | this.$map = map 70 | 71 | // Initialize the maps with the given options 72 | const options = { 73 | ...this.options, 74 | map, 75 | ...getPropsValues(this, mappedProps), 76 | } 77 | delete options.options // delete the extra options 78 | 79 | if (beforeCreate) { 80 | const result = beforeCreate.bind(this)(options) 81 | 82 | if (result instanceof Promise) { 83 | return result.then(() => ({ options })) 84 | } 85 | } 86 | return { options } 87 | }) 88 | .then(({ options }) => { 89 | const ConstructorObject = ctr() 90 | // https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible 91 | this[instanceName] = ctrArgs 92 | ? new (Function.prototype.bind.call( 93 | ConstructorObject, 94 | null, 95 | ...ctrArgs(options, getPropsValues(this, props || {})) 96 | ))() 97 | : new ConstructorObject(options) 98 | 99 | bindProps(this, this[instanceName], mappedProps) 100 | bindEvents(this, this[instanceName], events) 101 | 102 | if (afterCreate) { 103 | afterCreate.bind(this)(this[instanceName]) 104 | } 105 | return this[instanceName] 106 | }) 107 | this[promiseName] = promise 108 | return { [promiseName]: promise } 109 | }, 110 | unmounted() { 111 | // Note: not all Google Maps components support maps 112 | if (this[instanceName] && this[instanceName].setMap) { 113 | this[instanceName].setMap(null) 114 | } 115 | }, 116 | ...rest, 117 | } 118 | } 119 | 120 | function assert(v, message) { 121 | if (!v) throw new Error(message) 122 | } 123 | 124 | /** 125 | * Strips out the extraneous properties we have in our 126 | * props definitions 127 | * @param {Object} props 128 | */ 129 | export function mappedPropsToVueProps(mappedProps) { 130 | return Object.entries(mappedProps) 131 | .map(([key, prop]) => { 132 | const value = {} 133 | 134 | if ('type' in prop) value.type = prop.type 135 | if ('default' in prop) value.default = prop.default 136 | if ('required' in prop) value.required = prop.required 137 | 138 | return [key, value] 139 | }) 140 | .reduce((acc, [key, val]) => { 141 | acc[key] = val 142 | return acc 143 | }, {}) 144 | } 145 | -------------------------------------------------------------------------------- /src/components/circle.js: -------------------------------------------------------------------------------- 1 | import buildComponent from './build-component' 2 | 3 | const props = { 4 | center: { 5 | type: Object, 6 | twoWay: true, 7 | required: true, 8 | }, 9 | radius: { 10 | type: Number, 11 | twoWay: true, 12 | }, 13 | draggable: { 14 | type: Boolean, 15 | default: false, 16 | }, 17 | editable: { 18 | type: Boolean, 19 | default: false, 20 | }, 21 | options: { 22 | type: Object, 23 | twoWay: false, 24 | }, 25 | } 26 | 27 | const events = [ 28 | 'click', 29 | 'dblclick', 30 | 'drag', 31 | 'dragend', 32 | 'dragstart', 33 | 'mousedown', 34 | 'mousemove', 35 | 'mouseout', 36 | 'mouseover', 37 | 'mouseup', 38 | 'rightclick', 39 | ] 40 | 41 | export default buildComponent({ 42 | mappedProps: props, 43 | name: 'circle', 44 | ctr: () => google.maps.Circle, 45 | events, 46 | emits: events, 47 | afterCreate(inst) { 48 | events.forEach((event) => { 49 | inst.addListener(event, (payload) => { 50 | this.$emit(event, payload) 51 | }) 52 | }) 53 | }, 54 | }) 55 | -------------------------------------------------------------------------------- /src/components/cluster.vue: -------------------------------------------------------------------------------- 1 | 6 | 75 | -------------------------------------------------------------------------------- /src/components/heatmap.js: -------------------------------------------------------------------------------- 1 | import buildComponent from './build-component.js' 2 | 3 | const props = { 4 | options: { 5 | type: Object, 6 | twoWay: false, 7 | default: () => {}, 8 | }, 9 | data: { 10 | type: Array, 11 | twoWay: true, 12 | }, 13 | } 14 | 15 | const events = [] 16 | 17 | export default buildComponent({ 18 | mappedProps: props, 19 | name: 'heatmap', 20 | ctr: () => google.maps.visualization.HeatmapLayer, 21 | events, 22 | }) 23 | -------------------------------------------------------------------------------- /src/components/infoWindow.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 86 | -------------------------------------------------------------------------------- /src/components/map.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 222 | 229 | -------------------------------------------------------------------------------- /src/components/mapElementMixin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class MapElementMixin 3 | * 4 | * Extends components to include the following fields: 5 | * 6 | * @property $map The Google map (valid only after the promise returns) 7 | * 8 | * 9 | * */ 10 | export default { 11 | inject: { 12 | $mapPromise: { default: 'abcdef' }, 13 | }, 14 | 15 | provide() { 16 | // Note: although this mixin is not "providing" anything, 17 | // components' expect the `$map` property to be present on the component. 18 | // In order for that to happen, this mixin must intercept the $mapPromise 19 | // .then(() =>) first before its component does so. 20 | // 21 | // Since a provide() on a mixin is executed before a provide() on the 22 | // component, putting this code in provide() ensures that the $map is 23 | // already set by the time the 24 | // component's provide() is called. 25 | this.$mapPromise.then((map) => { 26 | this.$map = map 27 | }) 28 | 29 | return {} 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /src/components/marker.vue: -------------------------------------------------------------------------------- 1 | 12 | 134 | -------------------------------------------------------------------------------- /src/components/polygon.js: -------------------------------------------------------------------------------- 1 | import buildComponent from './build-component.js' 2 | 3 | const props = { 4 | draggable: { 5 | type: Boolean, 6 | }, 7 | editable: { 8 | type: Boolean, 9 | }, 10 | options: { 11 | type: Object, 12 | }, 13 | path: { 14 | type: Array, 15 | twoWay: true, 16 | noBind: true, 17 | }, 18 | paths: { 19 | type: Array, 20 | twoWay: true, 21 | noBind: true, 22 | }, 23 | } 24 | 25 | const events = [ 26 | 'click', 27 | 'dblclick', 28 | 'drag', 29 | 'dragend', 30 | 'dragstart', 31 | 'mousedown', 32 | 'mousemove', 33 | 'mouseout', 34 | 'mouseover', 35 | 'mouseup', 36 | 'rightclick', 37 | 'paths_changed', 38 | ] 39 | 40 | export default buildComponent({ 41 | props: { 42 | deepWatch: { 43 | type: Boolean, 44 | default: false, 45 | }, 46 | }, 47 | events, 48 | mappedProps: props, 49 | name: 'polygon', 50 | ctr: () => google.maps.Polygon, 51 | emits: events, 52 | beforeCreate(options) { 53 | if (!options.path) delete options.path 54 | if (!options.paths) delete options.paths 55 | }, 56 | 57 | afterCreate(inst) { 58 | let clearEvents = () => {} 59 | events.forEach((event) => { 60 | inst.addListener(event, (payload) => { 61 | this.$emit(event, payload) 62 | }) 63 | }) 64 | 65 | // Watch paths, on our own, because we do not want to set either when it is 66 | // empty 67 | this.$watch( 68 | 'paths', 69 | (paths) => { 70 | if (paths) { 71 | clearEvents() 72 | 73 | inst.setPaths(paths) 74 | 75 | const updatePaths = () => { 76 | this.$emit('paths_changed', inst.getPaths()) 77 | } 78 | const eventListeners = [] 79 | 80 | const mvcArray = inst.getPaths() 81 | for (let i = 0; i < mvcArray.getLength(); i++) { 82 | let mvcPath = mvcArray.getAt(i) 83 | eventListeners.push([mvcPath, mvcPath.addListener('insert_at', updatePaths)]) 84 | eventListeners.push([mvcPath, mvcPath.addListener('remove_at', updatePaths)]) 85 | eventListeners.push([mvcPath, mvcPath.addListener('set_at', updatePaths)]) 86 | } 87 | eventListeners.push([mvcArray, mvcArray.addListener('insert_at', updatePaths)]) 88 | eventListeners.push([mvcArray, mvcArray.addListener('remove_at', updatePaths)]) 89 | eventListeners.push([mvcArray, mvcArray.addListener('set_at', updatePaths)]) 90 | 91 | clearEvents = () => { 92 | eventListeners.map( 93 | ( 94 | [obj, listenerHandle] // eslint-disable-line no-unused-vars 95 | ) => google.maps.event.removeListener(listenerHandle) 96 | ) 97 | } 98 | } 99 | }, 100 | { 101 | deep: this.deepWatch, 102 | immediate: true, 103 | } 104 | ) 105 | 106 | this.$watch( 107 | 'path', 108 | (path) => { 109 | if (path) { 110 | clearEvents() 111 | 112 | inst.setPaths(path) 113 | 114 | const mvcPath = inst.getPath() 115 | const eventListeners = [] 116 | 117 | const updatePaths = () => { 118 | this.$emit('path_changed', inst.getPath()) 119 | } 120 | 121 | eventListeners.push([mvcPath, mvcPath.addListener('insert_at', updatePaths)]) 122 | eventListeners.push([mvcPath, mvcPath.addListener('remove_at', updatePaths)]) 123 | eventListeners.push([mvcPath, mvcPath.addListener('set_at', updatePaths)]) 124 | 125 | clearEvents = () => { 126 | eventListeners.map( 127 | ( 128 | [obj, listenerHandle] // eslint-disable-line no-unused-vars 129 | ) => google.maps.event.removeListener(listenerHandle) 130 | ) 131 | } 132 | } 133 | }, 134 | { 135 | deep: this.deepWatch, 136 | immediate: true, 137 | } 138 | ) 139 | }, 140 | }) 141 | -------------------------------------------------------------------------------- /src/components/polyline.js: -------------------------------------------------------------------------------- 1 | import buildComponent from './build-component.js' 2 | 3 | const props = { 4 | draggable: { 5 | type: Boolean, 6 | }, 7 | editable: { 8 | type: Boolean, 9 | }, 10 | options: { 11 | twoWay: false, 12 | type: Object, 13 | }, 14 | path: { 15 | type: Array, 16 | twoWay: true, 17 | }, 18 | } 19 | 20 | const events = [ 21 | 'click', 22 | 'dblclick', 23 | 'drag', 24 | 'dragend', 25 | 'dragstart', 26 | 'mousedown', 27 | 'mousemove', 28 | 'mouseout', 29 | 'mouseover', 30 | 'mouseup', 31 | 'rightclick', 32 | ] 33 | 34 | export default buildComponent({ 35 | mappedProps: props, 36 | props: { 37 | deepWatch: { 38 | type: Boolean, 39 | default: false, 40 | }, 41 | }, 42 | events, 43 | 44 | name: 'polyline', 45 | ctr: () => google.maps.Polyline, 46 | emits: events, 47 | afterCreate(inst) { 48 | let clearEvents = () => {} 49 | 50 | events.forEach((event) => { 51 | inst.addListener(event, (payload) => { 52 | this.$emit(event, payload) 53 | }) 54 | }) 55 | 56 | this.$watch( 57 | 'path', 58 | (path) => { 59 | if (path) { 60 | clearEvents() 61 | 62 | this.$polylineObject.setPath(path) 63 | 64 | const mvcPath = this.$polylineObject.getPath() 65 | const eventListeners = [] 66 | 67 | const updatePaths = () => { 68 | this.$emit('path_changed', this.$polylineObject.getPath()) 69 | } 70 | 71 | eventListeners.push([mvcPath, mvcPath.addListener('insert_at', updatePaths)]) 72 | eventListeners.push([mvcPath, mvcPath.addListener('remove_at', updatePaths)]) 73 | eventListeners.push([mvcPath, mvcPath.addListener('set_at', updatePaths)]) 74 | 75 | clearEvents = () => { 76 | eventListeners.map( 77 | ( 78 | [obj, listenerHandle] // eslint-disable-line no-unused-vars 79 | ) => google.maps.event.removeListener(listenerHandle) 80 | ) 81 | } 82 | } 83 | }, 84 | { 85 | deep: this.deepWatch, 86 | immediate: true, 87 | } 88 | ) 89 | }, 90 | }) 91 | -------------------------------------------------------------------------------- /src/components/rectangle.js: -------------------------------------------------------------------------------- 1 | import buildComponent from './build-component.js' 2 | 3 | const props = { 4 | bounds: { 5 | type: Object, 6 | twoWay: true, 7 | }, 8 | draggable: { 9 | type: Boolean, 10 | default: false, 11 | }, 12 | editable: { 13 | type: Boolean, 14 | default: false, 15 | }, 16 | options: { 17 | type: Object, 18 | twoWay: false, 19 | }, 20 | } 21 | 22 | const events = [ 23 | 'click', 24 | 'dblclick', 25 | 'drag', 26 | 'dragend', 27 | 'dragstart', 28 | 'mousedown', 29 | 'mousemove', 30 | 'mouseout', 31 | 'mouseover', 32 | 'mouseup', 33 | 'rightclick', 34 | ] 35 | 36 | export default buildComponent({ 37 | mappedProps: props, 38 | name: 'rectangle', 39 | ctr: () => google.maps.Rectangle, 40 | events, 41 | emits: events, 42 | afterCreate(inst) { 43 | events.forEach((event) => { 44 | inst.addListener(event, (payload) => { 45 | this.$emit(event, payload) 46 | }) 47 | }) 48 | }, 49 | }) 50 | -------------------------------------------------------------------------------- /src/load-google-maps.js: -------------------------------------------------------------------------------- 1 | import { Env } from './utils/env' 2 | import { createMapScript } from './utils/create-map-script' 3 | 4 | let isApiSetUp = false 5 | export function loadGMapApi(options) { 6 | if (Env.isServer()) { 7 | return 8 | } 9 | 10 | if (!isApiSetUp) { 11 | isApiSetUp = true 12 | const googleMapScript = createMapScript(options) 13 | document.head.appendChild(googleMapScript) 14 | } else { 15 | throw new Error('You already started the loading of google maps') 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import lazy from './utils/lazyValue' 2 | import { loadGMapApi } from './load-google-maps' 3 | import { createApp } from 'vue' 4 | import Polyline from './components/polyline' 5 | import Polygon from './components/polygon' 6 | import Circle from './components/circle' 7 | import Rectangle from './components/rectangle' 8 | import Marker from './components/marker.vue' 9 | import GMapCluster from './components/cluster.vue' 10 | import InfoWindow from './components/infoWindow.vue' 11 | import Map from './components/map.vue' 12 | import Heatmap from './components/heatmap' 13 | import Autocomplete from './components/autocomplete.vue' 14 | 15 | import MapElementMixin from './components/mapElementMixin' 16 | import buildComponent from './components/build-component' 17 | import MountableMixin from './utils/mountableMixin' 18 | import { Env } from './utils/env' 19 | let GMapApi = null 20 | 21 | export { 22 | loadGMapApi, 23 | Marker, 24 | Polyline, 25 | Polygon, 26 | Circle, 27 | GMapCluster, 28 | Rectangle, 29 | InfoWindow, 30 | Map, 31 | MapElementMixin, 32 | Heatmap, 33 | buildComponent, 34 | Autocomplete, 35 | MountableMixin, 36 | } 37 | 38 | export default function install(Vue, options) { 39 | options = { 40 | installComponents: true, 41 | autobindAllEvents: false, 42 | ...options, 43 | } 44 | 45 | GMapApi = createApp({ 46 | data: function () { 47 | return { gmapApi: null } 48 | }, 49 | }) 50 | 51 | const defaultResizeBus = createApp() 52 | 53 | // Use a lazy to only load the API when 54 | // a VGM component is loaded 55 | let gmapApiPromiseLazy = makeGMapApiPromiseLazy(options) 56 | 57 | Vue.mixin({ 58 | created() { 59 | this.$gmapDefaultResizeBus = defaultResizeBus 60 | this.$gmapOptions = options 61 | this.$gmapApiPromiseLazy = gmapApiPromiseLazy 62 | }, 63 | }) 64 | Vue.$gmapDefaultResizeBus = defaultResizeBus 65 | Vue.$gmapApiPromiseLazy = gmapApiPromiseLazy 66 | 67 | if (options.installComponents) { 68 | Vue.component('GMapMap', Map) 69 | Vue.component('GMapMarker', Marker) 70 | Vue.component('GMapInfoWindow', InfoWindow) 71 | Vue.component('GMapCluster', GMapCluster) 72 | Vue.component('GMapPolyline', Polyline) 73 | Vue.component('GMapPolygon', Polygon) 74 | Vue.component('GMapCircle', Circle) 75 | Vue.component('GMapRectangle', Rectangle) 76 | Vue.component('GMapAutocomplete', Autocomplete) 77 | Vue.component('GMapHeatmap', Heatmap) 78 | } 79 | } 80 | 81 | function makeGMapApiPromiseLazy(options) { 82 | // Things to do once the API is loaded 83 | function onApiLoaded() { 84 | GMapApi.gmapApi = {} 85 | return window.google 86 | } 87 | 88 | if (options.load) { 89 | // If library should load the API 90 | return lazy(() => { 91 | // Load the 92 | // This will only be evaluated once 93 | if (Env.isServer()) { 94 | return new Promise(() => {}).then(onApiLoaded) 95 | } else { 96 | return new Promise((resolve, reject) => { 97 | try { 98 | window['vueGoogleMapsInit'] = resolve 99 | loadGMapApi(options.load) 100 | } catch (err) { 101 | reject(err) 102 | } 103 | }).then(onApiLoaded) 104 | } 105 | }) 106 | } else { 107 | // If library should not handle API, provide 108 | // end-users with the global `vueGoogleMapsInit: () => undefined` 109 | // when the Google Maps API has been loaded 110 | const promise = new Promise((resolve) => { 111 | if (Env.isServer()) { 112 | return 113 | } 114 | window['vueGoogleMapsInit'] = resolve 115 | }).then(onApiLoaded) 116 | 117 | return lazy(() => promise) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/utils/TwoWayBindingWrapper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * When you have two-way bindings, but the actual bound value will not equal 3 | * the value you initially passed in, then to avoid an infinite loop you 4 | * need to increment a counter every time you pass in a value, decrement the 5 | * same counter every time the bound value changed, but only bubble up 6 | * the event when the counter is zero. 7 | * 8 | Example: 9 | 10 | Let's say DrawingRecognitionCanvas is a deep-learning backed canvas 11 | that, when given the name of an object (e.g. 'dog'), draws a dog. 12 | But whenever the drawing on it changes, it also sends back its interpretation 13 | of the image by way of the @newObjectRecognized event. 14 | 15 | 19 | 23 | 24 | new TwoWayBindingWrapper((increment, decrement, shouldUpdate) => { 25 | this.$watch('identifiedObject', () => { 26 | // new object passed in 27 | increment() 28 | }) 29 | this.$deepLearningBackend.on('drawingChanged', () => { 30 | recognizeObject(this.$deepLearningBackend) 31 | .then((object) => { 32 | decrement() 33 | if (shouldUpdate()) { 34 | this.$emit('newObjectRecognized', object.name) 35 | } 36 | }) 37 | }) 38 | }) 39 | */ 40 | export default function TwoWayBindingWrapper(fn) { 41 | let counter = 0 42 | 43 | fn( 44 | () => { 45 | counter += 1 46 | }, 47 | () => { 48 | counter = Math.max(0, counter - 1) 49 | }, 50 | () => counter === 0 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /src/utils/WatchPrimitiveProperties.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Watch the individual properties of a PoD object, instead of the object 3 | * per se. This is different from a deep watch where both the reference 4 | * and the individual values are watched. 5 | * 6 | * In effect, it throttles the multiple $watch to execute at most once per tick. 7 | */ 8 | export default function WatchPrimitiveProperties( 9 | vueInst, 10 | propertiesToTrack, 11 | handler, 12 | immediate = false 13 | ) { 14 | let isHandled = false 15 | 16 | function requestHandle() { 17 | if (!isHandled) { 18 | isHandled = true 19 | vueInst.$nextTick(() => { 20 | isHandled = false 21 | handler() 22 | }) 23 | } 24 | } 25 | 26 | for (let prop of propertiesToTrack) { 27 | vueInst.$watch(prop, requestHandle, { immediate }) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/bindEvents.js: -------------------------------------------------------------------------------- 1 | export default (vueInst, googleMapsInst, events) => { 2 | for (let eventName of events) { 3 | const propName = `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`.replace( 4 | /[-_]+(.)?/g, 5 | (_, c) => (c ? c.toUpperCase() : '') 6 | ) 7 | 8 | if (vueInst.$props[propName] || vueInst.$attrs[propName]) { 9 | googleMapsInst.addListener(eventName, (ev) => { 10 | vueInst.$emit(eventName, ev) 11 | }) 12 | } else if (vueInst.$gmapOptions.autobindAllEvents || vueInst.$attrs[eventName]) { 13 | googleMapsInst.addListener(eventName, (ev) => { 14 | vueInst.$emit(eventName, ev) 15 | }) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/bindProps.js: -------------------------------------------------------------------------------- 1 | import WatchPrimitiveProperties from '../utils/WatchPrimitiveProperties' 2 | import { Str } from './string' 3 | 4 | export function getPropsValues(vueInst, props) { 5 | return Object.keys(props).reduce((acc, prop) => { 6 | if (vueInst[prop] !== undefined) { 7 | acc[prop] = vueInst[prop] 8 | } 9 | return acc 10 | }, {}) 11 | } 12 | 13 | /** 14 | * Binds the properties defined in props to the google maps instance. 15 | * If the prop is an Object type, and we wish to track the properties 16 | * of the object (e.g. the lat and lng of a LatLng), then we do a deep 17 | * watch. For deep watch, we also prevent the _changed event from being 18 | * $emitted if the data source was external. 19 | */ 20 | export function bindProps(vueInst, googleMapsInst, props) { 21 | for (let attribute in props) { 22 | let { twoWay, type, trackProperties, noBind } = props[attribute] 23 | 24 | if (noBind) continue 25 | 26 | const setMethodName = 'set' + Str.capitalizeFirstLetter(attribute) 27 | const getMethodName = 'get' + Str.capitalizeFirstLetter(attribute) 28 | const eventName = attribute.toLowerCase() + '_changed' 29 | const initialValue = vueInst[attribute] 30 | 31 | if (typeof googleMapsInst[setMethodName] === 'undefined') { 32 | throw new Error( 33 | `${setMethodName} is not a method of (the Maps object corresponding to) ${vueInst.$options._componentTag}` 34 | ) 35 | } 36 | 37 | // We need to avoid an endless 38 | // propChanged -> event $emitted -> propChanged -> event $emitted loop 39 | // although this may really be the user's responsibility 40 | if (type !== Object || !trackProperties) { 41 | // Track the object deeply 42 | vueInst.$watch( 43 | attribute, 44 | () => { 45 | const attributeValue = vueInst[attribute] 46 | 47 | googleMapsInst[setMethodName](attributeValue) 48 | }, 49 | { 50 | immediate: typeof initialValue !== 'undefined', 51 | deep: type === Object, 52 | } 53 | ) 54 | } else { 55 | WatchPrimitiveProperties( 56 | vueInst, 57 | trackProperties.map((prop) => `${attribute}.${prop}`), 58 | () => { 59 | googleMapsInst[setMethodName](vueInst[attribute]) 60 | }, 61 | vueInst[attribute] !== undefined 62 | ) 63 | } 64 | 65 | if (twoWay && (vueInst.$gmapOptions.autobindAllEvents || vueInst.$attrs[eventName])) { 66 | googleMapsInst.addListener(eventName, () => { 67 | // eslint-disable-line no-unused-vars 68 | vueInst.$emit(eventName, googleMapsInst[getMethodName]()) 69 | }) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/utils/create-map-script.js: -------------------------------------------------------------------------------- 1 | export function createMapScript(options) { 2 | const googleMapScript = document.createElement('SCRIPT') 3 | if (typeof options !== 'object') { 4 | throw new Error('options should be an object') 5 | } 6 | 7 | // libraries 8 | /* eslint-disable no-prototype-builtins */ 9 | if (Array.prototype.isPrototypeOf(options.libraries)) { 10 | options.libraries = options.libraries.join(',') 11 | } 12 | options['callback'] = 'vueGoogleMapsInit' 13 | let baseUrl = 'https://maps.googleapis.com/maps/api/js?' 14 | 15 | let url = 16 | baseUrl + 17 | Object.keys(options) 18 | .map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(options[key])) 19 | .join('&') 20 | 21 | googleMapScript.setAttribute('src', url) 22 | googleMapScript.setAttribute('async', '') 23 | googleMapScript.setAttribute('defer', '') 24 | 25 | return googleMapScript 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/env.js: -------------------------------------------------------------------------------- 1 | export class Env { 2 | static isServer() { 3 | return typeof document === 'undefined' 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/lazyValue.js: -------------------------------------------------------------------------------- 1 | // lazy-value by sindresorhus 2 | 3 | export default (fn) => { 4 | let called = false 5 | let result 6 | 7 | return () => { 8 | if (!called) { 9 | called = true 10 | result = fn() 11 | } 12 | 13 | return result 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/load-google-maps.js: -------------------------------------------------------------------------------- 1 | export class Loader { 2 | constructor({ apiKey, libraries = [], language, region, version, mapIds }) { 3 | // @ts-ignore 4 | this.callbacks = [] 5 | this.CALLBACK = '__googleMapsCallback' 6 | this.version = version 7 | this.apiKey = apiKey 8 | this.libraries = libraries 9 | // @ts-ignore 10 | this.language = language 11 | // @ts-ignore 12 | this.region = region 13 | this.URL = 'https://maps.googleapis.com/maps/api/js' 14 | // @ts-ignore 15 | this.mapIds = mapIds 16 | } 17 | /** 18 | * CreateUrl returns the Google Maps JavaScript API script url given the [[LoaderOptions]]. 19 | * 20 | * @ignore 21 | */ 22 | createUrl() { 23 | let url = this.URL 24 | console.log(this.URL) 25 | 26 | url += `?callback=${this.CALLBACK}` 27 | 28 | if (this.apiKey) { 29 | url += `&key=${this.apiKey}` 30 | } 31 | 32 | if (this.libraries.length > 0) { 33 | url += `&libraries=${this.libraries.join(',')}` 34 | } 35 | 36 | if (this.language) { 37 | url += `&language=${this.language}` 38 | } 39 | 40 | if (this.region) { 41 | url += `®ion=${this.region}` 42 | } 43 | 44 | if (this.version) { 45 | url += `&v=${this.version}` 46 | } 47 | 48 | if (this.mapIds) { 49 | url += `&map_ids=${this.mapIds.join(',')}` 50 | } 51 | 52 | return url 53 | } 54 | 55 | /** 56 | * Load the Google Maps JavaScript API script and return a Promise. 57 | */ 58 | load() { 59 | return this.loadPromise() 60 | } 61 | 62 | /** 63 | * Load the Google Maps JavaScript API script and return a Promise. 64 | * 65 | * @ignore 66 | */ 67 | loadPromise() { 68 | return new Promise((resolve, reject) => { 69 | this.loadCallback((err) => { 70 | if (!err) { 71 | resolve() 72 | } else { 73 | reject(err) 74 | } 75 | }) 76 | }) 77 | } 78 | 79 | /** 80 | * Load the Google Maps JavaScript API script with a callback. 81 | */ 82 | loadCallback(fn) { 83 | this.callbacks.push(fn) 84 | this.execute() 85 | } 86 | 87 | /** 88 | * Set the script on document. 89 | */ 90 | setScript() { 91 | const url = this.createUrl() 92 | const script = document.createElement('script') 93 | 94 | script.type = 'text/javascript' 95 | script.src = url 96 | // @ts-ignore 97 | script.onerror = this.loadErrorCallback.bind(this) 98 | script.defer = true 99 | script.async = true 100 | document.head.appendChild(script) 101 | } 102 | 103 | loadErrorCallback(e) { 104 | this.onerrorEvent = e 105 | this.callback() 106 | } 107 | 108 | setCallback() { 109 | window.__googleMapsCallback = this.callback.bind(this) 110 | } 111 | 112 | callback() { 113 | this.done = true 114 | this.loading = false 115 | 116 | this.callbacks.forEach((cb) => { 117 | cb(this.onerrorEvent) 118 | }) 119 | 120 | this.callbacks = [] 121 | } 122 | 123 | execute() { 124 | if (this.done) { 125 | this.callback() 126 | } else { 127 | if (this.loading) { 128 | // do nothing but wait 129 | } else { 130 | this.loading = true 131 | this.setCallback() 132 | this.setScript() 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/utils/mountableMixin.js: -------------------------------------------------------------------------------- 1 | /* 2 | Mixin for objects that are mounted by Google Maps 3 | Javascript API. 4 | 5 | These are objects that are sensitive to element resize 6 | operations so it exposes a property which accepts a bus 7 | 8 | */ 9 | 10 | export default { 11 | props: ['resizeBus'], 12 | 13 | data() { 14 | return { 15 | _actualResizeBus: null, 16 | } 17 | }, 18 | 19 | created() { 20 | if (typeof this.resizeBus === 'undefined') { 21 | this.$data._actualResizeBus = this.$gmapDefaultResizeBus 22 | } else { 23 | this.$data._actualResizeBus = this.resizeBus 24 | } 25 | }, 26 | 27 | methods: { 28 | _resizeCallback() { 29 | this.resize() 30 | }, 31 | isFunction(functionToCheck) { 32 | return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]' 33 | }, 34 | _delayedResizeCallback() { 35 | this.$nextTick(() => this._resizeCallback()) 36 | }, 37 | }, 38 | 39 | watch: { 40 | resizeBus(newVal) { 41 | // eslint-disable-line no-unused-vars 42 | this.$data._actualResizeBus = newVal 43 | }, 44 | '$data._actualResizeBus'(newVal, oldVal) { 45 | if (oldVal) { 46 | oldVal.$off('resize', this._delayedResizeCallback) 47 | } 48 | if (newVal) { 49 | // newVal.$on('resize', this._delayedResizeCallback) 50 | } 51 | }, 52 | }, 53 | 54 | unmounted() { 55 | if (this.$data._actualResizeBus && this.isFunction(this.$data._actualResizeBus.$off)) { 56 | this.$data._actualResizeBus.$off('resize', this._delayedResizeCallback) 57 | } 58 | }, 59 | } 60 | -------------------------------------------------------------------------------- /src/utils/simulateArrowDown.js: -------------------------------------------------------------------------------- 1 | // This piece of code was orignally written by amirnissim and can be seen here 2 | // http://stackoverflow.com/a/11703018/2694653 3 | // This has been ported to Vanilla.js by GuillaumeLeclerc 4 | export default (input) => { 5 | const _addEventListener = input.addEventListener ? input.addEventListener : input.attachEvent 6 | 7 | function addEventListenerWrapper(type, listener) { 8 | // Simulate a 'down arrow' keypress on hitting 'return' when no pac suggestion is selected, 9 | // and then trigger the original listener. 10 | if (type === 'keydown') { 11 | const origListener = listener 12 | listener = function (event) { 13 | const suggestionSelected = document.getElementsByClassName('pac-item-selected').length > 0 14 | if (event.which === 13 && !suggestionSelected) { 15 | const simulatedEvent = document.createEvent('Event') 16 | simulatedEvent.keyCode = 40 17 | simulatedEvent.which = 40 18 | origListener.apply(input, [simulatedEvent]) 19 | } 20 | origListener.apply(input, [event]) 21 | } 22 | } 23 | _addEventListener.apply(input, [type, listener]) 24 | } 25 | 26 | input.addEventListener = addEventListenerWrapper 27 | input.attachEvent = addEventListenerWrapper 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/string.js: -------------------------------------------------------------------------------- 1 | export class Str { 2 | static capitalizeFirstLetter(string) { 3 | return string.charAt(0).toUpperCase() + string.slice(1) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "extends": "standard", 9 | "parserOptions": { 10 | "sourceType": "module" 11 | }, 12 | "rules": { 13 | "comma-dangle": 0 14 | }, 15 | "globals": { 16 | "google": true, 17 | "Vue": true, 18 | "VueGoogleMaps": true 19 | }, 20 | "plugins": [ 21 | "html" 22 | ], 23 | "settings": { 24 | "html/html-extensions": [".html", ".vue"] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | import Lab from 'lab' 2 | import assert from 'assert' 3 | import fs from 'fs' 4 | import path from 'path' 5 | import {getPage, loadFile} from './test-setup/test-common' 6 | 7 | export const lab = Lab.script() 8 | 9 | lab.experiment('Basic tests', {timeout: 15000}, function () { 10 | let page = null 11 | 12 | async function loadPage () { 13 | return loadFile(page, './test-pages/test-plain-map.html', { 14 | waitUntil: 'domcontentloaded' 15 | }) 16 | } 17 | 18 | async function mountVue () { 19 | return page.evaluateHandle(() => 20 | new Promise((resolve) => { 21 | new Vue({ 22 | created () { 23 | resolve(this) 24 | }, 25 | }).$mount('#test1') 26 | })) 27 | } 28 | 29 | lab.before({timeout: 15000}, getPage(p => { page = p })) 30 | 31 | lab.test('Maps API is loaded', async function () { 32 | await loadPage() 33 | const vue = await mountVue() 34 | 35 | assert(await page.evaluate( 36 | (vue) => 37 | vue.$refs.map.$mapPromise 38 | .then(() => vue.$refs.map.$mapObject instanceof google.maps.Map), 39 | vue), '$mapPromise is defined') 40 | 41 | assert(await page.evaluate( 42 | (vue) => 43 | vue.$refs.map.$mapObject 44 | .getDiv().parentNode.classList.contains('map-container'), 45 | vue), 46 | 'Parent of $mapObject.div is a .map-container') 47 | }) 48 | 49 | lab.test('Panning of map works', {timeout: 30000}, async function () { 50 | await loadPage() 51 | const vue = await mountVue() 52 | 53 | const [top, right, bottom, left] = await page.evaluate(() => { 54 | const el = document.querySelector('.map-container') 55 | const top = el.offsetTop 56 | const right = el.offsetLeft + el.offsetWidth 57 | const bottom = el.offsetTop + el.offsetHeight 58 | const left = el.offsetLeft 59 | 60 | return [top, right, bottom, left] 61 | }) 62 | 63 | // Wait for map to load first... 64 | await page.evaluate((vue) => 65 | vue.$refs.map.$mapPromise 66 | .then(() => new Promise(resolve => setTimeout(resolve, 500))), 67 | vue) 68 | 69 | // Then try to pan the page 70 | await page.mouse.move(right - 4, top + 4) 71 | await page.mouse.down() 72 | await page.mouse.move(left + 4, bottom - 4, {steps: 20}) 73 | await new Promise(resolve => setTimeout(resolve, 100)) 74 | await page.mouse.up() 75 | 76 | const {lat, lng} = await page.evaluate((vue) => { 77 | const c = vue.$refs.map.$mapObject.getCenter() 78 | return {lat: c.lat(), lng: c.lng()} 79 | }, vue) 80 | assert(lat > 1.45, 'Lat greater than 1.45') 81 | assert(lng > 103.9, 'Lng greater than 103.9') 82 | }) 83 | 84 | lab.test('Lodash library is not bloating up the library', async () => { 85 | const libraryOutput = fs.readFileSync( 86 | path.join(__dirname, '../dist/vue-google-maps.js'), 87 | 'utf-8' 88 | ) 89 | 90 | if (/Lodash /.test(libraryOutput)) { 91 | assert(false, 92 | 'Lodash found! This is bad because you are bloating up the library') 93 | } 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /test/gmapApi-guard.js: -------------------------------------------------------------------------------- 1 | import Lab from 'lab' 2 | import assert from 'assert' 3 | import {getPage, loadFile} from './test-setup/test-common' 4 | 5 | export const lab = Lab.script() 6 | 7 | lab.experiment('Effectiveness of gmapApi guard', {timeout: 15000}, function () { 8 | let page = null 9 | let isError = false 10 | 11 | async function loadPage () { 12 | return loadFile(page, './test-pages/test-gmapApi.html', { 13 | waitUntil: 'networkidle0' 14 | }) 15 | } 16 | 17 | lab.before({timeout: 15000}, getPage(p => { 18 | isError = false 19 | page = p 20 | 21 | page.on('error', (err) => { 22 | isError = err 23 | }) 24 | page.on('pageerror', (err) => { 25 | isError = err 26 | }) 27 | return p 28 | })) 29 | 30 | lab.test('gmapGuard prevents errors', async function () { 31 | await loadPage() 32 | 33 | assert(!isError) 34 | assert(await page.evaluate(() => { 35 | return google && (window.vue.$refs.myMarker.position instanceof google.maps.LatLng) 36 | }), 'Marker is loaded with a position') 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/maps-not-loaded.js: -------------------------------------------------------------------------------- 1 | import Lab from 'lab' 2 | import assert from 'assert' 3 | import {getPage, loadFile} from './test-setup/test-common' 4 | 5 | export const lab = Lab.script() 6 | 7 | lab.experiment('On-demand API loading', {timeout: 15000}, function () { 8 | let page = null 9 | 10 | async function loadPage () { 11 | return loadFile(page, './test-pages/test-page-without-maps.html', { 12 | waitUntil: 'networkidle0' 13 | }) 14 | } 15 | 16 | async function mountVue () { 17 | return page.evaluateHandle(() => 18 | new Promise((resolve) => { 19 | new Vue({ 20 | data: { 21 | loadMap: false, 22 | }, 23 | mounted () { 24 | resolve(this) 25 | }, 26 | }).$mount('#test1') 27 | })) 28 | } 29 | 30 | lab.before({timeout: 15000}, getPage(p => { page = p })) 31 | 32 | lab.test('Maps API is loaded only on demand', async function () { 33 | await loadPage() 34 | const vue = await mountVue() 35 | 36 | assert(await page.evaluate( 37 | (vue) => { 38 | const allScriptElements = Array.prototype.slice.call(document.getElementsByTagName('SCRIPT'), 0) 39 | return ( 40 | allScriptElements.every(s => !s.src.toLowerCase().includes('maps.googleapis.com')) && 41 | !window.google 42 | ) 43 | }, 44 | vue), 'Google APIs are not loaded') 45 | 46 | assert(await page.evaluate( 47 | (vue) => { 48 | return Promise.resolve(null) 49 | .then(() => { 50 | vue.loadMap = true 51 | 52 | return new Promise((resolve) => setTimeout(resolve, 100)) 53 | }) 54 | .then(() => vue.$refs.gmap.$mapPromise.then(() => !!window.google)) 55 | .then((isGoogleLoaded) => { 56 | const allScriptElements = Array.prototype.slice.call(document.getElementsByTagName('SCRIPT'), 0) 57 | return ( 58 | isGoogleLoaded && 59 | allScriptElements.some(s => s.src.toLowerCase().includes('maps.googleapis.com')) 60 | ) 61 | }) 62 | }, 63 | vue), 'Google APIs are loaded') 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /test/marker-with-infowindow.js: -------------------------------------------------------------------------------- 1 | import Lab from 'lab' 2 | import assert from 'assert' 3 | import {getPage, loadFile} from './test-setup/test-common' 4 | 5 | export const lab = Lab.script() 6 | 7 | lab.experiment('Marker / Infowindow tests', {timeout: 15000}, function () { 8 | let page = null 9 | 10 | async function loadPage () { 11 | return loadFile(page, './test-pages/test-marker-with-infowindow.html', { 12 | waitUntil: 'domcontentloaded' 13 | }) 14 | } 15 | 16 | lab.before({timeout: 15000}, getPage(p => { page = p })) 17 | 18 | lab.test('Clicking the marker triggers the infowindow', async function () { 19 | await loadPage() 20 | 21 | await page.evaluate(() => { 22 | // Ensure that the map has been created 23 | return window.theVue.$refs.map.$mapPromise 24 | .then(() => { 25 | // Give some more time for the marker to initialize 26 | return new Promise(resolve => setTimeout(resolve, 100)) 27 | }) 28 | }) 29 | 30 | // Is the infowindow hidden? 31 | assert(await page.evaluate(() => { 32 | return !document.getElementById('thediv') || ( 33 | document.getElementById('thediv').offsetWidth === 0 && 34 | document.getElementById('thediv').offsetHeight === 0 35 | ) 36 | }), 37 | 'infowindow is hidden') 38 | 39 | // Clicked is false 40 | assert(await page.evaluate(() => { 41 | return !window.theVue.clicked 42 | }), 43 | 'marker is not clicked') 44 | 45 | // Obtain the center 46 | const [x, y] = await page.evaluate(() => { 47 | const el = window.theVue.$refs.map.$el 48 | 49 | return Promise.resolve([ 50 | el.offsetLeft + 0.5 * el.offsetWidth, 51 | el.offsetTop + 0.5 * el.offsetHeight, 52 | ]) 53 | }) 54 | 55 | await page.mouse.click(x, y - 20) 56 | 57 | // Clicked is now true! 58 | assert(await page.evaluate(() => { 59 | return new Promise(resolve => setTimeout(resolve, 100)) 60 | .then(() => { 61 | return window.theVue.clicked 62 | }) 63 | }), 64 | 'marker is clicked') 65 | 66 | // Infowindow is now open! 67 | assert(await page.evaluate(() => { 68 | return document.getElementById('thediv').offsetWidth > 10 69 | }), 70 | '#thediv is visible') 71 | 72 | // shut the infowindow 73 | assert(await page.evaluate(() => { 74 | window.theVue.infoWindow.open = false 75 | return new Promise(resolve => setTimeout(resolve, 100)) 76 | .then(() => { 77 | return !document.getElementById('thediv') || ( 78 | document.getElementById('thediv').offsetWidth === 0 && 79 | document.getElementById('thediv').offsetHeight === 0 80 | ) 81 | }) 82 | }), 'setting open=false closes #thediv') 83 | }) 84 | }) 85 | -------------------------------------------------------------------------------- /test/test-all-examples.js: -------------------------------------------------------------------------------- 1 | import Lab from 'lab' 2 | import assert from 'assert' 3 | import fs from 'fs' 4 | import path from 'path' 5 | import {getPage, loadFile} from './test-setup/test-common' 6 | 7 | export const lab = Lab.script() 8 | 9 | lab.experiment('Examples test', {timeout: 15000}, function () { 10 | let page = null 11 | 12 | async function loadPage (f) { 13 | return loadFile(page, f, { 14 | waitUntil: 'networkidle0' 15 | }) 16 | } 17 | 18 | lab.before({timeout: 15000}, getPage(p => { page = p })) 19 | 20 | lab.test('Test all examples pages load without errors (does not test functionality)', {timeout: 50000}, async function () { 21 | const files = fs.readdirSync(path.join(__dirname, '../examples')).filter(f => f.endsWith('.html')) 22 | let isErrored = false 23 | 24 | page.on('error', (err) => { 25 | isErrored = err 26 | }) 27 | page.on('pageerror', (err) => { 28 | isErrored = err 29 | }) 30 | 31 | assert(!isErrored) 32 | 33 | for (let file of files) { 34 | await loadPage('../examples/' + file) 35 | if (isErrored) { 36 | throw new Error(`The example file ../examples/${file} threw an error ${isErrored}`) 37 | } 38 | } 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /test/test-pages/test-gmapApi.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 |
11 |

Test 1

12 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 41 | 42 | -------------------------------------------------------------------------------- /test/test-pages/test-marker-with-infowindow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 13 | 14 | 19 | 20 | 21 | 26 |
hello
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /test/test-pages/test-page-without-maps.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 |
11 |

Test 1

12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 28 | 29 | -------------------------------------------------------------------------------- /test/test-pages/test-plain-map.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 |
11 |

Test 1

12 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 30 | 31 | -------------------------------------------------------------------------------- /test/test-setup/babel-transform.js: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/nlf/lab-babel/blob/master/lib/index.js 2 | require('babel-polyfill') 3 | var Babel = require('babel-core') 4 | 5 | var internals = {} 6 | internals.transform = function (content, filename) { 7 | if (/^node_modules/.test(filename)) { 8 | return content 9 | } 10 | 11 | var transformed = Babel.transform(content, { 12 | filename: filename, 13 | sourceMap: 'inline', 14 | sourceFileName: filename, 15 | auxiliaryCommentBefore: '$lab:coverage:off$', 16 | auxiliaryCommentAfter: '$lab:coverage:on$', 17 | presets: ['es2015'], 18 | plugins: ['transform-object-rest-spread'], 19 | }) 20 | 21 | return transformed.code 22 | } 23 | 24 | internals.extensions = ['js', 'jsx', 'es', 'es6'] 25 | internals.methods = [] 26 | for (var i = 0, il = internals.extensions.length; i < il; ++i) { 27 | internals.methods.push({ ext: internals.extensions[i], transform: internals.transform }) 28 | } 29 | 30 | module.exports = internals.methods 31 | -------------------------------------------------------------------------------- /test/test-setup/compile-standalone.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack' 2 | import * as shell from 'shelljs' 3 | import path from 'path' 4 | 5 | export default new Promise((resolve, reject) => { 6 | const webpackConfig = require('../../webpack.config.js')[0] 7 | 8 | webpack( 9 | { 10 | ...webpackConfig, 11 | mode: 'development', 12 | }, 13 | (err, status) => { 14 | if (!err) { 15 | shell.cp( 16 | path.resolve(__dirname, '../../dist/vue-google-maps.js'), 17 | path.resolve(__dirname, '../../examples/vue-google-maps.js') 18 | ) 19 | resolve() 20 | } else { 21 | reject(err) 22 | } 23 | } 24 | ) 25 | }) 26 | -------------------------------------------------------------------------------- /test/test-setup/test-common.js: -------------------------------------------------------------------------------- 1 | import Puppeteer from 'puppeteer' 2 | import CompileStandalone from './compile-standalone' 3 | import path from 'path' 4 | 5 | const puppeteerPromise = CompileStandalone.then(() => { 6 | let options = {} 7 | 8 | if (process.env['THIS_IS_ON_TRAVIS_AND_SANDBOX_IS_NOT_ALLOWED'] === 'true') { 9 | options.args = ['--no-sandbox', '--disable-setuid-sandbox'] 10 | } 11 | 12 | return Puppeteer.launch(options) 13 | }) 14 | 15 | export function getPage (p) { 16 | return async () => { 17 | p(await puppeteerPromise.then(browser => browser.newPage())) 18 | } 19 | } 20 | 21 | export async function loadFile (page, relpath, options) { 22 | return page.goto('file:///' + path.join(__dirname, '../', relpath), options) 23 | } 24 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vue-google-maps-community-fork'; --------------------------------------------------------------------------------