diff --git a/library/composables/poster/useMapPoster.js b/library/composables/poster/useMapPoster.js index b69f773b6..1ca5e9b75 100644 --- a/library/composables/poster/useMapPoster.js +++ b/library/composables/poster/useMapPoster.js @@ -33,6 +33,7 @@ import { ref, readonly } from 'vue'; import { computeLayout } from './usePosterLayout.js'; +import { engineFor } from '../useMapProvider.js'; const MAX_SINGLE_CANVAS = 4096; // WebGL safe ceiling for most hardware const TILE_PX = 2048; // tile size for stitch path @@ -370,7 +371,7 @@ async function _spawnHiddenMap(sourceMap, w, h, opts = {}) { // eslint-disable-next-line no-undef const W = (typeof window !== 'undefined' ? window : {}); const isMaplibre = !!W.maplibregl && sourceMap instanceof W.maplibregl.Map; - const gl = isMaplibre ? W.maplibregl : W.mapboxgl; + const gl = engineFor(sourceMap); // the one per-instance helper (useMapProvider.js) if (!gl) throw new Error('useMapPoster: ' + (isMaplibre ? 'maplibregl' : 'mapboxgl') + ' global not found'); const styleObj = sourceMap.getStyle(); diff --git a/library/composables/useMapEvents.js b/library/composables/useMapEvents.js index a5cc61f60..6871af20b 100644 --- a/library/composables/useMapEvents.js +++ b/library/composables/useMapEvents.js @@ -5,6 +5,7 @@ // Import Vue functions from NPM package import { ref, inject } from 'vue'; import { useI18n } from 'vue-i18n'; +import { engineFor } from './useMapProvider.js'; /** * useMapEvents composable - manages Mapbox layer event handlers @@ -245,7 +246,7 @@ export function useMapEvents(options = {}) { } // Fallback: show popup (for non-prayer legend types) - new mapboxgl.Popup({ + new (engineFor(map).Popup)({ maxWidth: '320px', className: 'people-group-popup' }) @@ -290,7 +291,7 @@ export function useMapEvents(options = {}) { if (e.features && e.features.length > 0) { const properties = e.features[0].properties; - new mapboxgl.Popup() + new (engineFor(map).Popup)() .setLngLat(e.lngLat) .setHTML(generateRegionPopupContent(properties)) .addTo(map); diff --git a/library/composables/useMapProvider.js b/library/composables/useMapProvider.js index 00cbf3557..f764575cb 100644 --- a/library/composables/useMapProvider.js +++ b/library/composables/useMapProvider.js @@ -61,6 +61,28 @@ export function isMapboxMap(map) { return typeof window !== 'undefined' && !!window.mapboxgl && map instanceof window.mapboxgl.Map } +/** + * The engine namespace that built THIS map: window.maplibregl or window.mapboxgl. Use it for + * everything constructed alongside a map (Marker, Popup, a second Map, controls) instead of + * the bare `mapboxgl` global. + * + * ★ WHY. Every generated leaf
used to load mapbox-gl.js statically, whatever the provider, + * so a bare `new mapboxgl.Popup()` always found a global, even on a MapLibre map (where it + * built a MAPBOX popup and attached it to a MapLibre map). Removing that static peer (a visitor-IP + * leak to api.mapbox.com under MapLibre) turns each of those into a ReferenceError on the first + * pin select or click, and the poster's hidden map into 'mapboxgl global not found'. + * Asking the MAP which engine it is removes the dependency on what else the page loaded. + * @param {object} map the live map instance + * @returns {object|undefined} the engine namespace, or undefined when neither is loaded + */ +export function engineFor(map) { + if (typeof window === 'undefined') return undefined + const ml = window.maplibregl, mb = window.mapboxgl + if (ml && ml.Map && map instanceof ml.Map) return ml + if (mb && mb.Map && map instanceof mb.Map) return mb + return mb || ml +} + /** Mapbox-Standard-only lighting paint props, per engine. MapLibre rejects layers whose * paint carries unknown properties (the whole layer is dropped → invisible pins), so * spread this instead of writing '