import { useEffect, useRef } from 'react' import mapboxgl from 'mapbox-gl' import 'mapbox-gl/dist/mapbox-gl.css' import { isStandardFamily, supportsCustom3d, addCustom3dBuildings, addTerrainAndSky } from '../Map/mapboxSetup' interface Props { token: string style: string lat: number lng: number zoom: number enable3d: boolean quality?: boolean onClick?: (latlng: { lat: number; lng: number }) => void } export default function MapboxPreview({ token, style, lat, lng, zoom, enable3d, quality = false, onClick }: Props) { const containerRef = useRef(null) const mapRef = useRef(null) const onClickRef = useRef(onClick) onClickRef.current = onClick useEffect(() => { if (!containerRef.current || !token) return mapboxgl.accessToken = token const map = new mapboxgl.Map({ container: containerRef.current, style, center: [lng, lat], zoom, pitch: enable3d ? 45 : 0, attributionControl: true, antialias: quality, projection: quality ? 'globe' : 'mercator', }) mapRef.current = map map.on('load', () => { if (enable3d) { if (!isStandardFamily(style)) addTerrainAndSky(map) if (supportsCustom3d(style)) { const dark = document.documentElement.classList.contains('dark') addCustom3dBuildings(map, dark) } } if (style === 'mapbox://styles/mapbox/standard') { try { map.setTerrain(null) } catch { /* noop */ } } }) map.on('click', (e) => { onClickRef.current?.({ lat: e.lngLat.lat, lng: e.lngLat.lng }) }) return () => { try { map.remove() } catch { /* noop */ } mapRef.current = null } }, [token, style, enable3d, quality]) // Recenter without rebuilding the map when lat/lng/zoom change externally useEffect(() => { if (!mapRef.current) return try { mapRef.current.jumpTo({ center: [lng, lat], zoom }) } catch { /* noop */ } }, [lat, lng, zoom]) if (!token) { return (
Enter a Mapbox access token to preview
) } return
}