Skip to content

Commit

Permalink
refactor(Marker): 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
<Marker
  onClick={_.noop}
  onRightclick={_.noop}
  onDragstart={_.noop}
/>
```

After:

```js
<Marker
  onClick={_.noop}
  onRightClick={_.noop}
  onDragStart={_.noop}
/>
```
  • Loading branch information
tomchentw committed Oct 4, 2016
1 parent 4b22e42 commit c06aff2
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 219 deletions.
257 changes: 182 additions & 75 deletions src/lib/Marker.js
Original file line number Diff line number Diff line change
@@ -1,114 +1,221 @@
/* global google */
import _ from "lodash";

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

import {
default as canUseDOM,
} from "can-use-dom";
MAP,
MARKER,
} from "./constants";

import {
default as MarkerCreator,
markerDefaultPropTypes,
markerControlledPropTypes,
markerEventPropTypes,
} from "./creators/MarkerCreator";

import GoogleMapHolder from "./creators/GoogleMapHolder";

export default class Marker extends Component {
static propTypes = {
// Uncontrolled default[props] - used only in componentDidMount
...markerDefaultPropTypes,
// Controlled [props] - used in componentDidMount/componentDidUpdate
...markerControlledPropTypes,
// Event [onEventName]
...markerEventPropTypes,
}

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

const controlledPropTypes = {
// NOTICE!!!!!!
//
// 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#Marker
animation: PropTypes.any,

attribution: PropTypes.any,

clickable: PropTypes.bool,

cursor: PropTypes.string,

draggable: PropTypes.bool,

icon: PropTypes.any,

label: PropTypes.any,

opacity: PropTypes.number,

options: PropTypes.object,

place: PropTypes.any,

position: PropTypes.any,

shape: PropTypes.any,

title: PropTypes.string,

visible: PropTypes.bool,

zIndex: PropTypes.number,
};

const defaultUncontrolledPropTypes = addDefaultPrefixToPropTypes(controlledPropTypes);

const eventMap = {
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Marker
// [].map.call($0.querySelectorAll("tr>td>code"), function(it){ return it.textContent; })
onAnimationChanged: `animation_changed`,

onClick: `click`,

onClickableChanged: `clickable_changed`,

onCursorChanged: `cursor_changed`,

onDblClick: `dblclick`,

onDrag: `drag`,

onDragEnd: `dragend`,

onDraggableChanged: `draggable_changed`,

onDragStart: `dragstart`,

onFlatChanged: `flat_changed`,

onIconChanged: `icon_changed`,

onMouseDown: `mousedown`,

onMouseOut: `mouseout`,

onMouseOver: `mouseover`,

onMouseUp: `mouseup`,

onPositionChanged: `position_changed`,

onRightClick: `rightclick`,

onShapeChanged: `shape_changed`,

onTitleChanged: `title_changed`,

onVisibleChanged: `visible_changed`,

onZindexChanged: `zindex_changed`,
};

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

getAttribution() { return this.state.marker.getAttribution(); }
getAttribution(marker) { return marker.getAttribution(); },

getClickable() { return this.state.marker.getClickable(); }
getClickable(marker) { return marker.getClickable(); },

getCursor() { return this.state.marker.getCursor(); }
getCursor(marker) { return marker.getCursor(); },

getDraggable() { return this.state.marker.getDraggable(); }
getDraggable(marker) { return marker.getDraggable(); },

getIcon() { return this.state.marker.getIcon(); }
getIcon(marker) { return marker.getIcon(); },

getLabel() { return this.state.marker.getLabel(); }
getLabel(marker) { return marker.getLabel(); },

getOpacity() { return this.state.marker.getOpacity(); }
getOpacity(marker) { return marker.getOpacity(); },

getPlace() { return this.state.marker.getPlace(); }
getPlace(marker) { return marker.getPlace(); },

getPosition() { return this.state.marker.getPosition(); }
getPosition(marker) { return marker.getPosition(); },

getShape() { return this.state.marker.getShape(); }
getShape(marker) { return marker.getShape(); },

getTitle() { return this.state.marker.getTitle(); }
getTitle(marker) { return marker.getTitle(); },

getVisible() { return this.state.marker.getVisible(); }
getVisible(marker) { return marker.getVisible(); },

getZIndex() { return this.state.marker.getZIndex(); }
getZIndex(marker) { return marker.getZIndex(); },
// END - Public APIs
//
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Marker
};

state = {
}
const controlledPropUpdaterMap = {
animation(marker, animation) { marker.setAnimation(animation); },

componentWillMount() {
const { mapHolderRef } = this.context;
if (!canUseDOM) {
return;
}
const marker = MarkerCreator._createMarker({
...this.props,
mapHolderRef,
});
attribution(marker, attribution) { marker.setAttribution(attribution); },

this.setState({ marker });
}
clickable(marker, clickable) { marker.setClickable(clickable); },

componentWillUnmount() {
if (!canUseDOM) {
return;
}
cursor(marker, cursor) { marker.setCursor(cursor); },

const { anchorHolderRef } = this.props;
const { marker } = this.state;
draggable(marker, draggable) { marker.setDraggable(draggable); },

icon(marker, icon) { marker.setIcon(icon); },

if (anchorHolderRef) {
if (`MarkerClusterer` === anchorHolderRef.getAnchorType()) {
anchorHolderRef.getAnchor().removeMarker(marker);
}
label(marker, label) { marker.setLabel(label); },

opacity(marker, opacity) { marker.setOpacity(opacity); },

options(marker, options) { marker.setOptions(options); },

place(marker, place) { marker.setPlace(place); },

position(marker, position) { marker.setPosition(position); },

shape(marker, shape) { marker.setShape(shape); },

title(marker, title) { marker.setTitle(title); },

visible(marker, visible) { marker.setVisible(visible); },

zIndex(marker, zIndex) { marker.setZIndex(zIndex); },
};

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

export default _.flowRight(
React.createClass,
enhanceElement(getInstanceFromComponent, publicMethodMap, eventMap, controlledPropUpdaterMap),
)({
displayName: `Marker`,

propTypes: {
...controlledPropTypes,
...defaultUncontrolledPropTypes,
},

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

getInitialState() {
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Marker
const marker = new google.maps.Marker({
map: this.context[MAP],
...collectUncontrolledAndControlledProps(
defaultUncontrolledPropTypes,
controlledPropTypes,
this.props
),
});
return {
[MARKER]: marker,
};
},

componentWillUnmount() {
const marker = getInstanceFromComponent(this);
if (marker) {
marker.setMap(null);
}
}
},

render() {
if (this.state.marker) {
return (
<MarkerCreator marker={this.state.marker} {...this.props}>
{this.props.children}
</MarkerCreator>
);
} else {
return (<noscript />);
}
}
}
return false;
},
});
2 changes: 2 additions & 0 deletions src/lib/constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const MAP = `__SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;

// export const SKELETON = `__SECRET_SKELETON_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;

export const MARKER = `__SECRET_MARKER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;
Loading

0 comments on commit c06aff2

Please sign in to comment.