(null)
useEffect(() => {
let cancelled = false
void (async () => {
const mod = await import("leaflet")
const L = ((mod as unknown as { default?: typeof Leaflet }).default ?? mod) as typeof Leaflet
if (cancelled || !containerRef.current || mapRef.current) return
const map = L.map(containerRef.current, {
scrollWheelZoom: false,
zoomControl: true,
})
mapRef.current = map
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution:
'© OpenStreetMap contributors',
maxZoom: 19,
}).addTo(map)
const icon = L.divIcon({
html: PIN_SVG,
className: "pmn-map-pin",
iconSize: [28, 28],
iconAnchor: [14, 28],
popupAnchor: [0, -26],
})
const latlngs: [number, number][] = []
for (const m of markers) {
if (!Number.isFinite(m.lat) || !Number.isFinite(m.lng)) continue
const marker = L.marker([m.lat, m.lng], { icon }).addTo(map)
const link = m.href
? ``
: ""
const sub = m.subtitle ? `${escapeHtml(m.subtitle)}
` : ""
marker.bindPopup(`${escapeHtml(m.name)}
${sub}${link}`)
latlngs.push([m.lat, m.lng])
}
if (latlngs.length === 1) {
map.setView(latlngs[0], zoom)
} else if (latlngs.length > 1) {
map.fitBounds(latlngs, { padding: [40, 40] })
} else {
map.setView([39.8283, -98.5795], 4) // continental US fallback
}
// Tiles can render blank if the container sized after init.
setTimeout(() => map.invalidateSize(), 0)
})()
return () => {
cancelled = true
mapRef.current?.remove()
mapRef.current = null
}
}, [markers, zoom])
return
}