UNPKG

wix-style-react

Version:
449 lines (397 loc) • 17 kB
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized"; import _inherits from "@babel/runtime/helpers/inherits"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } import React from 'react'; import PropTypes from 'prop-types'; import { scaleTime, scaleLinear } from 'd3-scale'; import { max, bisector } from 'd3-array'; import { line, area, curveMonotoneX } from 'd3-shape'; import { select, pointer } from 'd3-selection'; import { easeQuadIn } from 'd3-ease'; import { ChartTooltip } from './ChartTooltip'; import { dataHooks } from './constants'; import { stVars as colors } from '../Foundation/stylable/colors.st.css'; import 'd3-transition'; var LINE_WIDTH = 2; var AREA_MASK_ID = 'areaMaskId'; var TOOLTIP_ELEMENT_RADIUS = 4; var DEFAULT_COLOR = colors.A1; /** SparklineChart */ var SparklineChart = /*#__PURE__*/function (_React$PureComponent) { _inherits(SparklineChart, _React$PureComponent); var _super = _createSuper(SparklineChart); function SparklineChart(props) { var _this; _classCallCheck(this, SparklineChart); _this = _super.call(this, props); _defineProperty(_assertThisInitialized(_this), "_shouldShowTooltip", function () { var hoveredLabel = _this.state.hoveredLabel; var getTooltipContent = _this.props.getTooltipContent; return getTooltipContent && typeof getTooltipContent === 'function' && hoveredLabel; }); _defineProperty(_assertThisInitialized(_this), "_useCreateContext", function () { var halfWidth = LINE_WIDTH / 2; var _this$props = _this.props, _this$props$width = _this$props.width, width = _this$props$width === void 0 ? 200 : _this$props$width, _this$props$height = _this$props.height, height = _this$props$height === void 0 ? 40 : _this$props$height, data = _this$props.data, _this$props$highlight = _this$props.highlightedStartingIndex, highlightedStartingIndex = _this$props$highlight === void 0 ? 0 : _this$props$highlight, _this$props$color = _this$props.color, color = _this$props$color === void 0 ? DEFAULT_COLOR : _this$props$color; var margin = { top: halfWidth + 2, right: halfWidth, bottom: halfWidth, left: halfWidth }; var innerTop = margin.top; var innerLeft = margin.left; var innerHeight = height - innerTop - margin.bottom; var innerWidth = width - innerLeft - margin.right; var maxValue = max(_this._getValues(data)); var firstLabel = _this._getLabelAt(data, 0); var lastLabel = _this._getLabelAt(data, data.length - 1); var xScale = scaleTime().domain([firstLabel, lastLabel]).range([innerLeft, innerWidth]); var yScale = scaleLinear().domain([0, maxValue]).range([innerHeight, innerTop]); var lineGenerator = line().x(function (dataPoint, i) { return xScale(_this._getLabelAt(data, i)); }).y(function (dataPoint) { return yScale(dataPoint); }).curve(curveMonotoneX); var areaGenerator = area().x(function (dataPoint, i) { return xScale(_this._getLabelAt(data, i)); }).y0(function () { return innerHeight; }).y1(function (dataPoint) { return yScale(dataPoint); }).curve(curveMonotoneX); return { margin: margin, width: width, height: height, innerTop: innerTop, innerLeft: innerLeft, innerBottom: margin.top + innerHeight, innerWidth: innerWidth, innerHeight: innerHeight, data: data, xScale: xScale, yScale: yScale, highlightedStartingIndex: highlightedStartingIndex, lineGenerator: lineGenerator, areaGenerator: areaGenerator, color: color }; }); _defineProperty(_assertThisInitialized(_this), "_getLabelAt", function (data, position) { return data[position] && data[position].label; }); _defineProperty(_assertThisInitialized(_this), "_getValues", function (data) { return data.map(function (pair) { return pair.value; }); }); _defineProperty(_assertThisInitialized(_this), "_getLabels", function (data) { return data.map(function (pair) { return pair.label; }); }); _defineProperty(_assertThisInitialized(_this), "_drawSparkline", function () { var _this$chartContext = _this.chartContext, width = _this$chartContext.width, height = _this$chartContext.height, data = _this$chartContext.data; var onHover = _this.props.onHover; var labels = _this._getLabels(data); var container = select(_this.svgRef.current); container.attr('width', width).attr('height', height); var dataContainer = container.select("[data-hook=\"".concat(dataHooks.dataContainer, "\"]")); _this._drawLines(dataContainer); select(_this.componentRef.current).on('mouseleave', function () { _this.setState({ hoveredLabel: null }); }).on('mousemove', function (d) { var dateUnderPointer = _this.chartContext.xScale.invert(pointer(d)[0]); var currentDateIndex = bisector(function (date) { return date; }).left(labels, dateUnderPointer, 1); var beforeDateIndex = currentDateIndex - 1; var beforeDate = labels[beforeDateIndex]; var afterDate = labels[currentDateIndex]; var closestDate = +dateUnderPointer - +beforeDate > +afterDate - +dateUnderPointer ? afterDate : beforeDate; if (typeof onHover === 'function' && !_this._areDatesEqual(closestDate, _this.state.hoveredLabel)) { var labelIndex = labels.indexOf(closestDate); onHover(labelIndex); } _this.setState({ hoveredLabel: closestDate }); }); }); _defineProperty(_assertThisInitialized(_this), "_drawLines", function (dataContainer) { var _this$chartContext2 = _this.chartContext, data = _this$chartContext2.data, lineGenerator = _this$chartContext2.lineGenerator, areaGenerator = _this$chartContext2.areaGenerator, color = _this$chartContext2.color; var dataSets = [data]; dataContainer.selectAll('.chartLines').data(dataSets).join('g').attr('class', 'chartLines').selectAll('g').data(function (dataSet) { return [dataSet]; }).join(function (enter) { var group = enter.append('g'); group.append('path').attr('class', 'innerArea').attr('mask', "url(#".concat(_this._getAreaMaskId(_this.randomComponentId), ")")).attr('fill', function (dataSet) { return "url(#".concat(color, ")"); }).attr('d', function (dataSet) { return areaGenerator(dataSet.map(function () { return 0; })); }); group.append('path').attr('class', 'innerLineBack').attr('fill', 'none').attr('stroke-width', LINE_WIDTH + 4).attr('stroke-linecap', 'round').attr('stroke', 'white').attr('d', function (dataSet) { return lineGenerator(dataSet.map(function () { return 0; })); }); group.append('path').attr('class', 'innerLine').attr('fill', 'none').attr('stroke-width', LINE_WIDTH).attr('stroke-linecap', 'round').attr('stroke', function (dataSet) { return "url(#".concat(_this._getLineColorId(dataSet, _this.randomComponentId), ")"); }).attr('d', function (dataSet) { return lineGenerator(dataSet.map(function () { return 0; })); }); _this._updateLines(group); return group; }, function (update) { _this._updateLines(update); return update; }); }); _defineProperty(_assertThisInitialized(_this), "_updateLines", function (container) { var _this$chartContext3 = _this.chartContext, lineGenerator = _this$chartContext3.lineGenerator, areaGenerator = _this$chartContext3.areaGenerator; _this._updateComponent(container, '.innerLine', function (set) { return lineGenerator(_this._getValues(set)); }); _this._updateComponent(container, '.innerLineBack', function (set) { return lineGenerator(_this._getValues(set)); }); _this._updateComponent(container, '.innerArea', function (set) { return areaGenerator(_this._getValues(set)); }); }); _defineProperty(_assertThisInitialized(_this), "_updateComponent", function (container, className, fncUpdater) { var animationDuration = _this.props.animationDuration; container.select(className).transition().duration(animationDuration).ease(easeQuadIn).attr('d', fncUpdater); }); _this.randomComponentId = Math.random().toString(); _this.chartContext = {}; _this.svgRef = /*#__PURE__*/React.createRef(null); _this.componentRef = /*#__PURE__*/React.createRef(null); _this.state = { hoveredLabel: null }; return _this; } _createClass(SparklineChart, [{ key: "_getValueAt", value: function _getValueAt(data, position) { return data[position] && data[position].value; } }, { key: "_areDatesEqual", value: function _areDatesEqual(date1, date2) { var date1Time = date1 && date1.getTime(); var date2Time = date2 && date2.getTime(); return date1Time === date2Time; } }, { key: "_getLineColorId", value: function _getLineColorId(dataSet, componentId) { return "".concat(componentId, "color"); } }, { key: "_getAreaMaskId", value: function _getAreaMaskId(componentId) { return "".concat(AREA_MASK_ID).concat(componentId); } }, { key: "componentDidMount", value: function componentDidMount() { this._drawSparkline(); } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { if (prevProps.data !== this.props.data) { this._drawSparkline(); } } }, { key: "_updateContext", value: function _updateContext() { this.chartContext = this._useCreateContext(); } }, { key: "render", value: function render() { this._updateContext(); var _this$props2 = this.props, getTooltipContent = _this$props2.getTooltipContent, className = _this$props2.className, dataHook = _this$props2.dataHook; var hoveredLabel = this.state.hoveredLabel; var context = this.chartContext; var data = context.data, highlightedStartingIndex = context.highlightedStartingIndex, innerWidth = context.innerWidth, height = context.height, width = context.width, color = context.color; var highlightedStartBefore = context.xScale(this._getLabelAt(data, highlightedStartingIndex - 1)); var highlightedStart = context.xScale(this._getLabelAt(data, highlightedStartingIndex)); var highlightedRelativeLocation = highlightedStart / innerWidth; var inter = (highlightedStart - highlightedStartBefore) / 2 / innerWidth; var labels = this._getLabels(data); var hoveredLabelIndex = bisector(function (d) { return d; }).left(labels, hoveredLabel, 0); var currentHoveredLabel = this._getLabelAt(data, hoveredLabelIndex); var currentHoveredValue = this._getValueAt(data, hoveredLabelIndex); var dataPoint = { content: getTooltipContent && typeof getTooltipContent === 'function' && getTooltipContent(hoveredLabelIndex), xCoordinate: context.xScale(currentHoveredLabel), yCoordinate: context.yScale(currentHoveredValue) - TOOLTIP_ELEMENT_RADIUS / 2 }; var enableHighlightedAreaEffect = highlightedStartingIndex > 0; return /*#__PURE__*/React.createElement("div", { style: { width: width, height: height, position: 'relative' }, ref: this.componentRef, className: className, "data-hook": dataHook }, /*#__PURE__*/React.createElement("svg", { style: { overflow: 'visible', zIndex: 1 }, ref: this.svgRef }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("mask", { id: this._getAreaMaskId(this.randomComponentId) }, /*#__PURE__*/React.createElement("rect", { x: highlightedStart, y: "0", width: width, height: height, fill: "white" })), /*#__PURE__*/React.createElement("linearGradient", { gradientUnits: 'userSpaceOnUse', key: "".concat(this.randomComponentId, "a"), id: this._getLineColorId(data, this.randomComponentId), x1: "0px", y1: "0px", x2: "".concat(innerWidth, "px"), y2: '0px' }, enableHighlightedAreaEffect && [/*#__PURE__*/React.createElement("stop", { key: 0, offset: "0", style: { stopColor: '#dfe5eb', stopOpacity: 1 } }), /*#__PURE__*/React.createElement("stop", { key: 1, offset: highlightedRelativeLocation - inter, style: { stopColor: '#dfe5eb', stopOpacity: 1 } }), /*#__PURE__*/React.createElement("stop", { key: 2, offset: highlightedRelativeLocation, style: { stopColor: color, stopOpacity: 1 } })], /*#__PURE__*/React.createElement("stop", { offset: "1", style: { stopColor: color, stopOpacity: 1 } })), /*#__PURE__*/React.createElement("linearGradient", { gradientUnits: 'userSpaceOnUse', key: this.randomComponentId, id: color, x1: "0px", y1: "".concat(context.innerHeight, "px"), x2: "0px", y2: '0px' }, /*#__PURE__*/React.createElement("stop", { offset: "10%", style: { stopColor: color, stopOpacity: 0 } }), /*#__PURE__*/React.createElement("stop", { offset: "90%", style: { stopColor: color, stopOpacity: 0.5 } }))), /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("g", { "data-hook": dataHooks.dataContainer }), this._shouldShowTooltip() && /*#__PURE__*/React.createElement("g", { transform: "translate(".concat(dataPoint.xCoordinate, ", ").concat(dataPoint.yCoordinate + TOOLTIP_ELEMENT_RADIUS / 2, ")") }, /*#__PURE__*/React.createElement("circle", { r: TOOLTIP_ELEMENT_RADIUS, fill: color })))), this._shouldShowTooltip() && /*#__PURE__*/React.createElement(ChartTooltip, { dataPoint: dataPoint })); } }]); return SparklineChart; }(React.PureComponent); SparklineChart.displayName = 'SparklineChart'; SparklineChart.propTypes = { /** Applied as data-hook HTML attribute that can be used in the tests */ dataHook: PropTypes.string, /** A css class to be applied to the component's root element */ className: PropTypes.string, /** Sets the width of the sparkline (pixels) */ width: PropTypes.number, /** Sets the height of the sparkline (pixels) */ height: PropTypes.number, /** Chart data */ data: PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.instanceOf(Date), value: PropTypes.number })).isRequired, /** Sets the color of the sparkline */ color: PropTypes.string, /** Indicates the starting index of the highlighted area. Default is 0 */ highlightedStartingIndex: PropTypes.number, /** Tooltip content (JSX) getter function. */ getTooltipContent: PropTypes.func, /** callback when graph is hovered*/ onHover: PropTypes.func, /** Sets the duration of the animation in milliseconds */ animationDuration: PropTypes.number }; SparklineChart.defaultProps = { animationDuration: 300 }; export default SparklineChart;