import { TrackPoint, Waypoint } from 'gpx'; import mapboxgl from 'mapbox-gl'; import { tick } from 'svelte'; import { get, writable, type Writable } from 'svelte/store'; import MapPopupComponent from './MapPopup.svelte'; export type PopupItem = { item: T; fileId?: string; hide?: () => void; }; export class MapPopup { map: mapboxgl.Map; popup: mapboxgl.Popup; item: Writable = writable(null); maybeHideBinded = this.maybeHide.bind(this); constructor(map: mapboxgl.Map, options?: mapboxgl.PopupOptions) { this.map = map; this.popup = new mapboxgl.Popup(options); let component = new MapPopupComponent({ target: document.body, props: { item: this.item, }, }); tick().then(() => this.popup.setDOMContent(component.container)); } setItem(item: PopupItem | null) { if (item) item.hide = () => this.hide(); this.item.set(item); if (item === null) { this.hide(); } else { tick().then(() => this.show()); } } show() { const i = get(this.item); if (i === null) { this.hide(); return; } this.popup.setLngLat(this.getCoordinates()).addTo(this.map); this.map.on('mousemove', this.maybeHideBinded); } maybeHide(e: mapboxgl.MapMouseEvent) { const i = get(this.item); if (i === null) { this.hide(); return; } if (this.map.project(this.getCoordinates()).dist(this.map.project(e.lngLat)) > 60) { this.hide(); } } hide() { this.popup.remove(); this.map.off('mousemove', this.maybeHideBinded); } remove() { this.popup.remove(); } getCoordinates() { const i = get(this.item); if (i === null) { return new mapboxgl.LngLat(0, 0); } return i.item instanceof Waypoint || i.item instanceof TrackPoint ? i.item.getCoordinates() : new mapboxgl.LngLat(i.item.lon, i.item.lat); } }