Skip to content

Commit fc1c25c

Browse files
committed
refactor(InfoWindow): rewrite with enhanceElement and cleaner interfaces
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} /> ```
1 parent 0694d56 commit fc1c25c

File tree

7 files changed

+163
-188
lines changed

7 files changed

+163
-188
lines changed

src/lib/InfoWindow.js

Lines changed: 135 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,160 @@
1+
/* global google */
2+
import _ from "lodash";
3+
4+
import invariant from "invariant";
5+
16
import {
27
default as React,
3-
Component,
48
PropTypes,
9+
Children,
510
} from "react";
611

712
import {
8-
default as canUseDOM,
9-
} from "can-use-dom";
13+
render,
14+
unmountComponentAtNode,
15+
} from "react-dom";
1016

1117
import {
12-
default as InfoWindowCreator,
13-
infoWindowDefaultPropTypes,
14-
infoWindowControlledPropTypes,
15-
infoWindowEventPropTypes,
16-
} from "./creators/InfoWindowCreator";
17-
18-
import { default as GoogleMapHolder } from "./creators/GoogleMapHolder";
19-
20-
export default class InfoWindow extends Component {
21-
static propTypes = {
22-
// Uncontrolled default[props] - used only in componentDidMount
23-
...infoWindowDefaultPropTypes,
24-
// Controlled [props] - used in componentDidMount/componentDidUpdate
25-
...infoWindowControlledPropTypes,
26-
// Event [onEventName]
27-
...infoWindowEventPropTypes,
28-
}
18+
MAP,
19+
ANCHOR,
20+
INFO_WINDOW,
21+
} from "./constants";
2922

30-
static contextTypes = {
31-
mapHolderRef: PropTypes.instanceOf(GoogleMapHolder),
32-
}
23+
import {
24+
addDefaultPrefixToPropTypes,
25+
collectUncontrolledAndControlledProps,
26+
default as enhanceElement,
27+
} from "./enhanceElement";
3328

34-
// Public APIs
29+
const controlledPropTypes = {
30+
// NOTICE!!!!!!
3531
//
36-
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
32+
// Only expose those with getters & setters in the table as controlled props.
3733
//
34+
// [].map.call($0.querySelectorAll("tr>td>code", function(it){ return it.textContent; })
35+
// .filter(function(it){ return it.match(/^set/) && !it.match(/^setMap/); })
36+
//
37+
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
38+
children: PropTypes.element,
39+
options: PropTypes.object,
40+
position: PropTypes.any,
41+
zIndex: PropTypes.number,
42+
};
43+
44+
const defaultUncontrolledPropTypes = addDefaultPrefixToPropTypes(controlledPropTypes);
45+
46+
const eventMap = {
47+
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
3848
// [].map.call($0.querySelectorAll("tr>td>code"), function(it){ return it.textContent; })
39-
// .filter(function(it){ return it.match(/^get/) && !it.match(/^getMap/); })
40-
getContent() { /* TODO: children */ }
49+
onCloseClick: `closeclick`,
50+
51+
onContentChanged: `content_changed`,
4152

42-
getPosition() { return this.state.infoWindow.getPosition(); }
53+
onDomReady: `domready`,
4354

44-
getZIndex() { return this.state.infoWindow.getZIndex(); }
45-
// END - Public APIs
55+
onPositionChanged: `position_changed`,
56+
57+
onZIndexChanged: `zindex_changed`,
58+
};
59+
60+
const publicMethodMap = {
61+
// Public APIs
4662
//
4763
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
64+
//
65+
// [].map.call($0.querySelectorAll("tr>td>code"), function(it){ return it.textContent; })
66+
// .filter(function(it){ return it.match(/^get/) && !it.match(/Map$/); })
67+
getPosition(infoWindow) { return infoWindow.getPosition(); },
4868

49-
state = {
69+
getZIndex(infoWindow) { return infoWindow.getZIndex(); },
70+
// END - Public APIs
71+
};
72+
73+
const controlledPropUpdaterMap = {
74+
children(infoWindow, children) {
75+
render(Children.only(children), infoWindow.getContent());
76+
},
77+
options(infoWindow, options) { infoWindow.setOptions(options); },
78+
position(infoWindow, position) { infoWindow.setPosition(position); },
79+
zIndex(infoWindow, zIndex) { infoWindow.setZIndex(zIndex); },
80+
};
81+
82+
function getInstanceFromComponent(component) {
83+
return component.state[INFO_WINDOW];
84+
}
85+
86+
function openInfoWindow(context, infoWindow) {
87+
const map = context[MAP];
88+
const anchor = context[ANCHOR];
89+
if (anchor) {
90+
infoWindow.open(map, anchor);
91+
} else if (infoWindow.getPosition()) {
92+
infoWindow.open(map);
93+
} else {
94+
invariant(false,
95+
`You must provide either an anchor (typically a <Marker>) or a position for <InfoWindow>.`
96+
);
5097
}
98+
}
5199

52-
componentWillMount() {
53-
const { mapHolderRef } = this.context;
100+
export default _.flowRight(
101+
React.createClass,
102+
enhanceElement(getInstanceFromComponent, publicMethodMap, eventMap, controlledPropUpdaterMap),
103+
)({
104+
displayName: `InfoWindow`,
54105

55-
if (!canUseDOM) {
56-
return;
57-
}
58-
const infoWindow = InfoWindowCreator._createInfoWindow({
59-
...this.props,
60-
mapHolderRef,
106+
propTypes: {
107+
...controlledPropTypes,
108+
...defaultUncontrolledPropTypes,
109+
},
110+
111+
contextTypes: {
112+
[MAP]: PropTypes.object,
113+
[ANCHOR]: PropTypes.object,
114+
},
115+
116+
getInitialState() {
117+
const map = this.context[MAP];
118+
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow
119+
const infoWindow = new google.maps.InfoWindow({
120+
map,
121+
...collectUncontrolledAndControlledProps(
122+
defaultUncontrolledPropTypes,
123+
controlledPropTypes,
124+
this.props
125+
),
126+
// Override props of ReactElement type
127+
content: document.createElement(`div`),
128+
children: undefined,
61129
});
130+
openInfoWindow(this.context, infoWindow);
131+
return {
132+
[INFO_WINDOW]: infoWindow,
133+
};
134+
},
62135

63-
this.setState({ infoWindow });
64-
}
136+
componentDidMount() {
137+
const infoWindow = getInstanceFromComponent(this);
138+
controlledPropUpdaterMap.children(infoWindow, this.props.children);
139+
},
65140

66-
render() {
67-
if (this.state.infoWindow) {
68-
return (
69-
<InfoWindowCreator infoWindow={this.state.infoWindow} {...this.props}>
70-
{this.props.children}
71-
</InfoWindowCreator>
72-
);
73-
} else {
74-
return (<noscript />);
141+
componentWillReceiveProps(nextProps, nextContext) {
142+
const anchorChanged = this.context[ANCHOR] !== nextContext[ANCHOR];
143+
if (anchorChanged) {
144+
const infoWindow = getInstanceFromComponent(this);
145+
openInfoWindow(nextContext, infoWindow);
75146
}
76-
}
77-
}
147+
},
148+
149+
componentWillUnmount() {
150+
const infoWindow = getInstanceFromComponent(this);
151+
if (infoWindow) {
152+
unmountComponentAtNode(infoWindow.getContent());
153+
infoWindow.setMap(null);
154+
}
155+
},
156+
157+
render() {
158+
return false;
159+
},
160+
});

src/lib/Marker.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import {
1010
MAP,
1111
MARKER,
12+
ANCHOR,
1213
} from "./constants";
1314

1415
import {
@@ -193,6 +194,10 @@ export default _.flowRight(
193194
[MAP]: PropTypes.object,
194195
},
195196

197+
childContextTypes: {
198+
[ANCHOR]: PropTypes.object,
199+
},
200+
196201
getInitialState() {
197202
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Marker
198203
const marker = new google.maps.Marker({
@@ -208,6 +213,12 @@ export default _.flowRight(
208213
};
209214
},
210215

216+
getChildContext() {
217+
return {
218+
[ANCHOR]: this.state[MARKER],
219+
};
220+
},
221+
211222
componentWillUnmount() {
212223
const marker = getInstanceFromComponent(this);
213224
if (marker) {
@@ -216,6 +227,14 @@ export default _.flowRight(
216227
},
217228

218229
render() {
219-
return false;
230+
const {
231+
children,
232+
} = this.props;
233+
234+
return (
235+
<div>
236+
{children}
237+
</div>
238+
);
220239
},
221240
});

src/lib/constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@ export const KML_LAYER = `__SECRET_KML_LAYER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;
1717
export const DIRECTIONS_RENDERER = `__SECRET_DIRECTIONS_RENDERER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;
1818

1919
export const HEATMAP_LAYER = `__SECRET_HEATMAP_LAYER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;
20+
21+
export const ANCHOR = `__SECRET_ANCHOR_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;
22+
23+
export const INFO_WINDOW = `__SECRET_INFO_WINDOW_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;

src/lib/creators/InfoWindowCreator.js

Lines changed: 0 additions & 90 deletions
This file was deleted.

src/lib/eventLists/InfoWindowEventList.js

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/lib/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,7 @@ export {
3737
export {
3838
default as HeatmapLayer,
3939
} from "./HeatmapLayer";
40+
41+
export {
42+
default as InfoWindow,
43+
} from "./InfoWindow";

0 commit comments

Comments
 (0)