UNPKG

maplibre-react-components

Version:
2,263 lines • 70.2 kB
"use client";

// src/components/RMap/RMap.tsx
import {
  forwardRef,
  useContext,
  useImperativeHandle,
  useMemo,
  useRef as useRef2,
  useState
} from "react";

// src/lib/MapManager.ts
import maplibregl2 from "maplibre-gl";

// src/lib/util.ts
import maplibregl from "maplibre-gl";
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(eventNameToCallbackName6, callbacks) {
  const activeEvents = Object.keys(eventNameToCallbackName6).filter(
    (eventName) => eventNameToCallbackName6[eventName] in callbacks
  );
  return activeEvents.sort();
}
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 = maplibregl.LngLat.convert(coords1);
  const lngLat2 = maplibregl.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);
    }
  });
}
var markerHeight = 41 - 5.8 / 2;
var markerRadius = 13.5;
var linearOffset = Math.abs(markerRadius) / Math.SQRT2;
var 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]
};
var gradientMarkerHeight = 50;
var 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]
};
var 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;
}

// src/lib/MapManager.ts
var eventNameToCallbackName = {
  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"
};
var mapReactiveOptionNames = [
  "maxBounds",
  "minZoom",
  "maxZoom",
  "minPitch",
  "maxPitch",
  "renderWorldCopies",
  "pixelRatio",
  "centerClampedToGround"
];
var mapHandlerNames = [
  "scrollZoom",
  "boxZoom",
  /**
   * DragRotateHandler is a composition of multiple handlers
   * MouseRotateHandler
   * MousePitchHandler (can be disabled with "pitchWithRotate" option)
   */
  "dragRotate",
  // right click rotate the map (right click can optionnaly pitch the map)
  "dragPan",
  // left click pan the map
  "keyboard",
  "doubleClickZoom",
  // shift + dbl-click dezoom
  /**
   * TwoFingersTouchZoomRotateHandler is a composition of multiple handlers
   * touchZoom   : TwoFingersTouchZoomHandler
   * touchRotate : TwoFingersTouchRotateHandler (can be disabled)
   * tapDragZoom : TapDragZoomHandler
   */
  "touchZoomRotate",
  "touchPitch",
  /**
   * desktop: ctrl + click to zoom
   * touch screen: two fingers to move the map
   */
  "cooperativeGestures"
];
var 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 maplibregl2.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, nextStyle2) => {
          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 = {
            ...nextStyle2,
            sources: {
              ...nextStyle2.sources,
              ...prevControlledSources
            },
            layers: [...nextStyle2.layers, ...prevControlledLayers],
            terrain: this.controlledTerrain ? prevStyle?.terrain : nextStyle2.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, 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[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();
  }
};

// src/hooks/useIsomorphicLayoutEffect.ts
import { useEffect, useLayoutEffect } from "react";
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;

// src/contexts/RMapContextProvider.tsx
import { createContext, useRef } from "react";

// 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);
  }
};

// src/contexts/RMapContextProvider.tsx
import { jsx } from "react/jsx-runtime";
var RMapContext = createContext(null);
var RMapContextProvider = ({ children }) => {
  const mapManagersRef = useRef(null);
  if (!mapManagersRef.current) {
    mapManagersRef.current = new MapManagers();
  }
  return /* @__PURE__ */ jsx(RMapContext.Provider, { value: mapManagersRef, children });
};

// src/contexts/CurrentMapIdContext.ts
import { createContext as createContext2 } from "react";
var CurrentMapIdContext = createContext2(null);

// src/components/RMap/RMap.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
var childContainerStyle = {
  height: "100%"
};
var RMap = forwardRef(function RMap2({
  /* RMapProps */
  children,
  style,
  id: propsId,
  className,
  onMounted,
  /* ManagerOptions */
  mapStyle,
  styleDiffing,
  styleTransformStyle,
  padding,
  /* MapProps */
  ...mapProps
}, ref) {
  const containerRef = useRef2(null);
  const needPropsUpdate = useRef2(true);
  const idRef = useRef2(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 = useContext(RMapContext);
  const localMapManagersRef = useRef2();
  if (!externalMapManagersRef && !localMapManagersRef.current) {
    localMapManagersRef.current = new MapManagers();
  }
  const mapManagers = externalMapManagersRef ? externalMapManagersRef.current : localMapManagersRef.current;
  const [, reRender] = useState(0);
  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);
      }
    };
  }, []);
  useImperativeHandle(ref, () => mapManagers.get(id)?.map || null, [id, mapManagers]);
  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 = useMemo(
    () => ({
      position: "relative",
      width: "100%",
      height: "100%",
      ...style
    }),
    [style]
  );
  return /* @__PURE__ */ jsx2("div", { ref: containerRef, id, style: completeStyle, children: mapManagers.get(id) && /* @__PURE__ */ jsx2(CurrentMapIdContext.Provider, { value: id, children: externalMapManagersRef ? /* @__PURE__ */ jsx2("div", { className: "maplibregl-children", style: childContainerStyle, children }) : /* @__PURE__ */ jsx2(RMapContext.Provider, { value: localMapManagersRef, children: /* @__PURE__ */ jsx2("div", { className: "maplibregl-children", style: childContainerStyle, children }) }) }) });
});

// src/components/RMarker/RMarker.tsx
import maplibregl3 from "maplibre-gl";
import {
  forwardRef as forwardRef2,
  memo,
  useEffect as useEffect3,
  useImperativeHandle as useImperativeHandle2,
  useMemo as useMemo2,
  useRef as useRef3
} from "react";
import { createPortal } from "react-dom";

// src/hooks/useMapManager.ts
import { useContext as useContext2, useEffect as useEffect2, useState as useState2 } from "react";
function useMapManager(optionalId) {
  const mapManagersRef = useContext2(RMapContext);
  const currentMapId = useContext2(CurrentMapIdContext);
  const id = optionalId ?? currentMapId;
  const mapManager = mapManagersRef?.current.get(id) ?? null;
  const mountedState = useState2(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 />");
  }
  useEffect2(() => {
    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;
}

// src/hooks/useMap.ts
function useMap(optionalId) {
  return useMapManager(optionalId)?.map ?? null;
}

// src/components/RMarker/RMarker.tsx
var eventNameToCallbackName2 = {
  dragstart: "onDragStart",
  drag: "onDrag",
  dragend: "onDragEnd",
  click: "onClick"
};
var RMarker = memo(
  forwardRef2(function RMarker2(props, ref) {
    const { longitude, latitude, children, ...markerProps } = props;
    const map = useMap();
    const [options, callbacks] = transformPropsToOptions(markerProps);
    const prevOptionsRef = useRef3(options);
    const callbacksRef = useRef3();
    callbacksRef.current = callbacks;
    const marker = useMemo2(() => {
      const completeOptions = {
        ...options,
        element: children ? document.createElement("div") : void 0
      };
      const mk = new maplibregl3.Marker(completeOptions);
      mk.setLngLat([longitude, latitude]);
      return mk;
    }, []);
    const nextEventsStr = prepareEventDep(eventNameToCallbackName2, callbacks).join("-");
    useEffect3(() => {
      function onMarkerEvent(e) {
        const eventType = e.type;
        const callbackName = eventNameToCallbackName2[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]);
    useEffect3(() => {
      marker.addTo(map);
      return () => void marker.remove();
    }, []);
    const {
      className,
      offset,
      draggable,
      clickTolerance = 0,
      rotation,
      rotationAlignment,
      subpixelPositioning = false,
      pitchAlignment,
      opacity,
      opacityWhenCovered
    } = options;
    useImperativeHandle2(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 ? createPortal(children, marker.getElement()) : null;
  })
);

// src/components/RPopup/RPopup.tsx
import maplibregl4 from "maplibre-gl";
import {
  forwardRef as forwardRef3,
  memo as memo2,
  useEffect as useEffect4,
  useImperativeHandle as useImperativeHandle3,
  useMemo as useMemo3,
  useRef as useRef4
} from "react";
import { createPortal as createPortal2 } from "react-dom";
var eventNameToCallbackName3 = {
  map_click: "onMapClick",
  map_move: "onMapMove"
};
var RPopup = memo2(
  forwardRef3(function RPopup2(props, ref) {
    const { longitude, latitude, children, ...popupProps } = props;
    const map = useMap();
    const [options, callbacks] = transformPropsToOptions(popupProps);
    const popupRef = useRef4(null);
    const prevOptionsRef = useRef4(options);
    const currCallbacksRef = useRef4();
    currCallbacksRef.current = callbacks;
    const container = useMemo3(() => {
      return document.createElement("div");
    }, []);
    if (!popupRef.current) {
      popupRef.current = new maplibregl4.Popup({
        ...options,
        closeButton: false,
        closeOnClick: false,
        closeOnMove: false
      });
      if (longitude !== void 0 && latitude !== void 0) {
        popupRef.current.setLngLat([longitude, latitude]);
      }
    }
    const nextEventsStr = prepareEventDep(eventNameToCallbackName3, callbacks).join("-");
    useEffect4(() => {
      function onPopupEvent(e) {
        const eventType = e.type;
        const callbackName = eventNameToCallbackName3[eventType] || // @ts-ignore
        eventNameToCallbackName3[`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]);
    useEffect4(() => {
      popupRef.current.setDOMContent(container).addTo(map);
      return () => void popupRef.current.remove();
    }, [container, map]);
    const { offset, maxWidth = "240px", className } = options;
    useImperativeHandle3(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 createPortal2(children, container);
  })
);

// src/components/RLayer/RLayer.tsx
import {
  forwardRef as forwardRef4,
  memo as memo3,
  useCallback,
  useEffect as useEffect5,
  useImperativeHandle as useImperativeHandle4,
  useRef as useRef5,
  useState as useState3
} from "react";
var eventNameToCallbackName4 = {
  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 (
      // BackgroundLayerSpecification and CustomLayerInterface has no source
      layerOptions.type === "background" || layerOptions.type === "custom" || // source exists for LayerSpecification who need one
      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);
  }
  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);
    }
  }
}
var RLayer = memo3(
  forwardRef4(function RLayer2(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 = useRef5(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] = useState3(0);
    const reRender = useCallback(() => setVersion((v) => v + 1), []);
    if (props.type !== prevProps.type) {
      throw new Error(`RLayer type should not change. "${props.type}" "${prevProps.type}"`);
    }
    const callbacksRef = useRef5();
    callbacksRef.current = callbacks;
    useEffect5(() => {
      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(eventNameToCallbackName4, callbacks).join("-");
    useEffect5(() => {
      function onLayerEvent(e) {
        const eventType = e.type;
        const callbackName = eventNameToCallbackName4[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);
      }
    }
    useImperativeHandle4(ref, () => layer || null, [layer]);
    mapManager.setControlledLayer(id, props);
    return null;
  })
);

// src/components/RSource/RSource.tsx
import {
  forwardRef as forwardRef5,
  useEffect as useEffect6,
  useRef as useRef6,
  useState as useState4,
  useImperativeHandle as useImperativeHandle5,
  memo as memo4,
  useCallback as useCallback2
} from "react";
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;
    }
  }
}
var RSource = memo4(
  forwardRef5(function RSource2(props, ref) {
    const { id, ...sourceOptions } = props;
    const mapManager = useMapManager();
    const map = mapManager.map;
    const initialId = useRef6(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] = useState4(0);
    const reRender = useCallback2(() => void setTimeout(() => setVersion((v) => v + 1), 0), []);
    useEffect6(() => {
      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);
      }
    }
    useImperativeHandle5(ref, () => source || null, [source]);
    mapManager.setControlledSource(id, props);
    return null;
  })
);

// src/components/RTerrain/RTerrain.tsx
import { useCallback as useCallback3, useEffect as useEffect7, useState as useState5 } from "react";
var RTerrain = (props) => {
  const terrainOptions = props;
  const mapManager = useMapManager();
  const map = mapManager.map;
  const prevOptions = mapManager.getControlledTerrain() ?? props;
  const [, setVersion] = useState5(0);
  const reRender = useCallback3(() => setVersion((v) => v + 1), []);
  useEffect7(() => {
    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;
};

// src/components/RGradientMarker/GradientMarker.ts
import maplibregl5 from "maplibre-gl";

// node_modules/.pnpm/@mapbox+point-geometry@1.1.0/node_modules/@mapbox/point-geometry/index.js
function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype = {
  /**
   * Clone this point, returning a new point that can be modified
   * without affecting the old one.
   * @return {Point} the clone
   */
  clone() {
    return new Point(this.x, this.y);
  },
  /**
   * Add this point's x & y coordinates to another point,
   * yielding a new point.
   * @param {Point} p the other point
   * @return {Point} output point
   */
  add(p) {
    return this.clone()._add(p);
  },
  /**
   * Subtract this point's x & y coordinates to from point,
   * yielding a new point.
   * @param {Point} p the other point
   * @return {Point} output point
   */
  sub(p) {
    return this.clone()._sub(p);
  },
  /**
   * Multiply this point's x & y coordinates by point,
   * yielding a new point.
   * @param {Point} p the other point
   * @return {Point} output point
   */
  multByPoint(p) {
    return this.clone()._multByPoint(p);
  },
  /**
   * Divide this point's x & y coordinates by point,
   * yielding a new point.
   * @param {Point} p the other point
   * @return {Point} output point
   */
  divByPoint(p) {
    return this.clone()._divByPoint(p);
  },
  /**
   * Multiply this point's x & y coordinates by a factor,
   * yielding a new point.
   * @param {number} k factor
   * @return {Point} output point
   */
  mult(k) {
    return this.clone()._mult(k);
  },
  /**
   * Divide this point's x & y coordinates by a factor,
   * yielding a new point.
   * @param {number} k factor
   * @return {Point} output point
   */
  div(k) {
    return this.clone()._div(k);
  },
  /**
   * Rotate this point around the 0, 0 origin by an angle a,
   * given in radians
   * @param {number} a angle to rotate around, in radians
   * @return {Point} output point
   */
  rotate(a) {
    return this.clone()._rotate(a);
  },
  /**
   * Rotate this point around p point by an angle a,
   * given in radians
   * @param {number} a angle to rotate around, in radians
   * @param {Point} p Point to rotate around
   * @return {Point} output point
   */
  rotateAround(a, p) {
    return this.clone()._rotateAround(a, p);
  },
  /**
   * Multiply this point by a 4x1 transformation matrix
   * @param {[number, number, number, number]} m transformation matrix
   * @return {Point} output point
   */
  matMult(m) {
    return this.clone()._matMult(m);
  },
  /**
   * Calculate this point but as a unit vector from 0, 0, meaning
   * that the distance from the resulting point to the 0, 0
   * coordinate will be equal to 1 and the angle from the resulting
   * point to the 0, 0 coordinate will be the same as before.
   * @return {Point} unit vector point
   */
  unit() {
    return this.clone()._unit();
  },
  /**
   * Compute a perpendicular point, where the new y coordinate
   * is the old x coordinate and the new x coordinate is the old y
   * coordinate multiplied by -1
   * @return {Point} perpendicular point
   */
  perp() {
    return this.clone()._perp();
  },
  /**
   * Return a version of this point with the x & y coordinates
   * rounded to integers.
   * @return {Point} rounded point
   */
  round() {
    return this.clone()._round();
  },
  /**
   * Return the magnitude of this point: this is the Euclidean
   * distance from the 0, 0 coordinate to this point's x and y
   * coordinates.
   * @return {number} magnitude
   */
  mag() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  },
  /**
   * Judge whether this point is equal to another point, returning
   * true or false.
   * @param {Point} other the other point
   * @return {boolean} whether the points are equal
   */
  equals(other) {
    return this.x === other.x && this.y === other.y;
  },
  /**
   * Calculate the distance from this point to another point
   * @param {Point} p the other point
   * @return {number} distance
   */
  dist(p) {
    return Math.sqrt(this.distSqr(p));
  },
  /**
   * Calculate the distance from this point to another point,
   * without the square root step. Useful if you're comparing
   * relative distances.
   * @param {Point} p the other point
   * @return {number} distance
   */
  distSqr(p) {
    const dx = p.x - this.x, dy = p.y - this.y;
    return dx * dx + dy * dy;
  },
  /**
   * Get the angle from the 0, 0 coordinate to this point, in radians
   * coordinates.
   * @return {number} angle
   */
  angle() {
    return Math.atan2(this.y, this.x);
  },
  /**
   * Get the angle from this point to another point, in radians
   * @param {Point} b the other point
   * @return {number} angle
   */
  angleTo(b) {
    return Math.atan2(this.y - b.y, this.x - b.x);
  },
  /**
   * Get the angle between this point and another point, in radians
   * @param {Point} b the other point
   * @return {number} angle
   */
  angleWith(b) {
    return this.angleWithSep(b.x, b.y);
  },
  /**
   * Find the angle of the two vectors, solving the formula for
   * the cross product a x b = |a||b|sin(θ) for θ.
   * @param {number} x the x-coordinate
   * @param {number} y the y-coordinate
   * @return {number} the angle in radians
   */
  angleWithSep(x, y) {
    return Math.atan2(
      this.x * y - this.y * x,
      this.x * x + this.y * y
    );
  },
  /** @param {[number, number, number, number]} m */
  _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;
  },
  /** @param {Point} p */
  _add(p) {
    this.x += p.x;
    this.y += p.y;
    return this;
  },
  /** @param {Point} p */
  _sub(p) {
    this.x -= p.x;
    this.y -= p.y;
    return this;
  },
  /** @param {number} k */
  _mult(k) {
    this.x *= k;
    this.y *= k;
    return this;
  },
  /** @param {number} k */
  _div(k) {
    this.x /= k;
    this.y /= k;
    return this;
  },
  /** @param {Point} p */
  _multByPoint(p) {
    this.x *= p.x;
    this.y *= p.y;
    return this;
  },
  /** @param {Point} p */
  _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;
  },
  /** @param {number} angle */
  _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;
  },
  /**
   * @param {number} angle
   * @param {Point} p
   */
  _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
};
Point.convert = function(p) {
  if (p instanceof Point) {
    return (
      /** @type {Point} */
      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");
};

// 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);
    }
  }
  // Suppress the next click, but only if it's immediate.
  // @ts-ignore
  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(
      // rect.left/top values are in page scale (like clientX/Y),
      // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
      (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);
    }
  }
};

// src/components/RGradientMarker/GradientMarker.ts
var defaultColor = "#ffe64b";
var defaultHeight = 50;
var GradientMarker = class extends maplibregl5.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 = () => {
    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) {
    maplibregl5.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);
    }
    maplibregl5.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, markerHeight2 = defaultHeight) {
    this._scale = scale;
    this._height = markerHeight2 * 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 === "pin" ? "bottom" : "center";
    this._element.dataset.shape = this._shape;
    this._update();
    return this;
  }
  getShape() {
    return this._shape;
  }
  setDraggable(shouldBeDraggable) {
    maplibregl5.Marker.prototype.setDraggable.apply(this, [shouldBeDraggable]);
    this._element.classList.toggle("draggable", shouldBeDraggable);
    return this;
  }
};

// src/components/RGradientMarker/RGradientMarker.tsx
import { forwardRef as forwardRef6, memo as memo5, useEffect as useEffect8, useImperativeHandle as useImperativeHandle6, useMemo as useMemo4, useRef as useRef7 } from "react";
var eventNameToCallbackName5 = {
  dragstart: "onDragStart",
  drag: "onDrag",
  dragend: "onDragEnd",
  click: "onClick"
};
var RGradientMarker = memo5(
  forwardRef6(function RGradientMarker2(props, ref) {
    const { longitude, latitude, ...markerProps } = props;
    const map = useMap();
    const [options, markerCallbacks] = transformPropsToOptions(markerProps);
    const prevOptionsRef = useRef7(options);
    const currCallbacksRef = useRef7();
    currCallbacksRef.current = markerCallbacks;
    const marker = useMemo4(() => {
      const mk = new GradientMarker({
        ...options,
        anchor: "bottom"
      });
      mk.setLngLat([longitude, latitude]);
      return mk;
    }, []);
    const eventDepStr = prepareEventDep(eventNameToCallbackName5, markerCallbacks).join("-");
    useEffect8(() => {
      function onGradientMarkerEvent(e) {
        const eventType = e.type;
        const callbackName = eventNameToCallbackName5[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]);
    useEffect8(() => {
      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;
    useImperativeHandle6(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);
    }
    if (marker.getIcon() !== icon) {
      marker.setIcon(icon);
    }
    prevOptionsRef.current = options;
    return null;
  })
);

// src/components/ContextMenuEventAdapter.ts
import { useEffect as useEffect9 } from "react";
function ContextMenuEventAdapter({
  customEventName = "contextmenu-maplibre",
  enabled = true
}) {
  const map = useMap();
  useEffect9(() => {
    if (!enabled) {
      return;
    }
    const eventName = window.matchMedia("(pointer: coarse)").matches ? "click" : "contextmenu";
    function handleContextMenu(evt) {
      setTimeout(() => {
        if (evt.defaultPrevented) {
          return;
        }
        const { originalEvent, point, lngLat } = evt;
        map.getCanvasContainer().dispatchEvent(
          new CustomEvent(customEventName, {
            detail: {
              originalEvent,
              point,
              lngLat,
              emulated: eventName !== "contextmenu"
            }
          })
        );
      });
    }
    map.on(eventName, handleContextMenu);
    return () => {
      map.off(eventName, handleContextMenu);
    };
  }, [map, enabled, customEventName]);
  return null;
}

// src/controls/MrcLogoControl.tsx
import { memo as memo6, useState as useState6 } from "react";

// src/hooks/useRControl.ts
import { useEffect as useEffect10, useRef as useRef8 } from "react";
function useRControl({
  position,
  className = "maplibregl-ctrl maplibregl-ctrl-group"
}) {
  const map = useMap();
  const containerRef = useRef8();
  const prevOptionsRef = useRef8({
    className
  });
  if (!containerRef.current) {
    const ctrl = document.createElement("div");
    ctrl.className = className;
    containerRef.current = ctrl;
  }
  useEffect10(() => {
    const ctrl = containerRef.current;
    if (ctrl && !ctrl.parentElement) {
      const positionContainer = map._controlPositions[position];
      if (!positionContainer) {
        throw new Error(`Unable to add control, position ${position} doesn't exists`);
      }
      if (position.indexOf("bottom") !== -1) {
        positionContainer.insertBefore(ctrl, positionContainer.firstChild);
      } else {
        positionContainer.appendChild(ctrl);
      }
    }
    return () => {
      containerRef.current && containerRef.current.remove();
    };
  }, [map, position]);
  if (prevOptionsRef.current.className !== className) {
    updateClassNames(
      containerRef.current,
      prevOptionsRef.current.className?.split(" ") || [],
      className?.split(" ") || []
    );
  }
  prevOptionsRef.current = {
    className
  };
  return {
    container: containerRef.current
  };
}

// src/controls/MrcLogoControl.tsx
import { createPortal as createPortal3 } from "react-dom";
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
function MapLibreReactLogo({ height = 27 }) {
  const [duration, setDuration] = useState6("25s");
  return /* @__PURE__ */ jsxs(
    "svg",
    {
      width: height * 21 / 27,
      height,
      viewBox: "-10.5 -10.5 21 27",
      fill: "none",
      version: "1.1",
      id: "svg3",
      xmlns: "http://www.w3.org/2000/svg",
      onMouseEnter: () => setDuration("2s"),
      onMouseLeave: () => setDuration("25s"),
      children: [
        /* @__PURE__ */ jsx3(
          "path",
          {
            fill: "#82b4fe",
            fillRule: "evenodd",
            d: "m 0.04378077,-7.0160133 c -3.95934197,-0.025693 -7.19979577,2.9709105 -7.22395607,6.67957072 -0.021351,3.28396108 1.6024867,4.88716988 3.3609363,6.62322508 1.2259466,1.2102943 2.5172339,2.4851233 3.36206037,4.4390285 0.056993,0.04631 0.095121,0.07722 0.1114948,0.09183 a 0.41450831,0.41450831 0 0 0 0.2727553,0.103225 0.41515047,0.41515047 0 0 0 0.2741963,-0.09961 c 0.00438,-0.0039 0.012527,-0.0074 0.022479,-0.01165 0.027304,-0.01171 0.068791,-0.02955 0.090222,-0.0789 C 1.1559887,8.7954149 2.4656545,7.5377628 3.7164045,6.3367792 5.4974902,4.626493 7.158975,3.0309869 7.1802478,-0.24301718 7.2043965,-3.9517594 4.0032063,-6.9901745 0.04378537,-7.016023 Z m -0.06903,10.6235008 C 1.9352357,3.6202595 3.5351559,2.0173639 3.5480768,0.02733852 3.5610008,-1.9626869 1.9821136,-3.5862858 0.02154667,-3.5990486 -1.9390213,-3.6118206 -3.5388592,-2.0089214 -3.5518634,-0.01889648 -3.5647874,1.9711289 -1.9858982,3.5947246 -0.02533123,3.6074875 Z"
          }
        ),
        /* @__PURE__ */ jsx3(
          "path",
          {
            fill: "currentColor",
            d: "m -2.4911489,12.068709 c -0.3270955,-0.0022 -0.5946317,0.359604 -0.5975214,0.807906 l -0.014767,2.269118 c -0.00295,0.448302 0.2598305,0.813445 0.587006,0.815611 l 5.0075702,0.03259 c 0.3270965,0.0022 0.5946306,-0.359606 0.5975218,-0.807909 l 0.014775,-2.269117 c 0.00304,-0.44838 -0.259831,-0.813523 -0.5870064,-0.815609 z"
          }
        ),
        /* @__PURE__ */ jsxs("g", { children: [
          /* @__PURE__ */ jsx3("circle", { cx: "0", cy: "0", r: "2", fill: "currentColor", id: "circle1" }),
          /* @__PURE__ */ jsxs("g", { stroke: "currentColor", strokeWidth: "1", fill: "none", id: "g3", children: [
            /* @__PURE__ */ jsx3("ellipse", { rx: "10", ry: "4.5", id: "ellipse1", cx: "0", cy: "0" }),
            /* @__PURE__ */ jsx3("ellipse", { rx: "10", ry: "4.5", transform: "rotate(60)", id: "ellipse2", cx: "0", cy: "0" }),
            /* @__PURE__ */ jsx3("ellipse", { rx: "10", ry: "4.5", transform: "rotate(120)", id: "ellipse3", cx: "0", cy: "0" })
          ] }),
          /* @__PURE__ */ jsx3(
            "animateTransform",
            {
              attributeName: "transform",
              type: "rotate",
              dur: duration,
              values: "0 0 0;360 0 0;",
              repeatCount: "indefinite"
            }
          )
        ] })
      ]
    }
  );
}
var MrcLogoControl = memo6(function MrcLogoControl2({
  position = "bottom-left"
}) {
  const { container } = useRControl({
    position,
    className: "maplibregl-ctrl maplibregl-ctrl-mrc-logo"
  });
  return createPortal3(
    /* @__PURE__ */ jsx3(
      "a",
      {
        target: "_blank",
        rel: "noopener nofollow",
        href: "https://maplibre-react-components.pentatrion.com",
        "aria-label": "MapLibre React components logo",
        children: /* @__PURE__ */ jsx3(MapLibreReactLogo, { height: 30 })
      }
    ),
    container
  );
});

// src/controls/RAttributionControl.ts
import maplibregl6 from "maplibre-gl";
import { memo as memo7, forwardRef as forwardRef7, useImperativeHandle as useImperativeHandle7 } from "react";

// src/hooks/useControl.ts
import { useEffect as useEffect11, useMemo as useMemo5 } from "react";

// src/hooks/useEventCallback.ts
import { useCallback as useCallback4, useRef as useRef9 } from "react";
function useEventCallback(callback) {
  const ref = useRef9(() => {
    throw new Error("Cannot call an event handler while rendering.");
  });
  ref.current = callback;
  return useCallback4((...args) => ref.current?.(...args), [ref]);
}

// src/hooks/useControl.ts
function useControl({
  position = "top-right",
  factory,
  onRemove
}) {
  const map = useMap();
  const ctrl = useMemo5(() => factory(map), [map]);
  const onRemoveStable = useEventCallback(onRemove || null);
  useEffect11(() => {
    if (!map.hasControl(ctrl)) {
      map.addControl(ctrl, position);
    }
    return () => {
      onRemoveStable && onRemoveStable(map);
      if (map.hasControl(ctrl)) {
        map.removeControl(ctrl);
      }
    };
  }, [map, ctrl, onRemoveStable, position]);
  return ctrl;
}

// src/controls/RAttributionControl.ts
var RAttributionControl = memo7(
  forwardRef7(function RAttributionControl2({ position = "bottom-right", ...controlOptions }, ref) {
    const control = useControl({
      position,
      factory: () => new maplibregl6.AttributionControl(controlOptions)
    });
    useImperativeHandle7(ref, () => control);
    return null;
  })
);

// src/controls/RFullscreenControl.ts
import maplibregl7 from "maplibre-gl";
import { memo as memo8, forwardRef as forwardRef8, useImperativeHandle as useImperativeHandle8 } from "react";
var RFullscreenControl = memo8(
  forwardRef8(function RFullscreenControl2({ position = "top-right", ...controlOptions }, ref) {
    const control = useControl({
      position,
      factory: () => new maplibregl7.FullscreenControl(controlOptions)
    });
    useImperativeHandle8(ref, () => control);
    return null;
  })
);

// src/controls/RGeolocateControl.ts
import maplibregl8 from "maplibre-gl";
import { memo as memo9, forwardRef as forwardRef9, useImperativeHandle as useImperativeHandle9 } from "react";
var RGeolocateControl = memo9(
  forwardRef9(function RGeolocateControl2({ position = "top-right", ...controlOptions }, ref) {
    const control = useControl({
      position,
      factory: () => new maplibregl8.GeolocateControl(controlOptions)
    });
    useImperativeHandle9(ref, () => control);
    return null;
  })
);

// src/controls/RGlobeControl.tsx
import maplibregl9 from "maplibre-gl";
import { forwardRef as forwardRef10, memo as memo10, useImperativeHandle as useImperativeHandle10 } from "react";
var RGlobeControl = memo10(
  forwardRef10(function RGlobeControl2({ position = "top-right" }, ref) {
    const control = useControl({
      position,
      factory: () => new maplibregl9.GlobeControl()
    });
    useImperativeHandle10(ref, () => control);
    return null;
  })
);

// src/controls/RLogoControl.ts
import maplibregl10 from "maplibre-gl";
import { memo as memo11, forwardRef as forwardRef11, useImperativeHandle as useImperativeHandle11 } from "react";
var RLogoControl = memo11(
  forwardRef11(function RLogoControl2({ position = "bottom-left", ...controlOptions }, ref) {
    const control = useControl({
      position,
      factory: () => new maplibregl10.LogoControl(controlOptions)
    });
    useImperativeHandle11(ref, () => control);
    return null;
  })
);

// src/controls/RNavigationControl.ts
import { forwardRef as forwardRef12, memo as memo12, useImperativeHandle as useImperativeHandle12 } from "react";
import maplibregl11 from "maplibre-gl";
var RNavigationControl = memo12(
  forwardRef12(function RNavigationControl2({ position = "top-right", ...controlOptions }, ref) {
    const control = useControl({
      position,
      factory: () => new maplibregl11.NavigationControl(controlOptions)
    });
    useImperativeHandle12(ref, () => control);
    return null;
  })
);

// src/controls/RScaleControl.ts
import maplibregl12 from "maplibre-gl";
import { memo as memo13, forwardRef as forwardRef13, useImperativeHandle as useImperativeHandle13 } from "react";
var RScaleControl = memo13(
  forwardRef13(function RScaleControl2({ position = "bottom-left", ...controlOptions }, ref) {
    const control = useControl({
      position,
      factory: () => new maplibregl12.ScaleControl(controlOptions)
    });
    useImperativeHandle13(ref, () => control);
    return null;
  })
);

// src/controls/RTerrainControl.ts
import maplibregl13 from "maplibre-gl";
import { memo as memo14, forwardRef as forwardRef14, useImperativeHandle as useImperativeHandle14 } from "react";
var RTerrainControl = memo14(
  forwardRef14(function RTerrainControl2({ position = "top-right", ...controlOptions }, ref) {
    const control = useControl({
      position,
      factory: () => new maplibregl13.TerrainControl(controlOptions)
    });
    useImperativeHandle14(ref, () => control);
    return null;
  })
);

// src/hooks/useMapAndCanvasRefs.ts
import { useCallback as useCallback5, useRef as useRef10 } from "react";
var useMapAndCanvasRefs = () => {
  const mapRef = useRef10(null);
  const canvasRef = useRef10(null);
  const setMapAndCanvasRef = useCallback5((map) => {
    mapRef.current = map;
    canvasRef.current = map?.getCanvasContainer();
  }, []);
  return { mapRef, canvasRef, setMapAndCanvasRef };
};

// src/hooks/useCanvasRef.ts
import { useRef as useRef11 } from "react";
var useCanvasRef = () => {
  const map = useMap();
  const canvasRef = useRef11(map.getCanvasContainer());
  return canvasRef;
};
export {
  ContextMenuEventAdapter,
  CurrentMapIdContext,
  GradientMarker,
  MapLibreReactLogo,
  MapManager,
  MrcLogoControl,
  RAttributionControl,
  RFullscreenControl,
  RGeolocateControl,
  RGlobeControl,
  RGradientMarker,
  RLayer,
  RLogoControl,
  RMap,
  RMapContext,
  RMapContextProvider,
  RMarker,
  RNavigationControl,
  RPopup,
  RScaleControl,
  RSource,
  RTerrain,
  RTerrainControl,
  areCoordsClose,
  areLngLatClose,
  arePointsEqual,
  deepEqual,
  emptyStyle,
  filterMapProps,
  gradientMarkerPopupOffset,
  lngLatClassToObj,
  markerPopupOffset,
  prepareEventDep,
  transformPropsToOptions,
  uniqueId,
  updateClassNames,
  updateListeners,
  useCanvasRef,
  useControl,
  useMap,
  useMapAndCanvasRefs,
  useMapManager,
  useRControl
};