javascriptreactjsleafletreact-leaflet

Auto pan and update map position when data inside Popup changes


I have some dynamic data inside <Popup> that will change the Popup box size after opening. When you first click on the <Marker> it will update the content, position and adjust the pan of your map to fit direct at the top of Popup box.

Auto Pan Popup Box

However if the data (in this case an img) inside the Popup box changes it, your view wont move and will be cut off: Image has been cut off

This is what I want to happen when updating data inside the Popup box. It adjusts your view to the very top of the popup box: Image perfectly in frame

I've tried a few things like setting autopan autoPan={true} or manually updating view on click:

const onClickHandler = () => {
    const currentCenter = map.getCenter();
    const mapZoom = map.getZoom();
    const newCenter = [currentCenter.lat + (5 / mapZoom), currentCenter.lng];
    map.flyTo(newCenter, mapZoom);
}

but it doesn't really work as well as clicking open <Marker>. Are there any hooks that I'm missing that is able to update the Popup when the size changes?

import React, { useEffect, useState, useRef } from 'react'
import { MapContainer, TileLayer, Popup, LayersControl, Marker,  useMap, useMapEvents  } from 'react-leaflet';


const MyMap = () => {
    const [showImage, setShowImage] = useState(false);

    const handleButtonClick = () => setShowImage(!showImage);
    const position = [51.805, -0.11];
    const popupRef = useRef(null);

    return (
        <MapContainer center={position} zoom={3} minZoom={2} scrollWheelZoom={true} style={{ height: "100vh", width: "100%" }}>
            <TileLayer
            attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            />
            <Marker position={position}>
                <Popup minWidth={200}>
                    {showImage && 
                    <img style={{maxWidth: "500px"}} src="https://upload.wikimedia.org/wikipedia/commons/9/9b/Photo_of_a_kitten.jpg" alt="cat" />
                    }
                    <button onClick={handleButtonClick} style={{width: "100px", height: "150px"}}>
                        Click me!
                    </button>
                </Popup>
            </Marker>
        </MapContainer>
    )
}

export default MyMap;

Solution

  • You could define a new state autopan which is false initially, but gets set to true when clicking the button to show the image, and back to false immediately after the image state is uodated:

    const [autopan, setAutopan] = useState(false);
    
    const handleButtonClick = () => { 
      setAutopan(true)
      setShowImage(!showImage)
      setAutopan(false)
    };
    

    Then wire the keepInView attribute of the <Popup> up to this new autopan state:

    <Popup minWidth={200} keepInView={autopan}>
      ...
    </Popup>
    

    This now means that after clicking the button to show the image, the map will pan to the top of the popup, and resetting it back to false after will allow the user to move the map so that the popup is off screen if they wish.

    Here's a StackBlitz that demonstrates this working.