diff --git a/packages/map-gl-native/src/components/Map.js b/packages/map-gl-native/src/components/Map.js index 198de89..c224e72 100644 --- a/packages/map-gl-native/src/components/Map.js +++ b/packages/map-gl-native/src/components/Map.js @@ -1,20 +1,16 @@ -import React, { useMemo, useContext, useRef, useCallback } from "react"; +import React, { useMemo, useContext, useRef, useState, useEffect } from "react"; import { MapView, Camera } from "@maplibre/maplibre-react-native"; import { MapContext } from "./MapProvider.js"; import { withWQ } from "@wq/react"; import { useBasemapStyle } from "../hooks.js"; import PropTypes from "prop-types"; -function Map({ - name, - initBounds, - children, - containerStyle, - basemap, - ...mapProps -}) { +function Map({ initBounds, children, containerStyle, basemap, ...mapProps }) { const { setInstance } = useContext(MapContext), fitBounds = useMemo(() => { + if (!initBounds) { + return null; + } const [[xmin, ymin], [xmax, ymax]] = initBounds; return { sw: [xmin, ymin], ne: [xmax, ymax] }; }, [initBounds]), @@ -29,27 +25,12 @@ function Map({ const mapRef = useRef(), cameraRef = useRef(), - mapInstance = useMemo(() => createMapInstance(mapRef, cameraRef), []), - onInit = useCallback( - () => setInstance(mapInstance, name), - [setInstance, mapInstance] - ), - onPress = useCallback( - (e) => { - if (mapInstance.handlers.click) { - mapInstance.handlers.click(e); - } - }, - [mapInstance] + [cameraProps, setCameraProps] = useState({}), + mapInstance = useMemo( + () => createMapInstance(mapRef, cameraRef, setCameraProps), + [] ), - onMove = useCallback( - (e) => { - if (mapInstance.handlers.move) { - mapInstance.handlers.move(e); - } - }, - [mapInstance] - ); + { handleClick, handleMove } = mapInstance; let rotateEnabled, pitchEnabled; if (mapProps.rotateEnabled !== undefined) { @@ -67,6 +48,11 @@ function Map({ pitchEnabled = rotateEnabled; } + useEffect(() => { + setInstance(mapInstance); + return () => setInstance(null); + }, [mapRef.current]); + return ( - + {children} ); @@ -98,17 +89,36 @@ Map.propTypes = { export default withWQ(Map); // Mimic some functions from maplibre-gl-js & react-map-gl -export function createMapInstance(mapRef, cameraRef) { - return { - handlers: {}, +export function createMapInstance(mapRef, cameraRef, setCameraProps) { + const instance = { + handlers: { + click: [], + move: [], + }, on(event, handler) { - if (!["click", "move"].includes(event)) { + if (!(event in this.handlers)) { console.warn("Unsupported event: " + event); } - this.handlers[event] = handler; + this.handlers[event].push(handler); }, - off(event) { - delete this.handlers[event]; + off(event, handler) { + if (!(event in this.handlers)) { + console.warn("Unsupported event: " + event); + } + this.handlers[event] = this.handlers[event].filter( + (h) => h !== handler + ); + }, + _runHandlers(event, e) { + for (const handler of this.handlers[event]) { + handler(e); + } + }, + handleClick(e) { + this._runHandlers("click", e); + }, + handleMove(e) { + this._runHandlers("move", e); }, flyTo({ center, zoom }) { this.getCamera()?.flyTo(center, zoom); @@ -130,5 +140,11 @@ export function createMapInstance(mapRef, cameraRef) { getCamera() { return cameraRef.current; }, + setCameraProps, }; + + instance.handleClick = instance.handleClick.bind(instance); + instance.handleMove = instance.handleMove.bind(instance); + + return instance; } diff --git a/packages/map-gl-web/src/components/MapProvider.js b/packages/map-gl-web/src/components/MapProvider.js index 4e3b176..b90d477 100644 --- a/packages/map-gl-web/src/components/MapProvider.js +++ b/packages/map-gl-web/src/components/MapProvider.js @@ -1,4 +1,27 @@ import { MapProvider } from "react-map-gl/maplibre"; import { withWQ } from "@wq/react"; +import Map from "./Map.js"; +import MapInteraction from "./MapInteraction.js"; +import MapAutoZoom from "./MapAutoZoom.js"; +import MapIdentify from "./MapIdentify.js"; +import HighlightPopup from "./HighlightPopup.js"; +import Geojson from "../overlays/Geojson.js"; +import Tile from "../overlays/Tile.js"; +import VectorTile from "../overlays/VectorTile.js"; +import Highlight from "../overlays/Highlight.js"; -export default withWQ(MapProvider); +const MapProviderDefaults = { + components: { + Map, + MapInteraction, + MapAutoZoom, + MapIdentify, + HighlightPopup, + Geojson, + Tile, + VectorTile, + Highlight, + }, +}; + +export default withWQ(MapProvider, { defaults: MapProviderDefaults }); diff --git a/packages/map/src/components/AutoMap.js b/packages/map/src/components/AutoMap.js index 7d84560..e44e2e7 100644 --- a/packages/map/src/components/AutoMap.js +++ b/packages/map/src/components/AutoMap.js @@ -61,11 +61,11 @@ export const AutoMapDefaults = { function AutoMap({ name, mapId, - toolbar = true, + toolbar: Toolbar = true, toolbarAnchor = "top-right", containerStyle, context = {}, - overlays: initialOverlays = [], + overlays: initialOverlays = null, basemaps: initialBasemaps = null, initBounds: initialInitBounds = null, tiles: initialTiles = null, @@ -104,8 +104,9 @@ function AutoMap({ onChangeBasemap, onChangeOverlays ), - [state, actions] = reducer, - { + [state, actions] = reducer; + + const { MapContainer, MapToolbar, Map, @@ -143,26 +144,31 @@ function AutoMap({ const identify = overlays.some((overlay) => !!overlay.popup); - if (toolbar === true) { - toolbar = ( - - ); - } else if (!toolbar) { - toolbar = false; - } + const toolbar = (() => { + const toolbarProps = { + name, + mapId, + basemaps, + overlays, + showOverlay, + hideOverlay, + setBasemap, + context, + anchor: toolbarAnchor, + }; + if (Toolbar === true) { + return ; + } else if (typeof Toolbar === "function") { + return ; + } else if (!Toolbar) { + return false; + } else { + return Toolbar; + } + })(); return ( - + {toolbarAnchor.endsWith("left") && toolbar} reducer(state, action), { - basemaps, - overlays, + basemaps: basemaps || [], + overlays: overlays || [], initBounds, tiles, autoZoom, diff --git a/packages/map/src/reducer.js b/packages/map/src/reducer.js index 17c1ed3..f862800 100644 --- a/packages/map/src/reducer.js +++ b/packages/map/src/reducer.js @@ -15,21 +15,21 @@ const emptyState = { basemaps: [], overlays: [], viewState: null, - initBounds: null, + initBounds: undefined, tiles: null, autoZoom: null, highlight: null, mapId: null, activeBasemap: null, - activeOverlays: [], + activeOverlays: null, }; export default function reducer(state = emptyState, action) { switch (action.type) { case MAP_INITIALIZE: { const { - basemaps, - overlays, + basemaps: initBasemaps, + overlays: initOverlays, viewState, initBounds, tiles, @@ -39,9 +39,11 @@ export default function reducer(state = emptyState, action) { activeBasemap, activeOverlays, } = { ...emptyState, ...action.payload }; + const basemaps = reduceBasemaps([], initBasemaps, activeBasemap), + overlays = reduceOverlays([], initOverlays, activeOverlays); return { - basemaps: reduceBasemaps([], basemaps, activeBasemap), - overlays: reduceOverlays([], overlays, activeOverlays), + basemaps, + overlays, viewState, initBounds, tiles, @@ -225,6 +227,11 @@ function reduceOverlays(lastOverlays, nextOverlays, activeOverlays) { ...overlay, active: true, }; + } else if (activeOverlays && !activeOverlays.includes(overlay.name)) { + return { + ...overlay, + active: false, + }; } else if (overlay.active) { return { ...overlay,