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.

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>
);
}