Skip to content

Commit

Permalink
refactor(InfoWindow): rewrite with enhanceElement and cleaner interfaces
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Naming convention for event handlers has tweaked to follow React's convention.

Before:

```js
<InfoWindow
  onCloseclick={_.noop}
  onDomready={_.noop}
  onZindexChanged={_.noop}
/>
```

After:

```js
<InfoWindow
  onCloseClick={_.noop}
  onDomReady={_.noop}
  onZIndexChanged={_.noop}
/>
```
  • Loading branch information
tomchentw committed Oct 4, 2016
1 parent 0694d56 commit fc1c25c
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 188 deletions.
187 changes: 135 additions & 52 deletions src/lib/InfoWindow.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,160 @@
/* global google */
import _ from "lodash";

import invariant from "invariant";

import {
default as React,
Component,
PropTypes,
Children,
} from "react";

import {
default as canUseDOM,
} from "can-use-dom";
render,
unmountComponentAtNode,
} from "react-dom";

import {
default as InfoWindowCreator,
infoWindowDefaultPropTypes,
infoWindowControlledPropTypes,
infoWindowEventPropTypes,
} from "./creators/InfoWindowCreator";

import { default as GoogleMapHolder } from "./creators/GoogleMapHolder";

export default class InfoWindow extends Component {
static propTypes = {
// Uncontrolled default[props] - used only in componentDidMount
...infoWindowDefaultPropTypes,
// Controlled [props] - used in componentDidMount/componentDidUpdate
...infoWindowControlledPropTypes,
// Event [onEventName]
...infoWindowEventPropTypes,
}
MAP,
ANCHOR,
INFO_WINDOW,
} from "./constants";

static contextTypes = {
mapHolderRef: PropTypes.instanceOf(GoogleMapHolder),
}
import {
addDefaultPrefixToPropTypes,
collectUncontrolledAndControlledProps,
default as enhanceElement,
} from "./enhanceElement";

// Public APIs
const controlledPropTypes = {
// NOTICE!!!!!!
//
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
// Only expose those with getters & setters in the table as controlled props.
//
// [].map.call($0.querySelectorAll("tr>td>code", function(it){ return it.textContent; })
// .filter(function(it){ return it.match(/^set/) && !it.match(/^setMap/); })
//
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
children: PropTypes.element,
options: PropTypes.object,
position: PropTypes.any,
zIndex: PropTypes.number,
};

const defaultUncontrolledPropTypes = addDefaultPrefixToPropTypes(controlledPropTypes);

const eventMap = {
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
// [].map.call($0.querySelectorAll("tr>td>code"), function(it){ return it.textContent; })
// .filter(function(it){ return it.match(/^get/) && !it.match(/^getMap/); })
getContent() { /* TODO: children */ }
onCloseClick: `closeclick`,

onContentChanged: `content_changed`,

getPosition() { return this.state.infoWindow.getPosition(); }
onDomReady: `domready`,

getZIndex() { return this.state.infoWindow.getZIndex(); }
// END - Public APIs
onPositionChanged: `position_changed`,

onZIndexChanged: `zindex_changed`,
};

const publicMethodMap = {
// Public APIs
//
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
//
// [].map.call($0.querySelectorAll("tr>td>code"), function(it){ return it.textContent; })
// .filter(function(it){ return it.match(/^get/) && !it.match(/Map$/); })
getPosition(infoWindow) { return infoWindow.getPosition(); },

state = {
getZIndex(infoWindow) { return infoWindow.getZIndex(); },
// END - Public APIs
};

const controlledPropUpdaterMap = {
children(infoWindow, children) {
render(Children.only(children), infoWindow.getContent());
},
options(infoWindow, options) { infoWindow.setOptions(options); },
position(infoWindow, position) { infoWindow.setPosition(position); },
zIndex(infoWindow, zIndex) { infoWindow.setZIndex(zIndex); },
};

function getInstanceFromComponent(component) {
return component.state[INFO_WINDOW];
}

function openInfoWindow(context, infoWindow) {
const map = context[MAP];
const anchor = context[ANCHOR];
if (anchor) {
infoWindow.open(map, anchor);
} else if (infoWindow.getPosition()) {
infoWindow.open(map);
} else {
invariant(false,
`You must provide either an anchor (typically a <Marker>) or a position for <InfoWindow>.`
);
}
}

componentWillMount() {
const { mapHolderRef } = this.context;
export default _.flowRight(
React.createClass,
enhanceElement(getInstanceFromComponent, publicMethodMap, eventMap, controlledPropUpdaterMap),
)({
displayName: `InfoWindow`,

if (!canUseDOM) {
return;
}
const infoWindow = InfoWindowCreator._createInfoWindow({
...this.props,
mapHolderRef,
propTypes: {
...controlledPropTypes,
...defaultUncontrolledPropTypes,
},

contextTypes: {
[MAP]: PropTypes.object,
[ANCHOR]: PropTypes.object,
},

getInitialState() {
const map = this.context[MAP];
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
const infoWindow = new google.maps.InfoWindow({
map,
...collectUncontrolledAndControlledProps(
defaultUncontrolledPropTypes,
controlledPropTypes,
this.props
),
// Override props of ReactElement type
content: document.createElement(`div`),
children: undefined,
});
openInfoWindow(this.context, infoWindow);
return {
[INFO_WINDOW]: infoWindow,
};
},

this.setState({ infoWindow });
}
componentDidMount() {
const infoWindow = getInstanceFromComponent(this);
controlledPropUpdaterMap.children(infoWindow, this.props.children);
},

render() {
if (this.state.infoWindow) {
return (
<InfoWindowCreator infoWindow={this.state.infoWindow} {...this.props}>
{this.props.children}
</InfoWindowCreator>
);
} else {
return (<noscript />);
componentWillReceiveProps(nextProps, nextContext) {
const anchorChanged = this.context[ANCHOR] !== nextContext[ANCHOR];
if (anchorChanged) {
const infoWindow = getInstanceFromComponent(this);
openInfoWindow(nextContext, infoWindow);
}
}
}
},

componentWillUnmount() {
const infoWindow = getInstanceFromComponent(this);
if (infoWindow) {
unmountComponentAtNode(infoWindow.getContent());
infoWindow.setMap(null);
}
},

render() {
return false;
},
});
21 changes: 20 additions & 1 deletion src/lib/Marker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import {
MAP,
MARKER,
ANCHOR,
} from "./constants";

import {
Expand Down Expand Up @@ -193,6 +194,10 @@ export default _.flowRight(
[MAP]: PropTypes.object,
},

childContextTypes: {
[ANCHOR]: PropTypes.object,
},

getInitialState() {
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Marker
const marker = new google.maps.Marker({
Expand All @@ -208,6 +213,12 @@ export default _.flowRight(
};
},

getChildContext() {
return {
[ANCHOR]: this.state[MARKER],
};
},

componentWillUnmount() {
const marker = getInstanceFromComponent(this);
if (marker) {
Expand All @@ -216,6 +227,14 @@ export default _.flowRight(
},

render() {
return false;
const {
children,
} = this.props;

return (
<div>
{children}
</div>
);
},
});
4 changes: 4 additions & 0 deletions src/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ export const KML_LAYER = `__SECRET_KML_LAYER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;
export const DIRECTIONS_RENDERER = `__SECRET_DIRECTIONS_RENDERER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;

export const HEATMAP_LAYER = `__SECRET_HEATMAP_LAYER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;

export const ANCHOR = `__SECRET_ANCHOR_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;

export const INFO_WINDOW = `__SECRET_INFO_WINDOW_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;
90 changes: 0 additions & 90 deletions src/lib/creators/InfoWindowCreator.js

This file was deleted.

9 changes: 0 additions & 9 deletions src/lib/eventLists/InfoWindowEventList.js

This file was deleted.

4 changes: 4 additions & 0 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ export {
export {
default as HeatmapLayer,
} from "./HeatmapLayer";

export {
default as InfoWindow,
} from "./InfoWindow";
Loading

0 comments on commit fc1c25c

Please sign in to comment.