Click a row's info area to expand a Leaflet mini-map centered on that pin, defaulting to ESRI World_Imagery satellite (zoom up to 21) with OSM/Topo toggles in a top-right layer control.
64 lines
1.8 KiB
TypeScript
64 lines
1.8 KiB
TypeScript
import type { ViewHook } from "phoenix_live_view"
|
|
|
|
interface LocationMapHook extends ViewHook {
|
|
map: L.Map
|
|
marker: L.Marker
|
|
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.marker([lat, lon]).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()
|
|
}
|
|
}
|