Files
e-tib.com/components/LeafletMap.tsx
Marc Mintel d14122005d Initial commit: E-TIB production hardening & E2E foundation
Former-commit-id: ef04fca3d76375630c05aac117bf586953f3b657
2026-04-28 19:11:38 +02:00

82 lines
2.4 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
// Fix for default marker icon in Leaflet with Next.js
if (typeof window !== 'undefined') {
const DefaultIcon = L.icon({
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
});
L.Marker.prototype.options.icon = DefaultIcon;
}
export interface MapLocation {
name: string;
address: string;
lat: number;
lng: number;
}
interface LeafletMapProps {
locations: MapLocation[];
}
export default function LeafletMap({ locations }: LeafletMapProps) {
const mapRef = useRef<HTMLDivElement>(null);
const mapInstanceRef = useRef<L.Map | null>(null);
useEffect(() => {
if (!mapRef.current || mapInstanceRef.current || !locations.length) return;
// Calculate center based on markers or first marker
const firstLoc = locations[0];
const map = L.map(mapRef.current, {
center: [firstLoc.lat, firstLoc.lng],
zoom: 6, // Zoom out to see both or more
scrollWheelZoom: false,
});
// Add tiles (Dark mode themed for industrial feel if possible, but standard OSM is safer for now)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);
const bounds = L.latLngBounds([]);
locations.forEach((loc) => {
const marker = L.marker([loc.lat, loc.lng]).addTo(map);
const popupContent = `
<div class="p-2">
<div class="text-primary font-bold border-b border-neutral-100 pb-1 mb-1">${loc.name}</div>
<div class="text-xs text-neutral-600">${loc.address.replace(/\n/g, '<br/>')}</div>
</div>
`;
marker.bindPopup(popupContent);
bounds.extend([loc.lat, loc.lng]);
});
// Fit bounds if more than 1 location
if (locations.length > 1) {
map.fitBounds(bounds, { padding: [50, 50] });
}
mapInstanceRef.current = map;
// Cleanup on unmount
return () => {
if (mapInstanceRef.current) {
mapInstanceRef.current.remove();
mapInstanceRef.current = null;
}
};
}, [locations]);
return <div ref={mapRef} className="h-full w-full z-0 rounded-2xl shadow-inner border border-neutral-200" />;
}