"use client"; import { useEffect, useRef } from "react"; import L from "leaflet"; import "leaflet/dist/leaflet.css"; interface Props { sites: any[]; clusters: any[]; onSiteClick: (site: any) => void; } export default function MapComponent({ sites, clusters, onSiteClick }: Props) { const containerRef = useRef(null); const mapRef = useRef(null); const onSiteClickRef = useRef(onSiteClick); onSiteClickRef.current = onSiteClick; useEffect(() => { if (!containerRef.current) return; // Destroy any pre-existing Leaflet instance on this element if ((containerRef.current as any)._leaflet_id) { (containerRef.current as any)._leaflet_id = null; } if (mapRef.current) { mapRef.current.remove(); mapRef.current = null; } const map = L.map(containerRef.current, { center: [39.5, -98.35], zoom: 4 }); mapRef.current = map; L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: '© OpenStreetMap', }).addTo(map); const siteIcon = L.divIcon({ className: "", html: `
`, iconSize: [28, 28], iconAnchor: [14, 28], popupAnchor: [0, -30], }); const validSites = sites.filter((s) => s.lat && s.lon); const validClusters = clusters.filter((c) => c.lat && c.lon); validClusters.forEach((cluster) => { L.circle([cluster.lat, cluster.lon], { radius: cluster.count * 800, color: "#6366f1", fillColor: "#818cf8", fillOpacity: 0.25, weight: 1, }) .bindPopup(`
${cluster.city}
${cluster.count} potential patients
`) .addTo(map); }); validSites.forEach((site) => { L.marker([site.lat, site.lon], { icon: siteIcon }) .bindPopup( `
${site.name}
` + `
${site.city}, ${site.state}
` + `
${site.trials} active trials ยท ${site.enrolled}/${site.capacity} enrolled
` ) .on("click", () => onSiteClickRef.current(site)) .addTo(map); }); if (validSites.length > 0) { const bounds = L.latLngBounds(validSites.map((s) => [s.lat, s.lon])); map.fitBounds(bounds, { padding: [40, 40] }); } return () => { map.remove(); mapRef.current = null; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return
; }