File size: 2,189 Bytes
2cc62ca ffc05fe 2cc62ca ffc05fe 2cc62ca ffc05fe 2cc62ca ffc05fe 2cc62ca ffc05fe 2cc62ca ffc05fe 2cc62ca ffc05fe 2cc62ca ffc05fe 2cc62ca ffc05fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /**
* src/components/RangeMap.jsx
* Renders a Leaflet map with a range circle centered on the species' known habitat.
*/
import React, { useEffect, useState } from 'react'
import { motion } from 'framer-motion'
import styles from './RangeMap.module.css'
export default function RangeMap({ range, commonName, color }) {
const [ready, setReady] = useState(false)
const [MapContainer, setMapContainer] = useState(null)
const [TileLayer, setTileLayer] = useState(null)
const [Circle, setCircle] = useState(null)
const [Popup, setPopup] = useState(null)
useEffect(() => {
import('react-leaflet').then(rl => {
setMapContainer(() => rl.MapContainer)
setTileLayer(() => rl.TileLayer)
setCircle(() => rl.Circle)
setPopup(() => rl.Popup)
setReady(true)
})
}, [])
if (!range) return null
return (
<motion.div
className={styles.wrapper}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
>
<p className={styles.label}>Range Map</p>
<div className={styles.mapContainer}>
{ready && MapContainer ? (
<MapContainer
center={range.center}
zoom={3}
scrollWheelZoom={false}
style={{ height: '100%', width: '100%' }}
zoomControl={false}
>
<TileLayer
attribution=""
url="https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png"
/>
<Circle
center={range.center}
radius={800000}
pathOptions={{
color: color ?? '#9a749a',
fillColor: color ?? '#9a749a',
fillOpacity: 0.18,
weight: 1.5,
}}
>
<Popup>{commonName} — approximate range</Popup>
</Circle>
</MapContainer>
) : (
<div className={styles.mapPlaceholder}>Loading map…</div>
)}
</div>
<p className={styles.rangeDesc}>{range.description}</p>
</motion.div>
)
}
|