import { useEffect, useRef } from 'react';
import { MapContainer, TileLayer, Marker, Popup, CircleMarker, useMap } from 'react-leaflet';
import L, { type LatLngTuple } from 'leaflet';
import 'leaflet/dist/leaflet.css';
const DEFAULT_ZOOM = 11;
const MIN_ZOOM = 8;
export interface NearbyMapPin {
id: string;
name: string;
cityLabel: string | null;
latitude: number;
longitude: number;
}
interface NearbyBreweryMapProps {
pins: NearbyMapPin[];
selectedId: string | null;
onSelect: (id: string) => void;
userLocation: LatLngTuple | null;
userLocationLabel: string;
}
// Leaflet paints markers as SVG attributes, which can't reference CSS custom
// properties the way class names can - resolve the current theme's colors instead.
const resolveThemeColor = (variable: string, fallback: string) => {
if (typeof document === 'undefined') return fallback;
const value = getComputedStyle(document.documentElement).getPropertyValue(variable).trim();
return value || fallback;
};
const pinIcon = (active: boolean) =>
L.divIcon({
className: '',
html: ``,
iconSize: [18, 18],
iconAnchor: [9, 9],
});
const FitAndFocus = ({
pins,
userLocation,
selectedId,
}: {
pins: NearbyMapPin[];
userLocation: LatLngTuple | null;
selectedId: string | null;
}) => {
const map = useMap();
const didInitialFit = useRef(false);
useEffect(() => {
if (didInitialFit.current) return;
const focus: LatLngTuple | null =
userLocation ?? (pins[0] ? [pins[0].latitude, pins[0].longitude] : null);
if (focus) {
map.setView(focus, DEFAULT_ZOOM, { animate: false });
didInitialFit.current = true;
}
const timeout = setTimeout(() => map.invalidateSize(), 200);
return () => clearTimeout(timeout);
}, [map, pins, userLocation]);
useEffect(() => {
if (!didInitialFit.current || !selectedId) return;
const pin = pins.find((p) => p.id === selectedId);
if (!pin) return;
map.flyTo([pin.latitude, pin.longitude], Math.max(map.getZoom(), 11), { duration: 0.8 });
}, [map, pins, selectedId]);
return null;
};
const RecenterButton = ({ userLocation }: { userLocation: LatLngTuple | null }) => {
const map = useMap();
if (!userLocation) return null;
return (
);
};
const NearbyBreweryMap = ({
pins,
selectedId,
onSelect,
userLocation,
userLocationLabel,
}: NearbyBreweryMapProps) => (
{userLocation && (
You are here — {userLocationLabel}
)}
{pins.map((pin) => (
onSelect(pin.id) }}
>
{pin.name}
{pin.cityLabel && (
{pin.cityLabel}
)}
View brewery →
))}
);
export default NearbyBreweryMap;