Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,25 @@ class App extends React.Component {
ReactDOM.render(<App />, document.getElementById('gmaps'));
```

MarkerClusterer
----

You can cluster markers together (see [Google's docs](https://developers.google.com/maps/documentation/javascript/marker-clustering)) by setting the `clusterMarkers` prop on the `Gmaps` component. By default this will expect icons for the clusters named `m1.png, m2.png, m3.png, m4.png, m5.png` to reside at `your_web_root/images/`.

You can pass an options object to this prop, allowing you to specify a new location for these cluster icons. The root format will append `1.png`, `2.png`, etc to the supplied path.

For example if your images reside at `http://localhost:3000/cluster-icons/` and are still called `m1.png`, you would supply a path of `http://localhost:3000/cluster-icons/m` similar to:

```
<Gmaps
clusterMarkers={{ imagePath: 'http://localhost:3000/cluster-icons/m' }}
...>
...
</Gmaps>
```

At current this is basic implementation and could be improved by adding Events to the clusters, another improvement would be to allow 'de-clustering' based on zoom level so when you have multiple markers with the exact same location you can still separate them and click on them ([stackoverlow example](https://stackoverflow.com/questions/15276908/google-markerclusterer-decluster-markers-below-a-certain-zoom-level?rq=1) or [OverlappingMarkerSpiderfier](https://github.com/jawj/OverlappingMarkerSpiderfier)).

Test
----

Expand Down
1 change: 1 addition & 0 deletions dist/components/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ exports['default'] = function (name, latLngProp, events) {
var options = this.getOptions(this.props);
this.entity = new google.maps[name](options);
this.addListeners(this.entity, events);
this.props.onCreate(name, this.entity);
},

componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
Expand Down
18 changes: 15 additions & 3 deletions dist/components/gmaps.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ var _objectAssign = require('object-assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

var _googleMarkerclusterer = require('@google/markerclusterer');

var _googleMarkerclusterer2 = _interopRequireDefault(_googleMarkerclusterer);

var _eventsMap = require('../events/map');

var _eventsMap2 = _interopRequireDefault(_eventsMap);
Expand All @@ -41,11 +45,12 @@ var _utilsCompareProps = require('../utils/compare-props');
var _utilsCompareProps2 = _interopRequireDefault(_utilsCompareProps);

var Gmaps = (0, _createReactClass2['default'])({

mixins: [_mixinsListener2['default']],

map: null,

markers: [],

getInitialState: function getInitialState() {
return {
isMapCreated: false
Expand Down Expand Up @@ -91,6 +96,9 @@ var Gmaps = (0, _createReactClass2['default'])({
if (this.props.onMapCreated) {
this.props.onMapCreated(this.map);
}
if (this.props.clusterMarkers) {
new _googleMarkerclusterer2['default'](this.map, this.markers, typeof this.props.clusterMarkers === 'object' ? this.props.clusterMarkers : null);
}
},

getChildren: function getChildren() {
Expand All @@ -102,11 +110,16 @@ var Gmaps = (0, _createReactClass2['default'])({
}
return _react2['default'].cloneElement(child, {
ref: child.ref,
map: _this.map
map: _this.map,
onCreate: _this.handleChildCreation
});
});
},

handleChildCreation: function handleChildCreation(entityType, entity) {
if (entityType === 'Marker') this.markers.push(entity);
},

render: function render() {
var style = (0, _objectAssign2['default'])({
width: this.props.width,
Expand All @@ -119,7 +132,6 @@ var Gmaps = (0, _createReactClass2['default'])({
this.state.isMapCreated ? this.getChildren() : null
);
}

});

exports['default'] = Gmaps;
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-gmaps",
"version": "1.9.0",
"version": "1.9.1",
"description": "A Google Maps component for React.js",
"main": "dist/index.js",
"scripts": {
Expand Down Expand Up @@ -57,6 +57,7 @@
]
},
"dependencies": {
"@google/markerclusterer": "^1.0.3",
"create-react-class": "^15.5.2",
"object-assign": "^4.0.1"
},
Expand Down
3 changes: 2 additions & 1 deletion src/components/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default (name, latLngProp, events) => {
const options = this.getOptions(this.props);
this.entity = new google.maps[name](options);
this.addListeners(this.entity, events);
this.props.onCreate(name, this.entity);
},

componentWillReceiveProps(nextProps) {
Expand Down Expand Up @@ -53,7 +54,7 @@ export default (name, latLngProp, events) => {
break;
}
},

render() {
return null;
}
Expand Down
36 changes: 27 additions & 9 deletions src/components/gmaps.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ import React from 'react';
import ReactDOM from 'react-dom';
import createReactClass from 'create-react-class';
import objectAssign from 'object-assign';
import MarkerClusterer from '@google/markerclusterer';
import MapEvents from '../events/map';
import Listener from '../mixins/listener';
import GoogleMaps from '../utils/google-maps';
import compareProps from '../utils/compare-props';

const Gmaps = createReactClass({

mixins: [Listener],

map: null,

markers: [],

getInitialState() {
return {
isMapCreated: false
Expand Down Expand Up @@ -60,33 +62,49 @@ const Gmaps = createReactClass({
if (this.props.onMapCreated) {
this.props.onMapCreated(this.map);
}
if (this.props.clusterMarkers) {
new MarkerClusterer(
this.map,
this.markers,
typeof this.props.clusterMarkers === 'object'
? this.props.clusterMarkers
: null
);
}
},

getChildren() {
return React.Children.map(this.props.children, (child) => {
return React.Children.map(this.props.children, child => {
if (!React.isValidElement(child)) {
return child;
}
return React.cloneElement(child, {
ref: child.ref,
map: this.map
map: this.map,
onCreate: this.handleChildCreation
});
});
},

handleChildCreation(entityType, entity) {
if (entityType === 'Marker') this.markers.push(entity);
},

render() {
const style = objectAssign({
width: this.props.width,
height: this.props.height
}, this.props.style);
const style = objectAssign(
{
width: this.props.width,
height: this.props.height
},
this.props.style
);
return (
<div style={style} className={this.props.className}>
{this.props.loadingMessage || 'Loading...'}
{this.state.isMapCreated ? this.getChildren() : null}
</div>
);
},

}
});

export default Gmaps;
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
# yarn lockfile v1


"@google/markerclusterer@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@google/markerclusterer/-/markerclusterer-1.0.3.tgz#830b9dbba85fae9537a16d17947b484f9e860c65"
integrity sha512-/fRbSPyQKnm43zRnoyMerbiGS3vG3WkZiLgpBF3ovoLO84sKhEAzKMVcyozy/khEHlZoMJ9Axkr0NRVJRAQhcg==

JSONStream@^1.0.3:
version "1.3.1"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.1.tgz#707f761e01dae9e16f1bcf93703b78c70966579a"
Expand Down