diff --git a/README.md b/README.md
index db28b17..f43db6e 100644
--- a/README.md
+++ b/README.md
@@ -93,6 +93,25 @@ class App extends React.Component {
ReactDOM.render(, 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:
+
+```
+
+ ...
+
+```
+
+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
----
diff --git a/dist/components/entity.js b/dist/components/entity.js
index b05aa1a..6471b64 100644
--- a/dist/components/entity.js
+++ b/dist/components/entity.js
@@ -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) {
diff --git a/dist/components/gmaps.js b/dist/components/gmaps.js
index 1e0c798..7d81c00 100644
--- a/dist/components/gmaps.js
+++ b/dist/components/gmaps.js
@@ -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);
@@ -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
@@ -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() {
@@ -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,
@@ -119,7 +132,6 @@ var Gmaps = (0, _createReactClass2['default'])({
this.state.isMapCreated ? this.getChildren() : null
);
}
-
});
exports['default'] = Gmaps;
diff --git a/package.json b/package.json
index 270b594..feeda84 100644
--- a/package.json
+++ b/package.json
@@ -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": {
@@ -57,6 +57,7 @@
]
},
"dependencies": {
+ "@google/markerclusterer": "^1.0.3",
"create-react-class": "^15.5.2",
"object-assign": "^4.0.1"
},
diff --git a/src/components/entity.js b/src/components/entity.js
index ccf1be4..e75cb64 100644
--- a/src/components/entity.js
+++ b/src/components/entity.js
@@ -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) {
@@ -53,7 +54,7 @@ export default (name, latLngProp, events) => {
break;
}
},
-
+
render() {
return null;
}
diff --git a/src/components/gmaps.js b/src/components/gmaps.js
index c0e3801..db14677 100644
--- a/src/components/gmaps.js
+++ b/src/components/gmaps.js
@@ -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
@@ -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 (
{this.props.loadingMessage || 'Loading...'}
{this.state.isMapCreated ? this.getChildren() : null}
);
- },
-
+ }
});
export default Gmaps;
diff --git a/yarn.lock b/yarn.lock
index 9824ca5..38e89b8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"