UNPKG

react-bkoi-gl

Version:

React components for Barikoi GL JS

156 lines (137 loc) 4.16 kB
/* 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, Marker as MarkerInstance, MarkerOptions, } from "../types/lib"; import type { MarkerEvent, MarkerDragEvent } from "../types/events"; import { MapContext } from "./map"; import { arePointsEqual } from "../utils/deep-equal"; import { compareClassNames } from "../utils/compare-class-names"; export type MarkerProps = MarkerOptions & { /** Longitude of the anchor location */ longitude: number; /** Latitude of the anchor location */ latitude: number; popup?: PopupInstance; /** CSS style override, applied to the control's container */ style?: React.CSSProperties; onClick?: (e: MarkerEvent<MouseEvent>) => void; onDragStart?: (e: MarkerDragEvent) => void; onDrag?: (e: MarkerDragEvent) => void; onDragEnd?: (e: MarkerDragEvent) => void; children?: React.ReactNode; }; /* eslint-disable complexity,max-statements */ export const Marker: React.FC<MarkerProps> = memo( forwardRef((props: MarkerProps, ref: React.Ref<MarkerInstance>) => { const { map, mapLib } = useContext(MapContext); const thisRef = useRef({ props }); const marker: MarkerInstance = useMemo(() => { let hasChildren = false; React.Children.forEach(props.children, (el) => { if (el) { hasChildren = true; } }); const options = { ...props, element: hasChildren ? document.createElement("div") : undefined, }; const mk = new mapLib.Marker(options); mk.setLngLat([props.longitude, props.latitude]); mk.getElement().addEventListener("click", (e: MouseEvent) => { thisRef.current.props.onClick?.({ type: "click", target: mk, originalEvent: e, }); }); mk.on("dragstart", (e) => { const evt = e as MarkerDragEvent; evt.lngLat = marker.getLngLat(); thisRef.current.props.onDragStart?.(evt); }); mk.on("drag", (e) => { const evt = e as MarkerDragEvent; evt.lngLat = marker.getLngLat(); thisRef.current.props.onDrag?.(evt); }); mk.on("dragend", (e) => { const evt = e as MarkerDragEvent; evt.lngLat = marker.getLngLat(); thisRef.current.props.onDragEnd?.(evt); }); return mk; }, []); useEffect(() => { marker.addTo(map.getMap()); return () => { marker.remove(); }; }, []); const { longitude, latitude, offset, style, draggable = false, popup = null, rotation = 0, rotationAlignment = "auto", pitchAlignment = "auto", } = props; useEffect(() => { applyReactStyle(marker.getElement(), style); }, [style]); useImperativeHandle(ref, () => marker, []); const oldProps = thisRef.current.props; if ( marker.getLngLat().lng !== longitude || marker.getLngLat().lat !== latitude ) { marker.setLngLat([longitude, latitude]); } if (offset && !arePointsEqual(marker.getOffset(), offset)) { marker.setOffset(offset); } if (marker.isDraggable() !== draggable) { marker.setDraggable(draggable); } if (marker.getRotation() !== rotation) { marker.setRotation(rotation); } if (marker.getRotationAlignment() !== rotationAlignment) { marker.setRotationAlignment(rotationAlignment); } if (marker.getPitchAlignment() !== pitchAlignment) { marker.setPitchAlignment(pitchAlignment); } if (marker.getPopup() !== popup) { marker.setPopup(popup); } const classNameDiff = compareClassNames( oldProps.className, props.className, ); if (classNameDiff) { for (const c of classNameDiff) { marker.toggleClassName(c); } } thisRef.current.props = props; return createPortal(props.children, marker.getElement()); }), );