Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recenter if map marker is obscured by other elements #264

Merged
merged 8 commits into from
Nov 14, 2024
68 changes: 54 additions & 14 deletions src/components/MapboxMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,37 @@ type MapboxMapProps = {
schools: School[];
};

const isVisible = (marker: mapboxgl.Marker, map: mapboxgl.Map) => {
// check if marker within bounds of map
const lngLat = marker.getLngLat();
const bounds = map.getBounds();
const isInsideMap = bounds.contains(lngLat);

// check if marker is obscured by other elements
const markerEl = marker.getElement();
const { top, right, bottom, left } = markerEl.getBoundingClientRect();
// find center of marker
const [cX, cY] = [
left + Math.round(right - left) / 2,
top + Math.round(bottom - top) / 2,
];

// assume points on top, bottom, right, left edges are within the bounding rect and test what the topmost element is at each point
const topEls = [
[cX, top + 1],
[right - 1, cY],
[cX, bottom - 1],
[left + 1, cY],
].map(([x, y]) => document.elementFromPoint(x, y));

// determine if all chosen marker points are visible
const isOnTop = topEls.reduce((acc, topEl) => {
return acc && markerEl.isSameNode(topEl);
}, true);

return isInsideMap && isOnTop;
};

const MapboxMap = ({ schools }: MapboxMapProps) => {
const { selectedSchool, setSelectedSchool } = useMapContext();
const mapContainer = useRef<HTMLDivElement | null>(null);
Expand All @@ -31,6 +62,7 @@ const MapboxMap = ({ schools }: MapboxMapProps) => {
isSelected: boolean,
) => {
const element = marker.getElement();

if (isSelected) {
element.className =
"marker-selected mapboxgl-marker mapboxgl-marker-anchor-center";
Expand Down Expand Up @@ -155,13 +187,11 @@ const MapboxMap = ({ schools }: MapboxMapProps) => {
if (!schoolMarker.getPopup().isOpen()) {
schoolMarker.togglePopup();
}

// if we are outside of the bounds, recenter/rezoom (intended for keyboard navigation)
const lngLat = schoolMarker.getLngLat();
const bounds = map.getBounds();

if (!bounds.contains(lngLat)) {
// pan to marker
// if we have focused on a school that's not visible, recenter (e.g., via filter or keyboard navigation)
if (!isVisible(schoolMarker, map)) {
// pan to school marker
map.flyTo({
center: [lngLat.lng, lngLat.lat],
...flyToOptions,
Expand Down Expand Up @@ -196,7 +226,7 @@ const MapboxMap = ({ schools }: MapboxMapProps) => {
mapRef.current = null;
}
};
}, [schools, setSelectedSchool]);
}, [schools, setSelectedSchool, flyToOptions]);

// Update marker appearance when selectedSchool changes and map is loaded
useEffect(() => {
Expand All @@ -213,16 +243,26 @@ const MapboxMap = ({ schools }: MapboxMapProps) => {

if (!userHasInteracted.current) {
// Use jumpTo when returning from detail page. it's less dizzying.
mapRef.current.jumpTo({
center: [lngLat.lng, lngLat.lat],
});
userHasInteracted.current = true;

// if we have focused on a school that's not visible, recenter (e.g., via filter or keyboard navigation)
if (!isVisible(selectedMarker, mapRef.current)) {
// jump to marker
mapRef.current.jumpTo({
center: [lngLat.lng, lngLat.lat],
});
userHasInteracted.current = true;
}
} else {
// Use flyTo for all other cases
mapRef.current.flyTo({
center: [lngLat.lng, lngLat.lat],
...flyToOptions,
});

// if we have focused on a school that's not visible, recenter (e.g., via filter or keyboard navigation)
if (!isVisible(selectedMarker, mapRef.current)) {
// pan to marker
mapRef.current.flyTo({
center: [lngLat.lng, lngLat.lat],
...flyToOptions,
});
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ h6 * {
border-radius: 0;
cursor: pointer;
outline: none;
z-index: 5;
}

.golden-gate-marker {
Expand Down