Skip to content

Commit 2bac1ed

Browse files
committed
Added custom overlay
1 parent 5038ac4 commit 2bac1ed

File tree

8 files changed

+202
-0
lines changed

8 files changed

+202
-0
lines changed

Diff for: examples/Container.js

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class Container extends Component {
5252
</h2>
5353
</div>
5454

55+
<div className={styles.mapContainer}>
5556
<Switch>
5657
{routes.map(route => (
5758
<Route
@@ -75,6 +76,7 @@ class Container extends Component {
7576
</div>
7677
</div>
7778
</div>
79+
</div>
7880
);
7981
}
8082
}

Diff for: examples/components/withCustomOverlay.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React, { useState, Fragment } from 'react';
2+
import Map from '../../src/index';
3+
import CustomOverlay from '../../src/components/CustomOverlay';
4+
import styles from './withCustomOverlay.module.css';
5+
6+
const WithCustomOverlay = (props) => {
7+
const [showOverlay, setShowOverlay] = useState(true);
8+
if (!props.loaded) return <div>Loading...</div>;
9+
10+
return (
11+
<Fragment>
12+
<button
13+
className={styles.button}
14+
onClick={() => setShowOverlay(!showOverlay)}
15+
>
16+
Toggle Popup
17+
</button>
18+
<Map google={props.google} className="map" zoom={14}>
19+
<CustomOverlay
20+
position={{ lat: 37.782551, lng: -122.425368 }}
21+
visible={showOverlay}
22+
className={styles.overlayContainer}
23+
passThroughMouseEvents={true}
24+
>
25+
<div>Hi there. I'm a custom overlay.</div>
26+
</CustomOverlay>
27+
</Map>
28+
</Fragment>
29+
);
30+
};
31+
32+
export default WithCustomOverlay;

Diff for: examples/components/withCustomOverlay.module.css

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.overlayContainer {
2+
background-color: white;
3+
padding: 20px;
4+
border-radius: 10px;
5+
transform: translate(-50%, -100%);
6+
}
7+
8+
.button {
9+
position: absolute;
10+
bottom: 10px;
11+
left: 10px;
12+
z-index: 1;
13+
}

Diff for: examples/index.js

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import Polygon from './components/withPolygons';
2121
import Polyline from './components/withPolylines';
2222
import Rectangle from './components/withRectangle';
2323
import CustomEvents from './components/resizeEvent';
24+
import CustomOverlay from './components/withCustomOverlay';
2425

2526
const routes = [
2627
{
@@ -72,6 +73,11 @@ const routes = [
7273
path: '/onResizeEvent',
7374
name: 'Custom events',
7475
component: CustomEvents
76+
},
77+
{
78+
path: '/onCustomPopup',
79+
name: 'Custom overlay',
80+
component: CustomOverlay
7581
}
7682
];
7783

Diff for: examples/styles.module.css

+7
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,12 @@ html, body {
6464
order: 2;
6565
position: relative;
6666
min-height: 100%;
67+
display: flex;
68+
flex-direction: column;
69+
70+
.mapContainer {
71+
flex: 1;
72+
position: relative;
73+
}
6774
}
6875
}

Diff for: index.d.ts

+13
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,16 @@ export interface IInfoWindowProps extends Partial<google.maps.InfoWindowOptions>
106106
export class InfoWindow extends React.Component<IInfoWindowProps, any> {
107107

108108
}
109+
110+
111+
export interface ICustomOverlayProps {
112+
google?: typeof google
113+
map?: google.maps.Map
114+
position: google.maps.LatLng | google.maps.LatLngLiteral,
115+
visible?: boolean,
116+
passThroughMouseEvents?: boolean
117+
}
118+
119+
export class CustomOverlay extends React.Component<ICustomOverlayProps, any> {
120+
121+
}

Diff for: src/components/CustomOverlay.js

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import React, { useRef, useEffect, useState } from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
function createPopupClass() {
5+
function Popup({ position, content, map, passThroughMouseEvents, onDraw }) {
6+
this.position = position;
7+
this.containerDiv = content;
8+
this.onDraw = onDraw;
9+
this.setMap(map);
10+
if (!passThroughMouseEvents) {
11+
google.maps.OverlayView.preventMapHitsAndGesturesFrom(this.containerDiv);
12+
}
13+
}
14+
15+
Popup.prototype = Object.create(google.maps.OverlayView.prototype);
16+
17+
Popup.prototype.show = function () {
18+
this.containerDiv.style.visibility = 'visible';
19+
};
20+
21+
Popup.prototype.hide = function () {
22+
this.containerDiv.style.visibility = 'hidden';
23+
};
24+
25+
Popup.prototype.onAdd = function () {
26+
this.getPanes().floatPane.appendChild(this.containerDiv);
27+
};
28+
29+
Popup.prototype.onRemove = function () {
30+
if (this.containerDiv.parentElement) {
31+
this.containerDiv.parentElement.removeChild(this.containerDiv);
32+
}
33+
};
34+
35+
Popup.prototype.draw = function () {
36+
if (!this.position) {
37+
return;
38+
}
39+
this.onDraw();
40+
var divPosition = this.getProjection().fromLatLngToDivPixel(this.position);
41+
var display =
42+
Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000
43+
? 'block'
44+
: 'none';
45+
46+
if (display === 'block') {
47+
this.containerDiv.style.left = divPosition.x + 'px';
48+
this.containerDiv.style.top = divPosition.y + 'px';
49+
}
50+
if (this.containerDiv.style.display !== display) {
51+
this.containerDiv.style.display = display;
52+
}
53+
};
54+
55+
return Popup;
56+
}
57+
58+
const asLatLng = (position) =>
59+
!position || position instanceof google.maps.LatLng
60+
? position
61+
: new google.maps.LatLng(position.lat, position.lng);
62+
63+
export const CustomOverlay = ({
64+
map,
65+
position,
66+
children,
67+
visible,
68+
className,
69+
passThroughMouseEvents
70+
}) => {
71+
const [hasDrawn, setHasDrawn] = useState(false);
72+
const containerRef = useRef(null);
73+
const popoverRef = useRef(null);
74+
75+
useEffect(() => {
76+
if (map) {
77+
const Popup = createPopupClass();
78+
popoverRef.current = new Popup({
79+
position: asLatLng(position),
80+
content: containerRef.current,
81+
map,
82+
passThroughMouseEvents,
83+
onDraw: () => setHasDrawn(true)
84+
});
85+
}
86+
}, [map]);
87+
88+
useEffect(() => {
89+
const popover = popoverRef.current;
90+
if (popover) {
91+
popover.position = asLatLng(position);
92+
popover.draw();
93+
}
94+
}, [position]);
95+
96+
useEffect(() => {
97+
const popover = popoverRef.current;
98+
if (popover) {
99+
visible ? popover.show() : popover.hide();
100+
}
101+
}, [visible]);
102+
103+
const display = hasDrawn ? 'block' : 'none';
104+
return (
105+
<div
106+
className={className}
107+
style={{ position: 'absolute', display }}
108+
ref={containerRef}
109+
>
110+
{visible && children}
111+
</div>
112+
);
113+
};
114+
115+
CustomOverlay.propTypes = {
116+
className: PropTypes.string,
117+
children: PropTypes.node.isRequired,
118+
map: PropTypes.object,
119+
position: PropTypes.object,
120+
visible: PropTypes.bool,
121+
passThroughMouseEvents: PropTypes.bool
122+
};
123+
124+
CustomOverlay.defaultProps = {
125+
visible: true
126+
};
127+
128+
export default CustomOverlay;

Diff for: src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export {Polygon} from './components/Polygon';
5050
export {Polyline} from './components/Polyline';
5151
export {Circle} from './components/Circle';
5252
export {Rectangle} from './components/Rectangle';
53+
export {CustomOverlay} from './components/CustomOverlay';
5354

5455
export class Map extends React.Component {
5556
constructor(props) {

0 commit comments

Comments
 (0)