diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..31e5e26 --- /dev/null +++ b/mise.toml @@ -0,0 +1,3 @@ +[tools] +node = "18" +pnpm = "8" diff --git a/package.json b/package.json index 0c0a54d..a52b58e 100644 --- a/package.json +++ b/package.json @@ -53,10 +53,10 @@ "dependencies": { "@googlemaps/js-api-loader": "^1.16.2", "@googlemaps/markerclusterer": "^2.4.0", + "debounce": "^2.2.0", "fast-deep-equal": "^3.1.3" }, "devDependencies": { - "pnpm": "^8.7.6", "@types/google.maps": "^3.58.1", "@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/parser": "^4.33.0", @@ -67,6 +67,7 @@ "eslint-config-prettier": "^8.10.0", "eslint-plugin-prettier": "^3.4.1", "eslint-plugin-vue": "^7.20.0", + "pnpm": "^8.7.6", "prettier": "^2.8.8", "rimraf": "^5.0.1", "standard-version": "^9.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04b1afc..a4b4bcf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@googlemaps/markerclusterer': specifier: ^2.4.0 version: 2.4.0 + debounce: + specifier: ^2.2.0 + version: 2.2.0 fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -1840,6 +1843,11 @@ packages: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} dev: true + /debounce@2.2.0: + resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==} + engines: {node: '>=18'} + dev: false + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} diff --git a/src/components/AdvancedMarker.vue b/src/components/AdvancedMarker.vue index 0f717fb..f93cbcb 100644 --- a/src/components/AdvancedMarker.vue +++ b/src/components/AdvancedMarker.vue @@ -20,7 +20,7 @@ import { watch, Comment, } from "vue"; -import { markerSymbol, apiSymbol, mapSymbol, markerClusterSymbol } from "../shared/index"; +import { markerSymbol, apiSymbol, mapSymbol, markerClusterSymbol, markerClusterMethodsSymbol } from "../shared/index"; import equal from "fast-deep-equal"; const markerEvents = ["click", "drag", "dragend", "dragstart", "gmp-click"]; @@ -50,6 +50,7 @@ export default defineComponent({ const map = inject(mapSymbol, ref()); const api = inject(apiSymbol, ref()); const markerCluster = inject(markerClusterSymbol, ref()); + const markerClusterMethods = inject(markerClusterMethodsSymbol, undefined); const isMarkerInCluster = computed( () => !!(markerCluster.value && api.value && marker.value instanceof google.maps.marker.AdvancedMarkerElement) @@ -78,8 +79,8 @@ export default defineComponent({ }); if (isMarkerInCluster.value) { - markerCluster.value?.removeMarker(marker.value); - markerCluster.value?.addMarker(marker.value); + markerClusterMethods?.removeMarker(marker.value); + markerClusterMethods?.addMarker(marker.value); } } else { if (hasSlotContent.value) { @@ -91,7 +92,7 @@ export default defineComponent({ marker.value = markRaw(new AdvancedMarkerElement(options.value)); if (isMarkerInCluster.value) { - markerCluster.value?.addMarker(marker.value); + markerClusterMethods?.addMarker(marker.value); } else { marker.value.map = map.value; } @@ -111,7 +112,7 @@ export default defineComponent({ api.value?.event.clearInstanceListeners(marker.value); if (isMarkerInCluster.value) { - markerCluster.value?.removeMarker(marker.value); + markerClusterMethods?.removeMarker(marker.value); } else { marker.value.map = null; } diff --git a/src/components/MarkerCluster.ts b/src/components/MarkerCluster.ts index 06f7909..71611cb 100644 --- a/src/components/MarkerCluster.ts +++ b/src/components/MarkerCluster.ts @@ -5,7 +5,8 @@ import { MarkerClustererEvents, SuperClusterViewportAlgorithm, } from "@googlemaps/markerclusterer"; -import { mapSymbol, apiSymbol, markerClusterSymbol } from "../shared/index"; +import { mapSymbol, apiSymbol, markerClusterSymbol, markerClusterMethodsSymbol } from "../shared/index"; +import debounce from 'debounce'; const markerClusterEvents = Object.values(MarkerClustererEvents); @@ -16,6 +17,10 @@ export default defineComponent({ type: Object as PropType, default: () => ({}), }, + renderDebounceDelay: { + type: Number, + default: 10, + } }, emits: markerClusterEvents, setup(props, { emit, expose, slots }) { @@ -23,7 +28,28 @@ export default defineComponent({ const map = inject(mapSymbol, ref()); const api = inject(apiSymbol, ref()); + const debouncedRender = debounce(() => { + if (markerCluster.value) { + markerCluster.value?.render(); + } + }, props.renderDebounceDelay); + + const addMarker = (marker: google.maps.Marker | google.maps.marker.AdvancedMarkerElement) => { + if (markerCluster.value) { + markerCluster.value?.addMarker(marker, true); + debouncedRender(); + } + }; + + const removeMarker = (marker: google.maps.Marker | google.maps.marker.AdvancedMarkerElement) => { + if (markerCluster.value) { + markerCluster.value?.removeMarker(marker, true); + debouncedRender(); + } + }; + provide(markerClusterSymbol, markerCluster); + provide(markerClusterMethodsSymbol, { addMarker, removeMarker }); watch( map, @@ -55,6 +81,7 @@ export default defineComponent({ markerCluster.value.clearMarkers(); markerCluster.value.setMap(null); } + debouncedRender.clear() }); expose({ markerCluster }); diff --git a/src/composables/useSetupMapComponent.ts b/src/composables/useSetupMapComponent.ts index 78d4a0d..a32a413 100644 --- a/src/composables/useSetupMapComponent.ts +++ b/src/composables/useSetupMapComponent.ts @@ -1,6 +1,6 @@ import { watch, ref, Ref, inject, onBeforeUnmount, computed, markRaw } from "vue"; import equal from "fast-deep-equal"; -import { apiSymbol, mapSymbol, markerClusterSymbol, customMarkerClassSymbol } from "../shared/index"; +import { apiSymbol, mapSymbol, markerClusterSymbol, customMarkerClassSymbol, markerClusterMethodsSymbol } from "../shared/index"; type ICtorKey = "Marker" | "Polyline" | "Polygon" | "Rectangle" | "Circle" | typeof customMarkerClassSymbol; @@ -50,6 +50,7 @@ export const useSetupMapComponent = ( const map = inject(mapSymbol, ref()); const api = inject(apiSymbol, ref()); const markerCluster = inject(markerClusterSymbol, ref()); + const markerClusterMethods = inject(markerClusterMethodsSymbol, undefined); const isMarkerInCluster = computed( () => @@ -72,8 +73,8 @@ export const useSetupMapComponent = ( component.value.setOptions(options.value as any); if (isMarkerInCluster.value) { - markerCluster.value?.removeMarker(component.value as google.maps.Marker); - markerCluster.value?.addMarker(component.value as google.maps.Marker); + markerClusterMethods?.removeMarker(component.value as google.maps.Marker); + markerClusterMethods?.addMarker(component.value as google.maps.Marker); } } else { if (isMarkerCtorKey(ctorKey)) { @@ -94,7 +95,7 @@ export const useSetupMapComponent = ( } if (isMarkerInCluster.value) { - markerCluster.value?.addMarker(component.value as google.maps.Marker); + markerClusterMethods?.addMarker(component.value as google.maps.Marker); } else { component.value.setMap(map.value); } @@ -114,7 +115,7 @@ export const useSetupMapComponent = ( api.value?.event.clearInstanceListeners(component.value); if (isMarkerInCluster.value) { - markerCluster.value?.removeMarker(component.value as google.maps.Marker); + markerClusterMethods?.removeMarker(component.value as google.maps.Marker); } else { component.value.setMap(null); } diff --git a/src/shared/index.ts b/src/shared/index.ts index eb822ee..14daef4 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -7,6 +7,11 @@ export const markerSymbol: InjectionKey< Ref > = Symbol("marker"); export const markerClusterSymbol: InjectionKey> = Symbol("markerCluster"); +export const markerClusterMethodsSymbol: InjectionKey<{ + addMarker: (marker: google.maps.Marker | google.maps.marker.AdvancedMarkerElement) => void; + removeMarker: (marker: google.maps.Marker | google.maps.marker.AdvancedMarkerElement) => void; +} | undefined> = Symbol("markerClusterMethods"); + export const customMarkerClassSymbol = Symbol("CustomMarker") as unknown as "CustomMarker"; /** * Utilitary flag for components that need to know the map