prop/assets/js/location_map_hook.ts

71 lines
2 KiB
TypeScript

import type { ViewHook } from "phoenix_live_view"
interface LocationMapHook extends ViewHook {
map: L.Map
marker: L.CircleMarker
visibilityHandler: (() => void) | null
}
export const LocationMap: Partial<LocationMapHook> = {
mounted(this: LocationMapHook) {
const lat = parseFloat(this.el.dataset.lat || "0")
const lon = parseFloat(this.el.dataset.lon || "0")
const osm = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 19
})
const satellite = L.tileLayer(
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
{ maxZoom: 21, maxNativeZoom: 19 }
)
const topo = L.tileLayer("https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", {
maxZoom: 17
})
const map = L.map(this.el, {
center: [lat, lon],
zoom: 16,
zoomControl: true,
attributionControl: false
})
this.map = map
satellite.addTo(map)
L.control.layers(
{ "Satellite": satellite, "OSM": osm, "Topo": topo },
{},
{ position: "topright", collapsed: false }
).addTo(map)
this.marker = L.circleMarker([lat, lon], {
radius: 8,
color: "#ffffff",
weight: 2,
fillColor: "#ef4444",
fillOpacity: 0.95,
pane: "markerPane"
}).addTo(map)
this.marker.bindPopup(`${lat.toFixed(6)}, ${lon.toFixed(6)}`)
// LiveView reconnect / tab visibility — Leaflet needs invalidateSize.
this.visibilityHandler = () => {
if (document.visibilityState === "visible") this.map.invalidateSize()
}
document.addEventListener("visibilitychange", this.visibilityHandler)
setTimeout(() => map.invalidateSize(), 50)
},
reconnected(this: LocationMapHook) {
this.map?.invalidateSize()
},
destroyed(this: LocationMapHook) {
if (this.visibilityHandler) {
document.removeEventListener("visibilitychange", this.visibilityHandler)
this.visibilityHandler = null
}
this.map?.remove()
}
}