UNPKG

react-bkoi-gl

Version:

React components for Barikoi GL JS

114 lines (100 loc) 3.38 kB
/* eslint-disable @typescript-eslint/no-floating-promises */ /* global document */ import * as React from "react"; import { createPortal } from "react-dom"; import { useImperativeHandle, useEffect, useMemo, useRef, useContext, forwardRef, memo, } from "react"; import { applyReactStyle } from "../utils/apply-react-style"; import type { Popup as PopupInstance, PopupOptions } from "../types/lib"; import type { PopupEvent } from "../types/events"; import { MapContext } from "./map"; import { deepEqual } from "../utils/deep-equal"; import { compareClassNames } from "../utils/compare-class-names"; export type PopupProps = PopupOptions & { /** Longitude of the anchor location */ longitude: number; /** Latitude of the anchor location */ latitude: number; /** CSS style override, applied to the control's container */ style?: React.CSSProperties; onOpen?: (e: PopupEvent) => void; onClose?: (e: PopupEvent) => void; children?: React.ReactNode; }; /* eslint-disable complexity,max-statements */ export const Popup: React.FC<PopupProps> = memo( forwardRef((props: PopupProps, ref: React.Ref<PopupInstance>) => { const { map, mapLib } = useContext(MapContext); const container = useMemo(() => { return document.createElement("div"); }, []); const thisRef = useRef({ props }); const popup: PopupInstance = useMemo(() => { const options = { ...props }; const pp = new mapLib.Popup(options); pp.setLngLat([props.longitude, props.latitude]); pp.once("open", (e) => { thisRef.current.props.onOpen?.(e as PopupEvent); }); return pp; }, []); useEffect(() => { const onClose = (e) => { thisRef.current.props.onClose?.(e as PopupEvent); }; popup.on("close", onClose); popup.setDOMContent(container).addTo(map.getMap()); return () => { // https://github.com/visgl/react-map-gl/issues/1825 // onClose should not be fired if the popup is removed by unmounting // When using React strict mode, the component is mounted twice. // Firing the onClose callback here would be a false signal to remove the component. popup.off("close", onClose); if (popup.isOpen()) { popup.remove(); } }; }, []); useEffect(() => { applyReactStyle(popup.getElement(), props.style); }, [props.style]); useImperativeHandle(ref, () => popup, []); if (popup.isOpen()) { const oldProps = thisRef.current.props; if ( popup.getLngLat().lng !== props.longitude || popup.getLngLat().lat !== props.latitude ) { popup.setLngLat([props.longitude, props.latitude]); } if (props.offset && !deepEqual(oldProps.offset, props.offset)) { popup.setOffset(props.offset); } if ( oldProps.anchor !== props.anchor || oldProps.maxWidth !== props.maxWidth ) { popup.options.anchor = props.anchor; popup.setMaxWidth(props.maxWidth); } const classNameDiff = compareClassNames( oldProps.className, props.className, ); if (classNameDiff) { for (const c of classNameDiff) { popup.toggleClassName(c); } } thisRef.current.props = props; } return createPortal(props.children, container); }), );