Skip to content
Snippets Groups Projects
Map.tsx 3.36 KiB
Newer Older
Moritz Stückler's avatar
Moritz Stückler committed
import type React from 'react';
import { useEffect, useState } from 'react';
Moritz Stückler's avatar
Moritz Stückler committed
import { useStore, useFilteredPoiData } from '../../hooks';
import ReactMapGl, { Marker, useMap, AttributionControl } from 'react-map-gl';
Moritz Stückler's avatar
Moritz Stückler committed
import { useHistory } from 'react-router-dom';
Moritz Stückler's avatar
Moritz Stückler committed
import MapLayerControl from './MapLayerControl';
import { calcBoundsFromCoordinates } from '../../util/geo';
Moritz Stückler's avatar
Moritz Stückler committed
import 'mapbox-gl/dist/mapbox-gl.css';
Moritz Stückler's avatar
Moritz Stückler committed

interface Props {
Moritz Stückler's avatar
Moritz Stückler committed
  mapboxToken: string;
Moritz Stückler's avatar
Moritz Stückler committed
interface MarkerProps {
  className: string;
}
Moritz Stückler's avatar
Moritz Stückler committed
const MarkerSvg: React.FC<MarkerProps> = ({ className }) => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" className={className}>
      <path
        fill="currentColor"
        d="M172.268 501.67C26.97 291.031 0 269.413 0 192 0 85.961 85.961 0 192 0s192 85.961 192 192c0 77.413-26.97 99.031-172.268 309.67-9.535 13.774-29.93 13.773-39.464 0zM192 272c44.183 0 80-35.817 80-80s-35.817-80-80-80-80 35.817-80 80 35.817 80 80 80z"
      />
    </svg>
Moritz Stückler's avatar
Moritz Stückler committed
  );
const Map: React.FC<Props> = ({ mapboxToken }) => {
Moritz Stückler's avatar
Moritz Stückler committed
  const history = useHistory();
Moritz Stückler's avatar
Moritz Stückler committed
  const data = useStore((state) => state.poiData);
  const selectedPoi = useStore((state) => state.selectedPoi);
  const hoveredPoi = useStore((state) => state.hoveredPoi);
  const { data: filteredData } = useFilteredPoiData();
  const DEFAULT_CENTER: [number, number] = [9.986701, 53.550359];
  const { fcmap } = useMap();
  const [bounds, setBounds] = useState<[[number, number], [number, number]]>();

  useEffect(() => {
    const newBounds = calcBoundsFromCoordinates(data?.map((poi) => [poi.lng, poi.lat]) || [DEFAULT_CENTER]);
    setBounds(newBounds);
  }, [JSON.stringify(data)]);
Moritz Stückler's avatar
Moritz Stückler committed
    if (selectedPoi) fcmap?.easeTo({ center: [selectedPoi.lng, selectedPoi.lat], zoom: 14, duration: 1500 });
    else {
      bounds && fcmap?.fitBounds(bounds, { padding: 50, maxZoom: 16 });
    }
Moritz Stückler's avatar
Moritz Stückler committed
  }, [selectedPoi]);
Moritz Stückler's avatar
Moritz Stückler committed
  return (
    <ReactMapGl
Moritz Stückler's avatar
Moritz Stückler committed
      initialViewState={{
        latitude: DEFAULT_CENTER[1],
        longitude: DEFAULT_CENTER[0],
        zoom: 10,
      }}
      style={{ width: '100%', height: '100%' }}
      mapStyle="mapbox://styles/mapbox/streets-v11"
      mapboxAccessToken={mapboxToken}
      maxPitch={0}
      id="fcmap"
      attributionControl={false}
    >
      <AttributionControl position="top-right" />
Moritz Stückler's avatar
Moritz Stückler committed
      <MapLayerControl />
      {selectedPoi ? (
        <Marker key={selectedPoi.id} longitude={selectedPoi.lng} latitude={selectedPoi.lat} anchor="bottom">
          <MarkerSvg className="fcmap-w-8 fcmap-h-8 fcmap-opacity-100 fcmap-scale-125 fcmap-text-fabcity-red" />
Moritz Stückler's avatar
Moritz Stückler committed
        </Marker>
      ) : (
        (filteredData || data)?.map((poi) => {
          const isHovered = hoveredPoi?.id === poi.id;
          return (
            <Marker
              key={poi.id}
              longitude={poi.lng}
              latitude={poi.lat}
              anchor="bottom"
              onClick={() => {
                history.push(`/poi/${String(poi.id)}`);
              }}
            >
              <MarkerSvg
                className={`fcmap-transition fcmap-ease-in-out fcmap-w-8 fcmap-h-8 hover:fcmap-scale-125 fcmap-opacity-70 hover:fcmap-opacity-100 hover:fcmap-cursor-pointer fcmap-text-fabcity-red ${
                  isHovered ? 'fcmap-scale-125 fcmap-opacity-100' : ''
Moritz Stückler's avatar
Moritz Stückler committed
                }`}
Fabian Schmidt's avatar
Fabian Schmidt committed
              />
Moritz Stückler's avatar
Moritz Stückler committed
            </Marker>
          );
        })
      )}
    </ReactMapGl>
export default Map;