diff --git a/examples/Container.js b/examples/Container.js index f1ffa64d..78eac928 100644 --- a/examples/Container.js +++ b/examples/Container.js @@ -52,6 +52,7 @@ class Container extends Component { +
{routes.map(route => (
+ ); } } diff --git a/examples/components/withCustomOverlay.js b/examples/components/withCustomOverlay.js new file mode 100644 index 00000000..439170b5 --- /dev/null +++ b/examples/components/withCustomOverlay.js @@ -0,0 +1,32 @@ +import React, { useState, Fragment } from 'react'; +import Map from '../../src/index'; +import CustomOverlay from '../../src/components/CustomOverlay'; +import styles from './withCustomOverlay.module.css'; + +const WithCustomOverlay = (props) => { + const [showOverlay, setShowOverlay] = useState(true); + if (!props.loaded) return
Loading...
; + + return ( + + + + +
Hi there. I'm a custom overlay.
+
+
+
+ ); +}; + +export default WithCustomOverlay; diff --git a/examples/components/withCustomOverlay.module.css b/examples/components/withCustomOverlay.module.css new file mode 100644 index 00000000..0a918c94 --- /dev/null +++ b/examples/components/withCustomOverlay.module.css @@ -0,0 +1,13 @@ +.overlayContainer { + background-color: white; + padding: 20px; + border-radius: 10px; + transform: translate(-50%, -100%); +} + +.button { + position: absolute; + bottom: 10px; + left: 10px; + z-index: 1; +} \ No newline at end of file diff --git a/examples/index.js b/examples/index.js index 672f4339..b7443407 100644 --- a/examples/index.js +++ b/examples/index.js @@ -21,6 +21,7 @@ import Polygon from './components/withPolygons'; import Polyline from './components/withPolylines'; import Rectangle from './components/withRectangle'; import CustomEvents from './components/resizeEvent'; +import CustomOverlay from './components/withCustomOverlay'; const routes = [ { @@ -72,6 +73,11 @@ const routes = [ path: '/onResizeEvent', name: 'Custom events', component: CustomEvents + }, + { + path: '/onCustomPopup', + name: 'Custom overlay', + component: CustomOverlay } ]; diff --git a/examples/styles.module.css b/examples/styles.module.css index dc14afda..813c1567 100644 --- a/examples/styles.module.css +++ b/examples/styles.module.css @@ -64,5 +64,12 @@ html, body { order: 2; position: relative; min-height: 100%; + display: flex; + flex-direction: column; + + .mapContainer { + flex: 1; + position: relative; + } } } diff --git a/index.d.ts b/index.d.ts index 9499bf60..fd1167ed 100644 --- a/index.d.ts +++ b/index.d.ts @@ -106,3 +106,16 @@ export interface IInfoWindowProps extends Partial export class InfoWindow extends React.Component { } + + +export interface ICustomOverlayProps { + google?: typeof google + map?: google.maps.Map + position: google.maps.LatLng | google.maps.LatLngLiteral, + visible?: boolean, + passThroughMouseEvents?: boolean +} + +export class CustomOverlay extends React.Component { + +} \ No newline at end of file diff --git a/src/components/CustomOverlay.js b/src/components/CustomOverlay.js new file mode 100644 index 00000000..e2cf250c --- /dev/null +++ b/src/components/CustomOverlay.js @@ -0,0 +1,128 @@ +import React, { useRef, useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; + +function createPopupClass() { + function Popup({ position, content, map, passThroughMouseEvents, onDraw }) { + this.position = position; + this.containerDiv = content; + this.onDraw = onDraw; + this.setMap(map); + if (!passThroughMouseEvents) { + google.maps.OverlayView.preventMapHitsAndGesturesFrom(this.containerDiv); + } + } + + Popup.prototype = Object.create(google.maps.OverlayView.prototype); + + Popup.prototype.show = function () { + this.containerDiv.style.visibility = 'visible'; + }; + + Popup.prototype.hide = function () { + this.containerDiv.style.visibility = 'hidden'; + }; + + Popup.prototype.onAdd = function () { + this.getPanes().floatPane.appendChild(this.containerDiv); + }; + + Popup.prototype.onRemove = function () { + if (this.containerDiv.parentElement) { + this.containerDiv.parentElement.removeChild(this.containerDiv); + } + }; + + Popup.prototype.draw = function () { + if (!this.position) { + return; + } + this.onDraw(); + var divPosition = this.getProjection().fromLatLngToDivPixel(this.position); + var display = + Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000 + ? 'block' + : 'none'; + + if (display === 'block') { + this.containerDiv.style.left = divPosition.x + 'px'; + this.containerDiv.style.top = divPosition.y + 'px'; + } + if (this.containerDiv.style.display !== display) { + this.containerDiv.style.display = display; + } + }; + + return Popup; +} + +const asLatLng = (position) => + !position || position instanceof google.maps.LatLng + ? position + : new google.maps.LatLng(position.lat, position.lng); + +export const CustomOverlay = ({ + map, + position, + children, + visible, + className, + passThroughMouseEvents +}) => { + const [hasDrawn, setHasDrawn] = useState(false); + const containerRef = useRef(null); + const popoverRef = useRef(null); + + useEffect(() => { + if (map) { + const Popup = createPopupClass(); + popoverRef.current = new Popup({ + position: asLatLng(position), + content: containerRef.current, + map, + passThroughMouseEvents, + onDraw: () => setHasDrawn(true) + }); + } + }, [map]); + + useEffect(() => { + const popover = popoverRef.current; + if (popover) { + popover.position = asLatLng(position); + popover.draw(); + } + }, [position]); + + useEffect(() => { + const popover = popoverRef.current; + if (popover) { + visible ? popover.show() : popover.hide(); + } + }, [visible]); + + const display = hasDrawn ? 'block' : 'none'; + return ( +
+ {visible && children} +
+ ); +}; + +CustomOverlay.propTypes = { + className: PropTypes.string, + children: PropTypes.node.isRequired, + map: PropTypes.object, + position: PropTypes.object, + visible: PropTypes.bool, + passThroughMouseEvents: PropTypes.bool +}; + +CustomOverlay.defaultProps = { + visible: true +}; + +export default CustomOverlay; diff --git a/src/index.js b/src/index.js index edd3abec..8d9141b9 100644 --- a/src/index.js +++ b/src/index.js @@ -50,6 +50,7 @@ export {Polygon} from './components/Polygon'; export {Polyline} from './components/Polyline'; export {Circle} from './components/Circle'; export {Rectangle} from './components/Rectangle'; +export {CustomOverlay} from './components/CustomOverlay'; export class Map extends React.Component { constructor(props) {