diff --git a/src/addons/InfoBox.js b/src/addons/InfoBox.js new file mode 100644 index 00000000..6ab16505 --- /dev/null +++ b/src/addons/InfoBox.js @@ -0,0 +1,8 @@ +import warning from "warning" + +warning( + false, + `[DEPRECATED] "react-google-maps/lib/addons/InfoBox" has been moved to "react-google-maps/lib/components/addons/InfoBox"` +) + +export { default } from "../components/addons/InfoBox" diff --git a/src/components/addons/InfoBox.jsx b/src/components/addons/InfoBox.jsx new file mode 100644 index 00000000..e9a8b365 --- /dev/null +++ b/src/components/addons/InfoBox.jsx @@ -0,0 +1,223 @@ +import canUseDOM from "can-use-dom" +import invariant from "invariant" +import React from "react" +import ReactDOM from "react-dom" +import PropTypes from "prop-types" + +import { + construct, + componentDidMount, + componentDidUpdate, + componentWillUnmount, +} from "../../utils/MapChildHelper" + +import { MAP, ANCHOR, INFO_BOX } from "../../constants" + +/** + * @url http://htmlpreview.github.io/?https://github.com/googlemaps/v3-utility-library/blob/master/infobox/docs/reference.html + */ +export class InfoBox extends React.PureComponent { + static propTypes = { + /** + * @type InfoBoxOptions + */ + defaultOptions: PropTypes.any, + + /** + * @type LatLng|LatLngLiteral + */ + defaultPosition: PropTypes.any, + + /** + * @type boolean + */ + defaultVisible: PropTypes.bool, + + /** + * @type number + */ + defaultZIndex: PropTypes.number, + + /** + * @type InfoBoxOptions + */ + options: PropTypes.any, + + /** + * @type LatLng|LatLngLiteral + */ + position: PropTypes.any, + + /** + * @type boolean + */ + visible: PropTypes.bool, + + /** + * @type number + */ + zIndex: PropTypes.number, + + /** + * function + */ + onCloseClick: PropTypes.func, + + /** + * function + */ + onDomReady: PropTypes.func, + + /** + * function + */ + onContentChanged: PropTypes.func, + + /** + * function + */ + onPositionChanged: PropTypes.func, + + /** + * function + */ + onZindexChanged: PropTypes.func, + } + + static contextTypes = { + [MAP]: PropTypes.object, + [ANCHOR]: PropTypes.object, + } + + state = { + [INFO_BOX]: null, + } + + /* + * @url https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoBox + */ + componentWillMount() { + if (!canUseDOM || this.state[INFO_BOX]) { + return + } + const GoogleMapsInfobox = require(/* "google-maps-infobox" uses "google" as a global variable. Since we don't + * have "google" on the server, we can not use it in server-side rendering. + * As a result, we import "google-maps-infobox" here to prevent an error on + * a isomorphic server. + */ `google-maps-infobox`) + const infoBox = new GoogleMapsInfobox() + construct(InfoBox.propTypes, updaterMap, this.props, infoBox) + infoBox.setMap(this.context[MAP]) + this.state = { + [INFO_BOX]: infoBox, + } + } + + componentDidMount() { + componentDidMount(this, this.state[INFO_BOX], eventMap) + const content = document.createElement(`div`) + ReactDOM.unstable_renderSubtreeIntoContainer( + this, + React.Children.only(this.props.children), + content + ) + this.state[INFO_BOX].setContent(content) + open(this.state[INFO_BOX], this.context[ANCHOR]) + } + + componentDidUpdate(prevProps) { + componentDidUpdate( + this, + this.state[INFO_BOX], + eventMap, + updaterMap, + prevProps + ) + if (this.props.children !== prevProps.children) { + ReactDOM.unstable_renderSubtreeIntoContainer( + this, + React.Children.only(this.props.children), + this.state[INFO_BOX].getContent() + ) + } + open(this.state[INFO_BOX], this.context[ANCHOR]) + } + + componentWillUnmount() { + componentWillUnmount(this) + const infoBox = this.state[INFO_BOX] + if (infoBox) { + if (infoBox.getContent()) { + ReactDOM.unmountComponentAtNode(infoBox.getContent()) + } + infoBox.setMap(null) + } + } + + render() { + return false + } + + /** + * + * @type LatLng + */ + getPosition() { + return this.state[INFO_BOX].getPosition() + } + + /** + * + * @type boolean + */ + getVisible() { + return this.state[INFO_BOX].getVisible() + } + + /** + * + * @type number + */ + getZIndex() { + return this.state[INFO_BOX].getZIndex() + } +} + +const open = (infoBox, anchor) => { + if (anchor) { + infoBox.open(infoBox.getMap(), anchor) + } else if (infoBox.getPosition()) { + infoBox.open(infoBox.getMap()) + } else { + invariant( + false, + `You must provide either an anchor (typically render it inside a ) or a position props for .` + ) + } +} + +const eventMap = { + onCloseClick: "closeclick", + onDomReady: "domready", + onContentChanged: "content_changed", + onPositionChanged: "position_changed", + onZindexChanged: "zindex_changed", +} + +const updaterMap = { + options(instance, options) { + instance.setOptions(options) + }, + + position(instance, position) { + instance.setPosition(position) + }, + + visible(instance, visible) { + instance.setVisible(visible) + }, + + zIndex(instance, zIndex) { + instance.setZIndex(zIndex) + }, +} diff --git a/src/lib/addons/InfoBox.js b/src/lib/addons/InfoBox.js deleted file mode 100644 index 814b9749..00000000 --- a/src/lib/addons/InfoBox.js +++ /dev/null @@ -1,166 +0,0 @@ -import _ from "lodash"; - -import invariant from "invariant"; - -import PropTypes from "prop-types"; - -import createReactClass from "create-react-class"; - -import { Children } from "react"; - -import { - unstable_renderSubtreeIntoContainer, -} from "react-dom"; - -import { - MAP, - ANCHOR, - INFO_BOX, -} from "../constants"; - -import { - addDefaultPrefixToPropTypes, - collectUncontrolledAndControlledProps, - default as enhanceElement, -} from "../enhanceElement"; - -const controlledPropTypes = { - // NOTICE!!!!!! - // - // Only expose those with getters & setters in the table as controlled props. - // - // http://htmlpreview.github.io/?https://github.com/googlemaps/v3-utility-library/blob/master/infobox/docs/reference.html - content: PropTypes.any, - options: PropTypes.object, - position: PropTypes.any, - visible: PropTypes.bool, - zIndex: PropTypes.number, -}; - -const defaultUncontrolledPropTypes = addDefaultPrefixToPropTypes(controlledPropTypes); - -const eventMap = { - // http://htmlpreview.github.io/?https://github.com/googlemaps/v3-utility-library/blob/master/infobox/docs/reference.html - onCloseClick: `closeclick`, - - onContentChanged: `content_changed`, - - onDomReady: `domready`, - - onPositionChanged: `position_changed`, - - onZIndexChanged: `zindex_changed`, -}; - -const publicMethodMap = { - // Public APIs - // - // http://htmlpreview.github.io/?https://github.com/googlemaps/v3-utility-library/blob/master/infobox/docs/reference.html - getPosition(infoBox) { return infoBox.getPosition(); }, - - getVisible(infoBox) { return infoBox.getVisible(); }, - - getZIndex(infoBox) { return infoBox.getZIndex(); }, - // END - Public APIs -}; - -const controlledPropUpdaterMap = { - children(infoWindow, children, component) { - unstable_renderSubtreeIntoContainer(component, Children.only(children), infoWindow.getContent()); - }, - options(infoBox, options) { infoBox.setOptions(options); }, - position(infoBox, position) { infoBox.setPosition(position); }, - visible(infoBox, visible) { infoBox.setVisible(visible); }, - zIndex(infoBox, zIndex) { infoBox.setZIndex(zIndex); }, -}; - -function getInstanceFromComponent(component) { - return component.state[INFO_BOX]; -} - -function openInfoBox(context, infoBox) { - const map = context[MAP]; - const anchor = context[ANCHOR]; - if (anchor) { - infoBox.open(map, anchor); - } else if (infoBox.getPosition()) { - infoBox.open(map); - } else { - invariant(false, -`You must provide either an anchor (typically a ) or a position for .` - ); - } -} - -export default _.flowRight( - createReactClass, - enhanceElement(getInstanceFromComponent, publicMethodMap, eventMap, controlledPropUpdaterMap), -)({ - displayName: `InfoBox`, - - propTypes: { - ...controlledPropTypes, - ...defaultUncontrolledPropTypes, - }, - - contextTypes: { - [MAP]: PropTypes.object, - [ANCHOR]: PropTypes.object, - }, - - getInitialState() { - const GoogleMapsInfobox = require( - // "google-maps-infobox" uses "google" as a global variable. Since we don't - // have "google" on the server, we can not use it in server-side rendering. - // As a result, we import "google-maps-infobox" here to prevent an error on - // a isomorphic server. - `google-maps-infobox` - ); - const map = this.context[MAP]; - const infoBoxProps = collectUncontrolledAndControlledProps( - defaultUncontrolledPropTypes, - controlledPropTypes, - this.props - ); - // http://htmlpreview.github.io/?https://github.com/googlemaps/v3-utility-library/blob/master/infobox/docs/reference.html - const infoBox = new GoogleMapsInfobox({ - map, - ...infoBoxProps, - // Override props of ReactElement type - content: document.createElement(`div`), - children: undefined, - }); - // BUG: the `GoogleMapsInfobox` does not take infoBoxProps.options - // into account in its constructor. Need to manually set - infoBox.setOptions(infoBoxProps.options || {}); - - openInfoBox(this.context, infoBox); - return { - [INFO_BOX]: infoBox, - }; - }, - - componentDidMount() { - const infoBox = getInstanceFromComponent(this); - controlledPropUpdaterMap.children(infoBox, this.props.children, this); - }, - - componentWillReceiveProps(nextProps, nextContext) { - const anchorChanged = this.context[ANCHOR] !== nextContext[ANCHOR]; - if (anchorChanged) { - const infoBox = getInstanceFromComponent(this); - openInfoBox(nextContext, infoBox); - } - }, - - componentWillUnmount() { - const infoBox = getInstanceFromComponent(this); - if (infoBox) { - infoBox.close(); - } - }, - - render() { - return false; - }, -}); diff --git a/src/lib/addons/infoBox.spec.js b/src/lib/addons/infoBox.spec.js deleted file mode 100644 index ba87f4d0..00000000 --- a/src/lib/addons/infoBox.spec.js +++ /dev/null @@ -1,9 +0,0 @@ -import InfoBox from "./InfoBox"; - -describe(`InfoBox`, () => { - - it(`should be exported`, () => { - expect(InfoBox).toBeDefined(); - }); - -});