src/lib/components/molecules/canvas-map/Map.jsx (111 lines of code) (raw):
import { useState, useEffect, useRef } from "preact/hooks"
import { Map } from "./lib/Map"
import { MapProvider } from "./context/MapContext"
// @ts-ignore
import styles from "./style.module.scss"
const mobileHelpText = "Use two fingers to zoom"
/**
* @typedef {{
* config: Object,
* inModalState?: boolean,
* onLoad?: (map: Map) => void,
* children: import('preact').ComponentChildren,
* mapRef?: import('preact').Ref<Map>,
* allowZoomPan?: boolean,
* }} MapComponentProps
*/
const Component = (
/** @type {MapComponentProps} */ {
config,
inModalState = false,
onLoad,
children,
mapRef,
allowZoomPan = true,
},
) => {
const targetRef = useRef()
const [map, setMap] = useState(/** @type {Map | null} */ (null))
const [zoomHelpText, setZoomHelpText] = useState("")
const [showHelpText, setShowHelpText] = useState(false)
useEffect(() => {
if (!targetRef.current) return
config.allowZoomPan = allowZoomPan
const map = new Map({
...config,
target: targetRef.current,
})
if (allowZoomPan) map.collaborativeGesturesEnabled(true)
setMap(map)
if (mapRef) {
// @ts-ignore
mapRef.current = map
}
if (onLoad) {
onLoad(map)
}
let zoomHelpText = ""
if (
// @ts-ignore
navigator.userAgentData?.mobile ||
navigator.userAgent.indexOf("Mobile") !== -1
) {
zoomHelpText = mobileHelpText
} else {
zoomHelpText =
navigator.userAgent.indexOf("Mac") !== -1
? "Use ⌘ + scroll to zoom"
: "Use Ctrl + scroll to zoom"
}
setZoomHelpText(zoomHelpText)
return () => {
map.destroy()
setMap(null)
if (mapRef) {
// @ts-ignore
mapRef.current = null
}
}
}, [config, onLoad, mapRef, allowZoomPan])
useEffect(() => {
if (!map || !allowZoomPan) return
let timeoutID
map.onFilterEvent((showHelpText) => {
if (timeoutID) clearTimeout(timeoutID)
setShowHelpText(showHelpText)
if (showHelpText) {
timeoutID = setTimeout(() => {
setShowHelpText(false)
}, 1000)
}
})
return () => {
if (timeoutID) clearTimeout(timeoutID)
}
}, [map, allowZoomPan])
useEffect(() => {
if (!map || !allowZoomPan) return
map.collaborativeGesturesEnabled(!inModalState)
}, [map, inModalState, allowZoomPan])
return (
<figure ref={targetRef} className={styles.mapContainer}>
{allowZoomPan && (
<div
className={styles.helpTextContainer}
style={{ opacity: showHelpText ? 1 : 0 }}
aria-hidden
>
<p className={[styles.helpText, styles.desktopHelpText].join(" ")}>
{zoomHelpText}
</p>
<p className={[styles.helpText, styles.mobileHelpText].join(" ")}>
{mobileHelpText}
</p>
</div>
)}
<MapProvider map={map}>{children}</MapProvider>
</figure>
)
}
// NOTE: we declare Component separately so the component has a "display name"
Map.Component = Component