diff --git a/react-app/src/App.tsx b/react-app/src/App.tsx
index 3b21b2e4e..121bd9ac8 100644
--- a/react-app/src/App.tsx
+++ b/react-app/src/App.tsx
@@ -49,7 +49,16 @@ const Router = () => {
const showMap = () => (
-
+
);
diff --git a/react-app/src/components/map/InventoryLayer.tsx b/react-app/src/components/map/InventoryLayer.tsx
index 6b0e21297..76466f5f2 100644
--- a/react-app/src/components/map/InventoryLayer.tsx
+++ b/react-app/src/components/map/InventoryLayer.tsx
@@ -194,6 +194,14 @@ export const InventoryLayer = (props: InventoryLayerProps) => {
}),
});
+ // Converts mouse event on clusters to a usable Point
+ const convertEventToPoint = (e: MouseEvent): Point => {
+ return {
+ x: e.clientX,
+ y: e.clientY,
+ } as Point;
+ };
+
return (
<>
{/* For all cluster objects */}
@@ -208,7 +216,7 @@ export const InventoryLayer = (props: InventoryLayerProps) => {
eventHandlers={{
click: () => zoomOnCluster(property),
mouseover: (e) => {
- openClusterPopup(property, e.containerPoint);
+ openClusterPopup(property, convertEventToPoint(e.originalEvent));
},
mouseout: cancelOpenPopup,
}}
@@ -232,7 +240,7 @@ export const InventoryLayer = (props: InventoryLayerProps) => {
},
);
},
- mouseover: (e) => openClusterPopup(property, e.containerPoint),
+ mouseover: (e) => openClusterPopup(property, convertEventToPoint(e.originalEvent)),
mouseout: cancelOpenPopup,
}}
/>
diff --git a/react-app/src/components/map/MapLayers.tsx b/react-app/src/components/map/MapLayers.tsx
index 3ba55db56..f1b0d7fda 100644
--- a/react-app/src/components/map/MapLayers.tsx
+++ b/react-app/src/components/map/MapLayers.tsx
@@ -97,12 +97,25 @@ interface MapLayersProps {
const MapLayers = (props: MapLayersProps) => {
const { hideControls } = props;
// If layer control is hidden, must still return a default tileset to use
+ // Also showing parcel boundaries
if (hideControls) {
+ const parcelBoundaryLayer = LAYER_CONFIGS.landOwnership.find(
+ (layer) => layer.name === 'Parcel Boundaries',
+ );
return (
-
+ <>
+
+
+ >
);
}
return (
diff --git a/react-app/src/components/map/ParcelMap.tsx b/react-app/src/components/map/ParcelMap.tsx
index 04cc668ba..9e97db563 100644
--- a/react-app/src/components/map/ParcelMap.tsx
+++ b/react-app/src/components/map/ParcelMap.tsx
@@ -34,6 +34,9 @@ type ParcelMapProps = {
hideControls?: boolean;
defaultZoom?: number;
defaultLocation?: LatLngExpression;
+ overrideProperties?: PropertyGeo[];
+ showClusterPopup?: boolean;
+ showSideBar?: boolean;
} & PropsWithChildren;
export const SelectedMarkerContext = createContext(null);
@@ -130,10 +133,13 @@ const ParcelMap = (props: ParcelMapProps) => {
loadProperties = false,
popupSize,
scrollOnClick,
- zoomOnScroll = true,
+ zoomOnScroll = false,
hideControls = false,
defaultLocation,
defaultZoom,
+ overrideProperties,
+ showClusterPopup = false,
+ showSideBar = false,
} = props;
// To access map outside of MapContainer
@@ -167,6 +173,13 @@ const ParcelMap = (props: ParcelMapProps) => {
}
}, [data, isLoading]);
+ // If override properties were supplied, set them.
+ useEffect(() => {
+ if (overrideProperties) {
+ setProperties(overrideProperties);
+ }
+ }, [overrideProperties]);
+
// Loops through any array and pairs it down to a flat list of its base elements
// Used here for breaking shape geography down to bounds coordinates
const extractLowestElements: (arr: any[]) => [number, number][] = (arr) => {
@@ -292,14 +305,16 @@ const ParcelMap = (props: ParcelMapProps) => {
],
),
{
- paddingBottomRight: [500, 0], // Padding for map sidebar
+ paddingBottomRight: showSideBar ? [500, 0] : [0, 0], // Padding for map sidebar
},
);
}
}, [properties]);
+ // Reference to containing div to help centre cluster popups
+ const mapBoxRef = useRef();
return (
-
+
{loadProperties ? : <>>}
{
mapEventsDisabled={mapEventsDisabled}
/>
- {loadProperties ? (
+ {loadProperties || overrideProperties ? (
{
))}
{props.children}
- {loadProperties ? (
- <>
-
-
- >
+ {loadProperties && showSideBar ? (
+
+ ) : (
+ <>>
+ )}
+ {(loadProperties || overrideProperties) && showClusterPopup ? (
+
) : (
<>>
)}
diff --git a/react-app/src/components/map/clusterPopup/ClusterPopup.tsx b/react-app/src/components/map/clusterPopup/ClusterPopup.tsx
index 9d646f3a6..89eb9f858 100644
--- a/react-app/src/components/map/clusterPopup/ClusterPopup.tsx
+++ b/react-app/src/components/map/clusterPopup/ClusterPopup.tsx
@@ -7,7 +7,7 @@ import { formatNumber, pidFormatter } from '@/utilities/formatters';
import { ArrowCircleLeft, ArrowCircleRight } from '@mui/icons-material';
import { Box, Grid, IconButton, Typography } from '@mui/material';
import { Point } from 'leaflet';
-import React, { useContext, useEffect } from 'react';
+import React, { useContext, useEffect, useRef } from 'react';
export interface PopupState {
open: boolean;
@@ -23,6 +23,7 @@ export interface PopupState {
interface ClusterPopupProps {
popupState: PopupState;
setPopupState: (stateUpdates: Partial) => void;
+ boundingBox?: DOMRect;
}
/**
@@ -34,46 +35,60 @@ interface ClusterPopupProps {
* @returns {JSX.Element} A React component representing the ClusterPopup.
*/
const ClusterPopup = (props: ClusterPopupProps) => {
- const { popupState, setPopupState } = props;
+ const { popupState, setPopupState, boundingBox } = props;
const { getLookupValueById } = useContext(LookupContext);
+ // Backups for when surrounding box isn't initialized
+ const within = boundingBox ?? {
+ x: 0,
+ y: 0,
+ height: 0,
+ width: 0,
+ right: 0,
+ left: 0,
+ top: 0,
+ bottom: 0,
+ };
+
/**
* The following block of code determines which direction and position the popup should open with.
- * Depending on the screen size, it determines the quadrant of the mouse event and choses a position and offset.
+ * Depending on the map size, it determines the quadrant of the mouse event and choses a position and offset.
*/
- const screenCentre = { x: window.innerWidth / 2 - 100, y: window.innerHeight / 2 }; // -100 to account for the side menu being open
+ const mapCentre = { x: within.width / 2 - 100, y: within.height / 2 }; // -100 to account for the side menu being open
+ const mousePositionOnMap = popupState.position;
+
let offset: { x: number; y: number } = { x: 0, y: 0 };
- // Depending on how many properties are available, y displacement changes. 1 = -60, 2 = -180, else -220
+ // Depending on how many properties are available, y displacement changes. 1 = -170, 2 = -260, else -300
const bottomYOffset =
- popupState.properties.length < 3 ? (popupState.properties.length === 2 ? -180 : -60) : -220;
+ popupState.properties.length < 3 ? (popupState.properties.length === 2 ? -260 : -170) : -300;
// Determine quadrant and set offset
- const leftXOffset = 5;
- const rightXOffset = -415;
- const topYOffset = 80;
+ const leftXOffset = 0;
+ const rightXOffset = -400;
+ const topYOffset = 0;
switch (true) {
// Top-left quadant
- case popupState.position.x <= screenCentre.x && popupState.position.y <= screenCentre.y:
+ case mousePositionOnMap.x <= mapCentre.x && mousePositionOnMap.y <= mapCentre.y:
offset = {
x: leftXOffset,
y: topYOffset,
};
break;
// Top-right quadrant
- case popupState.position.x > screenCentre.x && popupState.position.y <= screenCentre.y:
+ case mousePositionOnMap.x > mapCentre.x && mousePositionOnMap.y <= mapCentre.y:
offset = {
x: rightXOffset,
y: topYOffset,
};
break;
// Bottom-left quadrant
- case popupState.position.x <= screenCentre.x && popupState.position.y > screenCentre.y:
+ case mousePositionOnMap.x <= mapCentre.x && mousePositionOnMap.y > mapCentre.y:
offset = {
x: leftXOffset,
y: bottomYOffset,
};
break;
// Bottom-right quadrant
- case popupState.position.x > screenCentre.x && popupState.position.y > screenCentre.y:
+ case mousePositionOnMap.x > mapCentre.x && mousePositionOnMap.y > mapCentre.y:
offset = {
x: rightXOffset,
y: bottomYOffset,
@@ -95,16 +110,18 @@ const ClusterPopup = (props: ClusterPopupProps) => {
}
}, [popupState.pageIndex]);
+ const popupRef = useRef();
return (