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 (
);
}