-
-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: dynamic submissions legend chloropeth (#1250)
* fix projectSubmissions: dynamic mapLegend and taskChloropeth on increasing submissions count * feat projectSubmissionsMap: popup showing expected/submissions count add * fix homeSCSS: width fix * fix popupHeader: border fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- Loading branch information
1 parent
28f0ac0
commit 98fcf58
Showing
6 changed files
with
261 additions
and
17 deletions.
There are no files selected for viewing
181 changes: 181 additions & 0 deletions
181
src/frontend/src/components/MapComponent/OpenLayersComponent/AsyncPopup/AsyncPopup.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
import React, { useEffect, useRef, useState, useCallback } from 'react'; | ||
import { renderToString } from 'react-dom/server'; | ||
import Overlay from 'ol/Overlay'; | ||
import { getCenter } from 'ol/extent'; | ||
import './asyncpopup.scss'; | ||
|
||
type asyncPopupPropType = { | ||
map: any; | ||
fetchPopupData?: any; | ||
popupUI: any; | ||
openPopupFor?: () => void; | ||
zoomedExtent?: any; | ||
onPopupClose?: () => void; | ||
closePopup?: any; | ||
loading?: boolean; | ||
showOnHover?: string; | ||
}; | ||
|
||
function hasKey(obj, key) { | ||
return Object.keys(obj).some((item) => item === key); | ||
} | ||
|
||
const layerIds = ['code']; | ||
const popupId = 'popupx'; | ||
|
||
const AsyncPopup = ({ | ||
map, | ||
fetchPopupData, | ||
popupUI, | ||
openPopupFor, | ||
zoomedExtent, | ||
onPopupClose, | ||
closePopup = false, | ||
loading = false, | ||
showOnHover = 'click', | ||
}: asyncPopupPropType) => { | ||
const popupRef = useRef<any>(null); | ||
const popupCloserRef = useRef<any>(null); | ||
const [coordinates, setCoordinates] = useState<any>(null); | ||
const [overlay, setOverlay] = useState<any>(null); | ||
const [properties, setProperties] = useState(null); | ||
const [popupHTML, setPopupHTML] = useState<HTMLBodyElement | string>(''); | ||
|
||
// add overlay to popupRef | ||
useEffect(() => { | ||
if (!map || !popupRef.current) return; | ||
const overlayInstance = new Overlay({ | ||
element: popupRef.current, | ||
positioning: 'center-center', | ||
id: popupId, | ||
}); | ||
setOverlay(overlayInstance); | ||
}, [map, popupRef]); | ||
|
||
// function for closing popup | ||
const closePopupFn = useCallback(() => { | ||
if (!popupCloserRef.current || !overlay) return; | ||
overlay.setPosition(undefined); | ||
setPopupHTML(''); | ||
setProperties(null); | ||
if (popupCloserRef?.current instanceof HTMLElement) { | ||
popupCloserRef.current.blur(); | ||
} | ||
}, [overlay, popupCloserRef]); | ||
|
||
useEffect(() => { | ||
if (!map || !closePopup) return; | ||
closePopupFn(); | ||
}, [map, closePopup, closePopupFn]); | ||
|
||
useEffect(() => { | ||
/** | ||
* Alert if clicked on outside of element | ||
*/ | ||
function handleClickOutside(event) { | ||
if (popupRef.current && !popupRef?.current?.contains(event.target)) { | ||
// alert('You clicked outside of me!'); | ||
overlay.setPosition(undefined); | ||
setPopupHTML(''); | ||
setProperties(null); | ||
if (popupCloserRef?.current instanceof HTMLElement) { | ||
popupCloserRef?.current.blur(); | ||
} | ||
} | ||
} | ||
// Bind the event listener | ||
document.addEventListener('mousedown', handleClickOutside); | ||
return () => { | ||
// Unbind the event listener on clean up | ||
document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
}, [overlay]); | ||
// get properties and coordinates of feature | ||
useEffect(() => { | ||
if (!map) return; | ||
if (!overlay) return; | ||
|
||
map.on(showOnHover, (evt) => { | ||
map.updateSize(); | ||
overlay.setPosition(undefined); | ||
setPopupHTML(''); | ||
setProperties(null); | ||
if (popupCloserRef?.current instanceof HTMLElement) { | ||
popupCloserRef.current?.blur(); | ||
} | ||
const { coordinate } = evt; | ||
const features = map.getFeaturesAtPixel(evt.pixel); | ||
|
||
if (features.length < 1) { | ||
closePopupFn(); | ||
return; | ||
} | ||
const featureProperties = features[0].getProperties(); | ||
const { uid } = featureProperties; | ||
if (layerIds.includes(uid) || (hasKey(featureProperties, 'uid') && featureProperties?.uid)) { | ||
setProperties(featureProperties); | ||
setCoordinates(coordinate); | ||
} else { | ||
closePopupFn(); | ||
setProperties(null); | ||
setCoordinates(null); | ||
} | ||
}); | ||
}, [map, closePopupFn]); | ||
|
||
// fetch popup data when properties is set | ||
useEffect(() => { | ||
if (!map || !properties) return; | ||
const { layerId } = properties; | ||
if (layerIds.includes(layerId) || hasKey(properties, 'layer')) { | ||
fetchPopupData(properties); | ||
} | ||
// eslint-disable-next-line | ||
}, [map, properties]); | ||
|
||
useEffect(() => { | ||
if (!map || !coordinates || !overlay || !properties || closePopup) return; | ||
const htmlString = renderToString(popupUI(properties)); | ||
setPopupHTML(htmlString); | ||
|
||
overlay.setPosition([coordinates[0], coordinates[1]]); | ||
const popupOverlay = map.getOverlayById(popupId); | ||
if (!popupOverlay) { | ||
map.addOverlay(overlay); | ||
} | ||
}, [map, overlay, coordinates, popupUI, properties, closePopup]); | ||
|
||
// useEffect(() => { | ||
// if (!map || !openPopupFor || !zoomedExtent) return; | ||
// const center = getCenter(zoomedExtent); | ||
// setProperties({ id: openPopupFor, layer: 'site' }); | ||
// setCoordinates(center); | ||
// }, [map, openPopupFor, zoomedExtent]); | ||
|
||
return ( | ||
<div | ||
ref={popupRef} | ||
id="popup" | ||
className="ol-popup" | ||
style={{ zIndex: 100009 }} | ||
onBlur={(e) => { | ||
if (!e.currentTarget.contains(e.relatedTarget)) { | ||
// Not triggered when swapping focus between children | ||
closePopupFn(); | ||
} | ||
}} | ||
> | ||
<button | ||
ref={popupCloserRef} | ||
id="popup-closer" | ||
className="ol-popup-closer" | ||
type="button" | ||
onClick={closePopupFn} | ||
onKeyDown={closePopupFn} | ||
/> | ||
<div id="popup-content" dangerouslySetInnerHTML={{ __html: popupHTML }} /> | ||
</div> | ||
); | ||
}; | ||
|
||
export default AsyncPopup; |
38 changes: 38 additions & 0 deletions
38
src/frontend/src/components/MapComponent/OpenLayersComponent/AsyncPopup/asyncpopup.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
.ol-popup { | ||
position: absolute; | ||
background-color: white; | ||
border-radius: 10px; | ||
min-height: fit-content; | ||
margin-top: 12px; | ||
bottom: 12px; | ||
left: -50px; | ||
} | ||
.ol-popup:after, | ||
.ol-popup:before { | ||
top: 100%; | ||
border: solid transparent; | ||
content: ' '; | ||
height: 0; | ||
width: 0; | ||
position: absolute; | ||
pointer-events: none; | ||
} | ||
.ol-popup:after { | ||
border-top-color: white; | ||
border-width: 10px; | ||
left: 48px; | ||
margin-left: -10px; | ||
} | ||
.ol-popup:before { | ||
border-top-color: #cccccc; | ||
border-width: 11px; | ||
left: 48px; | ||
margin-left: -11px; | ||
} | ||
.ol-popup-closer { | ||
display: none; | ||
} | ||
|
||
#popup-content { | ||
height: fit-content; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters