@data-ui/xy-chart
Version:
A package of charts with standard x- and y- axes. https://williaster.github.io/data-ui
479 lines (446 loc) • 14.2 kB
JavaScript
function _extends() { _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; }; return _extends.apply(this, arguments); }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
import React from 'react';
import PropTypes from 'prop-types';
import { Group } from '@vx/group';
import { Bar } from '@vx/shape';
import BrushHandle from './BrushHandle';
import BrushCorner from './BrushCorner';
import BrushSelection from './BrushSelection';
import { Drag } from '../drag';
import { marginShape, generalStyleShape } from '../propShapes';
var propTypes = {
brushDirection: PropTypes.oneOf(['horizontal', 'vertical', 'both']),
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
left: PropTypes.number.isRequired,
top: PropTypes.number.isRequired,
inheritedMargin: marginShape,
onChange: PropTypes.func,
handleSize: PropTypes.number,
resizeTriggerAreas: PropTypes.arrayOf(PropTypes.oneOf(['left', 'right', 'top', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'])),
onBrushStart: PropTypes.func,
onBrushEnd: PropTypes.func,
selectedBoxStyle: generalStyleShape.isRequired,
onMouseLeave: PropTypes.func,
onMouseUp: PropTypes.func,
onMouseMove: PropTypes.func,
onClick: PropTypes.func,
clickSensitivity: PropTypes.number,
disableDraggingSelection: PropTypes.bool
};
var defaultProps = {
brushDirection: 'both',
inheritedMargin: {
left: 0,
top: 0,
right: 0,
bottom: 0
},
onChange: null,
handleSize: 4,
resizeTriggerAreas: ['left', 'right'],
onBrushStart: null,
onBrushEnd: null,
onMouseLeave: null,
onMouseUp: null,
onMouseMove: null,
onClick: null,
disableDraggingSelection: false,
clickSensitivity: 200
};
var Brush =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(Brush, _React$Component);
function Brush(props) {
var _this;
_this = _React$Component.call(this, props) || this;
var width = props.width,
height = props.height;
_this.state = {
start: {
x: 0,
y: 0
},
end: {
x: 0,
y: 0
},
extent: {
x0: 0,
x1: 0,
y0: 0,
y1: 0
},
bounds: {
x0: 0,
x1: width,
y0: 0,
y1: height
}
};
_this.width = _this.width.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.height = _this.height.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handles = _this.handles.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.corners = _this.corners.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.update = _this.update.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.reset = _this.reset.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handleDragStart = _this.handleDragStart.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handleDragMove = _this.handleDragMove.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handleDragEnd = _this.handleDragEnd.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.getExtent = _this.getExtent.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.mouseUpTime = 0;
_this.mouseDownTime = 0;
return _this;
}
var _proto = Brush.prototype;
_proto.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
var _this2 = this;
if (['width', 'height'].some(function (prop) {
return _this2.props[prop] !== nextProps[prop];
} // eslint-disable-line react/destructuring-assignment
)) {
this.setState(function () {
return {
bounds: {
x0: 0,
x1: nextProps.width,
y0: 0,
y1: nextProps.height
}
};
});
}
};
_proto.getExtent = function getExtent(start, end) {
var _this$props = this.props,
brushDirection = _this$props.brushDirection,
width = _this$props.width,
height = _this$props.height;
var x0 = brushDirection === 'vertical' ? 0 : Math.min(start.x, end.x);
var x1 = brushDirection === 'vertical' ? width : Math.max(start.x, end.x);
var y0 = brushDirection === 'horizontal' ? 0 : Math.min(start.y, end.y);
var y1 = brushDirection === 'horizontal' ? height : Math.max(start.y, end.y);
return {
x0: x0,
x1: x1,
y0: y0,
y1: y1
};
};
_proto.handleDragStart = function handleDragStart(draw) {
var _this$props2 = this.props,
onBrushStart = _this$props2.onBrushStart,
left = _this$props2.left,
top = _this$props2.top,
inheritedMargin = _this$props2.inheritedMargin;
var start = {
x: draw.x + draw.dx - left - inheritedMargin.left,
y: draw.y + draw.dy - top - inheritedMargin.top
};
var end = _extends({}, start);
if (onBrushStart) {
onBrushStart(start);
}
this.update(function (prevBrush) {
return _extends({}, prevBrush, {
start: start,
end: end,
extent: {
x0: -1,
x1: -1,
y0: -1,
y1: -1
},
isBrushing: true
});
});
};
_proto.handleDragMove = function handleDragMove(draw) {
var _this3 = this;
var _this$props3 = this.props,
left = _this$props3.left,
top = _this$props3.top,
inheritedMargin = _this$props3.inheritedMargin;
if (!draw.isDragging) return;
var end = {
x: draw.x + draw.dx - left - inheritedMargin.left,
y: draw.y + draw.dy - top - inheritedMargin.top
};
this.update(function (prevBrush) {
var start = prevBrush.start;
var extent = _this3.getExtent(start, end);
return _extends({}, prevBrush, {
end: end,
extent: extent
});
});
};
_proto.handleDragEnd = function handleDragEnd() {
var onBrushEnd = this.props.onBrushEnd;
this.update(function (prevBrush) {
var extent = prevBrush.extent;
var newState = _extends({}, prevBrush, {
start: {
x: extent.x0,
y: extent.y0
},
end: {
x: extent.x1,
y: extent.y1
},
isBrushing: false
});
if (onBrushEnd) {
onBrushEnd(newState);
}
return newState;
});
};
_proto.width = function width() {
var extent = this.state.extent;
var x0 = extent.x0,
x1 = extent.x1;
return Math.max(Math.max(x0, x1) - Math.min(x0, x1), 0);
};
_proto.height = function height() {
var extent = this.state.extent;
var y1 = extent.y1,
y0 = extent.y0;
return Math.max(y1 - y0, 0);
};
_proto.handles = function handles() {
var handleSize = this.props.handleSize;
var extent = this.state.extent;
var x0 = extent.x0,
x1 = extent.x1,
y0 = extent.y0,
y1 = extent.y1;
var offset = handleSize / 2;
var width = this.width();
var height = this.height();
return {
top: {
x: x0 - offset,
y: y0 - offset,
height: handleSize,
width: width + handleSize
},
bottom: {
x: x0 - offset,
y: y1 - offset,
height: handleSize,
width: width + handleSize
},
right: {
x: x1 - offset,
y: y0 - offset,
height: height + handleSize,
width: handleSize
},
left: {
x: x0 - offset,
y: y0 - offset,
height: height + handleSize,
width: handleSize
}
};
};
_proto.corners = function corners() {
var handleSize = this.props.handleSize;
var extent = this.state.extent;
var x0 = extent.x0,
x1 = extent.x1,
y0 = extent.y0,
y1 = extent.y1;
var offset = handleSize / 2;
return {
topLeft: {
x: Math.min(x0, x1) - offset,
y: Math.min(y0, y1) - offset
},
topRight: {
x: Math.max(x0, x1) - offset,
y: Math.min(y0, y1) - offset
},
bottomLeft: {
x: Math.min(x0, x1) - offset,
y: Math.max(y0, y1) - offset
},
bottomRight: {
x: Math.max(x0, x1) - offset,
y: Math.max(y0, y1) - offset
}
};
};
_proto.update = function update(updater) {
var _this4 = this;
var onChange = this.props.onChange;
this.setState(updater, function () {
if (onChange) {
onChange(_this4.state);
}
});
};
_proto.reset = function reset() {
var _this$props4 = this.props,
width = _this$props4.width,
height = _this$props4.height;
this.update(function () {
return {
start: undefined,
end: undefined,
extent: {
x0: undefined,
x1: undefined,
y0: undefined,
y1: undefined
},
bounds: {
x0: 0,
x1: width,
y0: 0,
y1: height
},
isBrushing: false,
activeHandle: undefined
};
});
};
_proto.render = function render() {
var _this5 = this;
var _this$state = this.state,
start = _this$state.start,
end = _this$state.end;
var _this$props5 = this.props,
top = _this$props5.top,
left = _this$props5.left,
stageWidth = _this$props5.width,
stageHeight = _this$props5.height,
handleSize = _this$props5.handleSize,
_onMouseLeave = _this$props5.onMouseLeave,
_onMouseUp = _this$props5.onMouseUp,
_onMouseMove = _this$props5.onMouseMove,
onBrushEnd = _this$props5.onBrushEnd,
_onClick = _this$props5.onClick,
resizeTriggerAreas = _this$props5.resizeTriggerAreas,
selectedBoxStyle = _this$props5.selectedBoxStyle,
disableDraggingSelection = _this$props5.disableDraggingSelection,
clickSensitivity = _this$props5.clickSensitivity;
var handles = this.handles();
var corners = this.corners();
var width = this.width();
var height = this.height();
var resizeTriggerAreaSet = new Set(resizeTriggerAreas);
return React.createElement(Group, {
className: "vx-brush",
top: top,
left: left
}, React.createElement(Drag, {
width: stageWidth,
height: stageHeight,
resetOnStart: true,
onDragStart: this.handleDragStart,
onDragMove: this.handleDragMove,
onDragEnd: this.handleDragEnd
}, function (draw) {
return React.createElement(Bar, {
className: "vx-brush-overlay",
fill: "transparent",
x: 0,
y: 0,
width: stageWidth,
height: stageHeight,
onDoubleClick: function onDoubleClick() {
return function (event) {
return _this5.reset(event);
};
},
onClick: function onClick() {
return function (event) {
var duration = _this5.mouseUpTime - _this5.mouseDownTime;
if (_onClick && duration < clickSensitivity) _onClick(event);
};
},
onMouseDown: function onMouseDown() {
return function (event) {
_this5.mouseDownTime = new Date();
draw.dragStart(event);
};
},
onMouseLeave: function onMouseLeave() {
return function (event) {
if (_onMouseLeave) _onMouseLeave(event);
};
},
onMouseMove: function onMouseMove() {
return function (event) {
if (!draw.isDragging && _onMouseMove) _onMouseMove(event);
if (draw.isDragging) draw.dragMove(event);
};
},
onMouseUp: function onMouseUp() {
return function (event) {
_this5.mouseUpTime = new Date();
if (_onMouseUp) _onMouseUp(event);
draw.dragEnd(event);
};
},
style: {
cursor: 'crosshair'
}
});
}), start && end && React.createElement(BrushSelection, _extends({
updateBrush: this.update,
width: width,
height: height,
stageWidth: stageWidth,
stageHeight: stageHeight,
brush: _extends({}, this.state),
disableDraggingSelection: disableDraggingSelection,
onBrushEnd: onBrushEnd,
onMouseLeave: _onMouseLeave,
onMouseMove: _onMouseMove,
onMouseUp: _onMouseUp,
onClick: _onClick
}, selectedBoxStyle)), start && end && Object.keys(handles).filter(function (handleKey) {
return resizeTriggerAreaSet.has(handleKey);
}).map(function (handleKey) {
var handle = handles[handleKey];
return React.createElement(BrushHandle, {
key: "handle-" + handleKey,
type: handleKey,
handle: handle,
stageWidth: stageWidth,
stageHeight: stageHeight,
updateBrush: _this5.update,
brush: _this5.state,
onBrushEnd: onBrushEnd
});
}), start && end && Object.keys(corners).filter(function (cornerKey) {
return resizeTriggerAreaSet.has(cornerKey);
}).map(function (cornerKey) {
var corner = corners[cornerKey];
return React.createElement(BrushCorner, {
key: "corner-" + cornerKey,
type: cornerKey,
brush: _this5.state,
updateBrush: _this5.update,
stageWidth: stageWidth,
stageHeight: stageHeight,
x: corner.x,
y: corner.y,
width: handleSize,
height: handleSize,
onBrushEnd: onBrushEnd,
fill: "transparent"
});
}));
};
return Brush;
}(React.Component);
export { Brush as default };
Brush.propTypes = propTypes;
Brush.defaultProps = defaultProps;