Skip to content

Commit

Permalink
feat: info windows
Browse files Browse the repository at this point in the history
  • Loading branch information
HusamElbashir authored Mar 20, 2022
1 parent 5374ea2 commit 6a294f2
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 12 deletions.
6 changes: 3 additions & 3 deletions src/components/CustomControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<script lang="ts">
import { defineComponent, PropType, watch, ref, inject, Ref, onBeforeUnmount } from "vue";
import { apiSymbol, mapSymbol, mapWasLoadedSymbol } from "../shared/index";
import { apiSymbol, mapSymbol, mapTilesLoadedSymbol } from "../shared/index";
import { IControlPosition } from "../@types/index";
type ControlRef = HTMLElement & { index: number };
Expand All @@ -34,13 +34,13 @@ export default defineComponent({
const map = inject(mapSymbol, ref(null));
const api = inject(apiSymbol, ref(null));
const mapWasLoaded = inject(mapWasLoadedSymbol, ref(false));
const mapTilesLoaded = inject(mapTilesLoadedSymbol, ref(false));
const showContent = ref(false);
// To avoid rendering the content outside the map we need to wait for the map AND the api to fully load
const stopWatchingOnMapLoad = watch(
[mapWasLoaded, api, controlRef],
[mapTilesLoaded, api, controlRef],
([newMapLoadedStatus, newApi, newControlRef]) => {
const contentRef = newControlRef as unknown as Ref<HTMLElement | null>;
const mapLoadedStatus = newMapLoadedStatus as boolean;
Expand Down
16 changes: 8 additions & 8 deletions src/components/GoogleMap.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { defineComponent, PropType, ref, onMounted, onBeforeUnmount, watch, toRef, provide } from "vue";
import { mapSymbol, apiSymbol, loaderInstance, mapWasLoadedSymbol } from "../shared/index";
import { mapSymbol, apiSymbol, loaderInstance, mapTilesLoadedSymbol } from "../shared/index";
import { Loader } from "@googlemaps/js-api-loader";
import { IControlPosition } from "../@types/index";
Expand Down Expand Up @@ -217,11 +217,11 @@ export default defineComponent({
const map = ref<google.maps.Map | null>(null);
const api = ref<typeof google.maps | null>(null);
const mapWasLoaded = ref(false);
const mapTilesLoaded = ref(false);
provide(mapSymbol, map);
provide(apiSymbol, api);
provide(mapWasLoadedSymbol, mapWasLoaded);
provide(mapTilesLoadedSymbol, mapTilesLoaded);
const resolveOptions = (): google.maps.MapOptions => {
const options: google.maps.MapOptions = { ...props };
Expand All @@ -243,7 +243,7 @@ export default defineComponent({
rotateControlOptions: createControlOptionsWithPosition(props.rotateControlPosition),
streetViewControlOptions: createControlOptionsWithPosition(props.streetViewControlPosition),
fullscreenControlOptions: createControlOptionsWithPosition(props.fullscreenControlPosition),
disableDefaultUI: props.disableDefaultUi
disableDefaultUI: props.disableDefaultUi,
};
return { ...options, ...otherOptions };
Expand All @@ -257,7 +257,7 @@ export default defineComponent({
if (api && map) {
api.event.addListenerOnce(map, "tilesloaded", () => {
mapWasLoaded.value = true;
mapTilesLoaded.value = true;
});
// As the watcher is imediately invoked if the api and the map was already loaded
// the watchStopHandler wasnt created because this function has not fully executed
Expand Down Expand Up @@ -314,19 +314,19 @@ export default defineComponent({
});
onBeforeUnmount(() => {
mapWasLoaded.value = false;
mapTilesLoaded.value = false;
if (map.value) api.value?.event.clearInstanceListeners(map.value);
});
return { mapRef, ready, map, api };
return { mapRef, ready, map, api, mapTilesLoaded };
},
});
</script>

<template>
<div>
<div ref="mapRef" class="mapdiv" />
<slot />
<slot v-bind="{ ready, map, api, mapTilesLoaded }" />
</div>
</template>

Expand Down
76 changes: 76 additions & 0 deletions src/components/InfoWindow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<div ref="infoWindowRef" v-if="hasSlotContent" v-show="mapTilesLoaded">
<slot />
</div>
</template>

<script lang="ts">
import { defineComponent, PropType, watch, ref, computed, inject, onBeforeUnmount, Comment } from "vue";
import { apiSymbol, mapSymbol, mapTilesLoadedSymbol } from "../shared/index";
const infoWindowEvents = ["closeclick", "content_changed", "domready", "position_changed", "visible", "zindex_changed"];
export default defineComponent({
props: {
options: {
type: Object as PropType<google.maps.InfoWindowOptions>,
required: true,
},
},
emits: infoWindowEvents,
setup(props, { slots, emit }) {
let _infoWindow: google.maps.InfoWindow;
const infoWindow = ref<google.maps.InfoWindow>();
const infoWindowRef = ref<HTMLElement>();
const map = inject(mapSymbol, ref(null));
const api = inject(apiSymbol, ref(null));
const mapTilesLoaded = inject(mapTilesLoadedSymbol, ref(false));
const hasSlotContent = computed(() => slots.default?.().some((vnode) => vnode.type !== Comment));
watch(
[map, () => props.options, mapTilesLoaded],
([_, options, newMapTilesLoaded], [oldMap, oldOptions, oldMapTilesLoaded]) => {
const checkIfChanged = JSON.stringify(options) !== JSON.stringify(oldOptions) || map.value !== oldMap;
if (map.value && api.value && (checkIfChanged || (newMapTilesLoaded && !oldMapTilesLoaded))) {
if (_infoWindow) {
_infoWindow.setOptions({
...options,
content: hasSlotContent.value ? infoWindowRef.value : options.content,
});
if (!hasSlotContent.value || mapTilesLoaded.value) _infoWindow.open({ map: map.value });
} else {
infoWindow.value = _infoWindow = new api.value.InfoWindow({
...options,
content: hasSlotContent.value ? infoWindowRef.value : options.content,
});
if (!hasSlotContent.value || mapTilesLoaded.value) _infoWindow.open({ map: map.value });
infoWindowEvents.forEach((event) => {
_infoWindow?.addListener(event, (e: unknown) => emit(event, e));
});
}
}
},
{
immediate: true,
}
);
onBeforeUnmount(() => {
if (_infoWindow) {
api.value?.event.clearInstanceListeners(_infoWindow);
_infoWindow.close();
}
});
return { infoWindow, infoWindowRef, hasSlotContent, mapTilesLoaded };
},
});
</script>
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { default as Polygon } from "./Polygon";
export { default as Rectangle } from "./Rectangle";
export { default as Circle } from "./Circle";
export { default as CustomControl } from "./CustomControl.vue";
export { default as InfoWindow } from "./InfoWindow.vue";
2 changes: 1 addition & 1 deletion src/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const apiSymbol: InjectionKey<Ref<typeof google.maps | null>> = Symbol("a
* Utilitary flag for components that need to know the map
* was fully loaded (including its tiles) to decide their behavior
*/
export const mapWasLoadedSymbol: InjectionKey<Ref<boolean>> = Symbol("mapwasloaded");
export const mapTilesLoadedSymbol: InjectionKey<Ref<boolean>> = Symbol("mapTilesLoaded");
export const loaderInstance = ref<Loader | null>(null);

export const polylineEvents = [
Expand Down

0 comments on commit 6a294f2

Please sign in to comment.