Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-simple-maps

Package Overview
Dependencies
Maintainers
1
Versions
54
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-simple-maps - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

lib/Annotation.js

128

lib/Geographies.js

@@ -13,12 +13,4 @@ "use strict";

var _defaultStyles = require("./defaultStyles");
var _topojsonClient = require("topojson-client");
var _defaultStyles2 = _interopRequireDefault(_defaultStyles);
var _utils = require("./utils");
var _Geography = require("./Geography");
var _Geography2 = _interopRequireDefault(_Geography);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -32,45 +24,86 @@

var Geographies = function (_React$Component) {
_inherits(Geographies, _React$Component);
var Geographies = function (_Component) {
_inherits(Geographies, _Component);
function Geographies() {
function Geographies(props) {
_classCallCheck(this, Geographies);
return _possibleConstructorReturn(this, (Geographies.__proto__ || Object.getPrototypeOf(Geographies)).apply(this, arguments));
var _this = _possibleConstructorReturn(this, (Geographies.__proto__ || Object.getPrototypeOf(Geographies)).call(this, props));
_this.state = {
geographyPaths: props.geographyPaths
};
_this.fetchGeographies = _this.fetchGeographies.bind(_this);
return _this;
}
_createClass(Geographies, [{
key: "fetchGeographies",
value: function fetchGeographies(geographyUrl) {
var _this2 = this;
var _props = this.props,
width = _props.width,
height = _props.height;
if (!geographyUrl) return;
var request = new XMLHttpRequest();
request.open("GET", geographyUrl, true);
request.onload = function () {
if (request.status >= 200 && request.status < 400) {
var geographyPaths = JSON.parse(request.responseText);
_this2.setState({
geographyPaths: (0, _topojsonClient.feature)(geographyPaths, geographyPaths.objects[Object.keys(geographyPaths.objects)[0]]).features
}, function () {
if (!_this2.props.onGeographiesLoaded) return;
_this2.props.onGeographyPathsLoaded(String(request.status));
});
} else {
if (!_this2.props.onGeographiesLoaded) return;
_this2.props.onGeographyPathsLoaded(String(request.status));
}
};
request.onerror = function () {
console.log("There was a connection error...");
};
request.send();
}
}, {
key: "componentWillReceiveProps",
value: function componentWillReceiveProps(nextProps) {
;
if (!nextProps.geographyUrl) return;
if (nextProps.geographyUrl !== this.props.geographyUrl) {
this.fetchGeographies(nextProps.geographyUrl);
}
}
}, {
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps) {
var geoPathsChanged = nextProps.geographyPaths.length !== this.props.geographyPaths.length;
var includesChanged = nextProps.include.length !== this.props.include.length;
var excludesChanged = nextProps.exclude.length !== this.props.exclude.length;
value: function shouldComponentUpdate(nextProps, nextState) {
var geoPathsChanged = nextState.geographyPaths.length !== this.state.geographyPaths.length;
var choroplethChanged = JSON.stringify(nextProps.choropleth) !== JSON.stringify(this.props.choropleth);
return !this.props.freezeGeographyPaths || geoPathsChanged || includesChanged || excludesChanged || choroplethChanged;
return geoPathsChanged || choroplethChanged || nextProps.disableOptimization;
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
this.fetchGeographies(this.props.geographyUrl);
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _props2 = this.props,
projection = _props2.projection,
style = _props2.style,
children = _props2.children;
var styles = this.props.styles.geography || _defaultStyles2.default.geography;
return _react2.default.createElement(
"g",
{ className: "rsm-geographies" },
this.props.geographyPaths.map(function (geography, i) {
var included = _this2.props.include.indexOf(geography.id) !== -1;
var notExcluded = _this2.props.exclude.indexOf(geography.id) === -1;
var shouldInclude = _this2.props.include.length > 0 ? included : notExcluded;
return shouldInclude ? _react2.default.createElement(_Geography2.default, {
zoom: _this2.props.zoom,
key: geography.id ? geography.id + "-" + i : i,
geography: geography,
projection: _this2.props.projection,
choroplethValue: _this2.props.choropleth[geography.id],
styles: styles,
events: _this2.props.events
}) : null;
})
{ className: "rsm-geographies", style: style },
children(this.state.geographyPaths, projection)
);

@@ -81,25 +114,10 @@ }

return Geographies;
}(_react2.default.Component);
}(_react.Component);
Geographies.propTypes = {
geographyPaths: _react.PropTypes.array,
projection: _react.PropTypes.func.isRequired,
freezeGeographyPaths: _react.PropTypes.bool,
exclude: _react.PropTypes.array,
include: _react.PropTypes.array,
styles: _react.PropTypes.object,
choropleth: _react.PropTypes.object,
events: _react.PropTypes.object
};
Geographies.defaultProps = {
geographyPaths: [],
freezeGeographyPaths: true,
exclude: [],
include: [],
styles: _defaultStyles2.default,
choropleth: {},
events: {}
componentIdentifier: "Geographies",
disableOptimization: false,
geographyPaths: []
};
exports.default = Geographies;

@@ -15,6 +15,2 @@ "use strict";

var _defaultStyles = require("./defaultStyles");
var _defaultStyles2 = _interopRequireDefault(_defaultStyles);
var _utils = require("./utils");

@@ -30,4 +26,4 @@

var Geography = function (_React$Component) {
_inherits(Geography, _React$Component);
var Geography = function (_Component) {
_inherits(Geography, _Component);

@@ -40,9 +36,14 @@ function Geography() {

_this.state = {
hovered: false
hover: false,
pressed: false
};
_this.handleMouseEnter = _this.handleMouseEnter.bind(_this);
_this.handleMouseMove = _this.handleMouseMove.bind(_this);
_this.handleMouseLeave = _this.handleMouseLeave.bind(_this);
_this.handleMouseMove = _this.handleMouseMove.bind(_this);
_this.handleClick = _this.handleClick.bind(_this);
_this.handleMouseDown = _this.handleMouseDown.bind(_this);
_this.handleMouseUp = _this.handleMouseUp.bind(_this);
_this.handleMouseClick = _this.handleMouseClick.bind(_this);
_this.handleFocus = _this.handleFocus.bind(_this);
_this.handleBlur = _this.handleBlur.bind(_this);
return _this;

@@ -52,71 +53,142 @@ }

_createClass(Geography, [{
key: "handleMouseClick",
value: function handleMouseClick(evt) {
evt.persist();
var _props = this.props,
onClick = _props.onClick,
geography = _props.geography;
return onClick && onClick(geography, evt);
}
}, {
key: "handleMouseEnter",
value: function handleMouseEnter(geography, evt) {
evt.preventDefault();
this.setState({ hovered: true });
if (this.props.events.onMouseEnter) {
this.props.events.onMouseEnter(geography, evt);
}
value: function handleMouseEnter(evt) {
evt.persist();
var _props2 = this.props,
onMouseEnter = _props2.onMouseEnter,
geography = _props2.geography;
this.setState({
hover: true
}, function () {
return onMouseEnter && onMouseEnter(geography, evt);
});
}
}, {
key: "handleMouseMove",
value: function handleMouseMove(evt) {
evt.persist();
if (this.state.pressed) return;
var _props3 = this.props,
onMouseMove = _props3.onMouseMove,
geography = _props3.geography;
if (!this.state.hover) {
this.setState({
hover: true
}, function () {
return onMouseMove && onMouseMove(geography, evt);
});
} else if (onMouseMove) onMouseMove(geography, evt);else return;
}
}, {
key: "handleMouseLeave",
value: function handleMouseLeave(geography, evt) {
evt.preventDefault();
this.setState({ hovered: false });
if (this.props.events.onMouseLeave) {
this.props.events.onMouseLeave(geography, evt);
}
value: function handleMouseLeave(evt) {
evt.persist();
var _props4 = this.props,
onMouseLeave = _props4.onMouseLeave,
geography = _props4.geography;
this.setState({
hover: false
}, function () {
return onMouseLeave && onMouseLeave(geography, evt);
});
}
}, {
key: "handleMouseMove",
value: function handleMouseMove(geography, evt) {
evt.preventDefault();
if (this.props.events.onMouseMove) {
this.props.events.onMouseMove(geography, evt);
}
key: "handleMouseDown",
value: function handleMouseDown(evt) {
evt.persist();
var _props5 = this.props,
onMouseDown = _props5.onMouseDown,
geography = _props5.geography;
this.setState({
pressed: true
}, function () {
return onMouseDown && onMouseDown(geography, evt);
});
}
}, {
key: "handleClick",
value: function handleClick(geography, evt) {
evt.preventDefault();
if (this.props.events.onClick) {
this.props.events.onClick(geography, evt);
}
key: "handleMouseUp",
value: function handleMouseUp(evt) {
evt.persist();
var _props6 = this.props,
onMouseUp = _props6.onMouseUp,
geography = _props6.geography;
this.setState({
pressed: false
}, function () {
return onMouseUp && onMouseUp(geography, evt);
});
}
}, {
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps, nextState) {
var changedHoverState = this.state.hovered !== nextState.hovered;
var changedChoroplethValue = this.props.choroplethValue !== nextProps.choroplethValue;
var changedGeography = this.props.geography !== nextProps.geography;
return changedGeography || changedChoroplethValue || changedHoverState;
key: "handleFocus",
value: function handleFocus(evt) {
evt.persist();
var _props7 = this.props,
onFocus = _props7.onFocus,
geography = _props7.geography;
this.setState({
hover: true
}, function () {
return onFocus && onFocus(geography, evt);
});
}
}, {
key: "handleBlur",
value: function handleBlur(evt) {
evt.persist();
var _props8 = this.props,
onBlur = _props8.onBlur,
geography = _props8.geography;
this.setState({
hover: false
}, function () {
return onBlur && onBlur(geography, evt);
});
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _props9 = this.props,
geography = _props9.geography,
projection = _props9.projection,
round = _props9.round,
precision = _props9.precision,
tabable = _props9.tabable,
style = _props9.style;
var _state = this.state,
hover = _state.hover,
pressed = _state.pressed;
var _props = this.props,
geography = _props.geography,
projection = _props.projection,
styles = _props.styles,
choroplethValue = _props.choroplethValue;
var pathString = (0, _d3Geo.geoPath)().projection(projection())(geography);
return _react2.default.createElement("path", {
d: (0, _d3Geo.geoPath)().projection(projection())(geography),
onMouseEnter: function onMouseEnter(evt) {
return _this2.handleMouseEnter(geography, evt);
},
onMouseLeave: function onMouseLeave(evt) {
return _this2.handleMouseLeave(geography, evt);
},
onMouseMove: function onMouseMove(evt) {
return _this2.handleMouseMove(geography, evt);
},
onClick: function onClick(evt) {
return _this2.handleClick(geography, evt);
},
style: styles(choroplethValue, geography)[this.state.hovered ? "hover" : "default"] || styles(choroplethValue, geography)["default"],
className: "rsm-geography"
d: round ? (0, _utils.roundPath)(pathString, precision) : pathString,
className: "rsm-geography" + (pressed && " rsm-geography--pressed") + (hover && " rsm-geography--hover"),
style: style[pressed || hover ? pressed ? "pressed" : "hover" : "default"],
onClick: this.handleMouseClick,
onMouseEnter: this.handleMouseEnter,
onMouseMove: this.handleMouseMove,
onMouseLeave: this.handleMouseLeave,
onMouseDown: this.handleMouseDown,
onMouseUp: this.handleMouseUp,
onFocus: tabable && this.handleFocus,
onBlur: tabable && this.handleBlur,
tabIndex: tabable ? 0 : -1
});

@@ -127,16 +199,15 @@ }

return Geography;
}(_react2.default.Component);
}(_react.Component);
Geography.propTypes = {
geography: _react.PropTypes.object.isRequired,
projection: _react.PropTypes.func.isRequired,
choroplethValue: _react.PropTypes.object,
events: _react.PropTypes.object
};
Geography.defaultProps = {
styles: _defaultStyles2.default.geography,
events: {}
precision: 0.1,
round: true,
tabable: true,
style: {
default: {},
hover: {},
pressed: {}
}
};
exports.default = Geography;

@@ -7,365 +7,65 @@ "use strict";

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 _ComposableMap = require("./ComposableMap");
var _react = require("react");
Object.defineProperty(exports, "ComposableMap", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_ComposableMap).default;
}
});
var _react2 = _interopRequireDefault(_react);
var _topojsonClient = require("topojson-client");
var _Loader = require("./Loader");
var _Loader2 = _interopRequireDefault(_Loader);
var _MapControls = require("./MapControls");
var _MapControls2 = _interopRequireDefault(_MapControls);
var _ZoomableGroup = require("./ZoomableGroup");
var _ZoomableGroup2 = _interopRequireDefault(_ZoomableGroup);
Object.defineProperty(exports, "ZoomableGroup", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_ZoomableGroup).default;
}
});
var _Geographies = require("./Geographies");
var _Geographies2 = _interopRequireDefault(_Geographies);
Object.defineProperty(exports, "Geographies", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_Geographies).default;
}
});
var _Markers = require("./Markers");
var _Geography = require("./Geography");
var _Markers2 = _interopRequireDefault(_Markers);
Object.defineProperty(exports, "Geography", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_Geography).default;
}
});
var _projections = require("./projections");
var _Marker = require("./Marker");
var _projections2 = _interopRequireDefault(_projections);
Object.defineProperty(exports, "Marker", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_Marker).default;
}
});
var _projectionConfig = require("./projectionConfig");
var _Markers = require("./Markers");
var _projectionConfig2 = _interopRequireDefault(_projectionConfig);
var _defaultStyles = require("./defaultStyles");
var _defaultStyles2 = _interopRequireDefault(_defaultStyles);
var _utils = require("./utils");
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 _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
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) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var ReactSimpleMap = function (_React$Component) {
_inherits(ReactSimpleMap, _React$Component);
function ReactSimpleMap(props) {
_classCallCheck(this, ReactSimpleMap);
var _this = _possibleConstructorReturn(this, (ReactSimpleMap.__proto__ || Object.getPrototypeOf(ReactSimpleMap)).call(this, props));
_this.state = {
geographyPaths: props.geographyPaths,
zoom: props.zoom,
mouseX: (0, _utils.calculateMousePosition)("x", _this.projection.bind(_this), props, props.zoom, 1),
mouseY: (0, _utils.calculateMousePosition)("y", _this.projection.bind(_this), props, props.zoom, 1),
mouseXStart: 0,
mouseYStart: 0,
isPressed: false,
loadingError: null,
resizeFactorX: 1,
resizeFactorY: 1
};
_this.projection = _this.projection.bind(_this);
_this.fetchGeographies = _this.fetchGeographies.bind(_this);
_this.handleZoomReset = _this.handleZoomReset.bind(_this);
_this.handleZoomIn = _this.handleZoomIn.bind(_this);
_this.handleZoomOut = _this.handleZoomOut.bind(_this);
_this.handleMouseDown = _this.handleMouseDown.bind(_this);
_this.handleMouseMove = _this.handleMouseMove.bind(_this);
_this.handleMouseUp = _this.handleMouseUp.bind(_this);
return _this;
Object.defineProperty(exports, "Markers", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_Markers).default;
}
});
_createClass(ReactSimpleMap, [{
key: "componentWillReceiveProps",
value: function componentWillReceiveProps(nextProps) {
if (nextProps.zoom !== this.state.zoom || nextProps.center !== this.props.center) {
this.setState({
zoom: nextProps.zoom,
mouseX: (0, _utils.calculateMousePosition)("x", this.projection, nextProps, nextProps.zoom, this.state.resizeFactorX),
mouseY: (0, _utils.calculateMousePosition)("y", this.projection, nextProps, nextProps.zoom, this.state.resizeFactorY)
});
}
}
}, {
key: "fetchGeographies",
value: function fetchGeographies(geographyUrl) {
var _this2 = this;
var _Annotation = require("./Annotation");
if (!this.props.geographyUrl) return;
var request = new XMLHttpRequest();
request.open("GET", geographyUrl, true);
request.onload = function () {
if (request.status >= 200 && request.status < 400) {
var geographyPaths = JSON.parse(request.responseText);
_this2.setState({
geographyPaths: (0, _topojsonClient.feature)(geographyPaths, geographyPaths.objects[Object.keys(geographyPaths.objects)[0]]).features
});
} else {
_this2.setState({
loadingError: String(request.status)
});
}
};
request.onerror = function () {
console.log("There was a connection error...");
};
request.send();
}
}, {
key: "projection",
value: function projection() {
if (typeof this.props.projection !== "function") {
return (0, _projections2.default)(this.props.width, this.props.height, this.props.projectionConfig, this.props.projection);
} else {
return this.props.projection(this.props.width, this.props.height, this.props.projectionConfig);
}
}
}, {
key: "handleZoomIn",
value: function handleZoomIn() {
if (this.state.zoom < this.props.maxZoom) {
this.setState({
zoom: this.state.zoom * 2,
mouseX: this.state.mouseX * 2,
mouseY: this.state.mouseY * 2
});
}
}
}, {
key: "handleZoomOut",
value: function handleZoomOut() {
if (this.state.zoom > this.props.minZoom) {
this.setState({
zoom: this.state.zoom / 2,
mouseX: this.state.zoom === 2 ? (0, _utils.calculateMousePosition)("x", this.projection, this.props, this.props.minZoom, this.state.resizeFactorX) : this.state.mouseX / 2,
mouseY: this.state.zoom === 2 ? (0, _utils.calculateMousePosition)("y", this.projection, this.props, this.props.minZoom, this.state.resizeFactorY) : this.state.mouseY / 2
});
}
}
}, {
key: "handleZoomReset",
value: function handleZoomReset() {
if (this.state.zoom > this.props.minZoom) {
this.setState({
zoom: this.props.minZoom,
mouseX: (0, _utils.calculateMousePosition)("x", this.projection, this.props, this.props.minZoom, this.state.resizeFactorX),
mouseY: (0, _utils.calculateMousePosition)("y", this.projection, this.props, this.props.minZoom, this.state.resizeFactorY)
});
}
}
}, {
key: "handleMouseDown",
value: function handleMouseDown(_ref) {
var pageX = _ref.pageX,
pageY = _ref.pageY;
this.setState({
isPressed: true,
mouseXStart: pageX - this.state.mouseX,
mouseYStart: pageY - this.state.mouseY
});
}
}, {
key: "handleMouseMove",
value: function handleMouseMove(_ref2) {
var pageX = _ref2.pageX,
pageY = _ref2.pageY;
if (this.state.isPressed) {
this.setState({
mouseX: pageX - this.state.mouseXStart,
mouseY: pageY - this.state.mouseYStart
});
}
}
}, {
key: "handleMouseUp",
value: function handleMouseUp(_ref3) {
var pageX = _ref3.pageX,
pageY = _ref3.pageY;
this.setState({
isPressed: false
});
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
var _this3 = this;
this.fetchGeographies(this.props.geographyUrl);
var actualWidth = this.wrapperNode.clientWidth;
var actualHeight = this.wrapperNode.clientHeight;
var resizeFactorX = (0, _utils.calculateResizeFactor)(actualWidth, this.props.width);
var resizeFactorY = (0, _utils.calculateResizeFactor)(actualHeight, this.props.height);
this.setState({
resizeFactorX: resizeFactorX,
resizeFactorY: resizeFactorY,
mouseX: (0, _utils.calculateMousePosition)("x", this.projection, this.props, this.state.zoom, resizeFactorX),
mouseY: (0, _utils.calculateMousePosition)("y", this.projection, this.props, this.state.zoom, resizeFactorY)
});
window.addEventListener("resize", function () {
var actualWidth = _this3.wrapperNode.clientWidth;
var actualHeight = _this3.wrapperNode.clientHeight;
var resizeFactorX = (0, _utils.calculateResizeFactor)(actualWidth, _this3.props.width);
var resizeFactorY = (0, _utils.calculateResizeFactor)(actualHeight, _this3.props.height);
_this3.setState({
resizeFactorX: resizeFactorX,
resizeFactorY: resizeFactorY,
mouseX: (0, _utils.calculateMousePosition)("x", _this3.projection, _this3.props, _this3.state.zoom, resizeFactorX),
mouseY: (0, _utils.calculateMousePosition)("y", _this3.projection, _this3.props, _this3.state.zoom, resizeFactorY)
});
});
}
}, {
key: "render",
value: function render() {
var _this4 = this;
var _props = this.props,
width = _props.width,
height = _props.height,
styles = _props.styles,
freezeGeographyPaths = _props.freezeGeographyPaths,
markers = _props.markers,
exclude = _props.exclude,
include = _props.include,
center = _props.center;
var _state = this.state,
geographyPaths = _state.geographyPaths,
loadingError = _state.loadingError;
return _react2.default.createElement(
"div",
{ style: styles.wrapper ? styles.wrapper() : _defaultStyles2.default.wrapper(), className: "rsm-wrapper" },
this.props.showControls ? _react2.default.createElement(_MapControls2.default, {
handleZoomIn: this.handleZoomIn,
handleZoomOut: this.handleZoomOut,
handleZoomReset: this.handleZoomReset
}) : null,
geographyPaths.length === 0 ? _react2.default.createElement(_Loader2.default, {
styles: styles.loader || _defaultStyles2.default.loader,
loadingError: loadingError
}) : null,
_react2.default.createElement(
"svg",
{ width: width,
height: height,
viewBox: "0 0 " + width + " " + height,
style: styles.svg ? styles.svg() : _defaultStyles2.default.svg(),
className: "rsm-svg",
ref: function ref(wrapperNode) {
return _this4.wrapperNode = wrapperNode;
}
},
_react2.default.createElement(
_ZoomableGroup2.default,
{
zoom: this.state.zoom,
mouseX: this.state.mouseX,
mouseY: this.state.mouseY,
width: width,
height: height,
isPressed: this.state.isPressed,
handleMouseMove: this.handleMouseMove,
handleMouseUp: this.handleMouseUp,
handleMouseDown: this.handleMouseDown,
styles: styles,
center: [-center[0], -center[1]],
projection: this.projection,
resizeFactorX: this.state.resizeFactorX,
resizeFactorY: this.state.resizeFactorY
},
geographyPaths.length > 0 ? _react2.default.createElement(_Geographies2.default, {
zoom: this.state.zoom,
projection: this.projection,
geographyPaths: geographyPaths,
freezeGeographyPaths: freezeGeographyPaths,
exclude: exclude,
include: include,
styles: styles,
choropleth: this.props.choropleth,
events: this.props.events.geography
}) : null,
geographyPaths.length > 0 ? _react2.default.createElement(_Markers2.default, {
projection: this.projection,
markers: markers,
zoom: this.state.zoom,
styles: styles,
events: this.props.events.marker
}) : null
)
)
);
}
}]);
return ReactSimpleMap;
}(_react2.default.Component);
ReactSimpleMap.propTypes = {
width: _react.PropTypes.number,
height: _react.PropTypes.number,
geographyUrl: _react.PropTypes.string,
geographyPaths: _react.PropTypes.array,
projection: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.func]),
freezeGeographyPaths: _react.PropTypes.bool,
styles: _react.PropTypes.object,
markers: _react.PropTypes.array,
exclude: _react.PropTypes.array,
include: _react.PropTypes.array,
zoom: _react.PropTypes.number,
minZoom: _react.PropTypes.number,
maxZoom: _react.PropTypes.number,
center: _react.PropTypes.array,
projectionConfig: _react.PropTypes.object,
choropleth: _react.PropTypes.object,
showControls: _react.PropTypes.bool
};
ReactSimpleMap.defaultProps = {
width: 800,
height: 450,
geographyUrl: null,
geographyPaths: [],
projection: "times",
choropleth: {},
freezeGeographyPaths: true,
styles: _defaultStyles2.default,
markers: [],
exclude: [],
include: [],
zoom: 1,
minZoom: 1,
maxZoom: 8,
center: [0, 0],
projectionConfig: _projectionConfig2.default,
showControls: false,
events: {
geography: {},
marker: {}
Object.defineProperty(exports, "Annotation", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_Annotation).default;
}
};
});
exports.default = ReactSimpleMap;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -13,6 +13,2 @@ "use strict";

var _defaultStyles = require("./defaultStyles");
var _defaultStyles2 = _interopRequireDefault(_defaultStyles);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -35,3 +31,4 @@

_this.state = {
hovered: false
hover: false,
pressed: false
};

@@ -41,4 +38,8 @@

_this.handleMouseLeave = _this.handleMouseLeave.bind(_this);
_this.handleMouseDown = _this.handleMouseDown.bind(_this);
_this.handleMouseUp = _this.handleMouseUp.bind(_this);
_this.handleMouseClick = _this.handleMouseClick.bind(_this);
_this.handleMouseMove = _this.handleMouseMove.bind(_this);
_this.handleClick = _this.handleClick.bind(_this);
_this.handleFocus = _this.handleFocus.bind(_this);
_this.handleBlur = _this.handleBlur.bind(_this);
return _this;

@@ -49,74 +50,144 @@ }

key: "handleMouseEnter",
value: function handleMouseEnter(marker, evt) {
evt.preventDefault();
this.setState({ hovered: true });
if (this.props.events.onMouseEnter) {
this.props.events.onMouseEnter(marker, evt);
}
value: function handleMouseEnter(evt) {
evt.persist();
var _props = this.props,
onMouseEnter = _props.onMouseEnter,
marker = _props.marker;
this.setState({
hover: true
}, function () {
return onMouseEnter && onMouseEnter(marker, evt);
});
}
}, {
key: "handleMouseMove",
value: function handleMouseMove(evt) {
evt.persist();
if (this.state.pressed) return;
var _props2 = this.props,
onMouseMove = _props2.onMouseMove,
marker = _props2.marker;
if (!this.state.hover) {
this.setState({
hover: true
}, function () {
return onMouseMove && onMouseMove(marker, evt);
});
} else if (onMouseMove) onMouseMove(marker, evt);else return;
}
}, {
key: "handleMouseLeave",
value: function handleMouseLeave(marker, evt) {
evt.preventDefault();
this.setState({ hovered: false });
if (this.props.events.onMouseLeave) {
this.props.events.onMouseLeave(marker, evt);
}
value: function handleMouseLeave(evt) {
evt.persist();
var _props3 = this.props,
onMouseLeave = _props3.onMouseLeave,
marker = _props3.marker;
this.setState({
hover: false
}, function () {
return onMouseLeave && onMouseLeave(marker, evt);
});
}
}, {
key: "handleMouseMove",
value: function handleMouseMove(marker, evt) {
evt.preventDefault();
if (this.props.events.onMouseMove) {
this.props.events.onMouseMove(marker, evt);
}
key: "handleMouseDown",
value: function handleMouseDown(evt) {
evt.persist();
var _props4 = this.props,
onMouseDown = _props4.onMouseDown,
marker = _props4.marker;
this.setState({
pressed: true
}, function () {
return onMouseDown && onMouseDown(marker, evt);
});
}
}, {
key: "handleClick",
value: function handleClick(marker, evt) {
evt.preventDefault();
if (this.props.events.onClick) {
this.props.events.onClick(marker, evt);
}
key: "handleMouseUp",
value: function handleMouseUp(evt) {
evt.persist();
var _props5 = this.props,
onMouseUp = _props5.onMouseUp,
marker = _props5.marker;
this.setState({
pressed: false
}, function () {
return onMouseUp && onMouseUp(marker, evt);
});
}
}, {
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps, nextState) {
var hoverStateChanged = nextState.hovered !== this.state.hovered;
var radiusChanged = nextProps.marker.radius !== this.props.marker.radius;
var zoomChanged = nextProps.zoom !== this.props.zoom;
return hoverStateChanged || radiusChanged || zoomChanged;
key: "handleMouseClick",
value: function handleMouseClick(evt) {
if (!this.props.onClick) return;
evt.persist();
var _props6 = this.props,
onClick = _props6.onClick,
marker = _props6.marker,
projection = _props6.projection;
return onClick && onClick(marker, projection()(marker.coordinates), evt);
}
}, {
key: "handleFocus",
value: function handleFocus(evt) {
evt.persist();
var _props7 = this.props,
onFocus = _props7.onFocus,
marker = _props7.marker;
this.setState({
hover: true
}, function () {
return onFocus && onFocus(marker, evt);
});
}
}, {
key: "handleBlur",
value: function handleBlur(evt) {
evt.persist();
var _props8 = this.props,
onBlur = _props8.onBlur,
marker = _props8.marker;
this.setState({
hover: false
}, function () {
return onBlur && onBlur(marker, evt);
});
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _props9 = this.props,
projection = _props9.projection,
marker = _props9.marker,
style = _props9.style,
tabable = _props9.tabable,
children = _props9.children;
var _state = this.state,
pressed = _state.pressed,
hover = _state.hover;
var _props = this.props,
marker = _props.marker,
styles = _props.styles,
zoom = _props.zoom,
projection = _props.projection;
return _react2.default.createElement("circle", {
cx: projection()(marker.coordinates)[0],
cy: projection()(marker.coordinates)[1],
r: marker.radius,
style: styles(marker, zoom)[this.state.hovered ? "hover" : "default"] || styles(marker, zoom)["default"],
className: "rsm-marker",
onMouseEnter: function onMouseEnter(evt) {
return _this2.handleMouseEnter(marker, evt);
return _react2.default.createElement(
"g",
{ className: "rsm-marker" + (pressed && " rsm-marker--pressed") + (hover && " rsm-marker--hover"),
transform: "translate(\n " + projection()(marker.coordinates)[0] + "\n " + projection()(marker.coordinates)[1] + "\n )",
style: style[pressed || hover ? pressed ? "pressed" : "hover" : "default"],
onMouseEnter: this.handleMouseEnter,
onMouseLeave: this.handleMouseLeave,
onMouseDown: this.handleMouseDown,
onMouseUp: this.handleMouseUp,
onClick: this.handleMouseClick,
onMouseMove: this.handleMouseMove,
onFocus: this.handleFocus,
onBlur: this.handleBlur,
tabIndex: tabable ? 0 : -1
},
onMouseLeave: function onMouseLeave(evt) {
return _this2.handleMouseLeave(marker, evt);
},
onMouseMove: function onMouseMove(evt) {
return _this2.handleMouseMove(marker, evt);
},
onClick: function onClick(evt) {
return _this2.handleClick(marker, evt);
},
transform: "\n translate(\n " + projection()(marker.coordinates)[0] + "\n " + projection()(marker.coordinates)[1] + "\n )\n scale(\n " + 1 / zoom + "\n )\n translate(\n " + -projection()(marker.coordinates)[0] + "\n " + -projection()(marker.coordinates)[1] + "\n )\n "
});
children
);
}

@@ -128,17 +199,11 @@ }]);

Marker.propTypes = {
marker: _react.PropTypes.object,
zoom: _react.PropTypes.number,
events: _react.PropTypes.object,
projection: _react.PropTypes.func,
styles: _react.PropTypes.func
};
Marker.defaultProps = {
marker: {},
zoom: 1,
events: {},
styles: _defaultStyles2.default.marker
style: {
default: {},
hover: {},
pressed: {}
},
tabable: true
};
exports.default = Marker;

@@ -13,10 +13,2 @@ "use strict";

var _defaultStyles = require("./defaultStyles");
var _defaultStyles2 = _interopRequireDefault(_defaultStyles);
var _Marker = require("./Marker");
var _Marker2 = _interopRequireDefault(_Marker);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -30,4 +22,4 @@

var Markers = function (_React$Component) {
_inherits(Markers, _React$Component);
var Markers = function (_Component) {
_inherits(Markers, _Component);

@@ -43,15 +35,17 @@ function Markers() {

value: function render() {
var _this2 = this;
var _props = this.props,
children = _props.children,
projection = _props.projection,
style = _props.style;
return _react2.default.createElement(
"g",
{ className: "rsm-markers" },
this.props.markers.map(function (marker, i) {
return _react2.default.createElement(_Marker2.default, {
key: Math.abs(marker.coordinates[0]) + "-" + Math.abs(marker.coordinates[1]) + "-" + i,
projection: _this2.props.projection,
marker: marker,
styles: _this2.props.styles.marker,
events: _this2.props.events,
zoom: _this2.props.zoom
{ className: "rsm-markers", style: style },
!children.length ? _react2.default.cloneElement(children, {
projection: projection
}) : children.map(function (child, i) {
return _react2.default.cloneElement(child, {
key: child.key || "marker-" + i,
projection: projection
});

@@ -64,19 +58,8 @@ })

return Markers;
}(_react2.default.Component);
}(_react.Component);
Markers.propTypes = {
markers: _react.PropTypes.array,
projection: _react.PropTypes.func.isRequired,
zoom: _react.PropTypes.number,
events: _react.PropTypes.object,
styles: _react.PropTypes.object
};
Markers.defaultProps = {
markers: [],
zoom: 1,
events: {},
styles: _defaultStyles2.default
componentIdentifier: "Markers"
};
exports.default = Markers;

@@ -10,2 +10,14 @@ "use strict";

exports.calculateMousePosition = calculateMousePosition;
exports.isChildOfType = isChildOfType;
exports.createNewChildren = createNewChildren;
exports.roundPath = roundPath;
exports.createConnectorPath = createConnectorPath;
exports.createTextAnchor = createTextAnchor;
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function replaceStrokeWidth(styles) {

@@ -36,8 +48,54 @@ var newStyles = {};

function calculateMousePosition(direction, projection, props, zoom, resizeFactor) {
var center = props.center,
width = props.width,
height = props.height;
var center = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : props.center;
var width = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : props.width;
var height = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : props.height;
var reference = { x: 0, y: 1 };
return (projection()([-center[0], -center[1]])[reference[direction]] - (reference[direction] === 0 ? width : height) / 2) * zoom * (1 / resizeFactor);
var reverseRotation = projection().rotate().map(function (item) {
return -item;
});
return (projection().rotate(reverseRotation)([-center[0], -center[1]])[reference[direction]] - (reference[direction] === 0 ? width : height) / 2) * zoom * (1 / resizeFactor);
}
function isChildOfType(child, expectedType) {
return child.props.componentIdentifier === expectedType;
}
function createNewChildren(children, props) {
if (!children.length) {
return isChildOfType(children, "Geographies") ? _react2.default.cloneElement(children, {
geographyPaths: props.geographyPaths,
projection: props.projection
}) : isChildOfType(children, "Markers") || isChildOfType(children, "Annotation") ? _react2.default.cloneElement(children, {
projection: props.projection,
zoom: props.zoom
}) : children;
} else {
return children.map(function (child, i) {
return isChildOfType(child, "Geographies") ? _react2.default.cloneElement(child, {
key: "zoomable-child-" + i,
geographyPaths: props.geographyPaths,
projection: props.projection
}) : isChildOfType(child, "Markers") || isChildOfType(child, "Annotation") ? _react2.default.cloneElement(child, {
key: "zoomable-child-" + i,
projection: props.projection,
zoom: props.zoom
}) : child;
});
}
}
function roundPath(path, precision) {
var query = /[\d\.-][\d\.e-]*/g;
return path.replace(query, function (n) {
return Math.round(n * (1 / precision)) / (1 / precision);
});
}
function createConnectorPath(connectorType, endPoint) {
return "M0,0 L" + endPoint[0] + "," + endPoint[1];
}
function createTextAnchor(dx) {
if (dx > 0) return "start";else if (dx < 0) return "end";else return "middle";
}

@@ -13,6 +13,4 @@ "use strict";

var _defaultStyles = require("./defaultStyles");
var _utils = require("./utils");
var _defaultStyles2 = _interopRequireDefault(_defaultStyles);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -26,36 +24,181 @@

var ZoomableGroup = function (_React$Component) {
_inherits(ZoomableGroup, _React$Component);
var ZoomableGroup = function (_Component) {
_inherits(ZoomableGroup, _Component);
function ZoomableGroup() {
function ZoomableGroup(props) {
_classCallCheck(this, ZoomableGroup);
return _possibleConstructorReturn(this, (ZoomableGroup.__proto__ || Object.getPrototypeOf(ZoomableGroup)).apply(this, arguments));
var _this = _possibleConstructorReturn(this, (ZoomableGroup.__proto__ || Object.getPrototypeOf(ZoomableGroup)).call(this, props));
_this.state = {
mouseX: (0, _utils.calculateMousePosition)("x", props.projection, props, props.zoom, 1),
mouseY: (0, _utils.calculateMousePosition)("y", props.projection, props, props.zoom, 1),
mouseXStart: 0,
mouseYStart: 0,
isPressed: false,
resizeFactorX: 1,
resizeFactorY: 1
};
_this.handleMouseMove = _this.handleMouseMove.bind(_this);
_this.handleMouseUp = _this.handleMouseUp.bind(_this);
_this.handleMouseDown = _this.handleMouseDown.bind(_this);
_this.handleResize = _this.handleResize.bind(_this);
return _this;
}
_createClass(ZoomableGroup, [{
key: "handleMouseMove",
value: function handleMouseMove(_ref) {
var pageX = _ref.pageX,
pageY = _ref.pageY;
if (this.props.disablePanning) return;
if (this.state.isPressed) {
this.setState({
mouseX: pageX - this.state.mouseXStart,
mouseY: pageY - this.state.mouseYStart
});
}
}
}, {
key: "handleMouseUp",
value: function handleMouseUp(_ref2) {
var pageX = _ref2.pageX,
pageY = _ref2.pageY;
if (this.props.disablePanning) return;
this.setState({
isPressed: false
});
}
}, {
key: "handleMouseDown",
value: function handleMouseDown(_ref3) {
var pageX = _ref3.pageX,
pageY = _ref3.pageY;
if (this.props.disablePanning) return;
this.setState({
isPressed: true,
mouseXStart: pageX - this.state.mouseX,
mouseYStart: pageY - this.state.mouseY
});
}
}, {
key: "componentWillReceiveProps",
value: function componentWillReceiveProps(nextProps) {
var _state = this.state,
mouseX = _state.mouseX,
mouseY = _state.mouseY,
resizeFactorX = _state.resizeFactorX,
resizeFactorY = _state.resizeFactorY;
var _props = this.props,
projection = _props.projection,
center = _props.center,
zoom = _props.zoom;
var zoomFactor = nextProps.zoom / zoom;
var centerChanged = JSON.stringify(nextProps.center) !== JSON.stringify(center);
this.setState({
zoom: nextProps.zoom,
mouseX: centerChanged ? (0, _utils.calculateMousePosition)("x", projection, nextProps, nextProps.zoom, resizeFactorX) : mouseX * zoomFactor,
mouseY: centerChanged ? (0, _utils.calculateMousePosition)("y", projection, nextProps, nextProps.zoom, resizeFactorY) : mouseY * zoomFactor
});
}
}, {
key: "handleResize",
value: function handleResize() {
var _props2 = this.props,
width = _props2.width,
height = _props2.height,
projection = _props2.projection,
zoom = _props2.zoom;
var resizeFactorX = (0, _utils.calculateResizeFactor)(this.zoomableGroupNode.parentElement.clientWidth, width);
var resizeFactorY = (0, _utils.calculateResizeFactor)(this.zoomableGroupNode.parentElement.clientHeight, height);
var xPercentageChange = 1 / resizeFactorX * this.state.resizeFactorX;
var yPercentageChange = 1 / resizeFactorY * this.state.resizeFactorY;
this.setState({
resizeFactorX: resizeFactorX,
resizeFactorY: resizeFactorY,
mouseX: this.state.mouseX * xPercentageChange,
mouseY: this.state.mouseY * yPercentageChange
});
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
var _props3 = this.props,
width = _props3.width,
height = _props3.height,
projection = _props3.projection,
zoom = _props3.zoom;
var resizeFactorX = (0, _utils.calculateResizeFactor)(this.zoomableGroupNode.parentElement.clientWidth, width);
var resizeFactorY = (0, _utils.calculateResizeFactor)(this.zoomableGroupNode.parentElement.clientHeight, height);
this.setState({
resizeFactorX: resizeFactorX,
resizeFactorY: resizeFactorY,
mouseX: (0, _utils.calculateMousePosition)("x", projection, this.props, zoom, resizeFactorX),
mouseY: (0, _utils.calculateMousePosition)("y", projection, this.props, zoom, resizeFactorY)
});
window.addEventListener("resize", this.handleResize);
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
window.removeEventListener("resize", this.handleResize);
}
}, {
key: "render",
value: function render() {
var _props = this.props,
zoom = _props.zoom,
mouseX = _props.mouseX,
mouseY = _props.mouseY,
width = _props.width,
height = _props.height,
isPressed = _props.isPressed,
center = _props.center;
var _this2 = this;
var _props4 = this.props,
width = _props4.width,
height = _props4.height,
zoom = _props4.zoom,
style = _props4.style,
projection = _props4.projection,
children = _props4.children;
var _state2 = this.state,
mouseX = _state2.mouseX,
mouseY = _state2.mouseY,
resizeFactorX = _state2.resizeFactorX,
resizeFactorY = _state2.resizeFactorY;
var backdropDimensions = [projection().scale() / 100 * width, projection().scale() / 100 * height];
return _react2.default.createElement(
"g",
{
transform: "\n translate(\n " + (width / 2 + this.props.resizeFactorX * mouseX) + "\n " + (height / 2 + this.props.resizeFactorY * mouseY) + "\n )\n scale(" + zoom + ")\n translate(" + -width / 2 + " " + -height / 2 + ")\n ",
style: this.props.styles.geographies ? this.props.styles.geographies(zoom) : _defaultStyles2.default.geographies(zoom),
onMouseMove: this.props.handleMouseMove,
onMouseUp: this.props.handleMouseUp,
onMouseDown: this.props.handleMouseDown,
className: "rsm-zoomable-group"
{ className: "rsm-zoomable-group",
ref: function ref(zoomableGroupNode) {
return _this2.zoomableGroupNode = zoomableGroupNode;
},
transform: "\n translate(\n " + (width / 2 + resizeFactorX * mouseX) + "\n " + (height / 2 + resizeFactorY * mouseY) + "\n )\n scale(" + zoom + ")\n translate(" + -width / 2 + " " + -height / 2 + ")\n ",
onMouseMove: this.handleMouseMove,
onMouseUp: this.handleMouseUp,
onMouseDown: this.handleMouseDown,
style: style
},
_react2.default.createElement("rect", { x: 0, y: 0, width: width, height: height, fill: "transparent" }),
this.props.children
_react2.default.createElement("rect", {
x: width / 2,
y: height / 2,
width: width,
height: height,
fill: "transparent",
transform: "translate(-" + width / 2 + ", -" + height / 2 + ")",
style: { strokeWidth: 0 }
}),
(0, _utils.createNewChildren)(children, this.props)
);

@@ -66,18 +209,10 @@ }

return ZoomableGroup;
}(_react2.default.Component);
}(_react.Component);
ZoomableGroup.propTypes = {
zoom: _react.PropTypes.number.isRequired,
mouseX: _react.PropTypes.number.isRequired,
mouseY: _react.PropTypes.number.isRequired,
isPressed: _react.PropTypes.bool.isRequired,
styles: _react.PropTypes.object,
center: _react.PropTypes.array
};
ZoomableGroup.defaultProps = {
styles: _defaultStyles2.default,
center: [0, 0]
center: [0, 0],
zoom: 1,
disablePanning: false
};
exports.default = ZoomableGroup;
{
"name": "react-simple-maps",
"version": "0.4.0",
"version": "0.5.0",
"description": "An svg map component built with and for React",

@@ -10,3 +10,3 @@ "main": "lib/index.js",

"prepublish": "npm run build",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "mocha './tests/**/*.spec.js' --compilers js:babel-core/register"
},

@@ -40,4 +40,7 @@ "babel": {

"babel-preset-react": "6.23.0",
"expect": "1.20.2",
"mocha": "3.4.1",
"react": "15.4.2",
"react-dom": "15.4.2"
"react-dom": "15.4.2",
"react-test-utils": "0.0.1"
},

@@ -44,0 +47,0 @@ "peerDependencies": {

import React, { PropTypes } from "react"
import defaultStyles from "./defaultStyles"
import { replaceStrokeWidth } from "./utils"
import Geography from "./Geography"
import React, { Component } from "react"
import { feature } from "topojson-client"
class Geographies extends React.Component {
shouldComponentUpdate(nextProps) {
const geoPathsChanged = nextProps.geographyPaths.length !== this.props.geographyPaths.length
const includesChanged = nextProps.include.length !== this.props.include.length
const excludesChanged = nextProps.exclude.length !== this.props.exclude.length
class Geographies extends Component {
constructor(props) {
super(props)
this.state = {
geographyPaths: props.geographyPaths,
}
this.fetchGeographies = this.fetchGeographies.bind(this)
}
fetchGeographies(geographyUrl) {
const { width, height } = this.props
if(!geographyUrl) return
const request = new XMLHttpRequest()
request.open("GET", geographyUrl, true)
request.onload = () => {
if (request.status >= 200 && request.status < 400) {
const geographyPaths = JSON.parse(request.responseText)
this.setState({
geographyPaths: feature(geographyPaths, geographyPaths.objects[Object.keys(geographyPaths.objects)[0]]).features,
}, () => {
if (!this.props.onGeographiesLoaded) return
this.props.onGeographyPathsLoaded(String(request.status))
})
} else {
if (!this.props.onGeographiesLoaded) return
this.props.onGeographyPathsLoaded(String(request.status))
}
}
request.onerror = () => {
console.log("There was a connection error...")
}
request.send()
}
componentWillReceiveProps(nextProps) {;
if (!nextProps.geographyUrl) return
if (nextProps.geographyUrl !== this.props.geographyUrl) {
this.fetchGeographies(nextProps.geographyUrl)
}
}
shouldComponentUpdate(nextProps, nextState) {
const geoPathsChanged = nextState.geographyPaths.length !== this.state.geographyPaths.length
const choroplethChanged = JSON.stringify(nextProps.choropleth) !== JSON.stringify(this.props.choropleth)
return !this.props.freezeGeographyPaths || geoPathsChanged || includesChanged || excludesChanged || choroplethChanged
return geoPathsChanged || choroplethChanged || nextProps.disableOptimization
}
componentDidMount() {
this.fetchGeographies(this.props.geographyUrl)
}
render() {
const styles = this.props.styles.geography || defaultStyles.geography
const {
projection,
style,
children,
} = this.props
return (
<g className="rsm-geographies">
{
this.props.geographyPaths.map((geography, i) => {
const included = this.props.include.indexOf(geography.id) !== -1
const notExcluded = this.props.exclude.indexOf(geography.id) === -1
const shouldInclude = this.props.include.length > 0 ? included : notExcluded
return shouldInclude ? (
<Geography
zoom={ this.props.zoom }
key={ geography.id ? `${geography.id}-${i}` : i }
geography={ geography }
projection={ this.props.projection }
choroplethValue={ this.props.choropleth[geography.id] }
styles={ styles }
events={ this.props.events }
/>
) : null
})
}
<g className="rsm-geographies" style={ style }>
{ children(this.state.geographyPaths, projection) }
</g>

@@ -45,23 +72,8 @@ )

Geographies.propTypes = {
geographyPaths: PropTypes.array,
projection: PropTypes.func.isRequired,
freezeGeographyPaths: PropTypes.bool,
exclude: PropTypes.array,
include: PropTypes.array,
styles: PropTypes.object,
choropleth: PropTypes.object,
events: PropTypes.object,
}
Geographies.defaultProps = {
componentIdentifier: "Geographies",
disableOptimization: false,
geographyPaths: [],
freezeGeographyPaths: true,
exclude: [],
include: [],
styles: defaultStyles,
choropleth: {},
events: {},
}
export default Geographies
import React, { PropTypes } from "react"
import React, { Component } from "react"
import { geoPath } from "d3-geo"
import defaultStyles from "./defaultStyles"
import { createChoroplethStyles } from "./utils"
class Geography extends React.Component {
import { roundPath } from "./utils"
class Geography extends Component {
constructor() {

@@ -12,42 +12,74 @@ super()

this.state = {
hovered: false,
hover: false,
pressed: false,
}
this.handleMouseEnter = this.handleMouseEnter.bind(this)
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleMouseLeave = this.handleMouseLeave.bind(this)
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleClick = this.handleClick.bind(this)
this.handleMouseDown = this.handleMouseDown.bind(this)
this.handleMouseUp = this.handleMouseUp.bind(this)
this.handleMouseClick = this.handleMouseClick.bind(this)
this.handleFocus = this.handleFocus.bind(this)
this.handleBlur = this.handleBlur.bind(this)
}
handleMouseEnter(geography, evt) {
evt.preventDefault()
this.setState({ hovered: true })
if(this.props.events.onMouseEnter) {
this.props.events.onMouseEnter(geography, evt)
}
handleMouseClick(evt) {
evt.persist()
const { onClick, geography } = this.props
return onClick && onClick(geography, evt)
}
handleMouseLeave(geography, evt) {
evt.preventDefault()
this.setState({ hovered: false })
if(this.props.events.onMouseLeave) {
this.props.events.onMouseLeave(geography, evt)
}
handleMouseEnter(evt) {
evt.persist()
const { onMouseEnter, geography } = this.props
this.setState({
hover: true,
}, () => onMouseEnter && onMouseEnter(geography, evt))
}
handleMouseMove(geography, evt) {
evt.preventDefault()
if(this.props.events.onMouseMove) {
this.props.events.onMouseMove(geography, evt)
handleMouseMove(evt) {
evt.persist()
if (this.state.pressed) return
const { onMouseMove, geography } = this.props
if (!this.state.hover) {
this.setState({
hover: true,
}, () => onMouseMove && onMouseMove(geography, evt))
}
else if (onMouseMove) onMouseMove(geography, evt)
else return
}
handleClick(geography, evt) {
evt.preventDefault()
if(this.props.events.onClick) {
this.props.events.onClick(geography, evt)
}
handleMouseLeave(evt) {
evt.persist()
const { onMouseLeave, geography } = this.props
this.setState({
hover: false,
}, () => onMouseLeave && onMouseLeave(geography, evt))
}
shouldComponentUpdate(nextProps, nextState) {
const changedHoverState = this.state.hovered !== nextState.hovered
const changedChoroplethValue = this.props.choroplethValue !== nextProps.choroplethValue
const changedGeography = this.props.geography !== nextProps.geography
return changedGeography || changedChoroplethValue || changedHoverState
handleMouseDown(evt) {
evt.persist()
const { onMouseDown, geography } = this.props
this.setState({
pressed: true,
}, () => onMouseDown && onMouseDown(geography, evt))
}
handleMouseUp(evt) {
evt.persist()
const { onMouseUp, geography } = this.props
this.setState({
pressed: false,
}, () => onMouseUp && onMouseUp(geography, evt))
}
handleFocus(evt) {
evt.persist()
const { onFocus, geography } = this.props
this.setState({
hover: true,
}, () => onFocus && onFocus(geography, evt))
}
handleBlur(evt) {
evt.persist()
const { onBlur, geography } = this.props
this.setState({
hover: false,
}, () => onBlur && onBlur(geography, evt))
}
render() {

@@ -58,15 +90,29 @@

projection,
styles,
choroplethValue,
round,
precision,
tabable,
style,
} = this.props
const {
hover,
pressed,
} = this.state
const pathString = geoPath().projection(projection())(geography)
return (
<path
d={ geoPath().projection(projection())(geography) }
onMouseEnter={(evt) => this.handleMouseEnter(geography, evt) }
onMouseLeave={(evt) => this.handleMouseLeave(geography, evt) }
onMouseMove={(evt) => this.handleMouseMove(geography, evt) }
onClick={(evt) => this.handleClick(geography, evt) }
style={ styles(choroplethValue, geography)[ this.state.hovered ? "hover" : "default" ] || styles(choroplethValue, geography)["default"] }
className="rsm-geography"
d={ round ? roundPath(pathString, precision) : pathString }
className={ `rsm-geography${ pressed && " rsm-geography--pressed" }${ hover && " rsm-geography--hover" }` }
style={ style[pressed || hover ? (pressed ? "pressed" : "hover") : "default"] }
onClick={ this.handleMouseClick }
onMouseEnter={ this.handleMouseEnter }
onMouseMove={ this.handleMouseMove }
onMouseLeave={ this.handleMouseLeave }
onMouseDown={ this.handleMouseDown }
onMouseUp={ this.handleMouseUp }
onFocus={ tabable && this.handleFocus }
onBlur={ tabable && this.handleBlur }
tabIndex={ tabable ? 0 : -1 }
/>

@@ -77,14 +123,13 @@ )

Geography.propTypes = {
geography: PropTypes.object.isRequired,
projection: PropTypes.func.isRequired,
choroplethValue: PropTypes.object,
events: PropTypes.object,
}
Geography.defaultProps = {
styles: defaultStyles.geography,
events: {},
precision: 0.1,
round: true,
tabable: true,
style: {
default: {},
hover: {},
pressed: {},
}
}
export default Geography
import React, { PropTypes } from "react"
import { feature } from "topojson-client"
import Loader from "./Loader"
import MapControls from "./MapControls"
import ZoomableGroup from "./ZoomableGroup"
import Geographies from "./Geographies"
import Markers from "./Markers"
import projections from "./projections"
import defaultProjectionConfig from "./projectionConfig"
import defaultStyles from "./defaultStyles"
import {
calculateResizeFactor,
calculateMousePosition,
} from "./utils"
class ReactSimpleMap extends React.Component {
constructor(props) {
super(props)
this.state = {
geographyPaths: props.geographyPaths,
zoom: props.zoom,
mouseX: calculateMousePosition("x", this.projection.bind(this), props, props.zoom, 1),
mouseY: calculateMousePosition("y", this.projection.bind(this), props, props.zoom, 1),
mouseXStart: 0,
mouseYStart: 0,
isPressed: false,
loadingError: null,
resizeFactorX: 1,
resizeFactorY: 1,
}
this.projection = this.projection.bind(this)
this.fetchGeographies = this.fetchGeographies.bind(this)
this.handleZoomReset = this.handleZoomReset.bind(this)
this.handleZoomIn = this.handleZoomIn.bind(this)
this.handleZoomOut = this.handleZoomOut.bind(this)
this.handleMouseDown = this.handleMouseDown.bind(this)
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleMouseUp = this.handleMouseUp.bind(this)
}
componentWillReceiveProps(nextProps) {
if(nextProps.zoom !== this.state.zoom || nextProps.center !== this.props.center) {
this.setState({
zoom: nextProps.zoom,
mouseX: calculateMousePosition("x", this.projection, nextProps, nextProps.zoom, this.state.resizeFactorX),
mouseY: calculateMousePosition("y", this.projection, nextProps, nextProps.zoom, this.state.resizeFactorY),
})
}
}
fetchGeographies(geographyUrl) {
if(!this.props.geographyUrl) return
const request = new XMLHttpRequest()
request.open("GET", geographyUrl, true)
request.onload = () => {
if (request.status >= 200 && request.status < 400) {
const geographyPaths = JSON.parse(request.responseText)
this.setState({
geographyPaths: feature(geographyPaths, geographyPaths.objects[Object.keys(geographyPaths.objects)[0]]).features,
})
} else {
this.setState({
loadingError: String(request.status),
})
}
}
request.onerror = () => {
console.log("There was a connection error...")
}
request.send()
}
projection() {
if(typeof this.props.projection !== "function") {
return projections(this.props.width, this.props.height, this.props.projectionConfig, this.props.projection)
}
else {
return this.props.projection(this.props.width, this.props.height, this.props.projectionConfig)
}
}
handleZoomIn() {
if(this.state.zoom < this.props.maxZoom) {
this.setState({
zoom: this.state.zoom * 2,
mouseX: this.state.mouseX * 2,
mouseY: this.state.mouseY * 2,
})
}
}
handleZoomOut() {
if(this.state.zoom > this.props.minZoom) {
this.setState({
zoom: this.state.zoom / 2,
mouseX: this.state.zoom === 2 ? calculateMousePosition("x", this.projection, this.props, this.props.minZoom, this.state.resizeFactorX) : this.state.mouseX / 2,
mouseY: this.state.zoom === 2 ? calculateMousePosition("y", this.projection, this.props, this.props.minZoom, this.state.resizeFactorY) : this.state.mouseY / 2,
})
}
}
handleZoomReset() {
if(this.state.zoom > this.props.minZoom) {
this.setState({
zoom: this.props.minZoom,
mouseX: calculateMousePosition("x", this.projection, this.props, this.props.minZoom, this.state.resizeFactorX),
mouseY: calculateMousePosition("y", this.projection, this.props, this.props.minZoom, this.state.resizeFactorY),
})
}
}
handleMouseDown({ pageX, pageY }) {
this.setState({
isPressed: true,
mouseXStart: pageX - this.state.mouseX,
mouseYStart: pageY - this.state.mouseY,
})
}
handleMouseMove({ pageX, pageY }) {
if(this.state.isPressed) {
this.setState({
mouseX: pageX - this.state.mouseXStart,
mouseY: pageY - this.state.mouseYStart,
})
}
}
handleMouseUp({ pageX, pageY }) {
this.setState({
isPressed: false,
})
}
componentDidMount() {
this.fetchGeographies(this.props.geographyUrl)
const actualWidth = this.wrapperNode.clientWidth
const actualHeight = this.wrapperNode.clientHeight
const resizeFactorX = calculateResizeFactor(actualWidth, this.props.width)
const resizeFactorY = calculateResizeFactor(actualHeight, this.props.height)
this.setState({
resizeFactorX: resizeFactorX,
resizeFactorY: resizeFactorY,
mouseX: calculateMousePosition("x", this.projection, this.props, this.state.zoom, resizeFactorX),
mouseY: calculateMousePosition("y", this.projection, this.props, this.state.zoom, resizeFactorY),
})
window.addEventListener("resize", () => {
const actualWidth = this.wrapperNode.clientWidth
const actualHeight = this.wrapperNode.clientHeight
const resizeFactorX = calculateResizeFactor(actualWidth, this.props.width)
const resizeFactorY = calculateResizeFactor(actualHeight, this.props.height)
this.setState({
resizeFactorX: resizeFactorX,
resizeFactorY: resizeFactorY,
mouseX: calculateMousePosition("x", this.projection, this.props, this.state.zoom, resizeFactorX),
mouseY: calculateMousePosition("y", this.projection, this.props, this.state.zoom, resizeFactorY),
})
})
}
render() {
const {
width,
height,
styles,
freezeGeographyPaths,
markers,
exclude,
include,
center,
} = this.props
const {
geographyPaths,
loadingError,
} = this.state
return (
<div style={ styles.wrapper ? styles.wrapper() : defaultStyles.wrapper() } className="rsm-wrapper">
{
this.props.showControls ? (
<MapControls
handleZoomIn={ this.handleZoomIn }
handleZoomOut={ this.handleZoomOut }
handleZoomReset={ this.handleZoomReset }
/>
) : null
}
{
geographyPaths.length === 0 ? (
<Loader
styles={ styles.loader || defaultStyles.loader }
loadingError={ loadingError }
/>
) : null
}
<svg width={ width }
height={ height }
viewBox={ `0 0 ${width} ${height}` }
style={ styles.svg ? styles.svg() : defaultStyles.svg() }
className="rsm-svg"
ref={(wrapperNode) => this.wrapperNode = wrapperNode}
>
<ZoomableGroup
zoom={ this.state.zoom }
mouseX={ this.state.mouseX }
mouseY={ this.state.mouseY }
width={ width }
height={ height }
isPressed={ this.state.isPressed }
handleMouseMove={ this.handleMouseMove }
handleMouseUp={ this.handleMouseUp }
handleMouseDown={ this.handleMouseDown }
styles={ styles }
center={ [-center[0],-center[1]] }
projection={ this.projection }
resizeFactorX={ this.state.resizeFactorX }
resizeFactorY={ this.state.resizeFactorY }
>
{
geographyPaths.length > 0 ? (
<Geographies
zoom={ this.state.zoom }
projection={ this.projection }
geographyPaths={ geographyPaths }
freezeGeographyPaths={ freezeGeographyPaths }
exclude={ exclude }
include={ include }
styles={ styles }
choropleth={ this.props.choropleth }
events={ this.props.events.geography }
/>
) : null
}
{
geographyPaths.length > 0 ? (
<Markers
projection={ this.projection }
markers={ markers }
zoom={ this.state.zoom }
styles={ styles }
events={ this.props.events.marker }
/>
) : null
}
</ZoomableGroup>
</svg>
</div>
)
}
}
ReactSimpleMap.propTypes = {
width: PropTypes.number,
height: PropTypes.number,
geographyUrl: PropTypes.string,
geographyPaths: PropTypes.array,
projection: PropTypes.oneOfType([
PropTypes.string,
PropTypes.func,
]),
freezeGeographyPaths: PropTypes.bool,
styles: PropTypes.object,
markers: PropTypes.array,
exclude: PropTypes.array,
include: PropTypes.array,
zoom: PropTypes.number,
minZoom: PropTypes.number,
maxZoom: PropTypes.number,
center: PropTypes.array,
projectionConfig: PropTypes.object,
choropleth: PropTypes.object,
showControls: PropTypes.bool,
}
ReactSimpleMap.defaultProps = {
width: 800,
height: 450,
geographyUrl: null,
geographyPaths: [],
projection: "times",
choropleth: {},
freezeGeographyPaths: true,
styles: defaultStyles,
markers: [],
exclude: [],
include: [],
zoom: 1,
minZoom: 1,
maxZoom: 8,
center: [0,0],
projectionConfig: defaultProjectionConfig,
showControls: false,
events: {
geography: {},
marker: {},
}
}
export default ReactSimpleMap
export { default as ComposableMap } from "./ComposableMap"
export { default as ZoomableGroup } from "./ZoomableGroup"
export { default as Geographies } from "./Geographies"
export { default as Geography } from "./Geography"
export { default as Marker } from "./Marker"
export { default as Markers } from "./Markers"
export { default as Annotation } from "./Annotation"
import React, { Component, PropTypes } from "react"
import defaultStyles from "./defaultStyles"
import React, { Component } from "react"

@@ -10,3 +9,4 @@ class Marker extends Component {

this.state = {
hovered: false,
hover: false,
pressed: false,
}

@@ -16,71 +16,103 @@

this.handleMouseLeave = this.handleMouseLeave.bind(this)
this.handleMouseDown = this.handleMouseDown.bind(this)
this.handleMouseUp = this.handleMouseUp.bind(this)
this.handleMouseClick = this.handleMouseClick.bind(this)
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleClick = this.handleClick.bind(this)
this.handleFocus = this.handleFocus.bind(this)
this.handleBlur = this.handleBlur.bind(this)
}
handleMouseEnter(marker, evt) {
evt.preventDefault()
this.setState({ hovered: true })
if(this.props.events.onMouseEnter) {
this.props.events.onMouseEnter(marker, evt)
}
handleMouseEnter(evt) {
evt.persist()
const { onMouseEnter, marker } = this.props
this.setState({
hover: true,
}, () => onMouseEnter && onMouseEnter(marker, evt))
}
handleMouseLeave(marker, evt) {
evt.preventDefault()
this.setState({ hovered: false })
if(this.props.events.onMouseLeave) {
this.props.events.onMouseLeave(marker, evt)
handleMouseMove(evt) {
evt.persist()
if (this.state.pressed) return
const { onMouseMove, marker } = this.props
if (!this.state.hover) {
this.setState({
hover: true
}, () => onMouseMove && onMouseMove(marker, evt))
}
else if (onMouseMove) onMouseMove(marker, evt)
else return
}
handleMouseMove(marker, evt) {
evt.preventDefault()
if(this.props.events.onMouseMove) {
this.props.events.onMouseMove(marker, evt)
}
handleMouseLeave(evt) {
evt.persist()
const { onMouseLeave, marker } = this.props
this.setState({
hover: false,
}, () => onMouseLeave && onMouseLeave(marker, evt))
}
handleClick(marker, evt) {
evt.preventDefault()
if(this.props.events.onClick) {
this.props.events.onClick(marker, evt)
}
handleMouseDown(evt) {
evt.persist()
const { onMouseDown, marker } = this.props
this.setState({
pressed: true,
}, () => onMouseDown && onMouseDown(marker, evt))
}
shouldComponentUpdate(nextProps, nextState) {
const hoverStateChanged = nextState.hovered !== this.state.hovered
const radiusChanged = nextProps.marker.radius !== this.props.marker.radius
const zoomChanged = nextProps.zoom !== this.props.zoom
return hoverStateChanged || radiusChanged || zoomChanged
handleMouseUp(evt) {
evt.persist()
const { onMouseUp, marker } = this.props
this.setState({
pressed: false,
}, () => onMouseUp && onMouseUp(marker, evt))
}
handleMouseClick(evt) {
if (!this.props.onClick) return
evt.persist()
const { onClick, marker, projection } = this.props
return onClick && onClick(marker, projection()(marker.coordinates), evt)
}
handleFocus(evt) {
evt.persist()
const { onFocus, marker } = this.props
this.setState({
hover: true,
}, () => onFocus && onFocus(marker, evt))
}
handleBlur(evt) {
evt.persist()
const { onBlur, marker } = this.props
this.setState({
hover: false,
}, () => onBlur && onBlur(marker, evt))
}
render() {
const {
projection,
marker,
styles,
zoom,
projection,
style,
tabable,
children,
} = this.props
const {
pressed,
hover,
} = this.state
return (
<circle
cx={ projection()(marker.coordinates)[0] }
cy={ projection()(marker.coordinates)[1] }
r={ marker.radius }
style={ styles(marker, zoom)[ this.state.hovered ? "hover" : "default" ] || styles(marker, zoom)["default"] }
className="rsm-marker"
onMouseEnter={ (evt) => this.handleMouseEnter(marker, evt) }
onMouseLeave={ (evt) => this.handleMouseLeave(marker, evt) }
onMouseMove={ (evt) => this.handleMouseMove(marker, evt) }
onClick={ (evt) => this.handleClick(marker, evt) }
transform={`
translate(
${ projection()(marker.coordinates)[0] }
${ projection()(marker.coordinates)[1] }
)
scale(
${ 1/zoom }
)
translate(
${ -projection()(marker.coordinates)[0] }
${ -projection()(marker.coordinates)[1] }
)
`}
/>
<g className={ `rsm-marker${ pressed && " rsm-marker--pressed" }${ hover && " rsm-marker--hover" }` }
transform={ `translate(
${ projection()(marker.coordinates)[0] }
${ projection()(marker.coordinates)[1] }
)`}
style={ style[pressed || hover ? (pressed ? "pressed" : "hover") : "default"] }
onMouseEnter={ this.handleMouseEnter }
onMouseLeave={ this.handleMouseLeave }
onMouseDown={ this.handleMouseDown }
onMouseUp={ this.handleMouseUp }
onClick={ this.handleMouseClick }
onMouseMove={ this.handleMouseMove }
onFocus={ this.handleFocus }
onBlur={ this.handleBlur }
tabIndex={ tabable ? 0 : -1 }
>
{ children }
</g>
)

@@ -90,17 +122,11 @@ }

Marker.propTypes = {
marker: PropTypes.object,
zoom: PropTypes.number,
events: PropTypes.object,
projection: PropTypes.func,
styles: PropTypes.func,
}
Marker.defaultProps = {
marker: {},
zoom: 1,
events: {},
styles: defaultStyles.marker,
style: {
default: {},
hover: {},
pressed: {},
},
tabable: true,
}
export default Marker
import React, { PropTypes } from "react"
import defaultStyles from "./defaultStyles"
import Marker from "./Marker"
import React, { Component } from "react"
class Markers extends React.Component {
class Markers extends Component {
render() {
const {
children,
projection,
style,
} = this.props
return (
<g className="rsm-markers">
<g className="rsm-markers" style={ style }>
{
this.props.markers.map((marker, i) => (
<Marker
key={ `${Math.abs(marker.coordinates[0])}-${Math.abs(marker.coordinates[1])}-${i}` }
projection={ this.props.projection }
marker={ marker }
styles={ this.props.styles.marker }
events={ this.props.events }
zoom={ this.props.zoom }
/>
))
!children.length ?
React.cloneElement(children, {
projection,
}) :
children.map((child, i) => (
React.cloneElement(child, {
key: child.key || `marker-${i}`,
projection,
})
))
}

@@ -27,17 +32,6 @@ </g>

Markers.propTypes = {
markers: PropTypes.array,
projection: PropTypes.func.isRequired,
zoom: PropTypes.number,
events: PropTypes.object,
styles: PropTypes.object,
}
Markers.defaultProps = {
markers: [],
zoom: 1,
events: {},
styles: defaultStyles,
componentIdentifier: "Markers",
}
export default Markers

@@ -27,6 +27,6 @@

return projectionReference[projectionName]()
.scale(scale)
.translate([ xOffset + width / 2, yOffset + height / 2 ])
.rotate(rotation)
.precision(precision)
.scale(scale)
.translate([ xOffset + width / 2, yOffset + height / 2 ])
.rotate(rotation)
.precision(precision)
}
import React from "react"
export function replaceStrokeWidth(styles) {

@@ -29,6 +31,55 @@ let newStyles = {}

export function calculateMousePosition(direction, projection, props, zoom, resizeFactor) {
const { center, width, height } = props
export function calculateMousePosition(direction, projection, props, zoom, resizeFactor, center = props.center, width = props.width, height = props.height) {
const reference = { x: 0, y: 1 }
return (projection()([-center[0],-center[1]])[reference[direction]] - (reference[direction] === 0 ? width : height) / 2) * zoom * (1/resizeFactor)
const reverseRotation = projection().rotate().map(item => -item)
return (projection().rotate(reverseRotation)([-center[0],-center[1]])[reference[direction]] - (reference[direction] === 0 ? width : height) / 2) * zoom * (1/resizeFactor)
}
export function isChildOfType(child, expectedType) {
return child.props.componentIdentifier === expectedType
}
export function createNewChildren(children, props) {
if (!children.length) {
return isChildOfType(children, "Geographies") ? React.cloneElement(children, {
geographyPaths: props.geographyPaths,
projection: props.projection,
}) : (isChildOfType(children, "Markers") || isChildOfType(children, "Annotation") ? React.cloneElement(children, {
projection: props.projection,
zoom: props.zoom,
}) : children)
}
else {
return children.map((child, i) => {
return isChildOfType(child, "Geographies") ?
React.cloneElement(child, {
key: `zoomable-child-${i}`,
geographyPaths: props.geographyPaths,
projection: props.projection,
}) : (isChildOfType(child, "Markers") || isChildOfType(child, "Annotation") ?
React.cloneElement(child, {
key: `zoomable-child-${i}`,
projection: props.projection,
zoom: props.zoom,
}): child)
})
}
}
export function roundPath(path, precision) {
const query = /[\d\.-][\d\.e-]*/g
return path.replace(query, n => Math.round(n * (1/precision)) / (1/precision))
}
export function createConnectorPath(connectorType, endPoint) {
return `M0,0 L${endPoint[0]},${endPoint[1]}`
}
export function createTextAnchor(dx) {
if (dx > 0)
return "start"
else if (dx < 0 )
return "end"
else
return "middle"
}
import React, { PropTypes } from "react"
import defaultStyles from "./defaultStyles"
import React, { Component } from "react"
class ZoomableGroup extends React.Component {
import {
calculateResizeFactor,
calculateMousePosition,
isChildOfType,
createNewChildren,
} from "./utils"
class ZoomableGroup extends Component {
constructor(props) {
super(props)
this.state = {
mouseX: calculateMousePosition("x", props.projection, props, props.zoom, 1),
mouseY: calculateMousePosition("y", props.projection, props, props.zoom, 1),
mouseXStart: 0,
mouseYStart: 0,
isPressed: false,
resizeFactorX: 1,
resizeFactorY: 1,
}
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleMouseUp = this.handleMouseUp.bind(this)
this.handleMouseDown = this.handleMouseDown.bind(this)
this.handleResize = this.handleResize.bind(this)
}
handleMouseMove({ pageX, pageY }) {
if (this.props.disablePanning) return
if(this.state.isPressed) {
this.setState({
mouseX: pageX - this.state.mouseXStart,
mouseY: pageY - this.state.mouseYStart,
})
}
}
handleMouseUp({ pageX, pageY }) {
if (this.props.disablePanning) return
this.setState({
isPressed: false,
})
}
handleMouseDown({ pageX, pageY }) {
if (this.props.disablePanning) return
this.setState({
isPressed: true,
mouseXStart: pageX - this.state.mouseX,
mouseYStart: pageY - this.state.mouseY,
})
}
componentWillReceiveProps(nextProps) {
const { mouseX, mouseY, resizeFactorX, resizeFactorY } = this.state
const { projection, center, zoom } = this.props
const zoomFactor = nextProps.zoom / zoom
const centerChanged = JSON.stringify(nextProps.center) !== JSON.stringify(center)
this.setState({
zoom: nextProps.zoom,
mouseX: centerChanged ? calculateMousePosition("x", projection, nextProps, nextProps.zoom, resizeFactorX) : mouseX * zoomFactor,
mouseY: centerChanged ? calculateMousePosition("y", projection, nextProps, nextProps.zoom, resizeFactorY) : mouseY * zoomFactor,
})
}
handleResize() {
const { width, height, projection, zoom } = this.props
const resizeFactorX = calculateResizeFactor(this.zoomableGroupNode.parentElement.clientWidth, width)
const resizeFactorY = calculateResizeFactor(this.zoomableGroupNode.parentElement.clientHeight, height)
const xPercentageChange = 1 / resizeFactorX * this.state.resizeFactorX
const yPercentageChange = 1 / resizeFactorY * this.state.resizeFactorY
this.setState({
resizeFactorX: resizeFactorX,
resizeFactorY: resizeFactorY,
mouseX: this.state.mouseX * xPercentageChange,
mouseY: this.state.mouseY * yPercentageChange,
})
}
componentDidMount() {
const { width, height, projection, zoom } = this.props
const resizeFactorX = calculateResizeFactor(this.zoomableGroupNode.parentElement.clientWidth, width)
const resizeFactorY = calculateResizeFactor(this.zoomableGroupNode.parentElement.clientHeight, height)
this.setState({
resizeFactorX: resizeFactorX,
resizeFactorY: resizeFactorY,
mouseX: calculateMousePosition("x", projection, this.props, zoom, resizeFactorX),
mouseY: calculateMousePosition("y", projection, this.props, zoom, resizeFactorY),
})
window.addEventListener("resize", this.handleResize)
}
componentWillUnmount() {
window.removeEventListener("resize", this.handleResize)
}
render() {
const {
zoom,
mouseX,
mouseY,
width,
height,
isPressed,
center,
zoom,
style,
projection,
children,
} = this.props
const {
mouseX,
mouseY,
resizeFactorX,
resizeFactorY,
} = this.state
const backdropDimensions = [
(projection().scale()) / 100 * width,
(projection().scale()) / 100 * height,
]
return (
<g
transform={`
translate(
${ (width / 2) + this.props.resizeFactorX * mouseX }
${ (height / 2) + this.props.resizeFactorY * mouseY }
)
scale(${ zoom })
translate(${ -width / 2 } ${ -height / 2 })
`}
style={ this.props.styles.geographies ? this.props.styles.geographies(zoom) : defaultStyles.geographies(zoom) }
onMouseMove={ this.props.handleMouseMove }
onMouseUp={ this.props.handleMouseUp }
onMouseDown={ this.props.handleMouseDown }
className="rsm-zoomable-group"
<g className="rsm-zoomable-group"
ref={ zoomableGroupNode => this.zoomableGroupNode = zoomableGroupNode }
transform={`
translate(
${ width / 2 + resizeFactorX * mouseX }
${ height / 2 + resizeFactorY * mouseY }
)
scale(${ zoom })
translate(${ -width / 2 } ${ -height / 2 })
`}
onMouseMove={ this.handleMouseMove }
onMouseUp={ this.handleMouseUp }
onMouseDown={ this.handleMouseDown }
style={ style }
>
<rect x={0} y={0} width={width} height={height} fill="transparent" />
{ this.props.children }
<rect
x={ width/2 }
y={ height/2 }
width={ width }
height={ height }
fill="transparent"
transform={ `translate(-${ width / 2 }, -${ height / 2 })` }
style={{ strokeWidth: 0 }}
/>
{ createNewChildren(children, this.props) }
</g>

@@ -41,16 +154,8 @@ )

ZoomableGroup.propTypes = {
zoom: PropTypes.number.isRequired,
mouseX: PropTypes.number.isRequired,
mouseY: PropTypes.number.isRequired,
isPressed: PropTypes.bool.isRequired,
styles: PropTypes.object,
center: PropTypes.array,
}
ZoomableGroup.defaultProps = {
styles: defaultStyles,
center: [0,0],
center: [ 0, 0 ],
zoom: 1,
disablePanning: false,
}
export default ZoomableGroup
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc