google-map-react
Version:
isomorphic google map react component, allows render react components on the google map
515 lines (410 loc) • 19.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _reactPureRenderFunction = require('react-pure-render/function');
var _reactPureRenderFunction2 = _interopRequireDefault(_reactPureRenderFunction);
var _marker_dispatcherJs = require('./marker_dispatcher.js');
var _marker_dispatcherJs2 = _interopRequireDefault(_marker_dispatcherJs);
var _google_map_mapJs = require('./google_map_map.js');
var _google_map_mapJs2 = _interopRequireDefault(_google_map_mapJs);
var _google_map_markersJs = require('./google_map_markers.js');
var _google_map_markersJs2 = _interopRequireDefault(_google_map_markersJs);
var _google_map_markers_prerenderJs = require('./google_map_markers_prerender.js');
var _google_map_markers_prerenderJs2 = _interopRequireDefault(_google_map_markers_prerenderJs);
var _utilsLoadersGoogle_map_loaderJs = require('./utils/loaders/google_map_loader.js');
var _utilsLoadersGoogle_map_loaderJs2 = _interopRequireDefault(_utilsLoadersGoogle_map_loaderJs);
var _utilsDetectJs = require('./utils/detect.js');
var _utilsDetectJs2 = _interopRequireDefault(_utilsDetectJs);
var _utilsGeoJs = require('./utils/geo.js');
var _utilsGeoJs2 = _interopRequireDefault(_utilsGeoJs);
var _utilsArray_helperJs = require('./utils/array_helper.js');
var _utilsArray_helperJs2 = _interopRequireDefault(_utilsArray_helperJs);
var kEPS = 0.00001;
var K_GOOGLE_TILE_SIZE = 256;
var K_MAP_CONTROL_OPTIONS = {
overviewMapControl: false,
streetViewControl: false,
rotateControl: true,
mapTypeControl: false,
// disable poi
styles: [{ featureType: 'poi', elementType: 'labels', stylers: [{ visibility: 'off' }] }],
minZoom: 3 // i need to dynamically calculate possible zoom value
};
var style = {
width: '100%',
height: '100%',
margin: 0,
padding: 0,
position: 'relative'
};
function isNumber(n) {
return !Number.isNaN(parseFloat(n)) && Number.isFinite(n);
}
var GoogleMap = (function (_Component) {
function GoogleMap(props) {
var _this = this;
_classCallCheck(this, GoogleMap);
_get(Object.getPrototypeOf(GoogleMap.prototype), 'constructor', this).call(this, props);
this.shouldComponentUpdate = _reactPureRenderFunction2['default'];
this._initMap = function () {
var center = _this.props.center;
_this.geoService_.setView(center, _this.props.zoom, 0);
_this._onBoundsChanged(); // now we can calculate map bounds center etc...
_this.props.googleMapLoader(_this.props.apiKey).then(function (maps) {
if (!_this.mounted_) {
return;
}
var centerLatLng = _this.geoService_.getCenter();
var propsOptions = {
zoom: _this.props.zoom,
center: new maps.LatLng(centerLatLng.lat, centerLatLng.lng)
};
var mapOptions = _extends({}, _this.props.options, propsOptions);
var map = new maps.Map(_react2['default'].findDOMNode(_this.refs.google_map_dom), mapOptions);
_this.map_ = map;
_this.maps_ = maps;
// render in overlay
var this_ = _this;
var overlay = Object.assign(new maps.OverlayView(), {
onAdd: function onAdd() {
var K_MAX_WIDTH = typeof screen !== 'undefined' ? '' + screen.width + 'px' : '2000px';
var K_MAX_HEIGHT = typeof screen !== 'undefined' ? '' + screen.height + 'px' : '2000px';
var div = document.createElement('div');
this.div = div;
div.style.backgroundColor = 'transparent';
div.style.position = 'absolute';
div.style.left = '0px';
div.style.top = '0px';
div.style.width = K_MAX_WIDTH; // prevents some chrome draw defects
div.style.height = K_MAX_HEIGHT;
var panes = this.getPanes();
panes.overlayMouseTarget.appendChild(div);
_react2['default'].render(_react2['default'].createElement(_google_map_markersJs2['default'], {
onChildClick: this_._onChildClick,
onChildMouseEnter: this_._onChildMouseEnter,
onChildMouseLeave: this_._onChildMouseLeave,
geoService: this_.geoService_,
projectFromLeftTop: true,
distanceToMouse: this_.props.distanceToMouse,
hoverDistance: this_.props.hoverDistance,
dispatcher: this_.markersDispatcher_ }), div, function () {
// remove prerendered markers
this_.setState({ overlayCreated: true });
});
},
draw: function draw() {
var div = overlay.div;
var overlayProjection = overlay.getProjection();
var bounds = map.getBounds();
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
var ptx = overlayProjection.fromLatLngToDivPixel(new maps.LatLng(ne.lat(), sw.lng()));
// need round for safari still can't find what need for firefox
var ptxRounded = (0, _utilsDetectJs2['default'])().isSafari ? { x: Math.round(ptx.x), y: Math.round(ptx.y) } : { x: ptx.x, y: ptx.y };
this_.updateCounter_++;
this_._onBoundsChanged(map, maps, !this_.props.debounced);
div.style.left = '' + ptxRounded.x + 'px';
div.style.top = '' + ptxRounded.y + 'px';
if (this_.markersDispatcher_) {
this_.markersDispatcher_.emit('kON_CHANGE');
}
}
});
overlay.setMap(map);
maps.event.addListener(map, 'idle', function () {
if (_this.resetSizeOnIdle_) {
_this._setViewSize();
_this.resetSizeOnIdle_ = false;
}
var div = overlay.div;
var overlayProjection = overlay.getProjection();
var bounds = map.getBounds();
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
var ptx = overlayProjection.fromLatLngToDivPixel(new maps.LatLng(ne.lat(), sw.lng()));
// need round for safari still can't find what need for firefox
var ptxRounded = (0, _utilsDetectJs2['default'])().isSafari ? { x: Math.round(ptx.x), y: Math.round(ptx.y) } : { x: ptx.x, y: ptx.y };
this_.updateCounter_++;
this_._onBoundsChanged(map, maps);
this_.dragTime_ = 0;
div.style.left = '' + ptxRounded.x + 'px';
div.style.top = '' + ptxRounded.y + 'px';
if (this_.markersDispatcher_) {
this_.markersDispatcher_.emit('kON_CHANGE');
if (this_.fireMouseEventOnIdle_) {
this_.markersDispatcher_.emit('kON_MOUSE_POSITION_CHANGE');
}
}
});
maps.event.addListener(map, 'mouseover', function () {
// has advantage over div MouseLeave
this_.mouseInMap_ = true;
});
maps.event.addListener(map, 'mouseout', function () {
// has advantage over div MouseLeave
this_.mouseInMap_ = false;
this_.mouse_ = null;
this_.markersDispatcher_.emit('kON_MOUSE_POSITION_CHANGE');
});
maps.event.addListener(map, 'drag', function () {
this_.dragTime_ = new Date().getTime();
});
})['catch'](function (e) {
console.error(e); // eslint-disable-line no-console
throw e;
});
};
this._onChildClick = function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
if (_this.props.onChildClick) {
var _props;
return (_props = _this.props).onChildClick.apply(_props, args);
}
};
this._onChildMouseEnter = function () {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
if (_this.props.onChildMouseEnter) {
var _props2;
return (_props2 = _this.props).onChildMouseEnter.apply(_props2, args);
}
};
this._onChildMouseLeave = function () {
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
if (_this.props.onChildMouseLeave) {
var _props3;
return (_props3 = _this.props).onChildMouseLeave.apply(_props3, args);
}
};
this._setViewSize = function () {
var mapDom = _react2['default'].findDOMNode(_this.refs.google_map_dom);
_this.geoService_.setViewSize(mapDom.clientWidth, mapDom.clientHeight);
_this._onBoundsChanged();
};
this._onWindowResize = function () {
_this.resetSizeOnIdle_ = true;
};
this._onBoundsChanged = function (map, maps, callExtBoundsChange) {
if (map) {
var gmC = map.getCenter();
_this.geoService_.setView([gmC.lat(), gmC.lng()], map.getZoom(), 0);
}
if (_this.props.onBoundsChange && _this.geoService_.canProject()) {
var zoom = _this.geoService_.getZoom();
var bounds = _this.geoService_.getBounds();
var centerLatLng = _this.geoService_.getCenter();
if (!(0, _utilsArray_helperJs2['default'])(bounds, _this.prevBounds_, kEPS)) {
if (callExtBoundsChange !== false) {
var marginBounds = _this.geoService_.getBounds(_this.props.margin);
_this.props.onBoundsChange([centerLatLng.lat, centerLatLng.lng], zoom, bounds, marginBounds);
_this.prevBounds_ = bounds;
}
}
// uncomment for strange bugs
if (process.env.NODE_ENV !== 'production') {
// compare with google calculations
if (map) {
var locBounds = map.getBounds();
var ne = locBounds.getNorthEast();
var sw = locBounds.getSouthWest();
var gmC = map.getCenter();
// compare with google map
if (!(0, _utilsArray_helperJs2['default'])([centerLatLng.lat, centerLatLng.lng], [gmC.lat(), gmC.lng()], kEPS)) {
console.info('GoogleMap center not eq:', [centerLatLng.lat, centerLatLng.lng], [gmC.lat(), gmC.lng()]); // eslint-disable-line no-console
}
if (!(0, _utilsArray_helperJs2['default'])(bounds, [ne.lat(), sw.lng(), sw.lat(), ne.lng()], kEPS)) {
// this is normal if this message occured on resize
console.info('GoogleMap bounds not eq:', '\n', bounds, '\n', [ne.lat(), sw.lng(), sw.lat(), ne.lng()]); // eslint-disable-line no-console
}
}
}
}
};
this._onMouseMove = function (e) {
if (!_this.mouseInMap_) return;
var currTime = new Date().getTime();
var K_RECALC_CLIENT_RECT_MS = 3000;
if (currTime - _this.mouseMoveTime_ > K_RECALC_CLIENT_RECT_MS) {
_this.boundingRect_ = e.currentTarget.getBoundingClientRect();
}
_this.mouseMoveTime_ = currTime;
var mousePosX = e.clientX - _this.boundingRect_.left;
var mousePosY = e.clientY - _this.boundingRect_.top;
if (!_this.mouse_) {
_this.mouse_ = { x: 0, y: 0, lat: 0, lng: 0 };
}
var K_IDLE_TIMEOUT = 100;
_this.mouse_.x = mousePosX;
_this.mouse_.y = mousePosY;
var latLng = _this.geoService_.unproject(_this.mouse_, true);
_this.mouse_.lat = latLng.lat;
_this.mouse_.lng = latLng.lng;
if (currTime - _this.dragTime_ < K_IDLE_TIMEOUT) {
_this.fireMouseEventOnIdle_ = true;
} else {
_this.markersDispatcher_.emit('kON_MOUSE_POSITION_CHANGE');
_this.fireMouseEventOnIdle_ = false;
}
};
this._onMapClick = function () {
if (_this.markersDispatcher_) {
var K_IDLE_TIMEOUT = 100;
var currTime = new Date().getTime();
if (currTime - _this.dragTime_ > K_IDLE_TIMEOUT) {
_this.markersDispatcher_.emit('kON_CLICK');
}
}
};
this._isCenterDefined = function (center) {
return center && center.length === 2 && isNumber(center[0]) && isNumber(center[1]);
};
this.mounted_ = false;
this.map_ = null;
this.maps_ = null;
this.prevBounds_ = null;
this.mouse_ = null;
this.mouseMoveTime_ = 0;
this.boundingRect_ = null;
this.mouseInMap_ = true;
this.dragTime_ = 0;
this.fireMouseEventOnIdle_ = false;
this.updateCounter_ = 0;
this.markersDispatcher_ = new _marker_dispatcherJs2['default'](this);
this.geoService_ = new _utilsGeoJs2['default'](K_GOOGLE_TILE_SIZE);
if (this._isCenterDefined(this.props.center)) {
this.geoService_.setView(this.props.center, this.props.zoom, 0);
}
this.state = {
overlayCreated: false
};
}
_inherits(GoogleMap, _Component);
_createClass(GoogleMap, [{
key: 'componentDidMount',
value: function componentDidMount() {
var _this2 = this;
this.mounted_ = true;
window.addEventListener('resize', this._onWindowResize);
setTimeout(function () {
// to detect size
_this2._setViewSize();
if (_this2._isCenterDefined(_this2.props.center)) {
_this2._initMap();
} else {
_this2.props.googleMapLoader(_this2.props.apiKey); // начать подгружать можно уже сейчас
}
}, 0, this);
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
this.mounted_ = false;
window.removeEventListener('resize', this._onWindowResize);
if (this.maps_ && this.map_) {
this.maps_.event.clearInstanceListeners(this.map_);
}
this.map_ = null;
this.maps_ = null;
this.markersDispatcher_.dispose();
this.resetSizeOnIdle_ = false;
delete this.map_;
delete this.markersDispatcher_;
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
var _this3 = this;
if (!this._isCenterDefined(this.props.center) && this._isCenterDefined(nextProps.center)) {
setTimeout(function () {
return _this3._initMap();
}, 0);
}
if (this.map_) {
var centerLatLng = this.geoService_.getCenter();
if (nextProps.center) {
if (Math.abs(nextProps.center[0] - centerLatLng.lat) + Math.abs(nextProps.center[1] - centerLatLng.lng) > kEPS) {
this.map_.panTo({ lat: nextProps.center[0], lng: nextProps.center[1] });
}
}
// if zoom chaged by user
if (Math.abs(nextProps.zoom - this.props.zoom) > 0) {
this.map_.setZoom(nextProps.zoom);
}
}
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate() {
this.markersDispatcher_.emit('kON_CHANGE');
}
}, {
key: 'render',
value: function render() {
var mapMarkerPrerender = !this.state.overlayCreated ? _react2['default'].createElement(_google_map_markers_prerenderJs2['default'], {
onChildClick: this._onChildClick,
onChildMouseEnter: this._onChildMouseEnter,
onChildMouseLeave: this._onChildMouseLeave,
geoService: this.geoService_,
projectFromLeftTop: false,
distanceToMouse: this.props.distanceToMouse,
hoverDistance: this.props.hoverDistance,
dispatcher: this.markersDispatcher_ }) : null;
return _react2['default'].createElement(
'div',
{ style: style, onMouseMove: this._onMouseMove, onClick: this._onMapClick },
_react2['default'].createElement(_google_map_mapJs2['default'], { ref: 'google_map_dom' }),
mapMarkerPrerender
);
}
}], [{
key: 'propTypes',
value: {
apiKey: _react.PropTypes.string,
center: _react.PropTypes.array.isRequired,
zoom: _react.PropTypes.number.isRequired,
onBoundsChange: _react.PropTypes.func,
onChildClick: _react.PropTypes.func,
onChildMouseEnter: _react.PropTypes.func,
onChildMouseLeave: _react.PropTypes.func,
options: _react.PropTypes.any,
distanceToMouse: _react.PropTypes.func,
hoverDistance: _react.PropTypes.number,
debounced: _react.PropTypes.bool,
margin: _react.PropTypes.array,
googleMapLoader: _react.PropTypes.any
},
enumerable: true
}, {
key: 'defaultProps',
value: {
distanceToMouse: function distanceToMouse(pt, mousePos /*, markerProps*/) {
var x = pt.x;
var y = pt.y; // - 20;
return Math.sqrt((x - mousePos.x) * (x - mousePos.x) + (y - mousePos.y) * (y - mousePos.y));
},
hoverDistance: 30,
debounced: true,
options: K_MAP_CONTROL_OPTIONS,
googleMapLoader: _utilsLoadersGoogle_map_loaderJs2['default']
},
enumerable: true
}]);
return GoogleMap;
})(_react.Component);
exports['default'] = GoogleMap;
module.exports = exports['default'];
/*render markers before map load done*/