Skip to content

Commit

Permalink
feat: advanced markers
Browse files Browse the repository at this point in the history
* Add AdvancedMarker component

* Add doc about advanced marker component

* Update component on options change

* Rename avanced-marker.md to advanced-marker.md

* fix: minor fixes

* docs: update readme

---------

Co-authored-by: Husam Elbashir <[email protected]>
  • Loading branch information
VictorBalbo and HusamElbashir authored Aug 4, 2024
1 parent 3d515a5 commit 69a1b66
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 5 deletions.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Note: Please refer to the [documentation site](https://vue3-google-map.com/) for
- [Installation](#installation)
- [Your First Map](#your-first-map)
- [Components](#components)
- [Advanced Marker](#advanced-marker)
- [Marker](#marker)
- [Polyline](#polyline)
- [Polygon](#polygon)
Expand Down Expand Up @@ -83,6 +84,7 @@ This library is intended to be used in a composable fashion. Therefore you will

The main mapping component is `GoogleMap`, however the following components are available at your disposal:

- [AdvancedMarker](#advanced-marker)
- [Marker](#marker)
- [Polyline](#polyline)
- [Polygon](#polygon)
Expand All @@ -93,6 +95,44 @@ The main mapping component is `GoogleMap`, however the following components are
- [CustomControl](#custom-control)
- [MarkerCluster](#marker-cluster)

### Advanced Marker

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)).

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)).

## Options

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.

You can also pass a [PinElementOptions interface](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#PinElementOptions) object to custumize pin used by the marker.

```vue
<script setup>
import { GoogleMap, AdvancedMarker } from 'vue3-google-map'
const center = { lat: 40.689247, lng: -74.044502 }
const markerOptions = { position: center, label: 'L', title: 'LADY LIBERTY' }
const pinOptions = { background: '#FBBC04' }
</script>
<template>
<GoogleMap
api-key="YOUR_GOOGLE_MAPS_API_KEY"
mapId="DEMO_MAP_ID"
style="width: 100%; height: 500px"
:center="center"
:zoom="15"
>
<AdvancedMarker :options="markerOptions" :pin-options="pinOptions"/>
</GoogleMap>
</template>
```

## Events

You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#AdvancedMarkerElement-Events) on the `AdvancedMarker` component.

### Marker

Use the `Marker` component to draw markers, drop pins or any custom icons on a map.
Expand Down
4 changes: 4 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export default defineConfig({
link: "/components/",
collapsed: false,
items: [
{
text: "Advanced Marker",
link: "/components/advanced-marker",
},
{
text: "Marker",
link: "/components/marker",
Expand Down
57 changes: 57 additions & 0 deletions docs/components/advanced-marker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script setup>
import { GoogleMap, AdvancedMarker } from '@lib'
import { apiPromise } from '@docs/shared'

const center = { lat: 40.689247, lng: -74.044502 }
</script>


# Advanced Marker

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)).

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)).

## Options

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.

You can also pass a [PinElementOptions interface](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#PinElementOptions) object to custumize pin used by the marker.

```vue
<script setup>
import { GoogleMap, AdvancedMarker } from 'vue3-google-map'
const center = { lat: 40.689247, lng: -74.044502 }
const markerOptions = { position: center, label: 'L', title: 'LADY LIBERTY' }
const pinOptions = { background: '#FBBC04' }
</script>
<template>
<GoogleMap
api-key="YOUR_GOOGLE_MAPS_API_KEY"
mapId="DEMO_MAP_ID"
style="width: 100%; height: 500px"
:center="center"
:zoom="15"
>
<AdvancedMarker :options="markerOptions" :pin-options="pinOptions"/>
</GoogleMap>
</template>
```

<ClientOnly>
<GoogleMap
:api-promise="apiPromise"
mapId="DEMO_MAP_ID"
style="width: 100%; height: 500px"
:center="center"
:zoom="15"
>
<AdvancedMarker :options="{ position: { lat: 40.689247, lng: -74.044502 } }" :pin-options="{ background: '#FBBC04' }" />
</GoogleMap>
</ClientOnly>
## Events

You can listen for [the following events](https://developers.google.com/maps/documentation/javascript/reference/advanced-markers#AdvancedMarkerElement-Events) on the `AdvancedMarker` component.
1 change: 1 addition & 0 deletions docs/components/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This library is intended to be used in a composable fashion. Therefore you will

The main mapping component is `GoogleMap`, however the following components are available at your disposal:

- [AdvancedMarker](./advanced-marker.md)
- [Marker](./marker.md)
- [Polyline](./polyline.md)
- [Polygon](./polygon.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Loader } from "@googlemaps/js-api-loader";
const loader = new Loader({
apiKey: import.meta.env.VITE_GOOGLE_API_KEY,
version: "weekly",
libraries: ["visualization"],
libraries: ["visualization", "marker"],
});

export const apiPromise = import.meta.env.SSR ? Promise.resolve() : loader.load();
97 changes: 97 additions & 0 deletions src/components/AdvancedMarker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { defineComponent, PropType, toRef, provide, computed, inject, markRaw, onBeforeUnmount, ref, watch } from "vue";
import { markerSymbol, apiSymbol, mapSymbol, markerClusterSymbol } from "../shared/index";
import equal from "fast-deep-equal";

const markerEvents = ["click", "drag", "dragend", "dragstart", "gmp-click"];

export default defineComponent({
name: "AdvancedMarker",
props: {
options: {
type: Object as PropType<google.maps.marker.AdvancedMarkerElementOptions>,
required: true,
},
pinOptions: {
type: Object as PropType<google.maps.marker.PinElementOptions>,
required: false,
},
},
emits: markerEvents,
setup(props, { emit, expose, slots }) {
const options = toRef(props, "options");
const pinOptions = toRef(props, "pinOptions");

const marker = ref<google.maps.marker.AdvancedMarkerElement>();

const map = inject(mapSymbol, ref());
const api = inject(apiSymbol, ref());
const markerCluster = inject(markerClusterSymbol, ref());

const isMarkerInCluster = computed(
() => !!(markerCluster.value && api.value && marker.value instanceof google.maps.marker.AdvancedMarkerElement)
);

watch(
[map, options, pinOptions],
async (_, [oldMap, oldOptions, oldPinOptions]) => {
const hasOptionChange = !equal(options.value, oldOptions) || !equal(pinOptions.value, oldPinOptions);
const hasChanged = hasOptionChange || map.value !== oldMap;

if (!map.value || !api.value || !hasChanged) return;

const { AdvancedMarkerElement, PinElement } = api.value.marker;

if (marker.value) {
const { map: _, content, ...otherOptions } = options.value;

Object.assign(marker.value, {
content: pinOptions.value ? new PinElement(pinOptions.value).element : content,
...otherOptions,
});

if (isMarkerInCluster.value) {
markerCluster.value?.removeMarker(marker.value);
markerCluster.value?.addMarker(marker.value);
}
} else {
if (pinOptions.value) {
options.value.content = new PinElement(pinOptions.value).element;
}

marker.value = markRaw(new AdvancedMarkerElement(options.value));

if (isMarkerInCluster.value) {
markerCluster.value?.addMarker(marker.value);
} else {
marker.value.map = map.value;
}

markerEvents.forEach((event) => {
marker.value?.addListener(event, (e: unknown) => emit(event, e));
});
}
},
{
immediate: true,
}
);

onBeforeUnmount(() => {
if (marker.value) {
api.value?.event.clearInstanceListeners(marker.value);

if (isMarkerInCluster.value) {
markerCluster.value?.removeMarker(marker.value);
} else {
marker.value.map = null;
}
}
});

provide(markerSymbol, marker);

expose({ marker });

return () => slots.default?.();
},
});
4 changes: 2 additions & 2 deletions src/components/GoogleMap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export default defineComponent({
default: "weekly",
},
libraries: {
type: Array as PropType<("drawing" | "geometry" | "localContext" | "places" | "visualization")[]>,
default: () => ["places"],
type: Array as PropType<Library[]>,
default: () => ["places", "marker"],
},
region: {
type: String,
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as GoogleMap } from "./GoogleMap.vue";
export { default as AdvancedMarker } from "./AdvancedMarker";
export { default as Marker } from "./Marker";
export { default as Polyline } from "./Polyline";
export { default as Polygon } from "./Polygon";
Expand Down
6 changes: 4 additions & 2 deletions src/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { InjectionKey, Ref } from "vue";

export const mapSymbol: InjectionKey<Ref<google.maps.Map | undefined>> = Symbol("map");
export const apiSymbol: InjectionKey<Ref<typeof google.maps | undefined>> = Symbol("api");
export const markerSymbol: InjectionKey<Ref<google.maps.Marker | undefined>> = Symbol("marker");
export const markerSymbol: InjectionKey<
Ref<google.maps.Marker | google.maps.marker.AdvancedMarkerElement | undefined>
> = Symbol("marker");
export const markerClusterSymbol: InjectionKey<Ref<MarkerClusterer | undefined>> = Symbol("markerCluster");
export const customMarkerClassSymbol = (Symbol("CustomMarker") as unknown) as "CustomMarker";
export const customMarkerClassSymbol = Symbol("CustomMarker") as unknown as "CustomMarker";
/**
* Utilitary flag for components that need to know the map
* was fully loaded (including its tiles) to decide their behavior
Expand Down

0 comments on commit 69a1b66

Please sign in to comment.