JustFly1984 / react-google-maps-api

React Google Maps API
MIT License
1.75k stars 421 forks source link

Right click doesn't work in InfoBox component #3329

Closed thomastvedt closed 5 months ago

thomastvedt commented 5 months ago

Bug description

When using the InfoBoxF component mouse right-click does not work. Selecting text doesn't work either. The InfoBoxF uses component uses the OverlayViewf component.

See my question in the discussion tab for more context here: https://github.com/JustFly1984/react-google-maps-api/discussions/3327

Here is a recorded video of the issue. InfoWindowF (which works) on the left, and InfoBoxF on the right:

https://github.com/JustFly1984/react-google-maps-api/assets/4746639/f7ba9ccb-d6fb-4665-a594-45785e27da92

Environment

os: mac

node --version v20.9.0

react version v18.2.0

webpack version vite 5.0.8

@babel version

@react-google-maps/api version "@react-google-maps/api": "^2.19.2", "@react-google-maps/infobox": "^2.19.2",

How does it behave?

Right-click doesn't work

How should it behave correctly?

Right-click should bring up the native browser context menu. I want to open a link in new tab

Basic implementation of incorrect behavior in codesandbox.com

I was unable to get codesandbox to work, here is a basic implementation:

import {
  GoogleMap,
  InfoBoxF,
  InfoWindowF,
  useLoadScript,
} from "@react-google-maps/api";
import styled from "styled-components";

const Container = styled.div`
  &&& {
    .gm-style-iw {
      background-color: orangered;
    }
  }
`;

function App() {
  const { loadError, isLoaded } = useLoadScript({
    id: "google-maps-script",
    googleMapsApiKey: "insert-your-google-maps-api-key-here",
  });

  const center = {
    lat: -3.745,
    lng: -38.523,
  };

  return (
    <Container>
      <h1>map</h1>
      {loadError && <div>load error</div>}
      <div>isLoaded: {isLoaded ? "yes" : "no"}</div>
      {!isLoaded && <div>loading</div>}
      {isLoaded && (
        <div
          style={{ width: 500, height: 500, background: "pink" }}
          className={"box1"}
        >
          <GoogleMap
            mapContainerStyle={{
              width: "500px",
              height: "500px",
            }}
            options={{
              mapTypeControl: false,
              scaleControl: false,
              streetViewControl: false,
              rotateControl: false,
              fullscreenControl: false,
              panControl: false,
              zoomControl: false,
              minZoom: 3,
            }}
            zoom={3}
            center={center}
          >
            <InfoWindowF position={center} options={{}}>
              <div
                style={{
                  width: 100,
                  height: 100,
                  borderRadius: 16,
                  background: "orange",
                }}
              >
                <a href={"/test"}>link iw</a>
              </div>
            </InfoWindowF>
            <InfoBoxF
              position={new google.maps.LatLng(center)}
              options={{
                enableEventPropagation: false,
                pane: "floatPane",
              }}
            >
              <div
                style={{
                  width: 100,
                  height: 100,
                  borderRadius: 16,
                  background: "aqua",
                }}
              >
                <a href={"/test"}>link iw</a>
              </div>
            </InfoBoxF>
          </GoogleMap>
        </div>
      )}
    </Container>
  );
}

export default App;
thomastvedt commented 5 months ago

Maybe this can be used? google.maps.OverlayView.preventMapHitsAndGesturesFrom(element)

thomastvedt commented 5 months ago

I finally figured this out.

const MapInfoWindow: React.FC<Props> = ({ position, children, style }) => {
  const map = useGoogleMap();
  if (map === null) {
    throw new Error('MapInfoWindow cannot be used outside a google map context');
  }

  const myRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (myRef.current) {
      // This prevents mouse and tocuh events to pass through the custom OverlayView to the map:
      google.maps.OverlayView.preventMapHitsAndGesturesFrom(myRef.current);
    }
  }, [myRef]);

  return (
    <OverlayViewF
      mapPaneName={'overlayMouseTarget'}
      position={{
        lat: position.lat,
        lng: position.lng,
      }}     
    >
      <div
        ref={myRef}
        onContextMenu={(event) => {
          // This stop the right click event from propagating, and will enable the native browser right click menu 
          event.stopPropagation();
        }}
      >
        {children}
      </div>
    </OverlayViewF>
  );
};

I'm writing my own "InfoBox" component based on the OverlayViewF component, and therefore I don't need this to be handled in the "InfoBox" any longer. It still would be nice if this worked out of the box in my opinion, but I'll leave that to someone else ;)