UNPKG

react-bkoi-gl

Version:

A React library for Barikoi Maps with interactive components, markers, popups, and controls.

1,591 lines (1,560 loc) 315 kB
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 __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { AttributionControl: () => AttributionControl, CanvasSource: () => CanvasSource, DrawControl: () => DrawControl, FullscreenControl: () => FullscreenControl, GeolocateControl: () => GeolocateControl, GlobeControl: () => GlobeControl, Layer: () => Layer, LogoControl: () => LogoControl, Map: () => Map, MapProvider: () => MapProvider, Marker: () => Marker, Minimap: () => Minimap, MinimapControl: () => MinimapControl, NavigationControl: () => NavigationControl, Popup: () => Popup, ScaleControl: () => ScaleControl, Source: () => Source, TerrainControl: () => TerrainControl, default: () => exports_maplibre_gl_default, logger: () => logger, setLogger: () => setLogger, useControl: () => useControl, useMap: () => useMap }); module.exports = __toCommonJS(index_exports); // src/components/map.tsx var React2 = __toESM(require("react"), 1); var import_react6 = require("react"); // src/components/use-map.tsx var React = __toESM(require("react"), 1); var import_react = require("react"); var MountedMapsContext = React.createContext(null); var MapProvider = (props) => { const [maps, setMaps] = (0, import_react.useState)({}); const onMapMount = (0, import_react.useCallback)((map, id = "default") => { setMaps((currMaps) => { if (id === "current") { throw new Error("'current' cannot be used as map id"); } if (currMaps[id]) { throw new Error(`Multiple maps with the same id: ${id}`); } return { ...currMaps, [id]: map }; }); }, []); const onMapUnmount = (0, import_react.useCallback)((id = "default") => { setMaps((currMaps) => { if (currMaps[id]) { const nextMaps = { ...currMaps }; delete nextMaps[id]; return nextMaps; } return currMaps; }); }, []); return /* @__PURE__ */ React.createElement( MountedMapsContext.Provider, { value: { maps, onMapMount, onMapUnmount } }, props.children ); }; function useMap() { const maps = (0, import_react.useContext)(MountedMapsContext)?.maps; const currentMap = (0, import_react.useContext)(MapContext); const mapsWithCurrent = (0, import_react.useMemo)(() => { const current = currentMap?.map || (maps ? Object.values(maps)[0] : void 0); return { ...maps, current }; }, [maps, currentMap]); return mapsWithCurrent; } // src/utils/deep-equal.ts 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 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; } // src/utils/transform.ts function transformToViewState(tr) { return { longitude: tr.center.lng, latitude: tr.center.lat, zoom: tr.zoom, pitch: tr.pitch, bearing: tr.bearing, padding: tr.padding }; } function applyViewStateToTransform(tr, props) { const v = props.viewState || props; const changes = {}; if ("longitude" in v && "latitude" in v && (v.longitude !== tr.center.lng || v.latitude !== tr.center.lat)) { const LngLat = tr.center.constructor; changes.center = new LngLat(v.longitude, v.latitude); } if ("zoom" in v && v.zoom !== tr.zoom) { changes.zoom = v.zoom; } if ("bearing" in v && v.bearing !== tr.bearing) { changes.bearing = v.bearing; } if ("pitch" in v && v.pitch !== tr.pitch) { changes.pitch = v.pitch; } if (v.padding && tr.padding && !deepEqual(v.padding, tr.padding)) { changes.padding = v.padding; } return changes; } // src/utils/style-utils.ts var refProps = ["type", "source", "source-layer", "minzoom", "maxzoom", "filter", "layout"]; function normalizeStyle(style) { if (!style) { return null; } if (typeof style === "string") { return style; } if ("toJS" in style) { style = style.toJS(); } if (!style.layers) { return style; } const layerIndex = {}; for (const layer of style.layers) { layerIndex[layer.id] = layer; } const layers = style.layers.map((layer) => { const legacyLayer = layer; let normalizedLayer = null; if ("interactive" in legacyLayer) { normalizedLayer = Object.assign({}, legacyLayer); delete normalizedLayer.interactive; } const layerRef = layerIndex[legacyLayer.ref]; if (layerRef) { normalizedLayer = normalizedLayer || Object.assign({}, legacyLayer); delete normalizedLayer.ref; for (const propName of refProps) { if (propName in layerRef) { normalizedLayer[propName] = layerRef[propName]; } } } return normalizedLayer || layer; }); return { ...style, layers }; } // src/utils/logger.ts var activeLogger = { warn: (message, ...args) => console.warn(message, ...args), error: (message, ...args) => console.error(message, ...args) }; var logger = { warn(message, ...args) { activeLogger.warn(message, ...args); }, error(message, ...args) { activeLogger.error(message, ...args); } }; function setLogger(customLogger) { activeLogger = { warn: typeof customLogger.warn === "function" ? customLogger.warn : (m, ...a) => console.warn(m, ...a), error: typeof customLogger.error === "function" ? customLogger.error : (m, ...a) => console.error(m, ...a) }; } // src/maplibre/maplibre.ts var DEFAULT_STYLE = { version: 8, sources: {}, layers: [] }; var pointerEvents = { mousedown: "onMouseDown", mouseup: "onMouseUp", mouseover: "onMouseOver", mousemove: "onMouseMove", click: "onClick", dblclick: "onDblClick", mouseenter: "onMouseEnter", mouseleave: "onMouseLeave", mouseout: "onMouseOut", contextmenu: "onContextMenu", touchstart: "onTouchStart", touchend: "onTouchEnd", touchmove: "onTouchMove", touchcancel: "onTouchCancel" }; var cameraEvents = { 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" }; var otherEvents = { wheel: "onWheel", boxzoomstart: "onBoxZoomStart", boxzoomend: "onBoxZoomEnd", boxzoomcancel: "onBoxZoomCancel", resize: "onResize", load: "onLoad", render: "onRender", idle: "onIdle", remove: "onRemove", data: "onData", styledata: "onStyleData", sourcedata: "onSourceData", error: "onError" }; var settingNames = [ "minZoom", "maxZoom", "minPitch", "maxPitch", "maxBounds", "projection", "renderWorldCopies" ]; var handlerNames = [ "scrollZoom", "boxZoom", "dragRotate", "dragPan", "keyboard", "doubleClickZoom", "touchZoomRotate", "touchPitch" ]; var Maplibre = class _Maplibre { _MapClass; // mapboxgl.Map instance _map = null; // User-supplied props props; // Internal states _internalUpdate = false; _hoveredFeatures = null; _propsedCameraUpdate = null; // Suppresses repeated queryRenderedFeatures warnings while the style loads _warnedQueryFeatures = false; _styleComponents = {}; static savedMaps = []; constructor(MapClass, props, container) { this._MapClass = MapClass; this.props = props; this._initialize(container); } get map() { return this._map; } setProps(props) { const oldProps = this.props; this.props = props; const settingsChanged = this._updateSettings(props, oldProps); const sizeChanged = this._updateSize(props); const viewStateChanged = this._updateViewState(props); this._updateStyle(props, oldProps); this._updateStyleComponents(props); this._updateHandlers(props, oldProps); if (settingsChanged || sizeChanged || viewStateChanged && !this._map.isMoving()) { this.redraw(); } } static reuse(props, container) { const that = _Maplibre.savedMaps.pop(); if (!that) { return null; } const map = that.map; const oldContainer = map.getContainer(); container.className = oldContainer.className; while (oldContainer.childNodes.length > 0) { container.appendChild(oldContainer.childNodes[0]); } const mapInternal = map; if (mapInternal && "_container" in mapInternal) { mapInternal._container = container; } const resizeObserver = mapInternal?._resizeObserver; if (resizeObserver && typeof resizeObserver.disconnect === "function" && typeof resizeObserver.observe === "function") { resizeObserver.disconnect(); resizeObserver.observe(container); } that.setProps({ ...props, styleDiffing: false }); map.resize(); const { initialViewState } = props; if (initialViewState) { if (initialViewState.bounds) { map.fitBounds(initialViewState.bounds, { ...initialViewState.fitBoundsOptions, duration: 0 }); } else { that._updateViewState(initialViewState); } } if (map.style && map.isStyleLoaded()) { map.fire("load"); } else { map.once("style.load", () => map.fire("load")); } const mapInternalForUpdate = map; if (mapInternalForUpdate && typeof mapInternalForUpdate._update === "function") { mapInternalForUpdate._update(); } return that; } _initialize(container) { const { props } = this; const { mapStyle = DEFAULT_STYLE } = props; const mapOptions = { ...props, ...props.initialViewState, container, style: normalizeStyle(mapStyle) }; const viewState = mapOptions.initialViewState || mapOptions.viewState || mapOptions; Object.assign(mapOptions, { center: [viewState.longitude || 0, viewState.latitude || 0], zoom: viewState.zoom || 0, pitch: viewState.pitch || 0, bearing: viewState.bearing || 0 }); let savedGetContext = null; if (props.gl) { savedGetContext = HTMLCanvasElement.prototype.getContext; HTMLCanvasElement.prototype.getContext = () => props.gl; } let map; try { map = new this._MapClass(mapOptions); } finally { if (savedGetContext) { HTMLCanvasElement.prototype.getContext = savedGetContext; } } if (viewState.padding) { map.setPadding(viewState.padding); } if (props.cursor) { map.getCanvas().style.cursor = props.cursor; } map.transformCameraUpdate = this._onCameraUpdate; map.on("style.load", () => { const mapWithProjection = map; this._styleComponents = { light: map.getLight(), sky: map.getSky(), projection: mapWithProjection.getProjection?.(), terrain: map.getTerrain() }; this._updateStyleComponents(this.props); }); map.on("sourcedata", () => { this._updateStyleComponents(this.props); }); for (const eventName in pointerEvents) { map.on(eventName, this._onPointerEvent); } for (const eventName in cameraEvents) { map.on(eventName, this._onCameraEvent); } for (const eventName in otherEvents) { map.on(eventName, this._onEvent); } this._map = map; } recycle() { const container = this.map.getContainer(); const children = container.querySelector("[mapboxgl-children]"); children?.remove(); _Maplibre.savedMaps.push(this); } destroy() { this._map.remove(); } // Force redraw the map now. Typically resize() and jumpTo() is reflected in the next // render cycle, which is managed by Mapbox's animation loop. // This removes the synchronization issue caused by requestAnimationFrame. redraw() { const map = this._map; if (map && map.style) { if (typeof map._render === "function") { if (map._frame && typeof map._frame.cancel === "function") { map._frame.cancel(); map._frame = null; } map._render(); } else if (typeof this._map.triggerRepaint === "function") { this._map.triggerRepaint(); } } } /* Trigger map resize if size is controlled @param {object} nextProps @returns {bool} true if size has changed */ _updateSize(nextProps) { const { viewState } = nextProps; if (viewState) { const map = this._map; if (viewState.width !== map.transform.width || viewState.height !== map.transform.height) { map.resize(); return true; } } return false; } // Adapted from map.jumpTo /* Update camera to match props @param {object} nextProps @param {bool} triggerEvents - should fire camera events @returns {bool} true if anything is changed */ _updateViewState(nextProps) { const map = this._map; const tr = map.transform; const isMoving = map.isMoving(); if (!isMoving) { const changes = applyViewStateToTransform(tr, nextProps); if (Object.keys(changes).length > 0) { this._internalUpdate = true; map.jumpTo(changes); this._internalUpdate = false; return true; } } return false; } /* Update camera constraints and projection settings to match props @param {object} nextProps @param {object} currProps @returns {bool} true if anything is changed */ _updateSettings(nextProps, currProps) { const map = this._map; let changed = false; for (const propName of settingNames) { if (propName in nextProps && !deepEqual(nextProps[propName], currProps[propName])) { changed = true; const setter = map[`set${propName[0].toUpperCase()}${propName.slice(1)}`]; setter?.call(map, nextProps[propName]); } } return changed; } /* Update map style to match props */ _updateStyle(nextProps, currProps) { if (nextProps.cursor !== currProps.cursor) { this._map.getCanvas().style.cursor = nextProps.cursor || ""; } if (nextProps.mapStyle !== currProps.mapStyle) { const { mapStyle = DEFAULT_STYLE, styleDiffing = true } = nextProps; const options = { diff: styleDiffing }; if ("localIdeographFontFamily" in nextProps) { options.localIdeographFontFamily = nextProps.localIdeographFontFamily; } this._map.setStyle(normalizeStyle(mapStyle), options); } } /* Update fog, light, projection and terrain to match props * These props are special because * 1. They can not be applied right away. Certain conditions (style loaded, source loaded, etc.) must be met * 2. They can be overwritten by mapStyle */ _updateStyleComponents({ light, projection, sky, terrain }) { const map = this._map; const currProps = this._styleComponents; const mapWithProjection = map; if (map.style && map.isStyleLoaded()) { if (light && !deepEqual(light, currProps.light)) { currProps.light = light; map.setLight(light); } if (projection && !deepEqual(projection, currProps.projection) && projection !== currProps.projection?.type) { currProps.projection = typeof projection === "string" ? { type: projection } : projection; mapWithProjection.setProjection?.(currProps.projection); } if (sky && !deepEqual(sky, currProps.sky)) { currProps.sky = sky; map.setSky(sky); } if (terrain !== void 0 && !deepEqual(terrain, currProps.terrain)) { if (!terrain || map.getSource(terrain.source)) { currProps.terrain = terrain; map.setTerrain(terrain); } } } } /* Update interaction handlers to match props */ _updateHandlers(nextProps, currProps) { const map = this._map; for (const propName of handlerNames) { const newValue = nextProps[propName] ?? true; const oldValue = currProps[propName] ?? true; if (!deepEqual(newValue, oldValue)) { if (newValue) { map[propName].enable(newValue); } else { map[propName].disable(); } } } } _onEvent = (e2) => { const handlerName = otherEvents[e2.type]; const cb = this.props[handlerName]; if (cb) { cb(e2); } else if (e2.type === "error") { logger.error(e2.error); } }; _onCameraEvent = (e2) => { if (this._internalUpdate) { return; } e2.viewState = this._propsedCameraUpdate || transformToViewState(this._map.transform); const handlerName = cameraEvents[e2.type]; const cb = this.props[handlerName]; if (cb) { cb(e2); } }; _onCameraUpdate = (tr) => { if (this._internalUpdate) { return tr; } this._propsedCameraUpdate = transformToViewState(tr); return applyViewStateToTransform(tr, this.props); }; /** Surface a non-fatal warning via the consumer's onWarning callback, * falling back to console.warn when none is provided. */ _warn(error) { const cb = this.props.onWarning; if (cb) { cb({ type: "error", target: this._map, originalEvent: null, error }); } else { logger.warn(error); } } _queryRenderedFeatures(point2) { const map = this._map; const { interactiveLayerIds = [] } = this.props; try { return map.queryRenderedFeatures(point2, { layers: interactiveLayerIds.filter(map.getLayer.bind(map)) }); } catch (err) { if (!this._warnedQueryFeatures) { this._warnedQueryFeatures = true; this._warn(err instanceof Error ? err : new Error(String(err))); } return []; } } _updateHover(e2) { const { props } = this; const shouldTrackHoveredFeatures = props.interactiveLayerIds && (props.onMouseMove || props.onMouseEnter || props.onMouseLeave); if (shouldTrackHoveredFeatures) { const eventType = e2.type; const wasHovering = this._hoveredFeatures?.length > 0; const features = this._queryRenderedFeatures(e2.point); const isHovering = features.length > 0; if (!isHovering && wasHovering) { e2.type = "mouseleave"; this._onPointerEvent(e2); } this._hoveredFeatures = features; if (isHovering && !wasHovering) { e2.type = "mouseenter"; this._onPointerEvent(e2); } e2.type = eventType; } else { this._hoveredFeatures = null; } } _onPointerEvent = (e2) => { if (e2.type === "mousemove" || e2.type === "mouseout") { this._updateHover(e2); } const handlerName = pointerEvents[e2.type]; const cb = this.props[handlerName]; if (cb) { if (this.props.interactiveLayerIds && e2.type !== "mouseover" && e2.type !== "mouseout") { e2.features = this._hoveredFeatures || this._queryRenderedFeatures(e2.point); } cb(e2); delete e2.features; } }; }; // src/maplibre/create-ref.ts var skipMethods = [ "setMaxBounds", "setMinZoom", "setMaxZoom", "setMinPitch", "setMaxPitch", "setRenderWorldCopies", "setProjection", "setStyle", "addSource", "removeSource", "addLayer", "removeLayer", "setLayerZoomRange", "setFilter", "setPaintProperty", "setLayoutProperty", "setLight", "setTerrain", "setFog", "remove" ]; function createRef(mapInstance) { if (!mapInstance) { return null; } const map = mapInstance.map; const result = { getMap: () => map }; for (const key of getMethodNames(map)) { if (!(key in result) && !skipMethods.includes(key)) { result[key] = map[key].bind(map); } } return result; } function getMethodNames(obj) { const result = /* @__PURE__ */ new Set(); let proto = obj; while (proto) { for (const key of Object.getOwnPropertyNames(proto)) { if (key[0] !== "_" && typeof obj[key] === "function" && key !== "fire" && key !== "setEventedParent") { result.add(key); } } proto = Object.getPrototypeOf(proto); } return Array.from(result); } // src/utils/use-isomorphic-layout-effect.ts var import_react2 = require("react"); var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react2.useLayoutEffect : import_react2.useEffect; var use_isomorphic_layout_effect_default = useIsomorphicLayoutEffect; // src/utils/set-globals.ts var validateUrl = (url, settingName) => { try { const parsed = new URL(url); if (!["http:", "https:"].includes(parsed.protocol)) { logger.warn(`${settingName}: Only http/https protocols are allowed, got: ${parsed.protocol}`); return false; } return true; } catch { logger.warn(`${settingName}: Invalid URL format: ${url}`); return false; } }; function setGlobals(mapLib, props) { const { RTLTextPlugin, maxParallelImageRequests, workerCount, workerUrl } = props; if (RTLTextPlugin && mapLib.getRTLTextPluginStatus && mapLib.getRTLTextPluginStatus() === "unavailable") { const { pluginUrl, lazy = true } = typeof RTLTextPlugin === "string" ? { pluginUrl: RTLTextPlugin } : RTLTextPlugin; if (validateUrl(pluginUrl, "RTLTextPlugin")) { if (typeof mapLib.setRTLTextPlugin !== "function") { logger.warn( `RTLTextPlugin was configured but the provided mapLib does not expose setRTLTextPlugin. Right-to-left scripts (Arabic, Hebrew) will not render correctly.` ); } else { mapLib.setRTLTextPlugin( pluginUrl, (error) => { if (error) { logger.error(error); } }, lazy ); } } } if (maxParallelImageRequests !== void 0) { mapLib.setMaxParallelImageRequests?.(maxParallelImageRequests); } if (workerCount !== void 0) { mapLib.setWorkerCount?.(workerCount); } if (workerUrl !== void 0) { if (validateUrl(workerUrl, "workerUrl")) { mapLib.setWorkerUrl?.(workerUrl); } } } // src/components/logo-control.ts var import_react4 = require("react"); // src/utils/apply-react-style.ts var unitlessNumber = /box|flex|grid|column|lineHeight|fontWeight|opacity|order|tabSize|zIndex/; function applyReactStyle(element, styles) { if (!element || !styles) { return; } const style = element.style; for (const key in styles) { const value = styles[key]; if (Number.isFinite(value) && !unitlessNumber.test(key)) { style[key] = `${value}px`; } else { style[key] = value; } } } // src/components/use-control.ts var import_react3 = require("react"); function useControl(onCreate, arg1, arg2, arg3) { const context = (0, import_react3.useContext)(MapContext); if (!context) { throw new Error("useControl must be used within a Map component"); } const ctrl = (0, import_react3.useMemo)(() => onCreate(context), []); (0, import_react3.useEffect)(() => { const opts = arg3 || arg2 || arg1; const onAdd = typeof arg1 === "function" && typeof arg2 === "function" ? arg1 : null; const onRemove = typeof arg2 === "function" ? arg2 : typeof arg1 === "function" ? arg1 : null; const { map } = context; const ctrlAsIControl = ctrl; if (!map.hasControl(ctrlAsIControl)) { map.addControl(ctrlAsIControl, opts?.position); if (onAdd) { onAdd(context); } } return () => { if (onRemove) { onRemove(context); } if (map.hasControl(ctrlAsIControl)) { map.removeControl(ctrlAsIControl); } }; }, []); return ctrl; } // src/components/logo-control.ts function _LogoControl(props) { const ctrl = useControl( () => { const control = { onAdd: (map) => { if (map.getContainer) { const existingLogo = map.getContainer().querySelector('a.maplibregl-ctrl-logo[href="https://www.barikoi.com"]'); if (existingLogo) { existingLogo.remove(); } } const container = document.createElement("a"); container.className = "maplibregl-ctrl-logo"; container.href = "https://www.barikoi.com"; container.target = "_blank"; container.setAttribute("alt", "Barikoi"); container.setAttribute("aria-label", "Barikoi logo"); container.setAttribute("rel", "noopener nofollow"); control._container = container; return container; }, onRemove: () => { delete control._container; } }; return control; }, { position: props.position } ); (0, import_react4.useEffect)(() => { applyReactStyle(ctrl._container, props.style); }, [props.style, ctrl._container]); return null; } var LogoControl = (0, import_react4.memo)(_LogoControl); // src/components/attribution-control.ts var import_react5 = require("react"); function _AttributionControl(props) { const { current: map } = useMap(); const ctrl = useControl( ({ mapLib }) => new mapLib.AttributionControl({ compact: true, ...props }), { position: props.position } ); (0, import_react5.useEffect)(() => { applyReactStyle(ctrl._container, props.style); if (!ctrl._container || !map) return; const onLoad = () => { setTimeout(() => { const inner = ctrl._container.querySelector(".maplibregl-ctrl-attrib-inner"); if (inner instanceof HTMLElement) { inner.textContent = ""; const createLink = (text, href) => { const a = document.createElement("a"); a.href = href; a.target = "_blank"; a.rel = "noopener noreferrer"; a.textContent = text; return a; }; inner.appendChild(createLink("Barikoi", "https://barikoi.com")); inner.appendChild(document.createTextNode(" \xA9 ")); inner.appendChild(createLink("OpenMapTiles", "https://openmaptiles.org")); inner.appendChild(document.createTextNode(" \xA9 ")); inner.appendChild( createLink("OpenStreetMap contributors", "https://www.openstreetmap.org/copyright") ); } else { logger.warn( "AttributionControl: .maplibregl-ctrl-attrib-inner element not found in control container. Legally-required attribution styling was not applied." ); } }, 0); }; if (map.loaded()) { onLoad(); } else { map.once("load", onLoad); } return () => { map.off("load", onLoad); }; }, [props.style, ctrl._container, map]); return null; } var AttributionControl = (0, import_react5.memo)(_AttributionControl); // src/components/map.tsx var MapContext = React2.createContext(null); function _Map(props, ref) { const mountedMapsContext = (0, import_react6.useContext)(MountedMapsContext); const [mapInstance, setMapInstance] = (0, import_react6.useState)(null); const containerRef = (0, import_react6.useRef)(null); const propsRef = (0, import_react6.useRef)(props); propsRef.current = props; const prevPropsRef = (0, import_react6.useRef)(props); const { current: contextValue } = (0, import_react6.useRef)({ mapLib: null, map: null }); (0, import_react6.useEffect)(() => { const initialMapLib = propsRef.current.mapLib; const initialId = propsRef.current.id; const initialReuseMaps = propsRef.current.reuseMaps; let isMounted = true; let maplibre = null; Promise.resolve(initialMapLib || import("maplibre-gl")).then((module2) => { if (!isMounted) { return; } if (!module2) { throw new Error("Invalid mapLib"); } const mapboxgl = "Map" in module2 ? module2 : module2.default; if (!mapboxgl.Map) { throw new Error("Invalid mapLib"); } setGlobals(mapboxgl, propsRef.current); if (initialReuseMaps) { maplibre = Maplibre.reuse(propsRef.current, containerRef.current); } if (!maplibre) { maplibre = new Maplibre( mapboxgl.Map, { ...propsRef.current, attributionControl: false }, containerRef.current ); } if (maplibre) { contextValue.map = createRef(maplibre); contextValue.mapLib = mapboxgl; setMapInstance(maplibre); } mountedMapsContext?.onMapMount(contextValue.map, propsRef.current.id || initialId); }).catch((error) => { const { onError } = propsRef.current; if (onError) { onError({ type: "error", target: null, originalEvent: null, error }); } else { logger.error(error); } }); return () => { isMounted = false; if (maplibre) { mountedMapsContext?.onMapUnmount(propsRef.current.id || initialId); if (initialReuseMaps) { maplibre.recycle(); } else { maplibre.destroy(); } } }; }, []); const latestPropsRef = (0, import_react6.useRef)(props); latestPropsRef.current = props; use_isomorphic_layout_effect_default(() => { if (!mapInstance) return; const prevProps = prevPropsRef.current; const currentProps = latestPropsRef.current; if (!prevProps) { prevPropsRef.current = currentProps; return; } let propsChanged = false; for (const key in currentProps) { if (key !== "children" && key !== "mapLib") { if (currentProps[key] !== prevProps[key]) { propsChanged = true; break; } } } if (!propsChanged) { for (const key in prevProps) { if (key !== "children" && key !== "mapLib") { if (!(key in currentProps)) { propsChanged = true; break; } } } } if (propsChanged) { mapInstance.setProps(currentProps); } prevPropsRef.current = currentProps; }, [mapInstance]); (0, import_react6.useImperativeHandle)(ref, () => contextValue.map, [mapInstance]); const style = (0, import_react6.useMemo)( () => ({ position: "relative", width: "100%", height: "100%", ...props.style }), [props.style] ); const CHILD_CONTAINER_STYLE = { height: "100%" }; contextValue.onWarning = props.onWarning; return /* @__PURE__ */ React2.createElement("div", { id: props.id, ref: containerRef, style }, mapInstance && /* @__PURE__ */ React2.createElement(MapContext.Provider, { value: contextValue }, /* @__PURE__ */ React2.createElement("div", { style: CHILD_CONTAINER_STYLE }, props.showBarikoiLogo !== false && /* @__PURE__ */ React2.createElement(LogoControl, { position: "bottom-left" }), props.showAttribution !== false && /* @__PURE__ */ React2.createElement(AttributionControl, { position: "bottom-right" }), props.children))); } var Map = React2.forwardRef(_Map); // src/components/marker.ts var React3 = __toESM(require("react"), 1); var import_react_dom = require("react-dom"); var import_react7 = require("react"); // src/utils/compare-class-names.ts function compareClassNames(prevClassName, nextClassName) { if (prevClassName === nextClassName) { return null; } const prevClassList = getClassList(prevClassName); const nextClassList = getClassList(nextClassName); const diff = []; for (const c of nextClassList) { if (!prevClassList.has(c)) { diff.push(c); } } for (const c of prevClassList) { if (!nextClassList.has(c)) { diff.push(c); } } return diff.length === 0 ? null : diff; } function getClassList(className) { return new Set(className ? className.trim().split(/\s+/) : []); } // src/components/marker.ts var Marker = (0, import_react7.memo)( (0, import_react7.forwardRef)((props, ref) => { const { map, mapLib } = (0, import_react7.useContext)(MapContext); const callbackRef = (0, import_react7.useRef)({}); const marker = (0, import_react7.useMemo)(() => { let hasChildren = false; React3.Children.forEach(props.children, (el) => { if (el) { hasChildren = true; } }); const options = { ...props, element: hasChildren ? document.createElement("div") : void 0 }; const mk = new mapLib.Marker(options); mk.setLngLat([props.longitude, props.latitude]); return mk; }, []); (0, import_react7.useEffect)(() => { callbackRef.current = { onClick: props.onClick, onDragStart: props.onDragStart, onDrag: props.onDrag, onDragEnd: props.onDragEnd }; }); (0, import_react7.useEffect)(() => { const clickHandler = (e2) => { callbackRef.current.onClick?.({ type: "click", target: marker, originalEvent: e2 }); }; marker.getElement().addEventListener("click", clickHandler); const dragStartHandler = (e2) => { e2.lngLat = marker.getLngLat(); callbackRef.current.onDragStart?.(e2); }; const dragHandler = (e2) => { e2.lngLat = marker.getLngLat(); callbackRef.current.onDrag?.(e2); }; const dragEndHandler = (e2) => { e2.lngLat = marker.getLngLat(); callbackRef.current.onDragEnd?.(e2); }; marker.on("dragstart", dragStartHandler); marker.on("drag", dragHandler); marker.on("dragend", dragEndHandler); marker.addTo(map.getMap()); return () => { marker.getElement().removeEventListener("click", clickHandler); marker.off("dragstart", dragStartHandler); marker.off("drag", dragHandler); marker.off("dragend", dragEndHandler); marker.remove(); }; }, []); const { longitude, latitude, offset, style, draggable = false, popup = null, rotation = 0, rotationAlignment = "auto", pitchAlignment = "auto", className } = props; (0, import_react7.useEffect)(() => { applyReactStyle(marker.getElement(), style); }, [style]); (0, import_react7.useImperativeHandle)(ref, () => marker, []); const prevClassNameRef = (0, import_react7.useRef)(className); (0, import_react7.useEffect)(() => { if (marker.getLngLat().lng !== longitude || marker.getLngLat().lat !== latitude) { marker.setLngLat([longitude, latitude]); } if (offset && !arePointsEqual(marker.getOffset(), offset)) { marker.setOffset(offset); } if (marker.isDraggable() !== draggable) { marker.setDraggable(draggable); } if (marker.getRotation() !== rotation) { marker.setRotation(rotation); } if (marker.getRotationAlignment() !== rotationAlignment) { marker.setRotationAlignment(rotationAlignment); } if (marker.getPitchAlignment() !== pitchAlignment) { marker.setPitchAlignment(pitchAlignment); } if (marker.getPopup() !== popup) { marker.setPopup(popup); } const classNameDiff = compareClassNames(prevClassNameRef.current, className); if (classNameDiff) { for (const c of classNameDiff) { marker.toggleClassName(c); } } prevClassNameRef.current = className; }); return (0, import_react_dom.createPortal)(props.children, marker.getElement()); }) ); // src/components/popup.ts var import_react_dom2 = require("react-dom"); var import_react8 = require("react"); var Popup = (0, import_react8.memo)( (0, import_react8.forwardRef)((props, ref) => { const { map, mapLib } = (0, import_react8.useContext)(MapContext); const container = (0, import_react8.useMemo)(() => { return document.createElement("div"); }, []); const popup = (0, import_react8.useMemo)(() => { const options = { ...props }; const pp = new mapLib.Popup(options); pp.setLngLat([props.longitude, props.latitude]); return pp; }, []); (0, import_react8.useImperativeHandle)(ref, () => popup, []); (0, import_react8.useEffect)(() => { const onOpen = (e2) => { props.onOpen?.(e2); }; const onClose = (e2) => { props.onClose?.(e2); }; popup.on("open", onOpen); popup.on("close", onClose); popup.setDOMContent(container).addTo(map.getMap()); return () => { popup.off("open", onOpen); popup.off("close", onClose); if (popup.isOpen()) { popup.remove(); } }; }, []); (0, import_react8.useEffect)(() => { applyReactStyle(popup.getElement(), props.style); }, [props.style]); (0, import_react8.useEffect)(() => { if (popup.isOpen()) { if (popup.getLngLat().lng !== props.longitude || popup.getLngLat().lat !== props.latitude) { popup.setLngLat([props.longitude, props.latitude]); } } }, [props.longitude, props.latitude]); (0, import_react8.useEffect)(() => { if (popup.isOpen() && props.offset) { popup.setOffset(props.offset); } }, [props.offset]); (0, import_react8.useEffect)(() => { if (popup.isOpen() && props.maxWidth !== void 0) { popup.setMaxWidth(props.maxWidth); } }, [props.maxWidth]); (0, import_react8.useEffect)(() => { if (popup.isOpen() && props.className) { const currentClassName = popup.options.className || ""; const classNameDiff = compareClassNames(currentClassName, props.className); if (classNameDiff) { for (const c of classNameDiff) { popup.toggleClassName(c); } } } }, [props.className]); return (0, import_react_dom2.createPortal)(props.children, container); }) ); // src/components/fullscreen-control.ts var import_react9 = require("react"); function _FullscreenControl(props) { const ctrl = useControl( ({ mapLib }) => new mapLib.FullscreenControl({ container: props.containerId && document.getElementById(props.containerId) }), { position: props.position } ); (0, import_react9.useEffect)(() => { applyReactStyle(ctrl._controlContainer, props.style); }, [props.style]); return null; } var FullscreenControl = (0, import_react9.memo)(_FullscreenControl); // src/components/geolocate-control.ts var import_react10 = require("react"); function _GeolocateControl(props, ref) { const thisRef = (0, import_react10.useRef)({ props }); const ctrl = useControl( ({ mapLib }) => { const gc = new mapLib.GeolocateControl(props); const setupUI = gc._setupUI; gc._setupUI = () => { if (!gc._container.hasChildNodes()) { setupUI(); } }; gc.on("geolocate", (e2) => { thisRef.current.props.onGeolocate?.(e2); }); gc.on("error", (e2) => { thisRef.current.props.onError?.(e2); }); gc.on("outofmaxbounds", (e2) => { thisRef.current.props.onOutOfMaxBounds?.(e2); }); gc.on("trackuserlocationstart", (e2) => { thisRef.current.props.onTrackUserLocationStart?.(e2); }); gc.on("trackuserlocationend", (e2) => { thisRef.current.props.onTrackUserLocationEnd?.(e2); }); return gc; }, { position: props.position } ); thisRef.current.props = props; (0, import_react10.useImperativeHandle)(ref, () => ctrl, []); (0, import_react10.useEffect)(() => { applyReactStyle(ctrl._container, props.style); }, [props.style]); return null; } var GeolocateControl = (0, import_react10.memo)((0, import_react10.forwardRef)(_GeolocateControl)); // src/components/navigation-control.ts var import_react11 = require("react"); function _NavigationControl(props) { const ctrl = useControl(({ mapLib }) => new mapLib.NavigationControl(props), { position: props.position }); (0, import_react11.useEffect)(() => { applyReactStyle(ctrl._container, props.style); }, [props.style]); return null; } var NavigationControl = (0, import_react11.memo)(_NavigationControl); // src/components/scale-control.ts var import_react12 = require("react"); function _ScaleControl(props) { const ctrl = useControl(({ mapLib }) => new mapLib.ScaleControl(props), { position: props.position }); const propsRef = (0, import_react12.useRef)(props); const prevProps = propsRef.current; propsRef.current = props; const { style, maxWidth, unit } = props; (0, import_react12.useEffect)(() => { if (maxWidth !== void 0 && maxWidth !== prevProps.maxWidth) { ctrl.options.maxWidth = maxWidth; } if (unit !== void 0 && unit !== prevProps.unit) { ctrl.setUnit(unit); } }, [ctrl, maxWidth, unit, prevProps.maxWidth, prevProps.unit]); (0, import_react12.useEffect)(() => { applyReactStyle(ctrl._container, style); }, [ctrl, style]); return null; } var ScaleControl = (0, import_react12.memo)(_ScaleControl); // src/components/terrain-control.ts var import_react13 = require("react"); function _TerrainControl(props) { const ctrl = useControl(({ mapLib }) => new mapLib.TerrainControl(props), { position: props.position }); (0, import_react13.useEffect)(() => { applyReactStyle(ctrl._container, props.style); }, [props.style]); return null; } var TerrainControl = (0, import_react13.memo)(_TerrainControl); // src/components/source.ts var React4 = __toESM(require("react"), 1); var import_react14 = require("react"); // src/utils/assert.ts function assert(condition, message) { if (!condition) { throw new Error(message); } } // src/utils/warn.ts function emitWarning(onWarning, error) { const err = error instanceof Error ? error : new Error(String(error)); if (onWarning) { onWarning({ type: "error", target: null, originalEvent: null, error: err }); } else { logger.warn(err); } } // src/components/source.ts function createSource(map, id, props) { const mapInternal = map; if (mapInternal.style && mapInternal.style._loaded) { const options = { ...props }; delete options.id; delete options.children; map.addSource(id, options); return map.getSource(id); } return null; } function updateSource(source, props, prevProps, onWarning) { assert(props.id === prevProps.id, "source id changed"); assert(props.type === prevProps.type, "source type changed"); let changedKey = ""; let changedKeyCount = 0; for (const key in props) { if (key !== "children" && key !== "id" && Object.prototype.hasOwnProperty.call(props, key) && !deepEqual(prevProps[key], props[key])) { changedKey = key; changedKeyCount++; } } if (!changedKeyCount) { return; } const type = props.type; if (type === "geojson") { ; source.setData(props.data); } else if (type === "image") { ; source.updateImage({ url: props.url, coordinates: props.coordinates }); } else { const sourceWithMethods = source; const propsWithOptional = props; switch (changedKey) { case "coordinates": sourceWithMethods.setCoordinates?.(propsWithOptional.coordinates); break; case "url": sourceWithMethods.setUrl?.(propsWithOptional.url); break; case "tiles": sourceWithMethods.setTiles?.(propsWithOptional.tiles); break; default: emitWarning(onWarning, new Error(`Unable to update <Source> prop: ${changedKey}`)); } } } function _Source(props, ref) { const context = (0, import_react14.useContext)(MapContext); if (!context) { throw new Error("<Source> must be used within a Map component"); } const map = context.map.getMap(); const propsRef = (0, import_react14.useRef)(props); const sourceRef = (0, import_react14.useRef)(null); const [, setStyleLoaded] = (0, import_react14.useState)(0); const generatedId = (0, import_react14.useId)(); const id = (0, import_react14.useMemo)( () => props.id || `jsx-source-${generatedId.replace(/:/g, "-")}`, // Empty deps - id is set once on mount and should not change [] ); (0, import_react14.useEffect)(() => { if (map) { const forceUpdate = () => setTimeout(() => setStyleLoaded((version) => version + 1), 0); map.on("styledata", forceUpdate); forceUpdate(); return () => { map.off("styledata", forceUpdate); const mapInternal2 = map; if (mapInternal2.style && mapInternal2.style._loaded && map.getSource(id)) { const allLayers = map.getStyle()?.layers; if (allLayers) { for (const layer of allLayers) { const layerWithSource = layer; if (layerWithSource.source === id) { map.removeLayer(layer.id); } } } map.removeSource(id); } }; } return void 0; }, [map]); const mapInternal = map; let source = map && mapInternal.style && map.getSource(id); if (source) { updateSource(source, props, propsRef.current, context.onWarning); } else { source = createSource(map, id, props); } sourceRef.current = source; propsRef.current = props; (0, import_react14.useImperativeHandle)(ref, () => sourceRef.current, [source]); return source && React4.Children.map( props.children, (child) => child && (0, import_react14.cloneElement)(child, { source: id }) ) || null; } var Source = (0, import_react14.memo)(React4.forwardRef(_Source)); // src/components/canvas-source.ts var React5 = __toESM(require("react"), 1); var import_react15 = require("react"); function _CanvasSource(props) { const map = (0, import_react15.useContext)(MapContext).map.getMap(); const propsRef = (0, import_react15.useRef)(props); const id = (0, import_react15.useMemo)(() => props.id || `canvas-source-${Date.now()}`, [props.id]); (0, import_react15.useEffect)(() => { if (!map) return void 0; const mapInternal2 = map; let source = null; const addSource = () => { if (!mapInternal2.style || !mapInternal2.style._loaded) return; if (map.getSource(id)) return; const { coordinates, canvas, animate } = props; map.addSource(id, { type: "canvas", coordinates, canvas, animate: animate || false }); source = map.getSource(id); }; const updateSource2 = () => { if (!source) return; const { coordinates } = props; const prevProps = propsRef.current; if (JSON.stringify(coordinates) !== JSON.stringify(prevProps.coordinates)) { const sourceWithMethods = source; sourceWithMethods.setCoordinates?.(coordinates); } }; if (mapInternal2.style && mapInternal2.style._loaded) { addSource(); } else { map.once("styledata", addSource); } return () => { map.off("styledata", addSource); const mapInternalForCleanup = map; if (mapInternalForCleanup.style && mapInternalForCleanup.style._loaded && map.getSource(id)) { const allLayers = map.getStyle()?.layers; if (allLayers) { for (const layer of allLayers) { if (layer.source === id) { map.removeLayer(layer.id); } } } map.removeSource(id); } }; }, [map, id]); (0, import_react15.useEffect)(() => { if (!map) return; const source = map.getSource(id); if (!source) return; const { coordinates } = props; const prevProps = propsRef.current; if (JSON.stringify(coordinates) !== JSON.stringify(prevProps.coordinates)) { const sourceWithMethods = source; sourceWithMethods.setCoordinates?.(coordinates); } propsRef.current = props; }, [map, id, props.coordinates, props.canvas]); if (!map) return null; const mapInternal = map; if (!mapInternal.style || !mapInternal.style._loaded || !map.getSource(id)) { return null; } return pro