import { prop } from 'ramda'; import type { FC } from 'react'; import type { MapContainerProps } from 'react-leaflet'; import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet'; import { Modal, ModalBody } from 'reactstrap'; import type { CityStats } from '../types'; import './MapModal.scss'; interface MapModalProps { toggle: () => void; isOpen: boolean; title: string; locations?: CityStats[]; } const OpenStreetMapTile: FC = () => ( <TileLayer attribution='&copy <a href="https://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> ); const calculateMapProps = (locations: CityStats[]): MapContainerProps => { if (locations.length === 0) { return {}; } if (locations.length > 1) { return { bounds: locations.map(prop('latLong')) }; } // When there's only one location, an error is thrown if trying to calculate the bounds. // When that happens, we use "zoom" and "center" as a workaround const [{ latLong: center }] = locations; return { zoom: 10, center }; }; export const MapModal = ({ toggle, isOpen, title, locations = [] }: MapModalProps) => ( <Modal toggle={toggle} isOpen={isOpen} className="map-modal__modal" contentClassName="map-modal__modal-content"> <ModalBody className="map-modal__modal-body"> <h3 className="map-modal__modal-title"> {title} <button type="button" className="btn-close float-end" aria-label="Close" onClick={toggle} /> </h3> <MapContainer {...calculateMapProps(locations)}> <OpenStreetMapTile /> {locations.map(({ cityName, latLong, count }, index) => ( <Marker key={index} position={latLong}> <Popup><b>{count}</b> visit{count > 1 ? 's' : ''} from <b>{cityName}</b></Popup> </Marker> ))} </MapContainer> </ModalBody> </Modal> );