import React from 'react';
import PropTypes from 'prop-types';
import ReactDom from 'react-dom';
/** The component designed to implement the google.maps.InfoWindow class. This component can be the child of either the `<Map />` or `<Marker />` components, but if you decide to put it within the `<Map />` component you must set its coordinate property so that it has an anchor point.
* @memberof Map
*
* @property {object} props
* @property {google.maps} props.maps Required.
* @property {google.maps.Map} props.map Required.
* @property {google.maps.MVCObject} props.anchor Required if coordinates aren't provided.
* @property {object} props.coords Required if anchor isn't provided.
* @property {number} props.coords.lng
* @property {number} props.coords.lat
* @property {bool} props.disableAutopan
* @property {number} props.maxWidth
* @property {object} props.pixelOffset
* @property {object} props.pixelOffset.width
* @property {object} props.pixelOffset.height
* @property {google.maps.InfoWindowOptions} props.options These will overwrite any of the convenience props above. See [google.maps.InfoWindowOptions]{@link https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindowOptions} documentation for all the options.
* @property {bool} props.open Allows you to open and close a window without fully unmounting it.
* @property {object} state
* @property {google.maps.InfoWindow} state.infoWindow The internal instance of the infoWindow.
* @property {function} props.onCloseClick Use this to listen for the close click event. When someone tries to close the infowindow. Implement closing.
*/
class InfoWindow extends React.Component {
constructor(props) {
super(props);
this.displayName = 'InfoWindow';
this.state = {
infoWindow : null,
anchor : null
}
this.loadInfoWindowContent = this.loadInfoWindowContent.bind(this);
this.node = null;
}
componentWillMount() {
var {maps, map, anchor, coords} = this.props;
if(maps && map) {
var options = {
position : anchor? undefined : coords
}
var infoWindow = new maps.InfoWindow(options)
if(this.props.open)
infoWindow.open(map, anchor);
else
infoWindow.close()
//Don't let the infowindow do it's default thing when a user tries to close it.
maps.event.addListener(infoWindow, 'closeclick', e => {
if(typeof this.props.onCloseClick === 'function')
this.props.onCloseClick(e);
});
this.setState({infoWindow, anchor})
}
else {
console.error("InfoWindow must live inside of a <Map /> component context.")
}
}
/** Load rendered children into infoWindow.
* @return {undefined}
*/
loadInfoWindowContent() {
if(this.state.infoWindow) {
this.node = ReactDom.findDOMNode(this.refs.infoWindowChildren);
this.state.infoWindow.setContent(this.node); //Set infowindow content
}
}
/** Place rendered children back into their normal location to await their destruction.
* @return {undefined}
*/
cleanInfoWindowContentForUnmount() {
//Undo our previous dom manipulation.
var parent = ReactDom.findDOMNode(this);
var child = this.node;
parent.appendChild(child);
}
componentDidMount() {
this.loadInfoWindowContent()
}
componentWillUnmount() {
if(this.state.infoWindow)
this.state.infoWindow.open(null);
this.setState({infoWindow : null});
this.cleanInfoWindowContentForUnmount();
}
componentDidUpdate(prevProps, prevState) {
if(this.state.infoWindow) {
if(this.props.open && !prevProps.open)
this.state.infoWindow.open(this.props.map, this.state.anchor);
else if(!this.props.open && prevProps.open)
this.state.infoWindow.close();
}
if(!this.node)
this.loadInfoWindowContent();
var {coords} = this.props;
if(!prevProps.coords || (coords.lat != prevProps.coords.lat && coords.lng != prevProps.coords.lng))
this.state.infoWindow.setPosition(this.props.coords);
}
render() {
return <div><div ref="infoWindowChildren">{this.props.children}</div></div>;
}
}
InfoWindow.propTypes = {
maps : PropTypes.object,
map : PropTypes.object,
coords : PropTypes.shape({
lat : PropTypes.number.isRequired,
lng : PropTypes.number.isRequired
}),
onCloseClick : PropTypes.func
}
export default InfoWindow;