I am trying to create a map using the 'react-zoom-pan-pinch' library in which there are pin icons superimposed over the image. I want the pin icons to remain in the same position so they are affiliated with a certain state as you zoom in. Currently, when you zoom in, the pin icons move around. The pin icons are superimposed using position:absolute over the map. I want the pin icons to stay in place as you zoom in using the + button. Map with pin icons

I am trying multiple approaches, using dynamic pin rendering or responsive breakpoints.

// SearchPageHeroResponsive.js
import React, { useRef, useState } from "react";
import "./SearchPageHeroResponsive.scss";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";

export default function SearchPageHeroResponsive() {
  const transformRef = useRef(null);
  const [zoomCount, setZoomCount] = useState(0);

  const handleZoomIn = () => {
    if (transformRef.current && zoomCount < 3) {
      transformRef.current.zoomIn(); // Zoom in once
      setZoomCount(prevCount => prevCount + 1);
    }
  };

  const handleZoomOut = () => {
    if (transformRef.current) {
      transformRef.current.zoomOut(); // Zoom out once
      setZoomCount(prevCount => Math.max(prevCount - 1, 0));
    }
  };

  const handleReset = () => {
    if (transformRef.current) {
      transformRef.current.resetTransform();
      setZoomCount(0);
    }
  };

  return (
    <div className="searchpage-hero-responsive">
      <div className="searchpage-hero-responsive__container">
        <p className="searchpage-hero-responsive__jobs--industry">
          Viewing 6 of 150 Open Jobs in Your Industry
        </p>
        <p className="searchpage-hero-responsive__openings">
          Showing openings listed by all agencies in the searched area.
        </p>
      </div>
      <TransformWrapper options={{ limitToBounds: true }} ref={transformRef}>
        {({ resetTransform }) => (
          <>
            <button
              className="searchpage-hero-responsive__button--reset"
              onClick={handleReset}
            >
              Reset
            </button>
            <div className="searchpage-hero-responsive__map">
              <TransformComponent>
                <img
                  src="https://www.alphr.com/wp-content/uploads/2023/06/How-to-Explore-Outdoor-Areas-and-Green-Spaces.png"
                  alt="test"
                  width="100%"
                />
              </TransformComponent>
            </div>
            <button
              className="searchpage-hero-responsive__button--zoom-in"
              onClick={handleZoomIn}
              disabled={zoomCount >= 3}
            >
              +
            </button>
            <button
              className="searchpage-hero-responsive__button--zoom-out"
              onClick={handleZoomOut}
            >
              -
            </button>
          </>
        )}
      </TransformWrapper>
    </div>
  );
}

// MapWithPinIconsResponsive.js
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import "./MapWithPinIconsResponsive.scss";
import ActionIcon from "../../assets/icons/action (1).svg";
import DotIcon from "../../assets/icons/tabler_point-filled.svg";
import BlueStar from "../../assets/icons/star (1).svg";
import LocationIcon from "../../assets/icons/location.svg";

export default function MapWithPinIconsResponsive() {
  const [data, setData] = useState({});
  const [isLoading, setIsLoading] = useState(false);
  const [selectedState, setSelectedState] = useState("Florida"); // State to track selected state
  const [popupText, setPopupText] = useState(""); // Text for the popup
  const [popupPosition, setPopupPosition] = useState({ top: 0, left: 0 }); // Position of the popup
  const [defaultCardsVisible, setDefaultCardsVisible] = useState(false); // State to track visibility of default cards
  const [rerenderTrigger, setRerenderTrigger] = useState(false); // State variable to trigger rerendering

  const baseApiUrl = "http://localhost:5000";

  useEffect(() => {
    const fetchData = async () => {
      try {
        setIsLoading(true);
        const response = await axios.get(`${baseApiUrl}/api/jobsPerState`);
        setData(response.data);
      } catch (error) {
        console.error("Error fetching data: ", error);
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();
  }, []); // Empty dependency array to fetch data only once when the component mounts

  // Inside the handleStateIconClick function
  const handleStateIconClick = (state, event) => {
    setSelectedState(state); // Set selectedState to the clicked state
    const cityJobsCount = 20; // Simulated job count
    setPopupText(`${state}, ${cityJobsCount} jobs`); // Set popup text
    
    // Calculate popup position relative to the clicked pin icon
    const iconRect = event.target.getBoundingClientRect();
    const popupTop = iconRect.top + window.scrollY + iconRect.height + 5; // Adjust for padding
    const popupLeft = iconRect.left + window.scrollX;
  
    // Set popup position
    setPopupPosition({
      top: popupTop,
      left: popupLeft,
    });
  
    // Hide default cards after an icon is clicked
    setDefaultCardsVisible(false);
  };

  const handleRerenderButtonClick = () => {
    // Toggle the state variable to trigger rerendering
    setRerenderTrigger((prev) => !prev);
  };

  const imageContainerRef = useRef(null);

  return (
    <div className="map-with-pin-icons-responsive">
      <div className="state-buttons-pin-icons-group">
        {Object.keys(data).map((state) => (
          <div
            key={state}
            className={`state-button-pin-icons-container state-button-pin-icons-${state.toLowerCase()}`}
          >
            {Object.entries(data[state])
              .slice(0, 1)
              .map(([jobId, jobDetails], index) => (
                <div
                  key={jobId}
                  className={`state-button-pin-icons-state-button-${state.toLowerCase()}-${
                    index + 1
                  }`}
                  onClick={(event) => handleStateIconClick(state, event)}
                >
                  <img
                    className={`state-button-pin-icons state-button-pin-icons-${state.toLowerCase()}-${
                      index + 1
                    }`}
                    src={LocationIcon}
                    alt={`Location ${index + 1}`}
                  />
                </div>
              ))}
          </div>
        ))}
      </div>

      {isLoading && <p>Loading...</p>}
      <div className="map-with-pin-icons-responsive__card-container">
        {/* Render default cards only if they are visible */}
        {defaultCardsVisible && (
          <>
            {Array.from({ length: 5 }).map((_, index) => (
              <div key={index} className="map-with-pin-icons-responsive__card">
                <h3>United States</h3>
                {/* Render other card content here */}
              </div>
            ))}
          </>
        )}
        {/* Render selected state cards */}
        {selectedState &&
          data[selectedState] &&
          !defaultCardsVisible &&
          Object.entries(data[selectedState])
            .slice(0, 5)
            .map(([jobId, jobDetails]) => (
              <div key={jobId} className="map-with-pin-icons-responsive__card">
                <img
                  className="map-with-pin-icons-responsive__action-icon"
                  src={ActionIcon}
                  alt="Action"
                />
                <h3 className="map-with-pin-icons-responsive__selected-state">{selectedState}</h3>
                <div className="job-details">
                  <button className="map-with-pin-icons-responsive__button">
                    {jobDetails.buttonText}
                  </button>
                  <div className="map-with-pin-icons-responsive__container-1">
                    <p className="map-with-pin-icons-responsive__staffing-agency">
                      {jobDetails["staffing agency"]}
                    </p>
                    <p className="map-with-pin-icons-responsive__rating">
                      {jobDetails.rating}
                      <img
                        className="map-with-pin-icons-responsive__blue-star"
                        src={BlueStar}
                        alt="Blue Star"
                      />
                    </p>
                  </div>
                  <div className="map-with-pin-icons-responsive__container-2">
                    <p className="map-with-pin-icons-responsive__role">
                      {jobDetails.role}
                    </p>
                    <img
                      className="map-with-pin-icons-responsive__dot-icon"
                      src={DotIcon}
                      alt="Dot"
                    />
                    <p className="map-with-pin-icons-responsive__nature-of-role">
                      {jobDetails["nature of role"]}
                    </p>
                  </div>
                  <p className="map-with-pin-icons-responsive__description">
                    {jobDetails.description}
                  </p>
                  <p className="map-with-pin-icons-responsive__salary">
                    {jobDetails.salary}
                  </p>
                  <div className="map-with-pin-icons-responsive__container-3">
                    <p className="map-with-pin-icons-responsive__location">
                      <img src={LocationIcon} alt="Location" />
                      {jobDetails.location}
                    </p>
                    <img
                      className="map-with-pin-icons-responsive__dot-icon--2"
                      src={DotIcon}
                      alt="Dot"
                    />
                    <p className="map-with-pin-icons-responsive__split-fee">
                      {jobDetails["split fee"]}
                    </p>
                  </div>
                </div>
              </div>
            ))}
      </div>
      {/* Popup */}
      {popupText && (
        <div
          className="popup"
          style={{ top: popupPosition.top, left: popupPosition.left }}
        >
          <p>{popupText}</p>
        </div>
      )}
    </div>
  );
}

import React from "react";
import SearchBar from "../../components/SearchBar/SearchBar";
import Header from "../../components/Header/Header";
import DashboardSelect from "../../components/DashboardSelect/DashboardSelect";
import Map from "../../components/Map/Map";
import SearchPageHeroResponsive from "../../components/SearchPageHeroResponsive/SearchPageHeroResponsive";
import MapWithPinIconsResponsive from "../../components/MapWithPinIconsResponsive/MapWithPinIconsResponsive";

export default function SearchPage() {
  return (
    <div className="searchpage">
      <Header />
      <div className="searchpage__blue--container">
        <DashboardSelect />
        <SearchBar />
        <SearchPageHeroResponsive />

        <MapWithPinIconsResponsive />
      </div>
    </div>
  );
}

0

There are 0 best solutions below