UNPKG

maplibre-react-components

Version:
1,422 lines (1,400 loc) 61.5 kB
"use client"; //#region rolldown:runtime var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion const react = __toESM(require("react")); const maplibre_gl = __toESM(require("maplibre-gl")); const react_jsx_runtime = __toESM(require("react/jsx-runtime")); const react_dom = __toESM(require("react-dom")); //#region src/lib/util.ts function filterMapProps(options) { const callbacks = {}; const mapHandlerOptions = {}; const mapReactiveOptions = {}; for (const key in options) if (key.startsWith("on")) callbacks[key] = options[key]; else if (mapHandlerNames.includes(key)) mapHandlerOptions[key] = options[key]; else if (mapReactiveOptionNames.includes(key)) mapReactiveOptions[key] = options[key]; else if (!key.startsWith("initial") && key !== "container" && key !== "style") throw Error(`unknown map option key ${key}`); return [ mapReactiveOptions, callbacks, mapHandlerOptions ]; } function transformPropsToOptions(props, optionKeyWhiteList) { const callbacks = {}; const options = {}; for (const key in props) { if (optionKeyWhiteList?.includes(key)) { options[key] = props[key]; continue; } if (key.startsWith("on")) callbacks[key] = props[key]; else { const definitiveKey = key.startsWith("initial") ? key[7].toLowerCase() + key.substring(8) : key; if (options[definitiveKey]) throw new Error(`duplicate key ${definitiveKey}`); else options[definitiveKey] = props[key]; } } return [options, callbacks]; } function prepareEventDep(eventNameToCallbackName$5, callbacks) { const activeEvents = Object.keys(eventNameToCallbackName$5).filter((eventName) => eventNameToCallbackName$5[eventName] in callbacks); return activeEvents.sort(); } /** * from : react-map-gl/src/utils/deep-equal.ts * Compare any two objects * @param a * @param b * @returns true if the objects are deep equal */ function deepEqual(a, b) { if (a === b) return true; if (!a || !b) return false; if (Array.isArray(a)) { if (!Array.isArray(b) || a.length !== b.length) return false; for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false; return true; } else if (Array.isArray(b)) return false; if (typeof a === "object" && typeof b === "object") { const aKeys = Object.keys(a); const bKeys = Object.keys(b); if (aKeys.length !== bKeys.length) return false; for (const key of aKeys) { if (!Object.prototype.hasOwnProperty.call(b, key)) return false; if (!deepEqual(a[key], b[key])) return false; } return true; } return false; } function areLngLatClose(lngLat1, lngLat2) { if (!lngLat1 && !lngLat2) return true; if (!lngLat1 || !lngLat2) return false; return Math.round(lngLat1.lng * 1e5) === Math.round(lngLat2.lng * 1e5) && Math.round(lngLat1.lat * 1e5) === Math.round(lngLat2.lat * 1e5); } function areCoordsClose(coords1, coords2) { if (!coords1 && !coords2) return true; if (!coords1 || !coords2) return false; const lngLat1 = maplibre_gl.default.LngLat.convert(coords1); const lngLat2 = maplibre_gl.default.LngLat.convert(coords2); return Math.round(lngLat1.lng * 1e5) === Math.round(lngLat2.lng * 1e5) && Math.round(lngLat1.lat * 1e5) === Math.round(lngLat2.lat * 1e5); } function lngLatClassToObj(lngLat) { return { lng: lngLat.lng, lat: lngLat.lat }; } function arePointsEqual(a, b) { const ax = Array.isArray(a) ? a[0] : a ? a.x : 0; const ay = Array.isArray(a) ? a[1] : a ? a.y : 0; const bx = Array.isArray(b) ? b[0] : b ? b.x : 0; const by = Array.isArray(b) ? b[1] : b ? b.y : 0; return ax === bx && ay === by; } function updateClassNames(elt, prevClassNames, nextClassNames) { prevClassNames.forEach((name) => { if (name === "") return; if (nextClassNames.indexOf(name) === -1) elt.classList.remove(name); }); nextClassNames.forEach((name) => { if (name === "") return; if (prevClassNames.indexOf(name) === -1 || !elt.classList.contains(name)) elt.classList.add(name); }); } function updateListeners(prevEventTypes, nextEventTypes, onSubscribe, onUnsubscribe) { prevEventTypes.forEach((eventName) => { if (eventName !== "" && nextEventTypes.indexOf(eventName) === -1) onUnsubscribe(eventName); }); nextEventTypes.forEach((eventName) => { if (eventName !== "" && prevEventTypes.indexOf(eventName) === -1) onSubscribe(eventName); }); } const markerHeight = 41 - 5.8 / 2; const markerRadius = 13.5; const linearOffset = Math.abs(markerRadius) / Math.SQRT2; const markerPopupOffset = { top: [0, 0], "top-left": [0, 0], "top-right": [0, 0], bottom: [0, -markerHeight], "bottom-left": [linearOffset, (markerHeight - markerRadius + linearOffset) * -1], "bottom-right": [-linearOffset, (markerHeight - markerRadius + linearOffset) * -1], left: [markerRadius, (markerHeight - markerRadius) * -1], right: [-markerRadius, (markerHeight - markerRadius) * -1] }; const gradientMarkerHeight = 50; const gradientMarkerPopupOffset = { top: [0, 0], "top-left": [0, 0], "top-right": [0, 0], bottom: [0, -gradientMarkerHeight], "bottom-left": [linearOffset, (gradientMarkerHeight - markerRadius + linearOffset) * -1], "bottom-right": [-linearOffset, (gradientMarkerHeight - markerRadius + linearOffset) * -1], left: [markerRadius, (gradientMarkerHeight - markerRadius) * -1], right: [-markerRadius, (gradientMarkerHeight - markerRadius) * -1] }; const emptyStyle = { version: 8, name: "Empty", sources: {}, layers: [] }; function uniqueId() { const characters = "abcdefghijklmnopqrstuvwxyz0123456789"; let result = ""; for (let i = 0; i < 8; i++) { const randomIndex = Math.floor(Math.random() * characters.length); result += characters[randomIndex]; } return result; } //#endregion //#region src/lib/MapManager.ts const eventNameToCallbackName$4 = { mousedown: "onMouseDown", mouseup: "onMouseUp", mouseover: "onMouseOver", mouseout: "onMouseOut", mousemove: "onMouseMove", mouseenter: "onMouseEnter", mouseleave: "onMouseLeave", click: "onClick", dblclick: "onDblClick", contextmenu: "onContextMenu", touchstart: "onTouchStart", touchend: "onTouchEnd", touchcancel: "onTouchCancel", touchmove: "onTouchMove", movestart: "onMoveStart", move: "onMove", moveend: "onMoveEnd", dragstart: "onDragStart", drag: "onDrag", dragend: "onDragEnd", zoomstart: "onZoomStart", zoom: "onZoom", zoomend: "onZoomEnd", rotatestart: "onRotateStart", rotate: "onRotate", rotateend: "onRotateEnd", pitchstart: "onPitchStart", pitch: "onPitch", pitchend: "onPitchEnd", wheel: "onWheel", resize: "onResize", remove: "onRemove", boxzoomstart: "onBoxZoomStart", boxzoomend: "onBoxZoomEnd", boxzoomcancel: "onBoxZoomCancel", webglcontextlost: "onWebglContextLost", webglcontextrestored: "onWebglContextRestored", load: "onLoad", render: "onRender", idle: "onIdle", error: "onError", data: "onData", styledata: "onStyleData", sourcedata: "onSourceData", dataloading: "onDataLoading", styledataloading: "onStyleDataLoading", sourcedataloading: "onSourceDataLoading", tiledataloading: "onTileDataLoading", styleimagemissing: "onStyleImageMissing", dataabort: "onDataAbort", sourcedataabort: "onSourceDataAbort", terrain: "onTerrain" }; const mapReactiveOptionNames = [ "maxBounds", "minZoom", "maxZoom", "minPitch", "maxPitch", "renderWorldCopies", "pixelRatio", "centerClampedToGround" ]; const mapHandlerNames = [ "scrollZoom", "boxZoom", "dragRotate", "dragPan", "keyboard", "doubleClickZoom", "touchZoomRotate", "touchPitch", "cooperativeGestures" ]; const DEFAULT_STYLE = "https://demotiles.maplibre.org/style.json"; var MapManager = class { reactiveOptions = {}; handlerOptions = {}; eventNames = []; callbacks; _map; padding; mapStyle; controlledSources = {}; controlledLayers = {}; controlledTerrain = null; constructor({ mapStyle = DEFAULT_STYLE, padding }, mapProps, container) { this.mapStyle = mapStyle; this.padding = padding; const [mapBaseOptions, callbacks] = transformPropsToOptions(mapProps); this.callbacks = callbacks; const mapOptions = { ...mapBaseOptions, container, style: mapStyle }; const map = new maplibre_gl.default.Map(mapOptions); map.style.on("error", this._onStyleError); if (padding) map.setPadding(padding); this._map = map; this._updateCallbacks(callbacks); } setProps({ mapStyle = DEFAULT_STYLE, styleDiffing = true, styleTransformStyle, padding }, mapProps) { const [reactiveOptions, callbacks, handlerOptions] = filterMapProps(mapProps); this._updateCallbacks(callbacks); this._updateStyle(mapStyle, { diff: styleDiffing, transformStyle: styleTransformStyle }); this._updateReactiveOptions(reactiveOptions, { padding }); this._updateHandlers(handlerOptions); } getControlledTerrain() { return this.controlledTerrain; } setControlledTerrain(terrainProps) { this.controlledTerrain = terrainProps; } getControlledLayer(id) { return this.controlledLayers[id] ?? null; } setControlledLayer(id, layerProps) { if (!layerProps) delete this.controlledLayers[id]; else this.controlledLayers[id] = layerProps; } getControlledSource(id) { return this.controlledSources[id] ?? null; } setControlledSource(id, layerProps) { if (!layerProps) delete this.controlledSources[id]; else this.controlledSources[id] = layerProps; } _updateStyle(nextStyle, options) { const curStyle = this.mapStyle; if (nextStyle !== curStyle) { this.mapStyle = nextStyle; this._map.setStyle(nextStyle, { diff: options.diff, transformStyle: (prevStyle, nextStyle$1) => { const prevControlledSources = prevStyle ? Object.fromEntries(Object.entries(prevStyle?.sources).filter(([sourceId]) => sourceId in this.controlledSources)) : {}; const prevControlledLayers = prevStyle ? prevStyle.layers.filter((layer) => layer.id in this.controlledLayers) : []; const result = { ...nextStyle$1, sources: { ...nextStyle$1.sources, ...prevControlledSources }, layers: [...nextStyle$1.layers, ...prevControlledLayers], terrain: this.controlledTerrain ? prevStyle?.terrain : nextStyle$1.terrain }; return options.transformStyle ? options.transformStyle(prevStyle, result) : result; } }); } } _updateReactiveOptions(nextReactiveOptions, { padding }) { const currReactiveOptions = this.reactiveOptions; this.reactiveOptions = nextReactiveOptions; for (const optionName of mapReactiveOptionNames) if (optionName in nextReactiveOptions && !deepEqual(currReactiveOptions[optionName], nextReactiveOptions[optionName])) { const setterName = `set${optionName[0].toUpperCase()}${optionName.substring(1)}`; this._map[setterName](nextReactiveOptions[optionName]); } if (padding && !deepEqual(this.padding, padding)) this._map.setPadding(padding); this.padding = padding; } _updateCallbacks(callbacks = {}) { this.callbacks = callbacks; const nextEventNames = prepareEventDep(eventNameToCallbackName$4, callbacks); if (this.eventNames.join("-") === nextEventNames.join("-")) return; updateListeners(this.eventNames, nextEventNames, (eventName) => this._map.on(eventName, this._onMapEvent), (eventName) => this._map.off(eventName, this._onMapEvent)); this.eventNames = nextEventNames; } _updateHandlers(nextHandlers) { const currHandlers = this.handlerOptions; this.handlerOptions = nextHandlers; for (const propName of mapHandlerNames) { const nextValue = nextHandlers[propName] ?? true; const currValue = currHandlers[propName] ?? true; if (!deepEqual(nextValue, currValue)) if (nextValue) this._map[propName].enable(nextValue); else this._map[propName].disable(); } } _onStyleError = (event) => { if (event.error.name !== "AbortError") console.error(event.error); }; _onMapEvent = (e) => { const eventType = e.type; const callbackName = eventNameToCallbackName$4[eventType]; if (this.callbacks[callbackName]) this.callbacks[callbackName]?.(e); else console.info("not managed RMap event", eventType, e); }; get map() { return this._map; } destroy() { this._updateCallbacks(); this._map.remove(); } }; //#endregion //#region src/hooks/useIsomorphicLayoutEffect.ts const useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect; //#endregion //#region src/lib/MapManagers.ts var MapManagers = class { _maps = {}; _listeners = {}; add(id, mapManager) { this._maps[id] = mapManager; this._listeners[id]?.forEach(([mounted, setMounted]) => { if (!mounted) setMounted(true); }); } remove(id) { delete this._maps[id]; this._listeners[id]?.forEach(([mounted, setMounted]) => { if (mounted) setMounted(false); }); } get(id) { if (!id) return null; return this._maps[id] ?? null; } addListener(id, mountedState) { if (this._listeners[id]) this._listeners[id].push(mountedState); else this._listeners[id] = [mountedState]; const isMounted = !!this._maps[id]; if (mountedState[0] !== isMounted) mountedState[1](isMounted); } removeListener(id, [, callback]) { this._listeners[id] = this._listeners[id].filter(([, cb]) => cb !== callback); } }; //#endregion //#region src/contexts/RMapContextProvider.tsx const RMapContext = (0, react.createContext)(null); const RMapContextProvider = ({ children }) => { const mapManagersRef = (0, react.useRef)(null); if (!mapManagersRef.current) mapManagersRef.current = new MapManagers(); return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RMapContext.Provider, { value: mapManagersRef, children }); }; //#endregion //#region src/contexts/CurrentMapIdContext.ts const CurrentMapIdContext = (0, react.createContext)(null); //#endregion //#region src/components/RMap/RMap.tsx const childContainerStyle = { height: "100%" }; const RMap = (0, react.forwardRef)(function RMap$1({ children, style, id: propsId, className, onMounted, mapStyle, styleDiffing, styleTransformStyle, padding,...mapProps }, ref) { const containerRef = (0, react.useRef)(null); const needPropsUpdate = (0, react.useRef)(true); const idRef = (0, react.useRef)(propsId ?? uniqueId()); if (propsId && propsId !== idRef.current) throw new Error(`RMap id should not change. "${propsId}" "${idRef.current}". If you defined id as const string add a "key" prop to your RMap component`); const id = idRef.current; const externalMapManagersRef = (0, react.useContext)(RMapContext); const localMapManagersRef = (0, react.useRef)(null); if (!externalMapManagersRef && !localMapManagersRef.current) localMapManagersRef.current = new MapManagers(); const mapManagers = externalMapManagersRef ? externalMapManagersRef.current : localMapManagersRef.current; const [, reRender] = (0, react.useState)(0); /** * we need to init mapManager before useImperativeHandle call * so necessary inside useLayoutEffect * (useLayoutEffect and useImperativeHandle are called in same priority) * parent component will have access to reference in useLayoutEffect / useEffect hooks */ useIsomorphicLayoutEffect(() => { const mapManager = mapManagers.get(id); if (!mapManager) { const instance = new MapManager({ mapStyle, styleDiffing, padding }, mapProps, containerRef.current); mapManagers.add(id, instance); onMounted && onMounted(instance.map); reRender((v) => v + 1); needPropsUpdate.current = false; } else if (needPropsUpdate.current) mapManager.setProps({ mapStyle, padding, styleDiffing, styleTransformStyle }, mapProps); else needPropsUpdate.current = true; }); useIsomorphicLayoutEffect(() => { return () => { const mapManager = mapManagers.get(id); if (mapManager) { mapManager.destroy(); mapManagers.remove(id); } }; }, []); (0, react.useImperativeHandle)(ref, () => mapManagers.get(id)?.map || null, [id, mapManagers]); /** * container class attribute must not be controlled by React * - maplibre GL add : maplibregl-map * - other plugins can add classes dynamically */ useIsomorphicLayoutEffect(() => { if (!className) return; const container = containerRef.current; className.split(" ").map((classItem) => container.classList.add(classItem)); return () => void className.split(" ").map((classItem) => container.classList.remove(classItem)); }, [className]); const completeStyle = (0, react.useMemo)(() => ({ position: "relative", width: "100%", height: "100%", ...style }), [style]); return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { ref: containerRef, id, style: completeStyle, children: mapManagers.get(id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CurrentMapIdContext.Provider, { value: id, children: externalMapManagersRef ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "maplibregl-children", style: childContainerStyle, children }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RMapContext.Provider, { value: localMapManagersRef, children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "maplibregl-children", style: childContainerStyle, children }) }) }) }); }); //#endregion //#region src/hooks/useMapManager.ts function useMapManager(optionalId) { const mapManagersRef = (0, react.useContext)(RMapContext); const currentMapId = (0, react.useContext)(CurrentMapIdContext); const id = optionalId ?? currentMapId; const mapManager = mapManagersRef?.current.get(id) ?? null; const mountedState = (0, react.useState)(mapManager !== null); if (!mapManagersRef?.current) throw new Error("use useMapManager in components inside <RMap /> or inside <RMapContextProvider />"); if (!id) throw new Error("provide an id to useMap or use inside <RMap />"); (0, react.useEffect)(() => { const mapManagers = mapManagersRef.current; if (!mapManagers) throw new Error("mapManagers can't disappear"); mapManagers.addListener(id, mountedState); return () => { mapManagers.removeListener(id, mountedState); }; }, [ id, mapManagersRef, mountedState, mapManager ]); return mapManager; } //#endregion //#region src/hooks/useMap.ts function useMap(optionalId) { return useMapManager(optionalId)?.map ?? null; } //#endregion //#region src/components/RMarker/RMarker.tsx const eventNameToCallbackName$3 = { dragstart: "onDragStart", drag: "onDrag", dragend: "onDragEnd", click: "onClick" }; const RMarker = (0, react.memo)((0, react.forwardRef)(function RMarker$1(props, ref) { const { longitude, latitude, children,...markerProps } = props; const map = useMap(); const [options, callbacks] = transformPropsToOptions(markerProps); const prevOptionsRef = (0, react.useRef)(options); const callbacksRef = (0, react.useRef)(null); callbacksRef.current = callbacks; const marker = (0, react.useMemo)(() => { const completeOptions = { ...options, element: children ? document.createElement("div") : void 0 }; const mk = new maplibre_gl.default.Marker(completeOptions); mk.setLngLat([longitude, latitude]); return mk; }, []); const nextEventsStr = prepareEventDep(eventNameToCallbackName$3, callbacks).join("-"); (0, react.useEffect)(() => { function onMarkerEvent(e) { const eventType = e.type; const callbackName = eventNameToCallbackName$3[eventType]; if (callbacksRef.current?.[callbackName]) callbacksRef.current[callbackName]?.(e); else console.info("not managed RMarker event", eventType, e); } const eventNames = nextEventsStr.split("-"); eventNames.forEach((eventName) => { if (eventName === "click") marker.getElement().addEventListener("click", onMarkerEvent); else marker.on(eventName, onMarkerEvent); }); return () => { eventNames.forEach((eventName) => { if (eventName === "click") marker.getElement().removeEventListener("click", onMarkerEvent); else marker.off(eventName, onMarkerEvent); }); }; }, [nextEventsStr, marker]); (0, react.useEffect)(() => { marker.addTo(map); return () => void marker.remove(); }, []); const { className, offset, draggable, clickTolerance = 0, rotation, rotationAlignment, subpixelPositioning = false, pitchAlignment, opacity, opacityWhenCovered } = options; (0, react.useImperativeHandle)(ref, () => marker, [marker]); if (prevOptionsRef.current.className !== className) updateClassNames(marker._element, prevOptionsRef.current.className?.split(" ") || [], className?.split(" ") || []); 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._clickTolerance !== clickTolerance) marker._clickTolerance = clickTolerance; if (marker.getRotation() !== rotation) marker.setRotation(rotation); if (marker.getRotationAlignment() !== rotationAlignment) marker.setRotationAlignment(rotationAlignment); if (marker.getPitchAlignment() !== pitchAlignment) marker.setPitchAlignment(pitchAlignment); if (marker._opacity !== opacity || marker._opacityWhenCovered !== opacityWhenCovered) marker.setOpacity(opacity, opacityWhenCovered); if (marker.setSubpixelPositioning && marker._subpixelPositioning !== subpixelPositioning) marker.setSubpixelPositioning(subpixelPositioning); prevOptionsRef.current = options; return children ? (0, react_dom.createPortal)(children, marker.getElement()) : null; })); //#endregion //#region src/components/RPopup/RPopup.tsx const eventNameToCallbackName$2 = { map_click: "onMapClick", map_move: "onMapMove" }; const RPopup = (0, react.memo)((0, react.forwardRef)(function RPopup$1(props, ref) { const { longitude, latitude, children,...popupProps } = props; const map = useMap(); const [options, callbacks] = transformPropsToOptions(popupProps); const popupRef = (0, react.useRef)(null); const prevOptionsRef = (0, react.useRef)(options); const currCallbacksRef = (0, react.useRef)(null); currCallbacksRef.current = callbacks; const container = (0, react.useMemo)(() => { return document.createElement("div"); }, []); if (!popupRef.current) { popupRef.current = new maplibre_gl.default.Popup({ ...options, closeButton: false, closeOnClick: false, closeOnMove: false }); if (longitude !== void 0 && latitude !== void 0) popupRef.current.setLngLat([longitude, latitude]); } const nextEventsStr = prepareEventDep(eventNameToCallbackName$2, callbacks).join("-"); (0, react.useEffect)(() => { function onPopupEvent(e) { const eventType = e.type; const callbackName = eventNameToCallbackName$2[eventType] || eventNameToCallbackName$2[`map_${eventType}`]; if (currCallbacksRef.current?.[callbackName]) currCallbacksRef.current[callbackName]?.(e); else console.info("not managed RPopup event", eventType, e); } if (nextEventsStr === "") return; const eventNames = nextEventsStr.split("-"); const popupStable = popupRef.current; eventNames.forEach((eventName) => { if (eventName.startsWith("map_")) map.on(eventName.substring(4), onPopupEvent); else popupStable.on(eventName, onPopupEvent); }); return () => { eventNames.forEach((eventName) => { if (eventName.startsWith("map_")) map.off(eventName.substring(4), onPopupEvent); else popupStable.off(eventName, onPopupEvent); }); }; }, [nextEventsStr, map]); (0, react.useEffect)(() => { popupRef.current.setDOMContent(container).addTo(map); return () => void popupRef.current.remove(); }, [container, map]); const { offset, maxWidth = "240px", className } = options; (0, react.useImperativeHandle)(ref, () => popupRef.current, [popupRef]); if (popupRef.current.isOpen()) { if (longitude !== void 0 && latitude !== void 0 && (popupRef.current.getLngLat().lng !== longitude || popupRef.current.getLngLat().lat !== latitude)) popupRef.current.setLngLat([longitude, latitude]); if (offset && !deepEqual(popupRef.current.options.offset, offset)) popupRef.current.setOffset(offset); if (prevOptionsRef.current.className !== className) updateClassNames(container, prevOptionsRef.current.className?.split(" ") || [], className?.split(" ") || []); if (popupRef.current.getMaxWidth() !== maxWidth) popupRef.current.setMaxWidth(maxWidth); } prevOptionsRef.current = options; return (0, react_dom.createPortal)(children, container); })); //#endregion //#region src/components/RLayer/RLayer.tsx const eventNameToCallbackName$1 = { mousedown: "onMouseDown", mouseup: "onMouseUp", mouseover: "onMouseOver", mouseout: "onMouseOut", mousemove: "onMouseMove", mouseenter: "onMouseEnter", mouseleave: "onMouseLeave", click: "onClick", dblclick: "onDblClick", contextmenu: "onContextMenu", touchstart: "onTouchStart", touchend: "onTouchEnd", touchcancel: "onTouchCancel", touchmove: "onTouchMove" }; function createLayer(map, layerOptions, beforeId) { if (map.style?._loaded) { if (layerOptions.type === "background" || layerOptions.type === "custom" || layerOptions.source && map.getSource(layerOptions.source)) { map.addLayer(layerOptions, beforeId && map.getLayer(beforeId) ? beforeId : void 0); return map.getLayer(layerOptions.id); } } return void 0; } function updateLayer(map, { beforeId: nextBeforeId,...nextOptions }, { beforeId: prevBeforeId,...prevOptions }) { if (prevOptions.type === "custom" || nextOptions.type === "custom") return; if (prevBeforeId !== nextBeforeId) map.moveLayer(nextOptions.id, nextBeforeId); /** * we take random LayerSpecification to simulate same specification. * here FillLayerSpecification */ if (nextOptions.type !== "background" && nextOptions.type !== "custom" && prevOptions.filter !== nextOptions.filter) map.setFilter(nextOptions.id, nextOptions.filter); const prevO = prevOptions; const nextO = nextOptions; if (prevO.layout !== nextO.layout) { if (nextO.layout) { for (const key of Object.keys(nextO.layout)) if (nextO.layout[key] !== prevO.layout?.[key]) map.setLayoutProperty(nextOptions.id, key, nextO.layout[key]); } for (const key in prevO.layout) if (!Object.prototype.hasOwnProperty.call(nextO.layout, key)) map.setLayoutProperty(nextOptions.id, key, void 0); } if (prevO.paint !== nextO.paint) { if (nextO.paint) { for (const key of Object.keys(nextO.paint)) if (nextO.paint[key] !== prevO.paint?.[key]) map.setPaintProperty(nextOptions.id, key, nextO.paint[key]); } for (const key in prevO.paint) if (!Object.prototype.hasOwnProperty.call(nextO.paint, key)) map.setPaintProperty(nextOptions.id, key, void 0); } if (prevO.minzoom !== nextO.minzoom || prevO.maxzoom !== nextO.maxzoom) { if (nextO.minzoom && nextO.maxzoom) map.setLayerZoomRange(nextOptions.id, nextO.minzoom, nextO.maxzoom); } } const RLayer = (0, react.memo)((0, react.forwardRef)(function RLayer$1(props, ref) { const { beforeId,...layerProps } = props; const [layerOptions, callbacks] = transformPropsToOptions(layerProps, ["onAdd"]); const id = layerOptions.id; const mapManager = useMapManager(); if (!mapManager) throw new Error("use <RLayer /> component inside <RMap />"); const map = mapManager.map; const initialLayerId = (0, react.useRef)(id); if (id !== initialLayerId.current) throw new Error(`RLayer id should not change. "${id}" "${initialLayerId.current}". If you defined id as const string add a "key" prop to your RLayer component`); const prevProps = mapManager.getControlledLayer(id) ?? props; const [, setVersion] = (0, react.useState)(0); const reRender = (0, react.useCallback)(() => setVersion((v) => v + 1), []); if (props.type !== prevProps.type) throw new Error(`RLayer type should not change. "${props.type}" "${prevProps.type}"`); const callbacksRef = (0, react.useRef)(null); callbacksRef.current = callbacks; (0, react.useEffect)(() => { map.on("styledata", reRender); if (map.style && map.style._loaded) reRender(); return () => { map.off("styledata", reRender); if (map.style && map.style._loaded && map.getLayer(id)) map.removeLayer(id); mapManager?.setControlledLayer(id, null); }; }, [ map, id, mapManager, reRender ]); const nextEventsStr = prepareEventDep(eventNameToCallbackName$1, callbacks).join("-"); (0, react.useEffect)(() => { function onLayerEvent(e) { const eventType = e.type; const callbackName = eventNameToCallbackName$1[eventType]; if (callbacksRef.current?.[callbackName]) callbacksRef.current[callbackName]?.(e); else console.info("not managed RLayer event", eventType, e); } const eventNames = nextEventsStr.split("-"); eventNames.forEach((eventName) => { map.on(eventName, id, onLayerEvent); }); return () => { eventNames.forEach((eventName) => { map.off(eventName, id, onLayerEvent); }); }; }, [ nextEventsStr, id, map ]); let layer = map.style?._loaded && map.getLayer(id); if (layer) updateLayer(map, props, prevProps); else { layer = createLayer(map, layerOptions, beforeId); if (layer) map.off("styledata", reRender); } (0, react.useImperativeHandle)(ref, () => layer || null, [layer]); mapManager.setControlledLayer(id, props); return null; })); //#endregion //#region src/components/RSource/RSource.tsx function createSource(map, id, sourceOptions) { if (map.style?._loaded) { map.addSource(id, sourceOptions); return map.getSource(id); } return void 0; } function updateSource(source, nextOptions, prevOptions) { switch (nextOptions.type) { case "image": { const prevO = prevOptions; const nextO = nextOptions; if (prevO.url !== nextO.url) source.updateImage({ url: nextO.url, coordinates: nextO.coordinates }); if (prevO.coordinates !== nextO.coordinates) source.setCoordinates(nextO.coordinates); break; } case "video": { const prevO = prevOptions; const nextO = nextOptions; if (prevO.coordinates !== nextO.coordinates) source.setCoordinates(nextO.coordinates); break; } case "geojson": { const prevO = prevOptions; const nextO = nextOptions; if (prevO.data !== nextO.data) source.setData(nextO.data); if (prevO.cluster !== nextO.cluster || prevO.clusterMaxZoom !== nextO.clusterMaxZoom || prevO.clusterRadius !== nextO.clusterRadius) source.setClusterOptions({ cluster: nextO.cluster, clusterMaxZoom: nextO.clusterMaxZoom, clusterRadius: nextO.clusterRadius }); break; } case "raster": case "raster-dem": case "vector": { const prevO = prevOptions; const nextO = nextOptions; if (prevO.tiles !== nextO.tiles && nextO.tiles) source.setTiles(nextO.tiles); if (prevO.url !== nextO.url && nextO.url) source.setUrl(nextO.url); break; } } } const RSource = (0, react.memo)((0, react.forwardRef)(function RSource$1(props, ref) { const { id,...sourceOptions } = props; const mapManager = useMapManager(); const map = mapManager.map; const initialId = (0, react.useRef)(id); if (id !== initialId.current) throw new Error(`RSource id should not change. "${id}" "${initialId.current}". If you defined id as const string add a "key" prop to your RSource component`); const { id: _,...prevOptions } = mapManager.getControlledSource(id) ?? props; if (sourceOptions.type !== prevOptions.type) throw new Error(`RSource type should not change. "${sourceOptions.type}" "${prevOptions.type}"`); const [, setVersion] = (0, react.useState)(0); const reRender = (0, react.useCallback)(() => void setTimeout(() => setVersion((v) => v + 1), 0), []); (0, react.useEffect)(() => { /** * fired when * - new source added/removed * - new layer added/removed * when event is fired map.style._loaded is always true */ map.on("styledata", reRender); if (map.style && map.style._loaded) reRender(); return () => { map.off("styledata", reRender); if (map.style && map.getSource(id)) { const layers = map.getStyle()?.layers; if (layers) { for (const layer of layers) if (layer.type !== "background" && layer.type !== "custom" && layer.source === id) map.removeLayer(layer.id); } map.removeSource(id); } mapManager?.setControlledSource(id, null); }; }, [ map, id, mapManager, reRender ]); let source = map.style?._loaded && map.getSource(id); if (source) updateSource(source, sourceOptions, prevOptions); else { source = createSource(map, id, sourceOptions); if (source) map.off("styledata", reRender); } (0, react.useImperativeHandle)(ref, () => source || null, [source]); mapManager.setControlledSource(id, props); return null; })); //#endregion //#region src/components/RTerrain/RTerrain.tsx const RTerrain = (props) => { const terrainOptions = props; const mapManager = useMapManager(); const map = mapManager.map; const prevOptions = mapManager.getControlledTerrain() ?? props; const [, setVersion] = (0, react.useState)(0); const reRender = (0, react.useCallback)(() => setVersion((v) => v + 1), []); (0, react.useEffect)(() => { map.on("styledata", reRender); if (map.style && map.style._loaded) reRender(); return () => { map.off("styledata", reRender); if (map.style?._loaded && map.getTerrain()) map.setTerrain(null); mapManager?.setControlledTerrain(null); }; }, [ map, mapManager, reRender ]); const terrain = map.style?._loaded && (map.getSource(terrainOptions.source) ? map.getTerrain() : false); if (terrain) { if (prevOptions.exaggeration !== terrainOptions.exaggeration || prevOptions.source !== terrainOptions.source) map.setTerrain(terrainOptions); } else if (map.style?._loaded) { if (map.getSource(terrainOptions.source)) { map.setTerrain(terrainOptions); map.off("styledata", reRender); } } mapManager.setControlledTerrain(props); return null; }; //#endregion //#region ../../node_modules/.pnpm/@mapbox+point-geometry@1.1.0/node_modules/@mapbox/point-geometry/index.js /** * A standalone point geometry with useful accessor, comparison, and * modification methods. * * @class * @param {number} x the x-coordinate. This could be longitude or screen pixels, or any other sort of unit. * @param {number} y the y-coordinate. This could be latitude or screen pixels, or any other sort of unit. * * @example * const point = new Point(-77, 38); */ function Point(x, y) { this.x = x; this.y = y; } Point.prototype = { clone() { return new Point(this.x, this.y); }, add(p) { return this.clone()._add(p); }, sub(p) { return this.clone()._sub(p); }, multByPoint(p) { return this.clone()._multByPoint(p); }, divByPoint(p) { return this.clone()._divByPoint(p); }, mult(k) { return this.clone()._mult(k); }, div(k) { return this.clone()._div(k); }, rotate(a) { return this.clone()._rotate(a); }, rotateAround(a, p) { return this.clone()._rotateAround(a, p); }, matMult(m) { return this.clone()._matMult(m); }, unit() { return this.clone()._unit(); }, perp() { return this.clone()._perp(); }, round() { return this.clone()._round(); }, mag() { return Math.sqrt(this.x * this.x + this.y * this.y); }, equals(other) { return this.x === other.x && this.y === other.y; }, dist(p) { return Math.sqrt(this.distSqr(p)); }, distSqr(p) { const dx = p.x - this.x, dy = p.y - this.y; return dx * dx + dy * dy; }, angle() { return Math.atan2(this.y, this.x); }, angleTo(b) { return Math.atan2(this.y - b.y, this.x - b.x); }, angleWith(b) { return this.angleWithSep(b.x, b.y); }, angleWithSep(x, y) { return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y); }, _matMult(m) { const x = m[0] * this.x + m[1] * this.y, y = m[2] * this.x + m[3] * this.y; this.x = x; this.y = y; return this; }, _add(p) { this.x += p.x; this.y += p.y; return this; }, _sub(p) { this.x -= p.x; this.y -= p.y; return this; }, _mult(k) { this.x *= k; this.y *= k; return this; }, _div(k) { this.x /= k; this.y /= k; return this; }, _multByPoint(p) { this.x *= p.x; this.y *= p.y; return this; }, _divByPoint(p) { this.x /= p.x; this.y /= p.y; return this; }, _unit() { this._div(this.mag()); return this; }, _perp() { const y = this.y; this.y = this.x; this.x = -y; return this; }, _rotate(angle) { const cos = Math.cos(angle), sin = Math.sin(angle), x = cos * this.x - sin * this.y, y = sin * this.x + cos * this.y; this.x = x; this.y = y; return this; }, _rotateAround(angle, p) { const cos = Math.cos(angle), sin = Math.sin(angle), x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y), y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y); this.x = x; this.y = y; return this; }, _round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; }, constructor: Point }; /** * Construct a point from an array if necessary, otherwise if the input * is already a Point, return it unchanged. * @param {Point | [number, number] | {x: number, y: number}} p input value * @return {Point} constructed point. * @example * // this * var point = Point.convert([0, 1]); * // is equivalent to * var point = new Point(0, 1); */ Point.convert = function(p) { if (p instanceof Point) return p; if (Array.isArray(p)) return new Point(+p[0], +p[1]); if (p.x !== void 0 && p.y !== void 0) return new Point(+p.x, +p.y); throw new Error("Expected [x, y] or {x, y} point format"); }; //#endregion //#region src/maplibre-core/util/dom.ts var DOM = class DOM { static docStyle = typeof window !== "undefined" && window.document && window.document.documentElement.style; static userSelect; static selectProp = DOM.testProp([ "userSelect", "MozUserSelect", "WebkitUserSelect", "msUserSelect" ]); static transformProp = DOM.testProp(["transform", "WebkitTransform"]); static testProp(props) { if (!DOM.docStyle) return props[0]; for (let i = 0; i < props.length; i++) if (props[i] in DOM.docStyle) return props[i]; return props[0]; } static create(tagName, className, container) { const el = window.document.createElement(tagName); if (className !== void 0) el.className = className; if (container) container.appendChild(el); return el; } static createNS(namespaceURI, tagName) { const el = window.document.createElementNS(namespaceURI, tagName); return el; } static disableDrag() { if (DOM.docStyle && DOM.selectProp) { DOM.userSelect = DOM.docStyle[DOM.selectProp]; DOM.docStyle[DOM.selectProp] = "none"; } } static enableDrag() { if (DOM.docStyle && DOM.selectProp) DOM.docStyle[DOM.selectProp] = DOM.userSelect; } static setTransform(el, value) { el.style[DOM.transformProp] = value; } static addEventListener(target, type, callback, options = {}) { if ("passive" in options) target.addEventListener(type, callback, options); else target.addEventListener(type, callback, options.capture); } static removeEventListener(target, type, callback, options = {}) { if ("passive" in options) target.removeEventListener(type, callback, options); else target.removeEventListener(type, callback, options.capture); } static suppressClickInternal(e) { e.preventDefault(); e.stopPropagation(); window.removeEventListener("click", DOM.suppressClickInternal, true); } static suppressClick() { window.addEventListener("click", DOM.suppressClickInternal, true); window.setTimeout(() => { window.removeEventListener("click", DOM.suppressClickInternal, true); }, 0); } static getScale(element) { const rect = element.getBoundingClientRect(); return { x: rect.width / element.offsetWidth || 1, y: rect.height / element.offsetHeight || 1, boundingClientRect: rect }; } static getPoint(el, scale, e) { const rect = scale.boundingClientRect; return new Point((e.clientX - rect.left) / scale.x - el.clientLeft, (e.clientY - rect.top) / scale.y - el.clientTop); } static mousePos(el, e) { const scale = DOM.getScale(el); return DOM.getPoint(el, scale, e); } static touchPos(el, touches) { const points = []; const scale = DOM.getScale(el); for (let i = 0; i < touches.length; i++) points.push(DOM.getPoint(el, scale, touches[i])); return points; } static mouseButton(e) { return e.button; } static remove(node) { if (node.parentNode) node.parentNode.removeChild(node); } }; //#endregion //#region src/components/RGradientMarker/GradientMarker.ts const defaultColor = "#ffe64b"; const defaultHeight = 50; var GradientMarker = class extends maplibre_gl.default.Marker { _interactive; _shape; _icon; _height = defaultHeight; _text; _circleElement = null; _iconElement = null; _textElement = null; _markerElement; constructor(options) { options ??= {}; options.element = DOM.create("div", "maplibregl-gradient-marker"); if (options.className) options.element.classList.add(options.className); super(options); if (this._draggable) this._element.classList.add("draggable"); this._interactive = options && options.interactive === false ? false : "pending"; this._shape = (options && options.shape) ?? "pin"; this._color = (options && options.color) ?? defaultColor; this._icon = options && options.icon; this._text = options && options.text; this._defaultMarker = true; this._element.setAttribute("aria-label", "Map marker"); this._element.setAttribute("tabindex", "0"); this.setScale(this._scale); this.setColor(this._color); this._markerElement = DOM.create("div", `marker`); this.setShape(this._shape); if (this._text) this.setText(this._text); else if (this._icon) this.setIcon(this._icon); const target = DOM.create("div", "target"); this._element.appendChild(this._markerElement); this._element.appendChild(target); } _onActive = () => { /** * draggable marker are pointer-events: none when dragging. we can't listen this._element. * when listening this._map.getContainer() we don't have this issue */ this._map.getContainer().addEventListener("mouseup", this._onInactive, { once: true }); this._map.getContainer().addEventListener("touchend", this._onInactive, { once: true }); this._element.classList.add("active"); }; _onInactive = () => { this._element.classList.remove("active"); }; addTo(map) { maplibre_gl.default.Marker.prototype.addTo.apply(this, [map]); if (this._interactive === "pending") this.setInteractive(true); return this; } setInteractive(interactive = true) { if (this._interactive === interactive) return; this._interactive = interactive; if (interactive) this._element.dataset.interactive = ""; else delete this._element.dataset.interactive; if (interactive) { this._element.addEventListener("mousedown", this._onActive); this._element.addEventListener("touchstart", this._onActive); } else { this._element.removeEventListener("mousedown", this._onInactive); this._element.removeEventListener("touchstart", this._onInactive); } } getInteractive() { return this._interactive; } remove() { if (this._map) this.setInteractive(false); maplibre_gl.default.Marker.prototype.remove.apply(this); return this; } setIcon(icon) { this.resetIconText(); this._icon = icon; if (!icon) return this; this._circleElement = DOM.create("div", "circle", this._markerElement); if (typeof icon === "string") { this._iconElement = DOM.create("i", icon, this._markerElement); this._iconElement.className = icon || ""; } else if (typeof icon === "function") { this._iconElement = icon(); this._markerElement?.append(this._iconElement); } else { this._iconElement = icon; this._markerElement?.append(icon); } return this; } getIcon() { return this._icon; } resetIconText() { this._circleElement?.remove(); this._iconElement?.remove(); this._textElement?.remove(); this._circleElement = null; this._iconElement = null; this._textElement = null; } setText(text) { this.resetIconText(); this._text = text; if (!text) return this; this._circleElement = DOM.create("div", "circle", this._markerElement); this._textElement = DOM.create("div", "text", this._markerElement); this._textElement.innerText = text; return this; } getText() { return this._text; } setColor(color) { this._color = color || defaultColor; this._element.style.setProperty("--marker-color", this._color); return this; } getColor() { return this._color; } setScale(scale = 1, markerHeight$1 = defaultHeight) { this._scale = scale; this._height = markerHeight$1 * this._scale; this._element.style.setProperty("--marker-scale", scale.toString()); return this; } getScale() { return this._scale; } setShape(shape) { this._shape = shape || "pin"; this._anchor = this._shape === "circle" ? "center" : "bottom"; this._element.dataset.shape = this._shape; this._update(); return this; } getShape() { return this._shape; } setDraggable(shouldBeDraggable) { maplibre_gl.default.Marker.prototype.setDraggable.apply(this, [shouldBeDraggable]); this._element.classList.toggle("draggable", shouldBeDraggable); return this; } }; //#endregion //#region src/components/RGradientMarker/RGradientMarker.tsx const eventNameToCallbackName = { dragstart: "onDragStart", drag: "onDrag", dragend: "onDragEnd", click: "onClick" }; const RGradientMarker = (0, react.memo)((0, react.forwardRef)(function RGradientMarker$1(props, ref) { const { longitude, latitude,...markerProps } = props; const map = useMap(); const [options, markerCallbacks] = transformPropsToOptions(markerProps); const prevOptionsRef = (0, react.useRef)(options); const currCallbacksRef = (0, react.useRef)(null); currCallbacksRef.current = markerCallbacks; const marker = (0, react.useMemo)(() => { const mk = new GradientMarker({ ...options, anchor: "bottom" }); mk.setLngLat([longitude, latitude]); return mk; }, []); const eventDepStr = prepareEventDep(eventNameToCallbackName, markerCallbacks).join("-"); (0, react.useEffect)(() => { function onGradientMarkerEvent(e) { const eventType = e.type; const callbackName = eventNameToCallbackName[eventType]; if (currCallbacksRef.current?.[callbackName]) currCallbacksRef.current[callbackName]?.(e); else console.info("not managed RGradientMarker event", eventType, e); } const eventNames = eventDepStr.split("-"); eventNames.forEach((eventName) => { if (eventName === "click") marker.getElement().addEventListener("click", onGradientMarkerEvent); else marker.on(eventName, onGradientMarkerEvent); }); return () => { eventNames.forEach((eventName) => { if (eventName === "click") marker.getElement().removeEventListener("click", onGradientMarkerEvent); else marker.off(eventName, onGradientMarkerEvent); }); }; }, [eventDepStr, marker]); (0, react.useEffect)(() => { marker.addTo(map); return () => void marker.remove(); }, []); const { scale, color, text, icon, interactive, className, draggable, clickTolerance = 0, rotation, rotationAlignment, subpixelPositioning = false, pitchAlignment, shape, opacity, opacityWhenCovered } = options; (0, react.useImperativeHandle)(ref, () => marker, [marker]); if (prevOptionsRef.current.className !== className) updateClassNames(marker._element, prevOptionsRef.current.className?.split(" ") || [], className?.split(" ") || []); if (marker.getInteractive() !== interactive) marker.setInteractive(interactive); if (marker.getShape() !== shape) marker.setShape(shape); if (marker.getLngLat().lng !== longitude || marker.getLngLat().lat !== latitude) marker.setLngLat([longitude, latitude]); if (marker.isDraggable() !== draggable) marker.setDraggable(draggable); if (marker._clickTolerance !== clickTolerance) marker._clickTolerance = clickTolerance; if (marker.getRotation() !== rotation) marker.setRotation(rotation); if (marker.getRotationAlignment() !== rotationAlignment) marker.setRotationAlignment(rotationAlignment); if (marker.getPitchAlignment() !== pitchAlignment) marker.setPitchAlignment(pitchAlignment); if (marker._opacity !== opacity || marker._opacityWhenCovered !== opacityWhenCovered) marker.setOpacity(opacity, opacityWhenCovered); if (marker.setSubpixelPositioning && marker._subpixelPositioning !== subpixelPositioning) marker.setSubpixelPositioning(subpixelPositioning); if (marker.getColor() !== color) marker.setColor(color); if (marker.getScale() !== scale) marker.setScale(scale); if (marker.getText() !== text) marker.setText(text); /** * getIcon return the option (string, HTMLElement, factory) not * the HTMLElement created. */ if (marker.getIcon() !== icon) marker.setIcon(icon); prevOptionsRef.current = options; return null; })); //#endregion //#region src/components/ContextMenuEventAdapter.ts function ContextMenuEventAdapter({ customEventName = "contextmenu-maplibre", enabled = true }) { const map = useMap(); (0, react.useEffect)(() => { if (!enabled) return; /** * related issue: contextmenu not managed by Touch devices * https://github.com/maplibre/maplibre-gl-js/issues/373 * * we use click for touch device because touchend/mouseup can conflict with handlers. */ const eventName = window.matchMedia("(pointer: coarse)").matches ? "click" : "contextmenu"; function handleContextMenu(evt) { /** * we put a setTimeout to give time for the event to propagate and possibly cancel it */ setTimeout(() => { if (evt.defaultPrevented) return; const { originalEvent, poin