├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .markdownlint.json ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets └── docs cover art.afphoto ├── docs ├── .vitepress │ ├── config.mts │ ├── npm.svg │ └── theme │ │ ├── index.ts │ │ └── style.css ├── advanced-usage │ └── index.md ├── components │ ├── advanced-marker.md │ ├── circle.md │ ├── custom-control.md │ ├── custom-marker.md │ ├── heatmap-layer.md │ ├── index.md │ ├── info-window.md │ ├── marker-cluster.md │ ├── marker.md │ ├── polygon.md │ ├── polyline.md │ └── rectangle.md ├── getting-started │ └── index.md ├── images │ ├── map-1200.jpg │ └── map-800.jpg ├── index.md ├── shared │ └── index.ts └── themes │ └── index.md ├── netlify.toml ├── package.json ├── playground ├── App.vue ├── index.html └── main.ts ├── pnpm-lock.yaml ├── scripts └── build-umd.js ├── src ├── @types │ └── index.ts ├── components │ ├── AdvancedMarker.vue │ ├── Circle.ts │ ├── CustomControl.vue │ ├── CustomMarker.vue │ ├── GoogleMap.vue │ ├── HeatmapLayer.ts │ ├── InfoWindow.vue │ ├── Marker.ts │ ├── MarkerCluster.ts │ ├── Polygon.ts │ ├── Polyline.ts │ ├── Rectangle.ts │ └── index.ts ├── composables │ ├── index.ts │ └── useSetupMapComponent.ts ├── index.ts ├── shared │ └── index.ts ├── shims-google-maps.ts ├── shims-vue.d.ts ├── themes │ ├── aubergine.ts │ ├── dark.ts │ ├── grey.ts │ ├── index.ts │ ├── minimal.ts │ ├── retro.ts │ ├── roadways.ts │ ├── roadwaysMinimal.ts │ └── ultraLight.ts └── utils │ └── index.ts ├── tsconfig.build.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | "plugin:vue/vue3-essential", 8 | "eslint:recommended", 9 | "@vue/typescript/recommended", 10 | "@vue/prettier", 11 | "@vue/prettier/@typescript-eslint", 12 | ], 13 | parserOptions: { 14 | ecmaVersion: 2020, 15 | }, 16 | globals: { 17 | google: true, 18 | }, 19 | rules: { 20 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", 21 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 22 | "@typescript-eslint/no-var-requires": "off", 23 | "brace-style": ["error", "1tbs", { allowSingleLine: true }], 24 | semi: ["error", "always"], 25 | quotes: ["warn", "double"], 26 | "@typescript-eslint/no-unused-vars": [ 27 | "error", 28 | { 29 | varsIgnorePattern: "cases|^_", 30 | argsIgnorePattern: "^_", 31 | }, 32 | ], 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build & Publish 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | build: 9 | name: Main Job 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 🛎 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Node ⚙️ 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: 18.17.0 20 | 21 | - name: Install dependencies 👨🏻‍💻 22 | uses: pnpm/action-setup@v2 23 | with: 24 | version: 8 25 | run_install: | 26 | args: [--frozen-lockfile, --strict-peer-dependencies] 27 | 28 | - name: Build library 🛠 29 | run: pnpm build 30 | 31 | - name: Publish package 📦 32 | uses: JS-DevTools/npm-publish@v1 33 | with: 34 | token: ${{ secrets.NPM_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env 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 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *.sw? 21 | .vscode 22 | 23 | # avoid having dist files in repo; these should only go to NPM 24 | dist 25 | docs/.vitepress/dist 26 | docs/.vitepress/cache 27 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "no-inline-html": false, 3 | "line-length": false, 4 | "no-duplicate-heading": false, 5 | "heading-increment": false 6 | } 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs 2 | README.md 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [0.22.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.21.1...v0.22.0) (2025-04-28) 6 | 7 | 8 | ### Features 9 | 10 | * **AdvancedMarker:** support slot custom content ([c1fdb3f](https://github.com/inocan-group/vue3-google-maps/commit/c1fdb3f50234fd874a21be7d8d5f14dc0d5d0bfd)) 11 | * colorScheme support ([29720fb](https://github.com/inocan-group/vue3-google-maps/commit/29720fbae3ebcd246e5614a5cd3a6972730ddb95)), closes [#289](https://github.com/inocan-group/vue3-google-maps/issues/289) 12 | * support cameraControl ([85b655e](https://github.com/inocan-group/vue3-google-maps/commit/85b655e1397e3e9cbb28115772e3fda3ab329100)) 13 | 14 | ### [0.21.1](https://github.com/inocan-group/vue3-google-maps/compare/v0.21.0...v0.21.1) (2025-01-10) 15 | 16 | optimize CustomMarkerClass draw method ([93c2b50](https://github.com/inocan-group/vue3-google-maps/commit/93c2b50c1199e1236dc4ddc7b5b829595964b905)) 17 | 18 | 19 | ### [0.21.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.20.0...v0.21.0) (2024-08-04) 20 | 21 | 22 | ### Features 23 | 24 | * advanced markers ([69a1b66](https://github.com/inocan-group/vue3-google-maps/commit/69a1b666267e2ee840c0df7530b1fbcc810c7021)) 25 | 26 | 27 | ### [0.20.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.19.0...v0.20.0) (2024-03-19) 28 | 29 | 30 | ### Features 31 | 32 | * expose nonce option from @googlemaps/js-api-loader ([6e7bcdc](https://github.com/inocan-group/vue3-google-maps/commit/6e7bcdc87919b561cc34d89fb6b7b6e7fef07e6c)) 33 | 34 | 35 | ### Bug Fixes 36 | 37 | * use with SSR ([5dd31c5](https://github.com/inocan-group/vue3-google-maps/commit/5dd31c5f9839259683c36cc6c43dcb8e37f8475c)) 38 | 39 | ### [0.19.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.18.0...v0.19.0) (2024-01-19) 40 | 41 | 42 | ### Features 43 | 44 | * add missing map props and events ([1e49172](https://github.com/inocan-group/vue3-google-maps/commit/1e491722b900925b827e1b572b644f51eb269fc3)) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * v-show with custom controls ([6cec18c](https://github.com/inocan-group/vue3-google-maps/commit/6cec18c369fffc4b48b4ff7dcf77c32a024ff9d7)) 50 | 51 | ### [0.18.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.17.1...v0.18.0) (2023-09-28) 52 | 53 | 54 | ### Features 55 | 56 | * add v-model to infowindow ([64ac3ac](https://github.com/inocan-group/vue3-google-maps/commit/64ac3ac42a9125ccf538553e97ba5b8dd0ac2c66)) 57 | 58 | ### [0.17.1](https://github.com/inocan-group/vue3-google-maps/compare/v0.17.0...v0.17.1) (2023-08-27) 59 | 60 | 61 | ### Bug Fixes 62 | 63 | * custom markers reactivity ([4a06b12](https://github.com/inocan-group/vue3-google-maps/commit/4a06b12b800fcb49d013c8b3cd5279f7ee0b5f25)) 64 | 65 | ### [0.17.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.16.0...v0.17.0) (2023-08-12) 66 | 67 | 68 | ### Features 69 | 70 | * **perf:** new default clustering algo ([#152](https://github.com/inocan-group/vue3-google-maps/issues/152)) ([3feb791](https://github.com/inocan-group/vue3-google-maps/commit/3feb791828f45066170e89aa94120a3cec36c447)) 71 | 72 | ### [0.16.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.15.0...v0.16.0) (2023-08-07) 73 | 74 | 75 | ### Features 76 | 77 | * expose open and close methods on InfoWindow ([d21dfba](https://github.com/inocan-group/vue3-google-maps/commit/d21dfbaea309c94d9c9ce8a8a58676cd1760b768)) 78 | * info window enhancements ([57c895d](https://github.com/inocan-group/vue3-google-maps/commit/57c895d14104dfa48467fd5e46797db9f3723363)) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * expose custom marker ([68f5a3e](https://github.com/inocan-group/vue3-google-maps/commit/68f5a3e2244e9a51abb3a7ccc3ebc394e3050b4b)) 84 | 85 | ### [0.15.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.14.1...v0.15.0) (2022-09-03) 86 | 87 | ### [0.14.1](https://github.com/inocan-group/vue3-google-maps/compare/v0.14.0...v0.14.1) (2022-08-31) 88 | 89 | 90 | ### Bug Fixes 91 | 92 | * heatmap layer ([ee04c85](https://github.com/inocan-group/vue3-google-maps/commit/ee04c85cc4491740d4d4593465b48eb7f618746b)) 93 | 94 | ### [0.14.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.13.2...v0.14.0) (2022-08-24) 95 | 96 | 97 | ### Features 98 | 99 | * heatmap layer ([d961636](https://github.com/inocan-group/vue3-google-maps/commit/d9616368e3aa263b45ad805058d73ae7e5c25ed2)) 100 | 101 | ### [0.13.2](https://github.com/inocan-group/vue3-google-maps/compare/v0.13.1...v0.13.2) (2022-06-28) 102 | 103 | 104 | ### Bug Fixes 105 | 106 | * attrs fallthrough in custom markers and info windows ([c2a821f](https://github.com/inocan-group/vue3-google-maps/commit/c2a821feb1a27254a167eea717ac9f64fdd343a1)) 107 | 108 | ### [0.13.1](https://github.com/inocan-group/vue3-google-maps/compare/v0.13.0...v0.13.1) (2022-06-27) 109 | 110 | 111 | ### Bug Fixes 112 | 113 | * dom management conflicts in custom markers ([f8f6bef](https://github.com/inocan-group/vue3-google-maps/commit/f8f6beff78a37499e981354dd51f81b1db1eaa6a)) 114 | * dom management conflicts in info windows ([1d6e269](https://github.com/inocan-group/vue3-google-maps/commit/1d6e26938012b09e7f17a0276085a8935902dcc9)) 115 | 116 | ## [0.13.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.12.0...v0.13.0) (2022-05-28) 117 | 118 | 119 | ### Features 120 | 121 | * allow loading api script externally ([2a55b60](https://github.com/inocan-group/vue3-google-maps/commit/2a55b60ae57fedf0e5315cb696bbbf9f70a1c2ae)) 122 | 123 | ## [0.12.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.10.0...v0.12.0) (2022-05-19) 124 | 125 | 126 | ### Features 127 | 128 | * custom markers ([ecc28b0](https://github.com/inocan-group/vue3-google-maps/commit/ecc28b0455a54502734ae1ae9b1d69cde9e0652e)) 129 | * add mapId in GoogleMap Component ([b72105c](https://github.com/inocan-group/vue3-google-maps/commit/b72105ca33bcf115ce83fe5a09ad4ccc5530d8bc)) 130 | ## [0.11.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.10.0...v0.11.0) (2022-05-11) 131 | 132 | 133 | ### Features 134 | 135 | * marker clusters ([5d58e2e](https://github.com/inocan-group/vue3-google-maps/commit/5d58e2e9ead8356c972d7700b9218ba77889ad15)) 136 | 137 | ## [0.10.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.9.0...v0.10.0) (2022-03-25) 138 | 139 | 140 | ### ⚠ BREAKING CHANGES 141 | 142 | * separate entry point for bundled themes 143 | 144 | ### build 145 | 146 | * separate entry point for bundled themes ([27ff148](https://github.com/inocan-group/vue3-google-maps/commit/27ff148714656d04415d84b2c11663e4b0c84e16)) 147 | 148 | ## [0.9.0](https://github.com/inocan-group/vue3-google-maps/compare/v0.8.5...v0.9.0) (2022-03-22) 149 | 150 | 151 | ### Features 152 | 153 | * ability to nest info windows inside markers ([c239cc7](https://github.com/inocan-group/vue3-google-maps/commit/c239cc7ad0851ec0238e178e10835a9dfb0169a9)) 154 | 155 | ### [0.8.5](https://github.com/inocan-group/vue3-google-maps/compare/v0.8.4...v0.8.5) (2022-03-21) 156 | 157 | 158 | ### Bug Fixes 159 | 160 | * unwrap component refs ([8d0443b](https://github.com/inocan-group/vue3-google-maps/commit/8d0443befd842dd40169a0bda70fe5a8380ebeca)) 161 | 162 | ### [0.8.4](https://github.com/inocan-group/vue3-google-maps/compare/v0.8.3...v0.8.4) (2022-03-20) 163 | 164 | 165 | ### Features 166 | 167 | * info windows ([6a294f2](https://github.com/inocan-group/vue3-google-maps/commit/6a294f2a86b55dca96137bde5e719923c634c4a7)) 168 | 169 | ### [0.8.3](https://github.com/inocan-group/vue3-google-maps/compare/v0.8.2...v0.8.3) (2021-12-12) 170 | 171 | 172 | ### Bug Fixes 173 | 174 | * make js-api-loader a dependency ([a8fb747](https://github.com/inocan-group/vue3-google-maps/commit/a8fb747ebd290e87a1572a2c8fcf6efd64b6f282)) 175 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-present, Inocan Group 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-google-map 2 | 3 | ![Build Status](https://github.com/inocan-group/vue3-google-map/actions/workflows/build.yml/badge.svg) 4 | [![License](https://img.shields.io/github/license/inocan-group/vue3-google-map)](https://github.com/inocan-group/vue3-google-map/blob/develop/LICENSE) 5 | 6 | > Composable components for easy use of Google Maps with Vue 3 7 | 8 | `vue3-google-map` offers a set of composable components for easy use of Google Maps in your Vue 3 projects. 9 | 10 | Note: Please refer to the [documentation site](https://vue3-google-map.com/) for rendered examples. 11 | 12 | ## Table of Contents 13 | 14 | - [Getting Started](#getting-started) 15 | - [Installation](#installation) 16 | - [Your First Map](#your-first-map) 17 | - [Components](#components) 18 | - [Advanced Marker](#advanced-marker) 19 | - [Marker](#marker) 20 | - [Polyline](#polyline) 21 | - [Polygon](#polygon) 22 | - [Rectangle](#rectangle) 23 | - [Circle](#circle) 24 | - [Info Window](#info-window) 25 | - [Custom Marker](#custom-marker) 26 | - [Custom Control](#custom-control) 27 | - [Marker Cluster](#marker-cluster) 28 | - [Heatmap Layer](#heatmap-layer) 29 | - [Advanced Usage](#advanced-usage) 30 | - [Contribution](#contribution) 31 | - [License](#license) 32 | 33 | ## Getting Started 34 | 35 | ### Installation 36 | 37 | #### NPM 38 | 39 | ```bash 40 | npm install vue3-google-map 41 | # OR 42 | pnpm add vue3-google-map 43 | ``` 44 | 45 | #### CDN 46 | 47 | Include the following script tag in your `index.html` (make sure to include it after Vue 3's global build). 48 | 49 | ```html 50 | 51 | ``` 52 | 53 | All the map components are available on the `Vue3GoogleMap` global variable. 54 | 55 | [Codepen demo](https://codepen.io/husamibrahim/pen/poQXZbR) 56 | 57 | ### Your First Map 58 | 59 | To construct a map using `vue3-google-map` you'll need to use the base `GoogleMap` component which receives your [Google Maps API key](https://developers.google.com/maps/documentation/javascript/get-api-key), styles (e.g. setting width and height), and any [MapOptions](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions) to configure your map ([see this](https://github.com/inocan-group/vue3-google-map/blob/develop/src/components/GoogleMap.vue#L36-L244) for all the supported `MapOptions`). 60 | Other map features can be added to your map by passing map subcomponents ([Marker](#marker), [Polyline](#polyline), [Polygon](#polygon), [Rectangle](#rectangle), [Circle](#circle), [InfoWindow](#info-window), [CustomMarker](#custom-marker), [CustomControl](#custom-control), or [MarkerCluster](#marker-cluster)) to the default slot of the `GoogleMap` component. 61 | 62 | The [the following events](https://developers.google.com/maps/documentation/javascript/reference/map#Map-Events) will be emitted by the `GoogleMap` component and can be listened to by using `@event_name`. 63 | 64 | ```vue 65 | 70 | 71 | 81 | ``` 82 | 83 | ## Components 84 | 85 | This library is intended to be used in a composable fashion. Therefore you will find yourself using nested components to build your map rather than just a complicated inline format. 86 | 87 | The main mapping component is `GoogleMap`, however the following components are available at your disposal: 88 | 89 | - [AdvancedMarker](#advanced-marker) 90 | - [Marker](#marker) 91 | - [Polyline](#polyline) 92 | - [Polygon](#polygon) 93 | - [Rectangle](#rectangle) 94 | - [Circle](#circle) 95 | - [InfoWindow](#info-window) 96 | - [CustomMarker](#custom-marker) 97 | - [CustomControl](#custom-control) 98 | - [MarkerCluster](#marker-cluster) 99 | 100 | ### Advanced Marker 101 | 102 | Use the `AdvancedMarker` component to draw markers, drop pins or any custom icons on a map. `AdvancedMarker` is the new version offered by google when deprecated the `Marker` component ([read more here](https://developers.google.com/maps/deprecations#googlemapsmarker_in_the_deprecated_as_of_february_2024)). 103 | 104 | In order to use the `AdvancedMarker` component is necessary to specify a MapId on declaring the `GoogleMap` component ([see more here](https://developers.google.com/maps/documentation/javascript/advanced-markers/start#create_a_map_id)). 105 | 106 | #### Options 107 | 108 | You can pass a [AdvancedMarkerElementOptions](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#AdvancedMarkerElementOptions) object to the `options` prop to configure your marker. 109 | 110 | You can also pass a [PinElementOptions interface](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#PinElementOptions) object to customize pin used by the marker. 111 | 112 | Additionally, `AdvancedMarker` supports default slot content, allowing you to use custom HTML or Vue components inside the marker. 113 | 114 | ```vue 115 | 122 | 123 | 139 | ``` 140 | 141 | #### Events 142 | 143 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#AdvancedMarkerElement-Events) on the `AdvancedMarker` component. 144 | 145 | ### Marker 146 | 147 | Use the `Marker` component to draw markers, drop pins or any custom icons on a map. 148 | 149 | #### Options 150 | 151 | You can pass a [MarkerOptions](https://developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions) object to the `options` prop to configure your marker. 152 | 153 | ```vue 154 | 160 | 161 | 171 | ``` 172 | 173 | #### Events 174 | 175 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/marker#Marker-Events) on the `Marker` component. 176 | 177 | ### Polyline 178 | 179 | Use the `Polyline` component to draw paths and arbitrary shapes on a map. 180 | 181 | #### Options 182 | 183 | You can pass a [PolylineOptions](https://developers.google.com/maps/documentation/javascript/reference/polygon#PolylineOptions) object to the `options` prop to configure your polyline. 184 | 185 | ```vue 186 | 204 | 205 | 215 | ``` 216 | 217 | #### Events 218 | 219 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline-Events) on the `Polyline` component. 220 | 221 | ### Polygon 222 | 223 | Use the `Polygon` component to draw polgons (arbitrary number of sides) on a map. 224 | 225 | #### Options 226 | 227 | You can pass a [PolylgonOptions](https://developers.google.com/maps/documentation/javascript/reference/polygon#PolygonOptions) object to the `options` prop to configure your polyline. 228 | 229 | ```vue 230 | 249 | 250 | 260 | ``` 261 | 262 | #### Events 263 | 264 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/polygon#Polygon-Events) on the `Polygon` component. 265 | 266 | ### Rectangle 267 | 268 | Use the `Rectangle` component to draw simple rectangles on a map. 269 | 270 | #### Options 271 | 272 | You can pass a [RectangleOptions](https://developers.google.com/maps/documentation/javascript/reference/polygon#RectangleOptions) object to the `options` prop to configure your rectangle. 273 | 274 | ```vue 275 | 293 | 294 | 305 | ``` 306 | 307 | #### Events 308 | 309 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/polygon#Rectangle-Events) on the `Rectangle` component. 310 | 311 | ### Circle 312 | 313 | Use the `Circle` component to draw circles on a map. 314 | 315 | #### Options 316 | 317 | You can pass a [CircleOptions](https://developers.google.com/maps/documentation/javascript/reference/polygon#CircleOptions) object to the `options` prop to configure your circle. 318 | 319 | ```vue 320 | 357 | 358 | 369 | ``` 370 | 371 | #### Events 372 | 373 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/polygon#Circle-Events) on the `Circle` component. 374 | 375 | ### Info Window 376 | 377 | Use the `InfoWindow` component to display content in a popup window above the map, at a given location. 378 | 379 | #### Options 380 | 381 | You can pass an [InfoWindowOptions](https://developers.google.com/maps/documentation/javascript/reference#InfoWindowOptions) object to the `options` prop to configure your info window. Note that you can optionally pass your content to the default slot of the `InfoWindow` component. 382 | 383 | ```vue 384 | 389 | 390 | 403 | ``` 404 | 405 | #### Use with Marker 406 | 407 | You can nest the `InfoWindow` component inside the `Marker` component to display an info window when the marker is clicked. 408 | 409 | ```vue 410 | 415 | 416 | 448 | ``` 449 | 450 | #### Open and close the Info Window 451 | 452 | You can use `v-model` to manage the state of the info window programmatically or to know whether it's open or closed 453 | 454 | ```vue 455 | 466 | 467 | 481 | ``` 482 | 483 | #### Events 484 | 485 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindow-Events) on the `InfoWindow` component. 486 | 487 | ### Custom Marker 488 | 489 | Regular markers can be customized a great deal but if you need to you can use the `CustomMarker` component and provide your own custom markup through it's `default` slot. 490 | 491 | #### Options 492 | 493 | | Parameter | Type | Description | 494 | | :-------- | :------- | :------------------------- | 495 | | `position` | `{ lat: number, lng: number}` | Sets the marker position. | 496 | | `anchorPoint` | `'CENTER' \| 'TOP_CENTER' \|'BOTTOM_CENTER' \| 'LEFT_CENTER' \| 'RIGHT_CENTER' \| 'TOP_LEFT' \| 'TOP_RIGHT' \| 'BOTTOM_LEFT' \| 'BOTTOM_RIGHT'` | Sets how the marker is anchored relative to it's `position` point. Default is `CENTER`. | 497 | | `offsetX` | `number` | Horizontal offset from the `position` point. | 498 | | `offsetY` | `number` | Vertical offset from the `position` point. | 499 | | `zIndex` | `number` | `z-index` value of the marker. | 500 | 501 | ```vue 502 | 507 | 508 | 523 | ``` 524 | 525 | ### Custom Control 526 | 527 | Use the `CustomControl` component to add custom buttons/controls to your map. 528 | 529 | #### Usage 530 | 531 | You can define the markup of your custom control in the `default` slot of the `CustomControl` component. The component itself takes two props: 532 | 533 | - `position`: Defines the position of your custom control on the map. Its value must be one of the [ControlPosition](https://developers.google.com/maps/documentation/javascript/reference/control#ControlPosition) constants. 534 | - `index` (optional): Controls the order of placement for custom controls that occupy the same position. 535 | 536 | Refer to the [Google Maps documentation](https://developers.google.com/maps/documentation/javascript/controls#CustomControls) on custom controls positioning. 537 | 538 | ```vue 539 | 545 | 546 | 558 | 559 | 578 | ``` 579 | 580 | ### Marker Cluster 581 | 582 | Use the `MarkerCluster` component to display a large number of markers on a map. It will combine markers of close proximity into clusters, and simplify the display of markers on the map. Can be used with the `Marker` or `CustomMarker` components. 583 | 584 | #### Usage 585 | 586 | Simply pass your `Marker`/`CustomMarker`(s) in the `default` slot of the `MarkerCluster` component. 587 | 588 | ```vue 589 | 620 | 621 | 637 | ``` 638 | 639 | #### Options 640 | 641 | `MarkerCluster` accepts an `options` prop (an object) where you can configure `algorithm`, `onClusterClick`, and `renderer` from the [MarkerClustererOptions](https://googlemaps.github.io/js-markerclusterer/interfaces/MarkerClustererOptions.html) interface. Note that all these options are completely optional but non-reactive. 642 | 643 | #### Events 644 | 645 | You can listen for [the following events](https://googlemaps.github.io/js-markerclusterer/enums/MarkerClustererEvents.html) on the `MarkerCluster` component. 646 | 647 | ### Heatmap Layer 648 | 649 | Use the `HeatmapLayer` component to depict the intensity of data at geographical points on the map. Make sure to include the `visualization` library in the `libraries` prop of the `GoogleMap` component. 650 | 651 | #### Options 652 | 653 | You can pass a [HeatmapLayerOptions](https://developers.google.com/maps/documentation/javascript/reference/visualization#HeatmapLayerOptions) object to the `options` prop to configure your heatmap layer. Note that for convenience you can use [LatLngLiteral](https://developers.google.com/maps/documentation/javascript/reference/coordinates#LatLngLiteral)s if you wish for the locations. 654 | 655 | ```vue 656 | 679 | 680 | 691 | ``` 692 | 693 | ## Advanced Usage 694 | 695 | The basic components that `vue3-google-map` provides are fully reactive and will get you pretty far. Should you need to access the Google Maps API, however, the `GoogleMap` component exposes the following: 696 | 697 | - `ready`: A boolean indicating when the Google Maps script has been loaded. By this point the map instance has been created, the API is ready for use and event listeners have been set up on the map. 698 | - `map`: The [Map](https://developers.google.com/maps/documentation/javascript/reference/map#Map) class instance. 699 | - `api`: The [Google Maps API](https://developers.google.com/maps/documentation/javascript/reference). 700 | - `mapTilesLoaded`: A boolean indicating when the map tiles have been fully loaded. 701 | 702 | In addition, most of the subcomponents expose their instance should you need it: 703 | 704 | - `Marker` exposes `marker` (a [Marker](https://developers.google.com/maps/documentation/javascript/reference/marker#Marker) class instance). 705 | - `Polyline` exposes `polyline` (a [Polyline](https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline) class instance). 706 | - `Polygon` exposes `polygon` (a [Polygon](https://developers.google.com/maps/documentation/javascript/reference/polygon#Polygon) class instance). 707 | - `Rectangle` exposes `rectangle` (a [Rectangle](https://developers.google.com/maps/documentation/javascript/reference/polygon#Rectangle) class instance). 708 | - `Circle` exposes `circle` (a [Circle](https://developers.google.com/maps/documentation/javascript/reference/polygon#Circle) class instance). 709 | - `InfoWindow` exposes `infoWindow` (an [InfoWindow](https://developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindow) class instance). 710 | - `MarkerCluster` exposes `markerCluster` (a [MarkerClusterer](https://googlemaps.github.io/js-markerclusterer/classes/MarkerClusterer.html) class instance). 711 | - `HeatmapLayer` exposes `heatmapLayer` (a [HeatmapLayer](https://developers.google.com/maps/documentation/javascript/reference/visualization#HeatmapLayer) class instance). 712 | 713 | ### Usage Patterns 714 | 715 | ```vue 716 | 738 | 739 | 748 | ``` 749 | 750 | Example: 751 | 752 | ```vue 753 | 783 | 784 | 795 | 796 | 824 | ``` 825 | 826 | ### Loading the Google Maps API script externally 827 | 828 | By default you would pass your API key as a prop to the `GoogleMap` component and it handles the loading of the Google Maps API script for you. There are cases, however, where you might want to load the script yourself. For example, you might be using other Google Maps components or your Vue app might be a part of a larger app that uses the Google Maps API elsewhere. In these cases you can use the `apiPromise` prop to pass a promise that resolves to the Google Maps API global `google` object. 829 | 830 | ```vue 831 | 845 | 846 | 856 | ``` 857 | 858 | ## Contribution 859 | 860 | All contributions are welcome. Before submitting a PR though it would be nice if you created an issue explaining what you want to acheive and why. 861 | 862 | ## License 863 | 864 | [MIT](http://opensource.org/licenses/MIT) 865 | -------------------------------------------------------------------------------- /assets/docs cover art.afphoto: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inocan-group/vue3-google-map/34c2cc4bde5eca3efc8ecc8562d484b68d346307/assets/docs cover art.afphoto -------------------------------------------------------------------------------- /docs/.vitepress/config.mts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from "node:url"; 2 | import { defineConfig } from 'vitepress' 3 | 4 | export default defineConfig({ 5 | title: "vue3-google-map", 6 | description: "vue3-google-map documentation", 7 | themeConfig: { 8 | search: { 9 | provider: 'local' 10 | }, 11 | 12 | sidebar: [ 13 | { 14 | text: "Getting Started", 15 | link: "/getting-started/", 16 | }, 17 | { 18 | text: "Components", 19 | link: "/components/", 20 | collapsed: false, 21 | items: [ 22 | { 23 | text: "Advanced Marker", 24 | link: "/components/advanced-marker", 25 | }, 26 | { 27 | text: "Marker", 28 | link: "/components/marker", 29 | }, 30 | { 31 | text: "Polyline", 32 | link: "/components/polyline", 33 | }, 34 | { 35 | text: "Polygon", 36 | link: "/components/polygon", 37 | }, 38 | { 39 | text: "Rectangle", 40 | link: "/components/rectangle", 41 | }, 42 | { 43 | text: "Circle", 44 | link: "/components/circle", 45 | }, 46 | { 47 | text: "Info Window", 48 | link: "/components/info-window", 49 | }, 50 | { 51 | text: "Custom Marker", 52 | link: "/components/custom-marker", 53 | }, 54 | { 55 | text: "Custom Control", 56 | link: "/components/custom-control", 57 | }, 58 | { 59 | text: "Marker Cluster", 60 | link: "/components/marker-cluster", 61 | }, 62 | { 63 | text: "Heatmap Layer", 64 | link: "/components/heatmap-layer", 65 | }, 66 | ], 67 | }, 68 | { 69 | text: "Advanced Usage", 70 | link: "/advanced-usage/", 71 | }, 72 | ], 73 | 74 | socialLinks: [ 75 | { 76 | icon: { 77 | svg:'', 78 | }, 79 | link: 'https://www.npmjs.com/package/vue3-google-map', 80 | ariaLabel: 'npm', 81 | }, 82 | { icon: 'github', link: 'https://github.com/inocan-group/vue3-google-map' }, 83 | ] 84 | }, 85 | 86 | vite: { 87 | ssr: { 88 | noExternal: [ 89 | '@googlemaps/js-api-loader', 90 | '@googlemaps/markerclusterer', 91 | ] 92 | }, 93 | resolve: { 94 | alias: { 95 | "@lib": fileURLToPath(new URL("../../src/index.ts", import.meta.url)), 96 | "@docs": fileURLToPath(new URL("..", import.meta.url)), 97 | }, 98 | } 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /docs/.vitepress/npm.svg: -------------------------------------------------------------------------------- 1 | 2 | npm 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import { h } from 'vue' 3 | import type { Theme } from 'vitepress' 4 | import DefaultTheme from 'vitepress/theme' 5 | import './style.css' 6 | 7 | export default { 8 | extends: DefaultTheme, 9 | Layout: () => { 10 | return h(DefaultTheme.Layout, null, { 11 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 12 | }) 13 | }, 14 | enhanceApp({ app, router, siteData }) { 15 | // ... 16 | } 17 | } satisfies Theme 18 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-c-brand-1: var(--vp-c-green-1); 3 | --vp-c-brand-2: var(--vp-c-green-2); 4 | --vp-c-brand-3: var(--vp-c-green-3); 5 | --vp-c-brand-soft: var(--vp-c-green-soft); 6 | --vp-code-color: #476582; 7 | } 8 | 9 | :root.dark { 10 | --vp-code-color: #c9def1; 11 | 12 | --vp-home-hero-image-filter: blur(72px); 13 | --vp-home-hero-image-background-image: linear-gradient( 14 | 0deg, 15 | var(--vp-c-brand-soft) 50%, 16 | var(--vp-c-brand-soft) 50% 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /docs/advanced-usage/index.md: -------------------------------------------------------------------------------- 1 | 32 | 33 | # Advanced Usage 34 | 35 | The basic components that `vue3-google-map` provides are fully reactive and will get you pretty far. Should you need to access the Google Maps API, however, the `GoogleMap` component exposes the following: 36 | 37 | - `ready`: A boolean indicating when the Google Maps script has been loaded. By this point the map instance has been created, the API is ready for use and event listeners have been set up on the map. 38 | - `map`: The [Map](https://developers.google.com/maps/documentation/javascript/reference/map#Map) class instance. 39 | - `api`: The [Google Maps API](https://developers.google.com/maps/documentation/javascript/reference). 40 | - `mapTilesLoaded`: A boolean indicating when the map tiles have been fully loaded. 41 | 42 | In addition, most of the subcomponents expose their instance should you need it: 43 | 44 | - `Marker` exposes `marker` (a [Marker](https://developers.google.com/maps/documentation/javascript/reference/marker#Marker) class instance). 45 | - `Polyline` exposes `polyline` (a [Polyline](https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline) class instance). 46 | - `Polygon` exposes `polygon` (a [Polygon](https://developers.google.com/maps/documentation/javascript/reference/polygon#Polygon) class instance). 47 | - `Rectangle` exposes `rectangle` (a [Rectangle](https://developers.google.com/maps/documentation/javascript/reference/polygon#Rectangle) class instance). 48 | - `Circle` exposes `circle` (a [Circle](https://developers.google.com/maps/documentation/javascript/reference/polygon#Circle) class instance). 49 | - `InfoWindow` exposes `infoWindow` (an [InfoWindow](https://developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindow) class instance). 50 | - `MarkerCluster` exposes `markerCluster` (a [MarkerClusterer](https://googlemaps.github.io/js-markerclusterer/classes/MarkerClusterer.html) class instance). 51 | - `HeatmapLayer` exposes `heatmapLayer` (a [HeatmapLayer](https://developers.google.com/maps/documentation/javascript/reference/visualization#HeatmapLayer) class instance). 52 | 53 | ## Usage Patterns 54 | 55 | ```vue 56 | 78 | 79 | 88 | ``` 89 | 90 | Example: 91 | 92 | ```vue 93 | 123 | 124 | 135 | 136 | 164 | ``` 165 | 166 | 167 | 174 | 175 | 176 | 177 | 178 | ## Loading the Google Maps API script externally 179 | 180 | By default you would pass your API key as a prop to the `GoogleMap` component and it handles the loading of the Google Maps API script for you. There are cases, however, where you might want to load the script yourself. For example, you might be using other Google Maps components or your Vue app might be a part of a larger app that uses the Google Maps API elsewhere. In these cases you can use the `apiPromise` prop to pass a promise that resolves to the Google Maps API global `google` object. 181 | 182 | ```vue 183 | 197 | 198 | 208 | ``` 209 | 210 | ## Events 211 | 212 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/map#Map-Events) on the `GoogleMap` component by using `@event_name`. For example if you want to call a function whenever the zoom value is changed, you can use it like this: 213 | 214 | ```vue 215 | 218 | ``` 219 | 220 | 221 | 249 | -------------------------------------------------------------------------------- /docs/components/advanced-marker.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | # Advanced Marker 10 | 11 | Use the `AdvancedMarker` component to draw markers, drop pins or any custom icons on a map. `AdvancedMarker` is the new version offered by google when deprecated the `Marker` component ([read more here](https://developers.google.com/maps/deprecations#googlemapsmarker_in_the_deprecated_as_of_february_2024)). 12 | 13 | In order to use the `AdvancedMarker` component is necessary to specify a MapId on declaring the `GoogleMap` component ([see more here](https://developers.google.com/maps/documentation/javascript/advanced-markers/start#create_a_map_id)). 14 | 15 | ## Options 16 | 17 | You can pass a [AdvancedMarkerElementOptions](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#AdvancedMarkerElementOptions) object to the `options` prop to configure your marker. 18 | 19 | You can also pass a [PinElementOptions interface](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#PinElementOptions) object to customize pin used by the marker. 20 | 21 | Additionally, `AdvancedMarker` supports default slot content, allowing you to use custom HTML or Vue components inside the marker. 22 | 23 | ```vue 24 | 31 | 32 | 48 | ``` 49 | 50 | 51 | 58 | 59 | 60 |
61 | Custom Content 62 |
63 |
64 |
65 |
66 | 67 | ## Events 68 | 69 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#AdvancedMarkerElement-Events) on the `AdvancedMarker` component. 70 | -------------------------------------------------------------------------------- /docs/components/circle.md: -------------------------------------------------------------------------------- 1 | 39 | 40 | # Circle 41 | 42 | Use the `Circle` component to draw circles on a map. 43 | 44 | ## Options 45 | 46 | You can pass a [CircleOptions](https://developers.google.com/maps/documentation/javascript/reference/polygon#CircleOptions) object to the `options` prop to configure your circle. 47 | 48 | ```vue 49 | 86 | 87 | 98 | ``` 99 | 100 | 101 | 108 | 109 | 110 | 111 | 112 | ## Events 113 | 114 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/polygon#Circle-Events) on the `Circle` component. 115 | -------------------------------------------------------------------------------- /docs/components/custom-control.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: false 3 | --- 4 | 5 | 12 | 13 | # Custom Control 14 | 15 | Use the `CustomControl` component to add custom buttons/controls to your map. 16 | 17 | ## Usage 18 | 19 | You can define the markup of your custom control in the `default` slot of the `CustomControl` component. The component itself takes two props: 20 | 21 | - `position`: Defines the position of your custom control on the map. Its value must be one of the [ControlPosition](https://developers.google.com/maps/documentation/javascript/reference/control#ControlPosition) constants. 22 | - `index` (optional): Controls the order of placement for custom controls that occupy the same position. 23 | 24 | Refer to the [Google Maps documentation](https://developers.google.com/maps/documentation/javascript/controls#CustomControls) on custom controls positioning. 25 | 26 | ```vue 27 | 33 | 34 | 46 | 47 | 66 | ``` 67 | 68 | 69 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 100 | -------------------------------------------------------------------------------- /docs/components/custom-marker.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: false 3 | --- 4 | 5 | 11 | 12 | # Custom Marker 13 | 14 | Regular markers can be customized a great deal but if you need to you can use the `CustomMarker` component and provide your own custom markup through it's `default` slot. 15 | 16 | ## Options 17 | 18 | | Parameter | Type | Description | 19 | | :-------- | :------- | :------------------------- | 20 | | `position` | `{ lat: number, lng: number}` | Sets the marker position. | 21 | | `anchorPoint` | `'CENTER' \| 'TOP_CENTER' \|'BOTTOM_CENTER' \| 'LEFT_CENTER' \| 'RIGHT_CENTER' \| 'TOP_LEFT' \| 'TOP_RIGHT' \| 'BOTTOM_LEFT' \| 'BOTTOM_RIGHT'` | Sets how the marker is anchored relative to it's `position` point. Default is `CENTER`. | 22 | | `offsetX` | `number` | Horizontal offset from the `position` point. | 23 | | `offsetY` | `number` | Vertical offset from the `position` point. | 24 | | `zIndex` | `number` | `z-index` value of the marker. | 25 | 26 | ```vue 27 | 32 | 33 | 48 | ``` 49 | 50 | 51 | 57 | 58 |
59 |
Vuejs Amsterdam
60 | 61 |
62 |
63 |
64 |
65 | -------------------------------------------------------------------------------- /docs/components/heatmap-layer.md: -------------------------------------------------------------------------------- 1 | 25 | 26 | # Heatmap Layer 27 | 28 | Use the `HeatmapLayer` component to depict the intensity of data at geographical points on the map. Make sure to include the `visualization` library in the `libraries` prop of the `GoogleMap` component. 29 | 30 | ## Options 31 | 32 | You can pass a [HeatmapLayerOptions](https://developers.google.com/maps/documentation/javascript/reference/visualization#HeatmapLayerOptions) object to the `options` prop to configure your heatmap layer. Note that for convenience you can use [LatLngLiteral](https://developers.google.com/maps/documentation/javascript/reference/coordinates#LatLngLiteral)s if you wish for the locations. 33 | 34 | ```vue 35 | 58 | 59 | 70 | ``` 71 | 72 | 73 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/components/index.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | This library is intended to be used in a composable fashion. Therefore you will find yourself using nested components to build your map rather than just a complicated inline format. 4 | 5 | The main mapping component is `GoogleMap`, however the following components are available at your disposal: 6 | 7 | - [AdvancedMarker](./advanced-marker.md) 8 | - [Marker](./marker.md) 9 | - [Polyline](./polyline.md) 10 | - [Polygon](./polygon.md) 11 | - [Rectangle](./rectangle.md) 12 | - [Circle](./circle.md) 13 | - [Info Window](./info-window.md) 14 | - [Custom Marker](./custom-marker.md) 15 | - [Custom Control](./custom-control.md) 16 | - [Marker Cluster](./marker-cluster.md) 17 | - [Heatmap Layer](./heatmap-layer.md) 18 | -------------------------------------------------------------------------------- /docs/components/info-window.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Info Window 7 | 8 | Use the `InfoWindow` component to display content in a popup window above the map, at a given location. 9 | 10 | ## Options 11 | 12 | You can pass an [InfoWindowOptions](https://developers.google.com/maps/documentation/javascript/reference#InfoWindowOptions) object to the `options` prop to configure your info window. Note that you can optionally pass your content to the default slot of the `InfoWindow` component. 13 | 14 | ```vue 15 | 20 | 21 | 34 | ``` 35 | 36 | 37 | 43 | 44 | 45 | Content passed through slot 46 | 47 | 48 | 49 | 50 | ## Use with Marker 51 | 52 | You can nest the `InfoWindow` component inside the `Marker` component to display an info window when the marker is clicked. 53 | 54 | ```vue 55 | 60 | 61 | 93 | ``` 94 | 95 | 96 | 102 | 103 | 104 |
105 |
106 |

Uluru

107 |
108 |

Uluru, also referred to as Ayers Rock, is a large 109 | sandstone rock formation in the southern part of the 110 | Northern Territory, central Australia. It lies 335 km (208 mi) 111 | south west of the nearest large town, Alice Springs; 450 km 112 | (280 mi) by road. Kata Tjuta and Uluru are the two major 113 | features of the Uluru - Kata Tjuta National Park. Uluru is 114 | sacred to the Pitjantjatjara and Yankunytjatjara, the 115 | Aboriginal people of the area. It has many springs, waterholes, 116 | rock caves and ancient paintings. Uluru is listed as a World 117 | Heritage Site.

118 |

Attribution: Uluru, 119 | https://en.wikipedia.org/w/index.php?title=Uluru 120 | (last visited June 22, 2009).

121 |
122 |
123 |
124 |
125 |
126 |
127 | 128 | ## Open and close the Info Window 129 | 130 | You can use `v-model` to manage the state of the info window programmatically or to know whether it's open or closed 131 | 132 | ```vue 133 | 144 | 145 | 159 | ``` 160 | 161 | ## Events 162 | 163 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindow-Events) on the `InfoWindow` component. 164 | 165 | 170 | -------------------------------------------------------------------------------- /docs/components/marker-cluster.md: -------------------------------------------------------------------------------- 1 | 32 | 33 | # Marker Cluster 34 | 35 | Use the `MarkerCluster` component to display a large number of markers on a map. It will combine markers of close proximity into clusters, and simplify the display of markers on the map. Can be used with the `Marker` or `CustomMarker` components. 36 | 37 | ## Usage 38 | 39 | Simply pass your `Marker`/`CustomMarker`(s) in the `default` slot of the `MarkerCluster` component. 40 | 41 | ```vue 42 | 73 | 74 | 90 | ``` 91 | 92 | 93 | 99 | 100 | 105 | 106 | 107 | 108 | 109 | ## Options 110 | 111 | `MarkerCluster` accepts an `options` prop (an object) where you can configure `algorithm`, `onClusterClick`, and `renderer` from the [MarkerClustererOptions](https://googlemaps.github.io/js-markerclusterer/interfaces/MarkerClustererOptions.html) interface. Note that all these options are completely optional but non-reactive. 112 | 113 | ## Events 114 | 115 | You can listen for [the following events](https://googlemaps.github.io/js-markerclusterer/enums/MarkerClustererEvents.html) on the `MarkerCluster` component. 116 | -------------------------------------------------------------------------------- /docs/components/marker.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Marker 9 | 10 | Use the `Marker` component to draw markers, drop pins or any custom icons on a map. 11 | 12 | ## Options 13 | 14 | You can pass a [MarkerOptions](https://developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions) object to the `options` prop to configure your marker. 15 | 16 | ```vue 17 | 23 | 24 | 34 | ``` 35 | 36 | 37 | 43 | 44 | 45 | 46 | 47 | ## Events 48 | 49 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/marker#Marker-Events) on the `Marker` component. 50 | -------------------------------------------------------------------------------- /docs/components/polygon.md: -------------------------------------------------------------------------------- 1 | 21 | 22 | # Polygon 23 | 24 | Use the `Polygon` component to draw polgons (arbitrary number of sides) on a map. 25 | 26 | ## Options 27 | 28 | You can pass a [PolylgonOptions](https://developers.google.com/maps/documentation/javascript/reference/polygon#PolygonOptions) object to the `options` prop to configure your polyline. 29 | 30 | ```vue 31 | 50 | 51 | 61 | ``` 62 | 63 | 64 | 70 | 71 | 72 | 73 | 74 | ## Events 75 | 76 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/polygon#Polygon-Events) on the `Polygon` component. 77 | -------------------------------------------------------------------------------- /docs/components/polyline.md: -------------------------------------------------------------------------------- 1 | 20 | 21 | # Polyline 22 | 23 | Use the `Polyline` component to draw paths and arbitrary shapes on a map. 24 | 25 | ## Options 26 | 27 | You can pass a [PolylineOptions](https://developers.google.com/maps/documentation/javascript/reference/polygon#PolylineOptions) object to the `options` prop to configure your polyline. 28 | 29 | ```vue 30 | 48 | 49 | 59 | ``` 60 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | ## Events 73 | 74 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline-Events) on the `Polyline` component. 75 | -------------------------------------------------------------------------------- /docs/components/rectangle.md: -------------------------------------------------------------------------------- 1 | 20 | 21 | # Rectangle 22 | 23 | Use the `Rectangle` component to draw simple rectangles on a map. 24 | 25 | ## Options 26 | 27 | You can pass a [RectangleOptions](https://developers.google.com/maps/documentation/javascript/reference/polygon#RectangleOptions) object to the `options` prop to configure your rectangle. 28 | 29 | ```vue 30 | 48 | 49 | 60 | ``` 61 | 62 | 63 | 70 | 71 | 72 | 73 | 74 | ## Events 75 | 76 | You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/polygon#Rectangle-Events) on the `Rectangle` component. 77 | -------------------------------------------------------------------------------- /docs/getting-started/index.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Getting Started 9 | 10 | `vue3-google-map` offers a set of composable components for easy use of Google Maps in your Vue 3 projects. 11 | 12 | ## Installation 13 | 14 | ### NPM 15 | 16 | ```bash 17 | npm install vue3-google-map 18 | # OR 19 | yarn add vue3-google-map 20 | ``` 21 | 22 | ### CDN 23 | 24 | Include the following script tag in your `index.html` (make sure to include it after Vue 3). 25 | 26 | ```html 27 | 28 | ``` 29 | 30 | ## Your first map 31 | 32 | To construct a map using `vue3-google-map` you'll need to use the base `GoogleMap` component which receives your [Google Maps API key](https://developers.google.com/maps/documentation/javascript/get-api-key), styles (e.g. setting width and height), and any [MapOptions](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions) to configure your map ([see this](https://github.com/inocan-group/vue3-google-map/blob/develop/src/components/GoogleMap.vue#L36-L244) for a all the supported `MapOptions`). 33 | Other map features can be added to your map by passing map subcomponents ([Marker](/components/marker), [Polyline](/components/polyline), [Polygon](/components/polygon), [Rectangle](/components/rectangle), [Circle](/components/circle), or [CustomControl](/components/custom-control)) to the default slot of the `GoogleMap` component. 34 | 35 | The [the following events](https://developers.google.com/maps/documentation/javascript/reference/map#Map-Events) will be emitted by the `GoogleMap` component and can be listened to by using `@event_name`. 36 | 37 | ```vue 38 | 43 | 44 | 54 | ``` 55 | 56 | 57 | 58 | 59 | 60 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /docs/images/map-1200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inocan-group/vue3-google-map/34c2cc4bde5eca3efc8ecc8562d484b68d346307/docs/images/map-1200.jpg -------------------------------------------------------------------------------- /docs/images/map-800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inocan-group/vue3-google-map/34c2cc4bde5eca3efc8ecc8562d484b68d346307/docs/images/map-800.jpg -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | prev: false 3 | next: false 4 | --- 5 | 6 | # vue3-google-map 7 | 8 | ![map](/images/map-1200.jpg) 9 | 10 | `vue3-google-map` offers a set of composable components for easy use of Google Maps in your Vue 3 projects. This component assumes Vue3's reactivity and is not intended to work with Vue 2. 11 | 12 | To get start, please navigate to the [Getting Started](getting-started/index.md) section. 13 | -------------------------------------------------------------------------------- /docs/shared/index.ts: -------------------------------------------------------------------------------- 1 | import { Loader } from "@googlemaps/js-api-loader"; 2 | 3 | const loader = new Loader({ 4 | apiKey: import.meta.env.VITE_GOOGLE_API_KEY, 5 | version: "weekly", 6 | libraries: ["visualization", "marker"], 7 | }); 8 | 9 | export const apiPromise = import.meta.env.SSR ? Promise.resolve() : loader.load(); 10 | -------------------------------------------------------------------------------- /docs/themes/index.md: -------------------------------------------------------------------------------- 1 | # Themes 2 | 3 | `vue3-google-map` comes with a curated set of default themes for you to use. You can also define custom styles utilizing the Google Maps API. 4 | 5 | ## Default Themes 6 | 7 | To use a default theme simply pass the theme's name to the `theme` prop of the `GoogleMap` component. The available themes are: 8 | 9 | - `aubergine` 10 | - `dark` 11 | - `grey` 12 | - `minimal` 13 | - `retro` 14 | - `roadways` 15 | - `roadwaysMinimal` 16 | - `ultraLight` 17 | 18 | ```vue 19 | 27 | 28 | 43 | 44 | 51 | ``` 52 | 53 | \ 54 | 55 | 56 | ## Custom Styles 57 | 58 | ::: warning 59 | Please be aware that if you specify a default theme that it will override any custom styles you define. 60 | ::: 61 | 62 | Alternatively you can define your own styles by passing them to the `styles` prop of the `GoogleMap` component. Please refer to the [Google Maps documentation](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions.styles) on custom styles. 63 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Directory that contains the deploy-ready HTML files and assets generated by 3 | # the build. 4 | publish = "docs/.vitepress/dist" 5 | 6 | # Default build command. 7 | command = "yarn docs:build" 8 | 9 | [[plugins]] 10 | package = "./docs/build" 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-google-map", 3 | "version": "0.22.0", 4 | "license": "MIT", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/inocan-group/vue3-google-maps.git" 8 | }, 9 | "homepage": "https://vue3-google-map.com", 10 | "description": "A set of composable components for easy use of Google Maps in your Vue 3 projects.", 11 | "unpkg": "dist/index.umd.js", 12 | "jsdelivr": "dist/index.umd.js", 13 | "main": "dist/index.cjs", 14 | "module": "dist/index.mjs", 15 | "files": [ 16 | "dist" 17 | ], 18 | "exports": { 19 | ".": { 20 | "import": "./dist/index.mjs", 21 | "require": "./dist/index.cjs", 22 | "types": "./dist/types/index.d.ts" 23 | }, 24 | "./themes": { 25 | "import": "./dist/themes/index.mjs", 26 | "require": "./dist/themes/index.cjs", 27 | "types": "./dist/types/themes/index.d.ts" 28 | } 29 | }, 30 | "typesVersions": { 31 | "*": { 32 | "*": [ 33 | "dist/types/index.d.ts" 34 | ], 35 | "themes": [ 36 | "dist/types/themes/index.d.ts" 37 | ] 38 | } 39 | }, 40 | "scripts": { 41 | "dev": "vite", 42 | "prepare": "pnpm run build", 43 | "clean": "rimraf dist/**/*", 44 | "lint": "npx eslint --ext .ts,.vue src dev docs --fix", 45 | "build:umd": "node ./scripts/build-umd.js", 46 | "build": "pnpm clean && vue-tsc --declaration --emitDeclarationOnly --project tsconfig.build.json && vite build && pnpm build:umd", 47 | "docs": "vitepress dev docs", 48 | "docs:dev": "vitepress dev docs", 49 | "docs:build": "vitepress build docs", 50 | "docs:serve": "vitepress serve docs", 51 | "release": "standard-version" 52 | }, 53 | "dependencies": { 54 | "@googlemaps/js-api-loader": "^1.16.2", 55 | "@googlemaps/markerclusterer": "^2.4.0", 56 | "fast-deep-equal": "^3.1.3" 57 | }, 58 | "devDependencies": { 59 | "pnpm": "^8.7.6", 60 | "@types/google.maps": "^3.58.1", 61 | "@typescript-eslint/eslint-plugin": "^4.33.0", 62 | "@typescript-eslint/parser": "^4.33.0", 63 | "@vitejs/plugin-vue": "^4.3.1", 64 | "@vue/eslint-config-prettier": "^6.0.0", 65 | "@vue/eslint-config-typescript": "^7.0.0", 66 | "eslint": "^7.32.0", 67 | "eslint-config-prettier": "^8.10.0", 68 | "eslint-plugin-prettier": "^3.4.1", 69 | "eslint-plugin-vue": "^7.20.0", 70 | "prettier": "^2.8.8", 71 | "rimraf": "^5.0.1", 72 | "standard-version": "^9.5.0", 73 | "typescript": "^5.1.6", 74 | "vite": "^4.5.3", 75 | "vite-plugin-css-injected-by-js": "^3.2.1", 76 | "vitepress": "^1.0.0-rc.40", 77 | "vue": "^3.3.4", 78 | "vue-tsc": "^1.8.8" 79 | }, 80 | "peerDependencies": { 81 | "vue": "^3" 82 | }, 83 | "engines": { 84 | "node": ">=16.11.0" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /playground/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | -------------------------------------------------------------------------------- /playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | vue3-google-map 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | 4 | createApp(App).mount("#app"); 5 | -------------------------------------------------------------------------------- /scripts/build-umd.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require("path"); 2 | const { build } = require("vite"); 3 | const vue = require("@vitejs/plugin-vue").default; 4 | const cssInjectedByJsPlugin = require("vite-plugin-css-injected-by-js").default; 5 | 6 | (async () => { 7 | await build({ 8 | configFile: false, 9 | plugins: [vue(), cssInjectedByJsPlugin()], 10 | build: { 11 | emptyOutDir: false, 12 | lib: { 13 | entry: resolve(__dirname, "../src/index.ts"), 14 | formats: ["umd"], 15 | name: "Vue3GoogleMap", 16 | fileName: "index", 17 | }, 18 | rollupOptions: { 19 | // make sure to externalize deps that shouldn't be bundled 20 | // into your library 21 | external: ["vue"], 22 | output: { 23 | // Provide global variables to use in the UMD build 24 | // for externalized deps 25 | globals: { 26 | vue: "Vue", 27 | }, 28 | dir: resolve(__dirname, "../dist"), 29 | }, 30 | }, 31 | }, 32 | }); 33 | })(); 34 | -------------------------------------------------------------------------------- /src/@types/index.ts: -------------------------------------------------------------------------------- 1 | import * as themes from "../themes/index"; 2 | 3 | export type IControlPosition = keyof typeof google.maps.ControlPosition; 4 | export type ITheme = keyof typeof themes; 5 | -------------------------------------------------------------------------------- /src/components/AdvancedMarker.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 129 | 130 | 139 | -------------------------------------------------------------------------------- /src/components/Circle.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, toRef } from "vue"; 2 | import { useSetupMapComponent } from "../composables/index"; 3 | import { polylineEvents } from "../shared/index"; 4 | 5 | const circleEvents = polylineEvents.concat(["center_changed", "radius_changed"]); 6 | 7 | export default defineComponent({ 8 | name: "Circle", 9 | props: { 10 | options: { 11 | type: Object as PropType, 12 | required: true, 13 | }, 14 | }, 15 | emits: circleEvents, 16 | setup(props, { emit }) { 17 | const options = toRef(props, "options"); 18 | const circle = useSetupMapComponent("Circle", circleEvents, options, emit); 19 | 20 | return { circle }; 21 | }, 22 | render: () => null, 23 | }); 24 | -------------------------------------------------------------------------------- /src/components/CustomControl.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 108 | 109 | 118 | -------------------------------------------------------------------------------- /src/components/CustomMarker.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 44 | 45 | 54 | -------------------------------------------------------------------------------- /src/components/GoogleMap.vue: -------------------------------------------------------------------------------- 1 | 372 | 373 | 379 | 380 | 386 | -------------------------------------------------------------------------------- /src/components/HeatmapLayer.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, ref, inject, watch, markRaw, onBeforeUnmount } from "vue"; 2 | import equal from "fast-deep-equal"; 3 | import { mapSymbol, apiSymbol } from "../shared/index"; 4 | 5 | type HeatmapLayerOptions = google.maps.visualization.HeatmapLayerOptions; 6 | 7 | interface ExtendedHeatmapLayerOptions extends Omit { 8 | data: HeatmapLayerOptions["data"] | (google.maps.LatLngLiteral | { location: google.maps.LatLngLiteral })[]; 9 | } 10 | 11 | export default defineComponent({ 12 | name: "HeatmapLayer", 13 | props: { 14 | options: { 15 | type: Object as PropType, 16 | default: () => ({}), 17 | }, 18 | }, 19 | setup(props) { 20 | const heatmapLayer = ref(); 21 | const map = inject(mapSymbol, ref()); 22 | const api = inject(apiSymbol, ref()); 23 | 24 | watch( 25 | [map, () => props.options], 26 | ([_, options], [oldMap, oldOptions]) => { 27 | const hasChanged = !equal(options, oldOptions) || map.value !== oldMap; 28 | 29 | if (map.value && api.value && hasChanged) { 30 | const opts: ExtendedHeatmapLayerOptions = structuredClone(options); 31 | 32 | if (opts.data && !(opts.data instanceof api.value.MVCArray)) { 33 | const LatLng = api.value.LatLng; 34 | 35 | opts.data = opts.data?.map((point) => { 36 | if ( 37 | point instanceof LatLng || 38 | ("location" in point && (point.location instanceof LatLng || point.location === null)) 39 | ) { 40 | return point; 41 | } else { 42 | if ("location" in point) { 43 | return { ...point, location: new LatLng(point.location as google.maps.LatLngLiteral) }; 44 | } 45 | 46 | return new LatLng(point); 47 | } 48 | }) as HeatmapLayerOptions["data"]; 49 | } 50 | 51 | if (heatmapLayer.value) { 52 | heatmapLayer.value.setOptions(opts as HeatmapLayerOptions); 53 | } else { 54 | heatmapLayer.value = markRaw( 55 | new api.value.visualization.HeatmapLayer({ 56 | ...opts, 57 | map: map.value, 58 | } as HeatmapLayerOptions) 59 | ); 60 | } 61 | } 62 | }, 63 | { immediate: true } 64 | ); 65 | 66 | onBeforeUnmount(() => { 67 | if (heatmapLayer.value) { 68 | heatmapLayer.value.setMap(null); 69 | } 70 | }); 71 | 72 | return { heatmapLayer }; 73 | }, 74 | render: () => null, 75 | }); 76 | -------------------------------------------------------------------------------- /src/components/InfoWindow.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 143 | 144 | 153 | -------------------------------------------------------------------------------- /src/components/Marker.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, toRef, provide } from "vue"; 2 | import { useSetupMapComponent } from "../composables/index"; 3 | import { markerSymbol } from "../shared/index"; 4 | 5 | const markerEvents = [ 6 | "animation_changed", 7 | "click", 8 | "dblclick", 9 | "rightclick", 10 | "dragstart", 11 | "dragend", 12 | "drag", 13 | "mouseover", 14 | "mousedown", 15 | "mouseout", 16 | "mouseup", 17 | "draggable_changed", 18 | "clickable_changed", 19 | "contextmenu", 20 | "cursor_changed", 21 | "flat_changed", 22 | "rightclick", 23 | "zindex_changed", 24 | "icon_changed", 25 | "position_changed", 26 | "shape_changed", 27 | "title_changed", 28 | "visible_changed", 29 | ]; 30 | 31 | export default defineComponent({ 32 | name: "Marker", 33 | props: { 34 | options: { 35 | type: Object as PropType, 36 | required: true, 37 | }, 38 | }, 39 | emits: markerEvents, 40 | setup(props, { emit, expose, slots }) { 41 | const options = toRef(props, "options"); 42 | const marker = useSetupMapComponent("Marker", markerEvents, options, emit); 43 | provide(markerSymbol, marker); 44 | 45 | expose({ marker }); 46 | 47 | return () => slots.default?.(); 48 | }, 49 | }); 50 | -------------------------------------------------------------------------------- /src/components/MarkerCluster.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, ref, provide, inject, watch, markRaw, onBeforeUnmount } from "vue"; 2 | import { 3 | MarkerClusterer, 4 | MarkerClustererOptions, 5 | MarkerClustererEvents, 6 | SuperClusterViewportAlgorithm, 7 | } from "@googlemaps/markerclusterer"; 8 | import { mapSymbol, apiSymbol, markerClusterSymbol } from "../shared/index"; 9 | 10 | const markerClusterEvents = Object.values(MarkerClustererEvents); 11 | 12 | export default defineComponent({ 13 | name: "MarkerCluster", 14 | props: { 15 | options: { 16 | type: Object as PropType, 17 | default: () => ({}), 18 | }, 19 | }, 20 | emits: markerClusterEvents, 21 | setup(props, { emit, expose, slots }) { 22 | const markerCluster = ref(); 23 | const map = inject(mapSymbol, ref()); 24 | const api = inject(apiSymbol, ref()); 25 | 26 | provide(markerClusterSymbol, markerCluster); 27 | 28 | watch( 29 | map, 30 | () => { 31 | if (map.value) { 32 | markerCluster.value = markRaw( 33 | new MarkerClusterer({ 34 | map: map.value, 35 | // Better perf than the default `SuperClusterAlgorithm`. See: 36 | // https://github.com/googlemaps/js-markerclusterer/pull/640 37 | algorithm: new SuperClusterViewportAlgorithm(props.options.algorithmOptions ?? {}), 38 | ...props.options, 39 | }) 40 | ); 41 | 42 | markerClusterEvents.forEach((event) => { 43 | markerCluster.value?.addListener(event, (e: unknown) => emit(event, e)); 44 | }); 45 | } 46 | }, 47 | { 48 | immediate: true, 49 | } 50 | ); 51 | 52 | onBeforeUnmount(() => { 53 | if (markerCluster.value) { 54 | api.value?.event.clearInstanceListeners(markerCluster.value); 55 | markerCluster.value.clearMarkers(); 56 | markerCluster.value.setMap(null); 57 | } 58 | }); 59 | 60 | expose({ markerCluster }); 61 | 62 | return () => slots.default?.(); 63 | }, 64 | }); 65 | -------------------------------------------------------------------------------- /src/components/Polygon.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, PropType, toRef } from "vue"; 2 | import { useSetupMapComponent } from "../composables/index"; 3 | import { polylineEvents as polygonEvents } from "../shared/index"; 4 | 5 | export default defineComponent({ 6 | name: "Polygon", 7 | props: { 8 | options: { 9 | type: Object as PropType, 10 | required: true, 11 | }, 12 | }, 13 | emits: polygonEvents, 14 | setup(props, { emit }) { 15 | const options = toRef(props, "options"); 16 | const polygon = useSetupMapComponent("Polygon", polygonEvents, options, emit); 17 | 18 | return { polygon }; 19 | }, 20 | render: () => null, 21 | }); 22 | -------------------------------------------------------------------------------- /src/components/Polyline.ts: -------------------------------------------------------------------------------- 1 | import { useSetupMapComponent } from "../composables/index"; 2 | import { defineComponent, PropType, toRef } from "vue"; 3 | import { polylineEvents } from "../shared/index"; 4 | 5 | export default defineComponent({ 6 | name: "Polyline", 7 | props: { 8 | options: { 9 | type: Object as PropType, 10 | required: true, 11 | }, 12 | }, 13 | emits: polylineEvents, 14 | setup(props, { emit }) { 15 | const options = toRef(props, "options"); 16 | const polyline = useSetupMapComponent("Polyline", polylineEvents, options, emit); 17 | 18 | return { polyline }; 19 | }, 20 | render: () => null, 21 | }); 22 | -------------------------------------------------------------------------------- /src/components/Rectangle.ts: -------------------------------------------------------------------------------- 1 | import { useSetupMapComponent } from "../composables/index"; 2 | import { defineComponent, PropType, toRef } from "vue"; 3 | import { polylineEvents } from "../shared/index"; 4 | 5 | const rectangleEvents = polylineEvents.concat(["bounds_changed"]); 6 | 7 | export default defineComponent({ 8 | name: "Rectangle", 9 | props: { 10 | options: { 11 | type: Object as PropType, 12 | required: true, 13 | }, 14 | }, 15 | emits: rectangleEvents, 16 | setup(props, { emit }) { 17 | const options = toRef(props, "options"); 18 | const rectangle = useSetupMapComponent("Rectangle", rectangleEvents, options, emit); 19 | 20 | return { rectangle }; 21 | }, 22 | render: () => null, 23 | }); 24 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as GoogleMap } from "./GoogleMap.vue"; 2 | export { default as AdvancedMarker } from "./AdvancedMarker.vue"; 3 | export { default as Marker } from "./Marker"; 4 | export { default as Polyline } from "./Polyline"; 5 | export { default as Polygon } from "./Polygon"; 6 | export { default as Rectangle } from "./Rectangle"; 7 | export { default as Circle } from "./Circle"; 8 | export { default as CustomControl } from "./CustomControl.vue"; 9 | export { default as InfoWindow } from "./InfoWindow.vue"; 10 | export { default as MarkerCluster } from "./MarkerCluster"; 11 | export { default as CustomMarker } from "./CustomMarker.vue"; 12 | export { default as HeatmapLayer } from "./HeatmapLayer"; 13 | -------------------------------------------------------------------------------- /src/composables/index.ts: -------------------------------------------------------------------------------- 1 | // #autoindex 2 | 3 | // #region autoindexed files 4 | 5 | // index last changed at: 12th Dec, 2020, 12:54 AM ( GMT+2 ) 6 | // export: named; exclusions: index, private. 7 | // files: useSetupMapComponent. 8 | 9 | // local file exports 10 | export * from "./useSetupMapComponent"; 11 | 12 | // Note: 13 | // ----- 14 | // This file was created by running: "do devops autoindex"; it assumes you have 15 | // the 'do-devops' pkg installed as a dev dep. 16 | // 17 | // By default it assumes that exports are named exports but this can be changed by 18 | // adding a modifier to the '// #autoindex' syntax: 19 | // 20 | // - autoindex:named same as default, exports "named symbols" 21 | // - autoindex:default assumes each file is exporting a default export 22 | // and converts the default export to the name of the 23 | // file 24 | // - autoindex:offset assumes files export "named symbols" but that each 25 | // file's symbols should be offset by the file's name 26 | // (useful for files which might symbols which collide 27 | // or where the namespacing helps consumers) 28 | // 29 | // You may also exclude certain files or directories by adding it to the 30 | // autoindex command. As an example: 31 | // 32 | // - autoindex:named, exclude: foo,bar,baz 33 | // 34 | // Also be aware that all of your content outside the defined region in this file 35 | // will be preserved in situations where you need to do something paricularly awesome. 36 | // Keep on being awesome. 37 | 38 | // #endregion 39 | -------------------------------------------------------------------------------- /src/composables/useSetupMapComponent.ts: -------------------------------------------------------------------------------- 1 | import { watch, ref, Ref, inject, onBeforeUnmount, computed, markRaw } from "vue"; 2 | import equal from "fast-deep-equal"; 3 | import { apiSymbol, mapSymbol, markerClusterSymbol, customMarkerClassSymbol } from "../shared/index"; 4 | 5 | type ICtorKey = "Marker" | "Polyline" | "Polygon" | "Rectangle" | "Circle" | typeof customMarkerClassSymbol; 6 | 7 | type IComponent = T extends "Marker" 8 | ? google.maps.Marker 9 | : T extends "Polyline" 10 | ? google.maps.Polyline 11 | : T extends "Polygon" 12 | ? google.maps.Polygon 13 | : T extends "Rectangle" 14 | ? google.maps.Rectangle 15 | : T extends "Circle" 16 | ? google.maps.Circle 17 | : T extends typeof customMarkerClassSymbol 18 | ? InstanceType 19 | : never; 20 | 21 | type IShape = Exclude, google.maps.Marker | typeof google.maps.CustomMarker>; 22 | 23 | type IComponentOptions = T extends "Marker" 24 | ? google.maps.MarkerOptions 25 | : T extends "Polyline" 26 | ? google.maps.PolylineOptions 27 | : T extends "Polygon" 28 | ? google.maps.PolygonOptions 29 | : T extends "Rectangle" 30 | ? google.maps.RectangleOptions 31 | : T extends "Circle" 32 | ? google.maps.CircleOptions 33 | : T extends typeof customMarkerClassSymbol 34 | ? google.maps.CustomMarkerOptions & { element?: HTMLElement } 35 | : never; 36 | 37 | type IShapeOptions = Exclude, google.maps.MarkerOptions | google.maps.CustomMarkerOptions>; 38 | 39 | const isMarkerCtorKey = (key: ICtorKey): key is "Marker" => key === "Marker"; 40 | const isCustomMarkerCtorKey = (key: ICtorKey): key is typeof customMarkerClassSymbol => key === customMarkerClassSymbol; 41 | 42 | export const useSetupMapComponent = ( 43 | ctorKey: T, 44 | events: string[], 45 | options: Ref>, 46 | emit: (event: string, ...args: unknown[]) => void 47 | ): Ref | undefined> => { 48 | const component = ref>(); 49 | 50 | const map = inject(mapSymbol, ref()); 51 | const api = inject(apiSymbol, ref()); 52 | const markerCluster = inject(markerClusterSymbol, ref()); 53 | 54 | const isMarkerInCluster = computed( 55 | () => 56 | !!( 57 | markerCluster.value && 58 | api.value && 59 | (component.value instanceof api.value.Marker || component.value instanceof api.value[customMarkerClassSymbol]) 60 | ) 61 | ); 62 | 63 | watch( 64 | [map, options], 65 | (_, [oldMap, oldOptions]) => { 66 | const hasChanged = !equal(options.value, oldOptions) || map.value !== oldMap; 67 | 68 | if (!map.value || !api.value || !hasChanged) return; 69 | 70 | if (component.value) { 71 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 72 | component.value.setOptions(options.value as any); 73 | 74 | if (isMarkerInCluster.value) { 75 | markerCluster.value?.removeMarker(component.value as google.maps.Marker); 76 | markerCluster.value?.addMarker(component.value as google.maps.Marker); 77 | } 78 | } else { 79 | if (isMarkerCtorKey(ctorKey)) { 80 | component.value = markRaw( 81 | new api.value[ctorKey](options.value as IComponentOptions) 82 | ) as IComponent; 83 | } else if (isCustomMarkerCtorKey(ctorKey)) { 84 | component.value = markRaw( 85 | new api.value[ctorKey](options.value as IComponentOptions) 86 | ) as IComponent; 87 | } else { 88 | component.value = markRaw( 89 | new api.value[ctorKey]({ 90 | ...options.value, 91 | map: map.value, 92 | } as IShapeOptions) 93 | ) as IShape; 94 | } 95 | 96 | if (isMarkerInCluster.value) { 97 | markerCluster.value?.addMarker(component.value as google.maps.Marker); 98 | } else { 99 | component.value.setMap(map.value); 100 | } 101 | 102 | events.forEach((event) => { 103 | component.value?.addListener(event, (e: unknown) => emit(event, e)); 104 | }); 105 | } 106 | }, 107 | { 108 | immediate: true, 109 | } 110 | ); 111 | 112 | onBeforeUnmount(() => { 113 | if (component.value) { 114 | api.value?.event.clearInstanceListeners(component.value); 115 | 116 | if (isMarkerInCluster.value) { 117 | markerCluster.value?.removeMarker(component.value as google.maps.Marker); 118 | } else { 119 | component.value.setMap(null); 120 | } 121 | } 122 | }); 123 | 124 | return component; 125 | }; 126 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./components/index"; 2 | -------------------------------------------------------------------------------- /src/shared/index.ts: -------------------------------------------------------------------------------- 1 | import type { MarkerClusterer } from "@googlemaps/markerclusterer"; 2 | import { InjectionKey, Ref } from "vue"; 3 | 4 | export const mapSymbol: InjectionKey> = Symbol("map"); 5 | export const apiSymbol: InjectionKey> = Symbol("api"); 6 | export const markerSymbol: InjectionKey< 7 | Ref 8 | > = Symbol("marker"); 9 | export const markerClusterSymbol: InjectionKey> = Symbol("markerCluster"); 10 | export const customMarkerClassSymbol = Symbol("CustomMarker") as unknown as "CustomMarker"; 11 | /** 12 | * Utilitary flag for components that need to know the map 13 | * was fully loaded (including its tiles) to decide their behavior 14 | */ 15 | export const mapTilesLoadedSymbol: InjectionKey> = Symbol("mapTilesLoaded"); 16 | 17 | export const polylineEvents = [ 18 | "click", 19 | "dblclick", 20 | "drag", 21 | "dragend", 22 | "dragstart", 23 | "mousedown", 24 | "mousemove", 25 | "mouseout", 26 | "mouseover", 27 | "mouseup", 28 | "rightclick", 29 | ]; 30 | -------------------------------------------------------------------------------- /src/shims-google-maps.ts: -------------------------------------------------------------------------------- 1 | import { createCustomMarkerClass } from "./utils"; 2 | 3 | declare global { 4 | // eslint-disable-next-line @typescript-eslint/no-namespace 5 | namespace google.maps { 6 | interface CustomMarkerOptions { 7 | position?: google.maps.MarkerOptions["position"]; 8 | map?: google.maps.Map | google.maps.StreetViewPanorama | null; 9 | anchorPoint?: 10 | | "CENTER" 11 | | "TOP_CENTER" 12 | | "BOTTOM_CENTER" 13 | | "LEFT_CENTER" 14 | | "RIGHT_CENTER" 15 | | "TOP_LEFT" 16 | | "TOP_RIGHT" 17 | | "BOTTOM_LEFT" 18 | | "BOTTOM_RIGHT"; 19 | offsetX?: number; 20 | offsetY?: number; 21 | zIndex?: number | null; 22 | } 23 | 24 | export let CustomMarker: ReturnType; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import { defineComponent } from "vue"; 3 | const component: ReturnType; 4 | export default component; 5 | } 6 | -------------------------------------------------------------------------------- /src/themes/aubergine.ts: -------------------------------------------------------------------------------- 1 | export const aubergine: google.maps.MapTypeStyle[] = [ 2 | { 3 | elementType: "geometry", 4 | stylers: [ 5 | { 6 | color: "#1d2c4d", 7 | }, 8 | ], 9 | }, 10 | { 11 | elementType: "labels.text.fill", 12 | stylers: [ 13 | { 14 | color: "#8ec3b9", 15 | }, 16 | ], 17 | }, 18 | { 19 | elementType: "labels.text.stroke", 20 | stylers: [ 21 | { 22 | color: "#1a3646", 23 | }, 24 | ], 25 | }, 26 | { 27 | featureType: "administrative.country", 28 | elementType: "geometry.stroke", 29 | stylers: [ 30 | { 31 | color: "#4b6878", 32 | }, 33 | ], 34 | }, 35 | { 36 | featureType: "administrative.land_parcel", 37 | elementType: "labels.text.fill", 38 | stylers: [ 39 | { 40 | color: "#64779e", 41 | }, 42 | ], 43 | }, 44 | { 45 | featureType: "administrative.province", 46 | elementType: "geometry.stroke", 47 | stylers: [ 48 | { 49 | color: "#4b6878", 50 | }, 51 | ], 52 | }, 53 | { 54 | featureType: "landscape.man_made", 55 | elementType: "geometry.stroke", 56 | stylers: [ 57 | { 58 | color: "#334e87", 59 | }, 60 | ], 61 | }, 62 | { 63 | featureType: "landscape.natural", 64 | elementType: "geometry", 65 | stylers: [ 66 | { 67 | color: "#023e58", 68 | }, 69 | ], 70 | }, 71 | { 72 | featureType: "poi", 73 | elementType: "geometry", 74 | stylers: [ 75 | { 76 | color: "#283d6a", 77 | }, 78 | ], 79 | }, 80 | { 81 | featureType: "poi", 82 | elementType: "labels.text.fill", 83 | stylers: [ 84 | { 85 | color: "#6f9ba5", 86 | }, 87 | ], 88 | }, 89 | { 90 | featureType: "poi", 91 | elementType: "labels.text.stroke", 92 | stylers: [ 93 | { 94 | color: "#1d2c4d", 95 | }, 96 | ], 97 | }, 98 | { 99 | featureType: "poi.park", 100 | elementType: "geometry.fill", 101 | stylers: [ 102 | { 103 | color: "#023e58", 104 | }, 105 | ], 106 | }, 107 | { 108 | featureType: "poi.park", 109 | elementType: "labels.text.fill", 110 | stylers: [ 111 | { 112 | color: "#3C7680", 113 | }, 114 | ], 115 | }, 116 | { 117 | featureType: "road", 118 | elementType: "geometry", 119 | stylers: [ 120 | { 121 | color: "#304a7d", 122 | }, 123 | ], 124 | }, 125 | { 126 | featureType: "road", 127 | elementType: "labels.text.fill", 128 | stylers: [ 129 | { 130 | color: "#98a5be", 131 | }, 132 | ], 133 | }, 134 | { 135 | featureType: "road", 136 | elementType: "labels.text.stroke", 137 | stylers: [ 138 | { 139 | color: "#1d2c4d", 140 | }, 141 | ], 142 | }, 143 | { 144 | featureType: "road.highway", 145 | elementType: "geometry", 146 | stylers: [ 147 | { 148 | color: "#2c6675", 149 | }, 150 | ], 151 | }, 152 | { 153 | featureType: "road.highway", 154 | elementType: "geometry.stroke", 155 | stylers: [ 156 | { 157 | color: "#255763", 158 | }, 159 | ], 160 | }, 161 | { 162 | featureType: "road.highway", 163 | elementType: "labels.text.fill", 164 | stylers: [ 165 | { 166 | color: "#b0d5ce", 167 | }, 168 | ], 169 | }, 170 | { 171 | featureType: "road.highway", 172 | elementType: "labels.text.stroke", 173 | stylers: [ 174 | { 175 | color: "#023e58", 176 | }, 177 | ], 178 | }, 179 | { 180 | featureType: "transit", 181 | elementType: "labels.text.fill", 182 | stylers: [ 183 | { 184 | color: "#98a5be", 185 | }, 186 | ], 187 | }, 188 | { 189 | featureType: "transit", 190 | elementType: "labels.text.stroke", 191 | stylers: [ 192 | { 193 | color: "#1d2c4d", 194 | }, 195 | ], 196 | }, 197 | { 198 | featureType: "transit.line", 199 | elementType: "geometry.fill", 200 | stylers: [ 201 | { 202 | color: "#283d6a", 203 | }, 204 | ], 205 | }, 206 | { 207 | featureType: "transit.station", 208 | elementType: "geometry", 209 | stylers: [ 210 | { 211 | color: "#3a4762", 212 | }, 213 | ], 214 | }, 215 | { 216 | featureType: "water", 217 | elementType: "geometry", 218 | stylers: [ 219 | { 220 | color: "#0e1626", 221 | }, 222 | ], 223 | }, 224 | { 225 | featureType: "water", 226 | elementType: "labels.text.fill", 227 | stylers: [ 228 | { 229 | color: "#4e6d70", 230 | }, 231 | ], 232 | }, 233 | ]; 234 | -------------------------------------------------------------------------------- /src/themes/dark.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Dark mode theme from: 3 | * [dark mode](https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/style-array) 4 | */ 5 | export const dark: google.maps.MapTypeStyle[] = [ 6 | { elementType: "geometry", stylers: [{ color: "#242f3e" }] }, 7 | { elementType: "labels.text.stroke", stylers: [{ color: "#242f3e" }] }, 8 | { elementType: "labels.text.fill", stylers: [{ color: "#746855" }] }, 9 | { 10 | featureType: "administrative.locality", 11 | elementType: "labels.text.fill", 12 | stylers: [{ color: "#d59563" }], 13 | }, 14 | { 15 | featureType: "poi", 16 | elementType: "labels.text.fill", 17 | stylers: [{ color: "#d59563" }], 18 | }, 19 | { 20 | featureType: "poi.park", 21 | elementType: "geometry", 22 | stylers: [{ color: "#263c3f" }], 23 | }, 24 | { 25 | featureType: "poi.park", 26 | elementType: "labels.text.fill", 27 | stylers: [{ color: "#6b9a76" }], 28 | }, 29 | { 30 | featureType: "road", 31 | elementType: "geometry", 32 | stylers: [{ color: "#38414e" }], 33 | }, 34 | { 35 | featureType: "road", 36 | elementType: "geometry.stroke", 37 | stylers: [{ color: "#212a37" }], 38 | }, 39 | { 40 | featureType: "road", 41 | elementType: "labels.text.fill", 42 | stylers: [{ color: "#9ca5b3" }], 43 | }, 44 | { 45 | featureType: "road.highway", 46 | elementType: "geometry", 47 | stylers: [{ color: "#746855" }], 48 | }, 49 | { 50 | featureType: "road.highway", 51 | elementType: "geometry.stroke", 52 | stylers: [{ color: "#1f2835" }], 53 | }, 54 | { 55 | featureType: "road.highway", 56 | elementType: "labels.text.fill", 57 | stylers: [{ color: "#f3d19c" }], 58 | }, 59 | { 60 | featureType: "transit", 61 | elementType: "geometry", 62 | stylers: [{ color: "#2f3948" }], 63 | }, 64 | { 65 | featureType: "transit.station", 66 | elementType: "labels.text.fill", 67 | stylers: [{ color: "#d59563" }], 68 | }, 69 | { 70 | featureType: "water", 71 | elementType: "geometry", 72 | stylers: [{ color: "#17263c" }], 73 | }, 74 | { 75 | featureType: "water", 76 | elementType: "labels.text.fill", 77 | stylers: [{ color: "#515c6d" }], 78 | }, 79 | { 80 | featureType: "water", 81 | elementType: "labels.text.stroke", 82 | stylers: [{ color: "#17263c" }], 83 | }, 84 | ]; 85 | -------------------------------------------------------------------------------- /src/themes/grey.ts: -------------------------------------------------------------------------------- 1 | export const grey: google.maps.MapTypeStyle[] = [ 2 | { 3 | featureType: "all", 4 | elementType: "labels.text.fill", 5 | stylers: [ 6 | { 7 | saturation: 36, 8 | }, 9 | { 10 | color: "#000000", 11 | }, 12 | { 13 | lightness: 40, 14 | }, 15 | ], 16 | }, 17 | { 18 | featureType: "all", 19 | elementType: "labels.text.stroke", 20 | stylers: [ 21 | { 22 | visibility: "on", 23 | }, 24 | { 25 | color: "#000000", 26 | }, 27 | { 28 | lightness: 16, 29 | }, 30 | ], 31 | }, 32 | { 33 | featureType: "all", 34 | elementType: "labels.icon", 35 | stylers: [ 36 | { 37 | visibility: "off", 38 | }, 39 | ], 40 | }, 41 | { 42 | featureType: "administrative", 43 | elementType: "geometry.fill", 44 | stylers: [ 45 | { 46 | color: "#000000", 47 | }, 48 | { 49 | lightness: 20, 50 | }, 51 | ], 52 | }, 53 | { 54 | featureType: "administrative", 55 | elementType: "geometry.stroke", 56 | stylers: [ 57 | { 58 | color: "#000000", 59 | }, 60 | { 61 | lightness: 17, 62 | }, 63 | { 64 | weight: 1.2, 65 | }, 66 | ], 67 | }, 68 | { 69 | featureType: "landscape", 70 | elementType: "geometry", 71 | stylers: [ 72 | { 73 | color: "#000000", 74 | }, 75 | { 76 | lightness: 20, 77 | }, 78 | ], 79 | }, 80 | { 81 | featureType: "poi", 82 | elementType: "geometry", 83 | stylers: [ 84 | { 85 | color: "#000000", 86 | }, 87 | { 88 | lightness: 21, 89 | }, 90 | ], 91 | }, 92 | { 93 | featureType: "road.highway", 94 | elementType: "geometry.fill", 95 | stylers: [ 96 | { 97 | color: "#000000", 98 | }, 99 | { 100 | lightness: 17, 101 | }, 102 | ], 103 | }, 104 | { 105 | featureType: "road.highway", 106 | elementType: "geometry.stroke", 107 | stylers: [ 108 | { 109 | color: "#000000", 110 | }, 111 | { 112 | lightness: 29, 113 | }, 114 | { 115 | weight: 0.2, 116 | }, 117 | ], 118 | }, 119 | { 120 | featureType: "road.arterial", 121 | elementType: "geometry", 122 | stylers: [ 123 | { 124 | color: "#000000", 125 | }, 126 | { 127 | lightness: 18, 128 | }, 129 | ], 130 | }, 131 | { 132 | featureType: "road.local", 133 | elementType: "geometry", 134 | stylers: [ 135 | { 136 | color: "#000000", 137 | }, 138 | { 139 | lightness: 16, 140 | }, 141 | ], 142 | }, 143 | { 144 | featureType: "transit", 145 | elementType: "geometry", 146 | stylers: [ 147 | { 148 | color: "#000000", 149 | }, 150 | { 151 | lightness: 19, 152 | }, 153 | ], 154 | }, 155 | { 156 | featureType: "water", 157 | elementType: "geometry", 158 | stylers: [ 159 | { 160 | color: "#000000", 161 | }, 162 | { 163 | lightness: 17, 164 | }, 165 | ], 166 | }, 167 | ]; 168 | -------------------------------------------------------------------------------- /src/themes/index.ts: -------------------------------------------------------------------------------- 1 | // #autoindex 2 | 3 | // #region autoindexed files 4 | 5 | // index last changed at: 25th Mar, 2022, 04:04 AM ( GMT+2 ) 6 | // export: named; exclusions: index, private. 7 | // files: aubergine, dark, grey, minimal, retro, roadways, roadwaysMinimal, ultraLight. 8 | 9 | // local file exports 10 | export * from "./aubergine"; 11 | export * from "./dark"; 12 | export * from "./grey"; 13 | export * from "./minimal"; 14 | export * from "./retro"; 15 | export * from "./roadways"; 16 | export * from "./roadwaysMinimal"; 17 | export * from "./ultraLight"; 18 | 19 | // Note: 20 | // ----- 21 | // This file was created by running: "dd devops autoindex"; it assumes you have 22 | // the 'do-devops' pkg installed as a dev dep. 23 | // 24 | // By default it assumes that exports are named exports but this can be changed by 25 | // adding a modifier to the '// #autoindex' syntax: 26 | // 27 | // - autoindex:named same as default, exports "named symbols" 28 | // - autoindex:default assumes each file is exporting a default export 29 | // and converts the default export to the name of the 30 | // file 31 | // - autoindex:offset assumes files export "named symbols" but that each 32 | // file's symbols should be offset by the file's name 33 | // (useful for files which might symbols which collide 34 | // or where the namespacing helps consumers) 35 | // 36 | // You may also exclude certain files or directories by adding it to the 37 | // autoindex command. As an example: 38 | // 39 | // - autoindex:named, exclude: foo,bar,baz 40 | // 41 | // Also be aware that all of your content outside the defined region in this file 42 | // will be preserved in situations where you need to do something paricularly awesome. 43 | // Keep on being awesome. 44 | 45 | // #endregion 46 | -------------------------------------------------------------------------------- /src/themes/minimal.ts: -------------------------------------------------------------------------------- 1 | export const minimal: google.maps.MapTypeStyle[] = [ 2 | { 3 | featureType: "administrative.land_parcel", 4 | elementType: "all", 5 | stylers: [ 6 | { 7 | visibility: "off", 8 | }, 9 | ], 10 | }, 11 | { 12 | featureType: "administrative.land_parcel", 13 | elementType: "geometry.stroke", 14 | stylers: [ 15 | { 16 | visibility: "off", 17 | }, 18 | { 19 | weight: 7, 20 | }, 21 | ], 22 | }, 23 | { 24 | featureType: "administrative.locality", 25 | elementType: "geometry.stroke", 26 | stylers: [ 27 | { 28 | visibility: "on", 29 | }, 30 | ], 31 | }, 32 | { 33 | featureType: "administrative.locality", 34 | elementType: "labels.text.fill", 35 | stylers: [ 36 | { 37 | visibility: "on", 38 | }, 39 | ], 40 | }, 41 | { 42 | featureType: "administrative.locality", 43 | elementType: "labels.text.stroke", 44 | stylers: [ 45 | { 46 | visibility: "on", 47 | }, 48 | ], 49 | }, 50 | { 51 | featureType: "administrative.neighborhood", 52 | stylers: [ 53 | { 54 | visibility: "off", 55 | }, 56 | ], 57 | }, 58 | { 59 | featureType: "administrative.neighborhood", 60 | elementType: "geometry.fill", 61 | stylers: [ 62 | { 63 | color: "#00ff28", 64 | }, 65 | { 66 | visibility: "on", 67 | }, 68 | { 69 | weight: 2, 70 | }, 71 | ], 72 | }, 73 | { 74 | featureType: "administrative.neighborhood", 75 | elementType: "geometry.stroke", 76 | stylers: [ 77 | { 78 | color: "#00ff28", 79 | }, 80 | { 81 | visibility: "on", 82 | }, 83 | ], 84 | }, 85 | { 86 | featureType: "administrative.neighborhood", 87 | elementType: "labels.icon", 88 | stylers: [ 89 | { 90 | visibility: "off", 91 | }, 92 | ], 93 | }, 94 | { 95 | featureType: "administrative.neighborhood", 96 | elementType: "labels.text.stroke", 97 | stylers: [ 98 | { 99 | visibility: "off", 100 | }, 101 | ], 102 | }, 103 | { 104 | featureType: "landscape.natural", 105 | elementType: "geometry.fill", 106 | stylers: [ 107 | { 108 | visibility: "on", 109 | }, 110 | ], 111 | }, 112 | { 113 | featureType: "poi", 114 | elementType: "labels.text", 115 | stylers: [ 116 | { 117 | visibility: "off", 118 | }, 119 | ], 120 | }, 121 | { 122 | featureType: "poi.attraction", 123 | stylers: [ 124 | { 125 | visibility: "off", 126 | }, 127 | ], 128 | }, 129 | { 130 | featureType: "poi.business", 131 | elementType: "all", 132 | stylers: [ 133 | { 134 | visibility: "off", 135 | }, 136 | ], 137 | }, 138 | { 139 | featureType: "poi.medical", 140 | elementType: "all", 141 | stylers: [ 142 | { 143 | visibility: "off", 144 | }, 145 | ], 146 | }, 147 | { 148 | featureType: "poi.place_of_worship", 149 | elementType: "all", 150 | stylers: [ 151 | { 152 | visibility: "off", 153 | }, 154 | ], 155 | }, 156 | { 157 | featureType: "poi.school", 158 | elementType: "all", 159 | stylers: [ 160 | { 161 | visibility: "off", 162 | }, 163 | ], 164 | }, 165 | { 166 | featureType: "poi.sports_complex", 167 | elementType: "geometry.fill", 168 | stylers: [ 169 | { 170 | visibility: "on", 171 | }, 172 | ], 173 | }, 174 | { 175 | featureType: "poi.sports_complex", 176 | elementType: "labels.icon", 177 | stylers: [ 178 | { 179 | visibility: "off", 180 | }, 181 | ], 182 | }, 183 | { 184 | featureType: "road", 185 | elementType: "labels.text", 186 | stylers: [ 187 | { 188 | visibility: "on", 189 | }, 190 | { 191 | lightness: -10, 192 | }, 193 | { 194 | color: "#b5b5b5", 195 | }, 196 | { 197 | weight: 0.2, 198 | }, 199 | ], 200 | }, 201 | { 202 | featureType: "road", 203 | elementType: "labels.icon", 204 | stylers: [ 205 | { 206 | visibility: "off", 207 | }, 208 | ], 209 | }, 210 | { 211 | featureType: "road.local", 212 | elementType: "geometry.fill", 213 | stylers: [ 214 | { 215 | color: "#fbfbfb", 216 | }, 217 | { 218 | lightness: -15, 219 | }, 220 | { 221 | weight: 0.5, 222 | }, 223 | ], 224 | }, 225 | { 226 | featureType: "road.local", 227 | elementType: "geometry.stroke", 228 | stylers: [ 229 | { 230 | visibility: "off", 231 | }, 232 | ], 233 | }, 234 | { 235 | featureType: "transit", 236 | elementType: "all", 237 | stylers: [ 238 | { 239 | visibility: "off", 240 | }, 241 | ], 242 | }, 243 | { 244 | featureType: "transit.station", 245 | elementType: "labels.icon", 246 | stylers: [ 247 | { 248 | visibility: "off", 249 | }, 250 | ], 251 | }, 252 | { 253 | featureType: "water", 254 | elementType: "labels.text", 255 | stylers: [ 256 | { 257 | visibility: "off", 258 | }, 259 | ], 260 | }, 261 | ]; 262 | -------------------------------------------------------------------------------- /src/themes/retro.ts: -------------------------------------------------------------------------------- 1 | export const retro: google.maps.MapTypeStyle[] = [ 2 | { elementType: "geometry", stylers: [{ color: "#ebe3cd" }] }, 3 | { elementType: "labels.text.fill", stylers: [{ color: "#523735" }] }, 4 | { elementType: "labels.text.stroke", stylers: [{ color: "#f5f1e6" }] }, 5 | { 6 | featureType: "administrative", 7 | elementType: "geometry.stroke", 8 | stylers: [{ color: "#c9b2a6" }], 9 | }, 10 | { 11 | featureType: "administrative.land_parcel", 12 | elementType: "geometry.stroke", 13 | stylers: [{ color: "#dcd2be" }], 14 | }, 15 | { 16 | featureType: "administrative.land_parcel", 17 | elementType: "labels.text.fill", 18 | stylers: [{ color: "#ae9e90" }], 19 | }, 20 | { 21 | featureType: "landscape.natural", 22 | elementType: "geometry", 23 | stylers: [{ color: "#dfd2ae" }], 24 | }, 25 | { 26 | featureType: "poi", 27 | elementType: "geometry", 28 | stylers: [{ color: "#dfd2ae" }], 29 | }, 30 | { 31 | featureType: "poi", 32 | elementType: "labels.text.fill", 33 | stylers: [{ color: "#93817c" }], 34 | }, 35 | { 36 | featureType: "poi.park", 37 | elementType: "geometry.fill", 38 | stylers: [{ color: "#a5b076" }], 39 | }, 40 | { 41 | featureType: "poi.park", 42 | elementType: "labels.text.fill", 43 | stylers: [{ color: "#447530" }], 44 | }, 45 | { 46 | featureType: "road", 47 | elementType: "geometry", 48 | stylers: [{ color: "#f5f1e6" }], 49 | }, 50 | { 51 | featureType: "road.arterial", 52 | elementType: "geometry", 53 | stylers: [{ color: "#fdfcf8" }], 54 | }, 55 | { 56 | featureType: "road.highway", 57 | elementType: "geometry", 58 | stylers: [{ color: "#f8c967" }], 59 | }, 60 | { 61 | featureType: "road.highway", 62 | elementType: "geometry.stroke", 63 | stylers: [{ color: "#e9bc62" }], 64 | }, 65 | { 66 | featureType: "road.highway.controlled_access", 67 | elementType: "geometry", 68 | stylers: [{ color: "#e98d58" }], 69 | }, 70 | { 71 | featureType: "road.highway.controlled_access", 72 | elementType: "geometry.stroke", 73 | stylers: [{ color: "#db8555" }], 74 | }, 75 | { 76 | featureType: "road.local", 77 | elementType: "labels.text.fill", 78 | stylers: [{ color: "#806b63" }], 79 | }, 80 | { 81 | featureType: "transit.line", 82 | elementType: "geometry", 83 | stylers: [{ color: "#dfd2ae" }], 84 | }, 85 | { 86 | featureType: "transit.line", 87 | elementType: "labels.text.fill", 88 | stylers: [{ color: "#8f7d77" }], 89 | }, 90 | { 91 | featureType: "transit.line", 92 | elementType: "labels.text.stroke", 93 | stylers: [{ color: "#ebe3cd" }], 94 | }, 95 | { 96 | featureType: "transit.station", 97 | elementType: "geometry", 98 | stylers: [{ color: "#dfd2ae" }], 99 | }, 100 | { 101 | featureType: "water", 102 | elementType: "geometry.fill", 103 | stylers: [{ color: "#b9d3c2" }], 104 | }, 105 | { 106 | featureType: "water", 107 | elementType: "labels.text.fill", 108 | stylers: [{ color: "#92998d" }], 109 | }, 110 | ]; 111 | -------------------------------------------------------------------------------- /src/themes/roadways.ts: -------------------------------------------------------------------------------- 1 | export const roadways: google.maps.MapTypeStyle[] = [ 2 | { 3 | featureType: "all", 4 | elementType: "labels", 5 | stylers: [ 6 | { 7 | visibility: "on", 8 | }, 9 | ], 10 | }, 11 | { 12 | featureType: "all", 13 | elementType: "labels.text.fill", 14 | stylers: [ 15 | { 16 | saturation: 36, 17 | }, 18 | { 19 | color: "#000000", 20 | }, 21 | { 22 | lightness: 40, 23 | }, 24 | ], 25 | }, 26 | { 27 | featureType: "all", 28 | elementType: "labels.text.stroke", 29 | stylers: [ 30 | { 31 | visibility: "on", 32 | }, 33 | { 34 | color: "#000000", 35 | }, 36 | { 37 | lightness: 16, 38 | }, 39 | ], 40 | }, 41 | { 42 | featureType: "all", 43 | elementType: "labels.icon", 44 | stylers: [ 45 | { 46 | visibility: "off", 47 | }, 48 | ], 49 | }, 50 | { 51 | featureType: "administrative", 52 | elementType: "geometry.fill", 53 | stylers: [ 54 | { 55 | color: "#000000", 56 | }, 57 | { 58 | lightness: 20, 59 | }, 60 | ], 61 | }, 62 | { 63 | featureType: "administrative", 64 | elementType: "geometry.stroke", 65 | stylers: [ 66 | { 67 | color: "#000000", 68 | }, 69 | { 70 | lightness: 17, 71 | }, 72 | { 73 | weight: 1.2, 74 | }, 75 | ], 76 | }, 77 | { 78 | featureType: "administrative.country", 79 | elementType: "labels.text.fill", 80 | stylers: [ 81 | { 82 | color: "#e5c163", 83 | }, 84 | ], 85 | }, 86 | { 87 | featureType: "administrative.locality", 88 | elementType: "labels.text.fill", 89 | stylers: [ 90 | { 91 | color: "#c4c4c4", 92 | }, 93 | ], 94 | }, 95 | { 96 | featureType: "administrative.neighborhood", 97 | elementType: "labels.text.fill", 98 | stylers: [ 99 | { 100 | color: "#e5c163", 101 | }, 102 | ], 103 | }, 104 | { 105 | featureType: "landscape", 106 | elementType: "geometry", 107 | stylers: [ 108 | { 109 | color: "#000000", 110 | }, 111 | { 112 | lightness: 20, 113 | }, 114 | ], 115 | }, 116 | { 117 | featureType: "poi", 118 | elementType: "geometry", 119 | stylers: [ 120 | { 121 | color: "#000000", 122 | }, 123 | { 124 | lightness: 21, 125 | }, 126 | { 127 | visibility: "on", 128 | }, 129 | ], 130 | }, 131 | { 132 | featureType: "poi.business", 133 | elementType: "geometry", 134 | stylers: [ 135 | { 136 | visibility: "on", 137 | }, 138 | ], 139 | }, 140 | { 141 | featureType: "road.highway", 142 | elementType: "geometry.fill", 143 | stylers: [ 144 | { 145 | color: "#e5c163", 146 | }, 147 | { 148 | lightness: 0, 149 | }, 150 | ], 151 | }, 152 | { 153 | featureType: "road.highway", 154 | elementType: "geometry.stroke", 155 | stylers: [ 156 | { 157 | visibility: "off", 158 | }, 159 | ], 160 | }, 161 | { 162 | featureType: "road.highway", 163 | elementType: "labels.text.fill", 164 | stylers: [ 165 | { 166 | color: "#ffffff", 167 | }, 168 | ], 169 | }, 170 | { 171 | featureType: "road.highway", 172 | elementType: "labels.text.stroke", 173 | stylers: [ 174 | { 175 | color: "#e5c163", 176 | }, 177 | ], 178 | }, 179 | { 180 | featureType: "road.arterial", 181 | elementType: "geometry", 182 | stylers: [ 183 | { 184 | color: "#000000", 185 | }, 186 | { 187 | lightness: 18, 188 | }, 189 | ], 190 | }, 191 | { 192 | featureType: "road.arterial", 193 | elementType: "geometry.fill", 194 | stylers: [ 195 | { 196 | color: "#575757", 197 | }, 198 | ], 199 | }, 200 | { 201 | featureType: "road.arterial", 202 | elementType: "labels.text.fill", 203 | stylers: [ 204 | { 205 | color: "#ffffff", 206 | }, 207 | ], 208 | }, 209 | { 210 | featureType: "road.arterial", 211 | elementType: "labels.text.stroke", 212 | stylers: [ 213 | { 214 | color: "#2c2c2c", 215 | }, 216 | ], 217 | }, 218 | { 219 | featureType: "road.local", 220 | elementType: "geometry", 221 | stylers: [ 222 | { 223 | color: "#000000", 224 | }, 225 | { 226 | lightness: 16, 227 | }, 228 | ], 229 | }, 230 | { 231 | featureType: "road.local", 232 | elementType: "labels.text.fill", 233 | stylers: [ 234 | { 235 | color: "#999999", 236 | }, 237 | ], 238 | }, 239 | { 240 | featureType: "transit", 241 | elementType: "geometry", 242 | stylers: [ 243 | { 244 | color: "#000000", 245 | }, 246 | { 247 | lightness: 19, 248 | }, 249 | ], 250 | }, 251 | { 252 | featureType: "water", 253 | elementType: "geometry", 254 | stylers: [ 255 | { 256 | color: "#000000", 257 | }, 258 | { 259 | lightness: 17, 260 | }, 261 | ], 262 | }, 263 | ]; 264 | -------------------------------------------------------------------------------- /src/themes/roadwaysMinimal.ts: -------------------------------------------------------------------------------- 1 | import { roadways, minimal } from "./index"; 2 | 3 | export const roadwaysMinimal: google.maps.MapTypeStyle[] = [...roadways, ...minimal]; 4 | -------------------------------------------------------------------------------- /src/themes/ultraLight.ts: -------------------------------------------------------------------------------- 1 | export const ultraLight: google.maps.MapTypeStyle[] = [ 2 | { 3 | featureType: "water", 4 | elementType: "geometry", 5 | stylers: [ 6 | { 7 | color: "#e9e9e9", 8 | }, 9 | { 10 | lightness: 17, 11 | }, 12 | ], 13 | }, 14 | { 15 | featureType: "landscape", 16 | elementType: "geometry", 17 | stylers: [ 18 | { 19 | color: "#f5f5f5", 20 | }, 21 | { 22 | lightness: 20, 23 | }, 24 | ], 25 | }, 26 | { 27 | featureType: "road.highway", 28 | elementType: "geometry.fill", 29 | stylers: [ 30 | { 31 | color: "#ffffff", 32 | }, 33 | { 34 | lightness: 17, 35 | }, 36 | ], 37 | }, 38 | { 39 | featureType: "road.highway", 40 | elementType: "geometry.stroke", 41 | stylers: [ 42 | { 43 | color: "#ffffff", 44 | }, 45 | { 46 | lightness: 29, 47 | }, 48 | { 49 | weight: 0.2, 50 | }, 51 | ], 52 | }, 53 | { 54 | featureType: "road.arterial", 55 | elementType: "geometry", 56 | stylers: [ 57 | { 58 | color: "#ffffff", 59 | }, 60 | { 61 | lightness: 18, 62 | }, 63 | ], 64 | }, 65 | { 66 | featureType: "road.local", 67 | elementType: "geometry", 68 | stylers: [ 69 | { 70 | color: "#ffffff", 71 | }, 72 | { 73 | lightness: 16, 74 | }, 75 | ], 76 | }, 77 | { 78 | featureType: "poi", 79 | elementType: "geometry", 80 | stylers: [ 81 | { 82 | color: "#f5f5f5", 83 | }, 84 | { 85 | lightness: 21, 86 | }, 87 | ], 88 | }, 89 | { 90 | featureType: "poi.park", 91 | elementType: "geometry", 92 | stylers: [ 93 | { 94 | color: "#dedede", 95 | }, 96 | { 97 | lightness: 21, 98 | }, 99 | ], 100 | }, 101 | { 102 | elementType: "labels.text.stroke", 103 | stylers: [ 104 | { 105 | visibility: "on", 106 | }, 107 | { 108 | color: "#ffffff", 109 | }, 110 | { 111 | lightness: 16, 112 | }, 113 | ], 114 | }, 115 | { 116 | elementType: "labels.text.fill", 117 | stylers: [ 118 | { 119 | saturation: 36, 120 | }, 121 | { 122 | color: "#333333", 123 | }, 124 | { 125 | lightness: 40, 126 | }, 127 | ], 128 | }, 129 | { 130 | elementType: "labels.icon", 131 | stylers: [ 132 | { 133 | visibility: "off", 134 | }, 135 | ], 136 | }, 137 | { 138 | featureType: "transit", 139 | elementType: "geometry", 140 | stylers: [ 141 | { 142 | color: "#f2f2f2", 143 | }, 144 | { 145 | lightness: 19, 146 | }, 147 | ], 148 | }, 149 | { 150 | featureType: "administrative", 151 | elementType: "geometry.fill", 152 | stylers: [ 153 | { 154 | color: "#fefefe", 155 | }, 156 | { 157 | lightness: 20, 158 | }, 159 | ], 160 | }, 161 | { 162 | featureType: "administrative", 163 | elementType: "geometry.stroke", 164 | stylers: [ 165 | { 166 | color: "#fefefe", 167 | }, 168 | { 169 | lightness: 17, 170 | }, 171 | { 172 | weight: 1.2, 173 | }, 174 | ], 175 | }, 176 | ]; 177 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | type ICustomMarkerInterface = google.maps.OverlayView & { 2 | getPosition(): google.maps.LatLng | null; 3 | getVisible(): boolean; 4 | setOptions(options: google.maps.CustomMarkerOptions): void; 5 | }; 6 | 7 | interface ICustomMarkerCtor { 8 | new (opts: google.maps.CustomMarkerOptions & { element?: HTMLElement }): ICustomMarkerInterface; 9 | } 10 | 11 | export function createCustomMarkerClass(api: typeof google.maps): ICustomMarkerCtor { 12 | return class CustomMarker extends api.OverlayView { 13 | private element?: HTMLElement; 14 | private opts: google.maps.CustomMarkerOptions; 15 | 16 | constructor(opts: google.maps.CustomMarkerOptions & { element?: HTMLElement }) { 17 | super(); 18 | 19 | const { element, ...rest } = opts; 20 | 21 | this.element = element; 22 | this.opts = rest; 23 | 24 | if (this.opts.map) { 25 | this.setMap(this.opts.map); 26 | } 27 | } 28 | 29 | getPosition() { 30 | return this.opts.position 31 | ? this.opts.position instanceof api.LatLng 32 | ? this.opts.position 33 | : new api.LatLng(this.opts.position) 34 | : null; 35 | } 36 | 37 | getVisible() { 38 | if (!this.element) return false; 39 | 40 | const element = this.element; 41 | return ( 42 | element.style.display !== "none" && 43 | element.style.visibility !== "hidden" && 44 | (element.style.opacity === "" || Number(element.style.opacity) > 0.01) 45 | ); 46 | } 47 | 48 | onAdd() { 49 | if (!this.element) return; 50 | 51 | const panes = this.getPanes(); 52 | 53 | if (panes) { 54 | panes.overlayMouseTarget.appendChild(this.element); 55 | } 56 | } 57 | 58 | draw() { 59 | if (!this.element) return; 60 | 61 | const overlayProjection = this.getProjection(); 62 | const point = overlayProjection?.fromLatLngToDivPixel(this.getPosition()); 63 | 64 | if (point) { 65 | this.element.style.position = "absolute"; 66 | let transformX: string, transformY: string; 67 | 68 | switch (this.opts.anchorPoint) { 69 | case "TOP_CENTER": 70 | transformX = "-50%"; 71 | transformY = "-100%"; 72 | break; 73 | case "BOTTOM_CENTER": 74 | transformX = "-50%"; 75 | transformY = "0"; 76 | break; 77 | case "LEFT_CENTER": 78 | transformX = "-100%"; 79 | transformY = "-50%"; 80 | break; 81 | case "RIGHT_CENTER": 82 | transformX = "0"; 83 | transformY = "-50%"; 84 | break; 85 | case "TOP_LEFT": 86 | transformX = "-100%"; 87 | transformY = "-100%"; 88 | break; 89 | case "TOP_RIGHT": 90 | transformX = "0"; 91 | transformY = "-100%"; 92 | break; 93 | case "BOTTOM_LEFT": 94 | transformX = "-100%"; 95 | transformY = "0"; 96 | break; 97 | case "BOTTOM_RIGHT": 98 | transformX = "0"; 99 | transformY = "0"; 100 | break; 101 | default: 102 | // "center" 103 | transformX = "-50%"; 104 | transformY = "-50%"; 105 | } 106 | 107 | const xPos = point.x + (this.opts.offsetX || 0) + "px"; 108 | const yPos = point.y + (this.opts.offsetY || 0) + "px"; 109 | // eslint-disable-next-line prettier/prettier 110 | this.element.style.transform = `translateX(${transformX}) translateX(${xPos}) translateY(${transformY}) translateY(${yPos})`; 111 | 112 | if (this.opts.zIndex) { 113 | this.element.style.zIndex = this.opts.zIndex.toString(); 114 | } 115 | } 116 | } 117 | 118 | onRemove() { 119 | if (!this.element) return; 120 | 121 | this.element.remove(); 122 | } 123 | 124 | setOptions(opts: google.maps.CustomMarkerOptions & { element?: HTMLElement }) { 125 | const { element, ...rest } = opts; 126 | 127 | this.element = element; 128 | this.opts = rest; 129 | this.draw(); 130 | } 131 | }; 132 | } 133 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist/types", 5 | }, 6 | "exclude": ["dev", "docs"] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ES2020", 5 | "esModuleInterop": true, 6 | "useDefineForClassFields": true, 7 | "module": "ESNext", 8 | "lib": [ 9 | "ES2020", 10 | "DOM", 11 | "DOM.Iterable" 12 | ], 13 | "skipLibCheck": true, 14 | "types": [ 15 | "google.maps", 16 | "vite/client", 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ], 22 | }, 23 | /* Bundler mode */ 24 | "moduleResolution": "bundler", 25 | "allowImportingTsExtensions": true, 26 | "resolveJsonModule": true, 27 | "isolatedModules": true, 28 | "declaration": true, 29 | "emitDeclarationOnly": true, 30 | "jsx": "preserve", 31 | /* Linting */ 32 | "strict": true, 33 | "noUnusedLocals": true, 34 | "noUnusedParameters": true, 35 | "noFallthroughCasesInSwitch": true 36 | }, 37 | "include": [ 38 | "src/**/*.ts", 39 | "src/**/*.d.ts", 40 | "src/**/*.tsx", 41 | "src/**/*.vue", 42 | "dev/**/*.ts", 43 | "dev/**/*.vue", 44 | "docs/**/*.ts", 45 | ], 46 | "references": [ 47 | { 48 | "path": "./tsconfig.node.json" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | // vite.config.js 2 | import { resolve } from "path"; 3 | import { defineConfig } from "vite"; 4 | import { fileURLToPath, URL } from "url"; 5 | import vue from "@vitejs/plugin-vue"; 6 | import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; 7 | 8 | export default defineConfig({ 9 | root: "./playground", 10 | plugins: [ 11 | vue(), 12 | cssInjectedByJsPlugin({ 13 | jsAssetsFilterFunction: ({ fileName }) => fileName == "index.mjs" || fileName == "index.cjs", 14 | }), 15 | ], 16 | build: { 17 | lib: { 18 | entry: { 19 | main: resolve(__dirname, "src/index.ts"), 20 | themes: resolve(__dirname, "src/themes/index.ts"), 21 | }, 22 | fileName: (format, entryName) => { 23 | const formatExtensionMap = { 24 | es: "mjs", 25 | cjs: "cjs", 26 | umd: "umd.js", 27 | }; 28 | 29 | const ext = formatExtensionMap[format]; 30 | 31 | if (entryName === "themes") { 32 | return `themes/index.${ext}`; 33 | } 34 | 35 | return `index.${ext}`; 36 | }, 37 | }, 38 | rollupOptions: { 39 | // make sure to externalize deps that shouldn't be bundled 40 | // into your library 41 | external: ["vue"], 42 | output: { 43 | dir: resolve(__dirname, "dist"), 44 | }, 45 | }, 46 | }, 47 | resolve: { 48 | alias: { 49 | "@": fileURLToPath(new URL("./src", import.meta.url)), 50 | }, 51 | }, 52 | }); 53 | --------------------------------------------------------------------------------