import { useEffect, useRef } from 'react'; import L from 'leaflet'; export interface MapMarker { id?: string; lat: number; lng: number; label?: string; type?: 'pickup' | 'destination' | 'driver' | 'default'; } export interface MapViewProps { center: [number, number]; zoom?: number; markers?: MapMarker[]; polyline?: [number, number][]; className?: string; onMapClick?: (lat: number, lng: number) => void; } const createCustomIcon = (type: MapMarker['type'] = 'default') => { let bgColor = '#0f172a'; let ringColor = 'rgba(15, 23, 42, 0.2)'; let symbol = '•'; if (type === 'pickup') { bgColor = '#15803d'; ringColor = 'rgba(21, 128, 61, 0.3)'; symbol = 'A'; } else if (type === 'destination') { bgColor = '#dc2626'; ringColor = 'rgba(220, 38, 38, 0.3)'; symbol = 'B'; } else if (type === 'driver') { bgColor = '#0284c7'; ringColor = 'rgba(2, 132, 199, 0.35)'; symbol = '🚕'; } const html = `
${symbol}
`; return L.divIcon({ html, className: 'custom-leaflet-marker-icon', iconSize: [32, 32], iconAnchor: [16, 16], popupAnchor: [0, -16], }); }; export default function MapView({ center, zoom = 13, markers = [], polyline = [], className, onMapClick, }: MapViewProps) { const containerRef = useRef(null); const mapRef = useRef(null); const markerLayerRef = useRef(null); const polylineLayerRef = useRef(null); useEffect(() => { if (!containerRef.current || mapRef.current) return; const map = L.map(containerRef.current, { zoomControl: true, attributionControl: false, }).setView(center, zoom); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap contributors', }).addTo(map); markerLayerRef.current = L.layerGroup().addTo(map); mapRef.current = map; if (onMapClick) { map.on('click', (e: L.LeafletMouseEvent) => { onMapClick(e.latlng.lat, e.latlng.lng); }); } const timer = setTimeout(() => { map.invalidateSize(); }, 150); return () => { clearTimeout(timer); map.remove(); mapRef.current = null; }; }, []); useEffect(() => { if (mapRef.current) { mapRef.current.setView(center, zoom); } }, [center[0], center[1], zoom]); useEffect(() => { const layer = markerLayerRef.current; if (!layer) return; layer.clearLayers(); markers.forEach((m) => { const icon = createCustomIcon(m.type); const marker = L.marker([m.lat, m.lng], { icon }); if (m.label) { marker.bindPopup( `
${m.label}
` ); } layer.addLayer(marker); }); }, [markers]); useEffect(() => { if (!mapRef.current) return; if (polylineLayerRef.current) { polylineLayerRef.current.remove(); polylineLayerRef.current = null; } if (polyline && polyline.length > 0) { polylineLayerRef.current = L.polyline(polyline, { color: '#0284c7', weight: 5, opacity: 0.85, lineCap: 'round', lineJoin: 'round', }).addTo(mapRef.current); } }, [polyline]); return (
); }