ct-react-stockcharts
Version:
Highly customizable stock charts with ReactJS and d3
418 lines (334 loc) • 13.3 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
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 _react = require("react");
var _react2 = _interopRequireDefault(_react);
var _propTypes = require("prop-types");
var _propTypes2 = _interopRequireDefault(_propTypes);
var _GenericComponent = require("../GenericComponent");
var _GenericComponent2 = _interopRequireDefault(_GenericComponent);
var _d3Array = require("d3-array");
var _utils = require("../utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
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 HoverTooltip = function (_Component) {
_inherits(HoverTooltip, _Component);
function HoverTooltip(props) {
_classCallCheck(this, HoverTooltip);
var _this = _possibleConstructorReturn(this, (HoverTooltip.__proto__ || Object.getPrototypeOf(HoverTooltip)).call(this, props));
_this.renderSVG = _this.renderSVG.bind(_this);
_this.drawOnCanvas = _this.drawOnCanvas.bind(_this);
return _this;
}
_createClass(HoverTooltip, [{
key: "drawOnCanvas",
value: function drawOnCanvas(ctx, moreProps) {
var pointer = helper(this.props, moreProps, ctx);
var height = moreProps.height;
if ((0, _utils.isNotDefined)(pointer)) return null;
_drawOnCanvas(ctx, this.props, this.context, pointer, height, moreProps);
}
}, {
key: "render",
value: function render() {
return _react2.default.createElement(_GenericComponent2.default, {
svgDraw: this.renderSVG,
canvasDraw: this.drawOnCanvas,
drawOn: ["mousemove", "pan" /* , "mouseleave" */]
});
}
}, {
key: "renderSVG",
value: function renderSVG(moreProps) {
var pointer = helper(this.props, moreProps);
if ((0, _utils.isNotDefined)(pointer)) return null;
var _props = this.props,
bgFill = _props.bgFill,
bgOpacity = _props.bgOpacity,
backgroundShapeSVG = _props.backgroundShapeSVG,
tooltipSVG = _props.tooltipSVG;
var _props2 = this.props,
bgheight = _props2.bgheight,
bgwidth = _props2.bgwidth;
var height = moreProps.height;
var x = pointer.x,
y = pointer.y,
content = pointer.content,
centerX = pointer.centerX,
pointWidth = pointer.pointWidth,
bgSize = pointer.bgSize;
var bgShape = (0, _utils.isDefined)(bgwidth) && (0, _utils.isDefined)(bgheight) ? { width: bgwidth, height: bgheight } : bgSize;
return _react2.default.createElement(
"g",
null,
_react2.default.createElement("rect", { x: centerX - pointWidth / 2,
y: 0,
width: pointWidth,
height: height,
fill: bgFill,
opacity: bgOpacity }),
_react2.default.createElement(
"g",
{ className: "react-stockcharts-tooltip-content", transform: "translate(" + x + ", " + y + ")" },
backgroundShapeSVG(this.props, bgShape),
tooltipSVG(this.props, content)
)
);
}
}]);
return HoverTooltip;
}(_react.Component);
HoverTooltip.propTypes = {
chartId: _propTypes2.default.oneOfType([_propTypes2.default.number, _propTypes2.default.string]),
yAccessor: _propTypes2.default.func,
tooltipSVG: _propTypes2.default.func,
backgroundShapeSVG: _propTypes2.default.func,
bgwidth: _propTypes2.default.number,
bgheight: _propTypes2.default.number,
bgFill: _propTypes2.default.string.isRequired,
bgOpacity: _propTypes2.default.number.isRequired,
tooltipContent: _propTypes2.default.func.isRequired,
origin: _propTypes2.default.oneOfType([_propTypes2.default.array, _propTypes2.default.func]).isRequired,
fontFamily: _propTypes2.default.string,
fontSize: _propTypes2.default.number
};
HoverTooltip.contextTypes = {
margin: _propTypes2.default.object.isRequired,
ratio: _propTypes2.default.number.isRequired
};
HoverTooltip.defaultProps = {
// bgwidth: 150,
// bgheight: 50,
tooltipSVG: tooltipSVG,
tooltipCanvas: tooltipCanvas,
origin: origin,
fill: "#D4E2FD",
bgFill: "#D4E2FD",
bgOpacity: 0.5,
stroke: "#9B9BFF",
fontFill: "#000000",
opacity: 0.8,
backgroundShapeSVG: backgroundShapeSVG,
backgroundShapeCanvas: backgroundShapeCanvas,
fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif",
fontSize: 12
};
var PADDING = 5;
var X = 10;
var Y = 10;
/* eslint-disable react/prop-types */
function backgroundShapeSVG(_ref, _ref2) {
var fill = _ref.fill,
stroke = _ref.stroke,
opacity = _ref.opacity;
var height = _ref2.height,
width = _ref2.width;
return _react2.default.createElement("rect", {
height: height,
width: width,
fill: fill,
opacity: opacity,
stroke: stroke });
}
function tooltipSVG(_ref3, content) {
var fontFamily = _ref3.fontFamily,
fontSize = _ref3.fontSize,
fontFill = _ref3.fontFill;
var tspans = [];
var startY = Y + fontSize * 0.9;
for (var i = 0; i < content.y.length; i++) {
var y = content.y[i];
var textY = startY + fontSize * (i + 1);
tspans.push(_react2.default.createElement(
"tspan",
{ key: "L-" + i, x: X, y: textY, fill: y.stroke },
y.label
));
tspans.push(_react2.default.createElement(
"tspan",
{ key: i },
": "
));
tspans.push(_react2.default.createElement(
"tspan",
{ key: "V-" + i },
y.value
));
}
return _react2.default.createElement(
"text",
{ fontFamily: fontFamily, fontSize: fontSize, fill: fontFill },
_react2.default.createElement(
"tspan",
{ x: X, y: startY },
content.x
),
tspans
);
}
/* eslint-enable react/prop-types */
function backgroundShapeCanvas(props, _ref4, ctx) {
var width = _ref4.width,
height = _ref4.height;
var fill = props.fill,
stroke = props.stroke,
opacity = props.opacity;
ctx.fillStyle = (0, _utils.hexToRGBA)(fill, opacity);
ctx.strokeStyle = stroke;
ctx.beginPath();
ctx.rect(0, 0, width, height);
ctx.fill();
ctx.stroke();
}
function tooltipCanvas(_ref5, content, ctx) {
var fontFamily = _ref5.fontFamily,
fontSize = _ref5.fontSize,
fontFill = _ref5.fontFill;
var startY = Y + fontSize * 0.9;
ctx.font = fontSize + "px " + fontFamily;
ctx.fillStyle = fontFill;
ctx.textAlign = "left";
ctx.fillText(content.x, X, startY);
for (var i = 0; i < content.y.length; i++) {
var y = content.y[i];
var textY = startY + fontSize * (i + 1);
ctx.fillStyle = y.stroke || fontFill;
ctx.fillText(y.label, X, textY);
ctx.fillStyle = fontFill;
ctx.fillText(": " + y.value, X + ctx.measureText(y.label).width, textY);
}
}
function _drawOnCanvas(ctx, props, context, pointer, height) {
var margin = context.margin,
ratio = context.ratio;
var bgFill = props.bgFill,
bgOpacity = props.bgOpacity;
var backgroundShapeCanvas = props.backgroundShapeCanvas,
tooltipCanvas = props.tooltipCanvas;
var originX = 0.5 * ratio + margin.left;
var originY = 0.5 * ratio + margin.top;
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.scale(ratio, ratio);
ctx.translate(originX, originY);
var x = pointer.x,
y = pointer.y,
content = pointer.content,
centerX = pointer.centerX,
pointWidth = pointer.pointWidth,
bgSize = pointer.bgSize;
ctx.fillStyle = (0, _utils.hexToRGBA)(bgFill, bgOpacity);
ctx.beginPath();
ctx.rect(centerX - pointWidth / 2, 0, pointWidth, height);
ctx.fill();
ctx.translate(x, y);
backgroundShapeCanvas(props, bgSize, ctx);
tooltipCanvas(props, content, ctx);
ctx.restore();
}
function calculateTooltipSize(_ref6, content, ctx) {
var fontFamily = _ref6.fontFamily,
fontSize = _ref6.fontSize,
fontFill = _ref6.fontFill;
if ((0, _utils.isNotDefined)(ctx)) {
var canvas = document.createElement("canvas");
ctx = canvas.getContext("2d");
}
ctx.font = fontSize + "px " + fontFamily;
ctx.fillStyle = fontFill;
ctx.textAlign = "left";
var measureText = function measureText(str) {
return {
width: ctx.measureText(str).width,
height: fontSize
};
};
var _content$y$map$reduce = content.y.map(function (_ref7) {
var label = _ref7.label,
value = _ref7.value;
return measureText(label + ": " + value);
})
// Sum all y and x sizes (begin with x label size)
.reduce(function (res, size) {
return sumSizes(res, size);
}, measureText(String(content.x))),
width = _content$y$map$reduce.width,
height = _content$y$map$reduce.height;
return {
width: width + 2 * X,
height: height + 2 * Y
};
}
function sumSizes() {
for (var _len = arguments.length, sizes = Array(_len), _key = 0; _key < _len; _key++) {
sizes[_key] = arguments[_key];
}
return {
width: Math.max.apply(Math, _toConsumableArray(sizes.map(function (size) {
return size.width;
}))),
height: (0, _d3Array.sum)(sizes, function (d) {
return d.height;
})
};
}
function normalizeX(x, bgSize, pointWidth, width) {
// return x - bgSize.width - pointWidth / 2 - PADDING * 2 < 0
return x < width / 2 ? x + pointWidth / 2 + PADDING : x - bgSize.width - pointWidth / 2 - PADDING;
}
function normalizeY(y, bgSize) {
return y - bgSize.height <= 0 ? y + PADDING : y - bgSize.height - PADDING;
}
function origin(props, moreProps, bgSize, pointWidth) {
var chartId = props.chartId,
yAccessor = props.yAccessor;
var mouseXY = moreProps.mouseXY,
xAccessor = moreProps.xAccessor,
currentItem = moreProps.currentItem,
xScale = moreProps.xScale,
chartConfig = moreProps.chartConfig,
width = moreProps.width;
var y = (0, _utils.last)(mouseXY);
var xValue = xAccessor(currentItem);
var x = Math.round(xScale(xValue));
if ((0, _utils.isDefined)(chartId) && (0, _utils.isDefined)(yAccessor) && (0, _utils.isDefined)(chartConfig) && (0, _utils.isDefined)(chartConfig.findIndex)) {
var yValue = yAccessor(currentItem);
var chartIndex = chartConfig.findIndex(function (x) {
return x.id === chartId;
});
y = Math.round(chartConfig[chartIndex].yScale(yValue));
}
x = normalizeX(x, bgSize, pointWidth, width);
y = normalizeY(y, bgSize);
return [x, y];
}
function helper(props, moreProps, ctx) {
var show = moreProps.show,
xScale = moreProps.xScale,
currentItem = moreProps.currentItem,
plotData = moreProps.plotData;
var origin = props.origin,
tooltipContent = props.tooltipContent;
var xAccessor = moreProps.xAccessor,
displayXAccessor = moreProps.displayXAccessor;
if (!show || (0, _utils.isNotDefined)(currentItem)) return;
var xValue = xAccessor(currentItem);
if (!show || (0, _utils.isNotDefined)(xValue)) return;
var content = tooltipContent({ currentItem: currentItem, xAccessor: displayXAccessor });
var centerX = xScale(xValue);
var pointWidth = Math.abs(xScale(xAccessor((0, _utils.last)(plotData))) - xScale(xAccessor((0, _utils.first)(plotData)))) / (plotData.length - 1);
var bgSize = calculateTooltipSize(props, content, ctx);
var _origin = origin(props, moreProps, bgSize, pointWidth),
_origin2 = _slicedToArray(_origin, 2),
x = _origin2[0],
y = _origin2[1];
return { x: x, y: y, content: content, centerX: centerX, pointWidth: pointWidth, bgSize: bgSize };
}
exports.default = HoverTooltip;
//# sourceMappingURL=HoverTooltip.js.map
;