UNPKG

@stokr/components-library

Version:

STOKR - Components Library

263 lines (243 loc) 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.BarChart = void 0; var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var d3 = _interopRequireWildcard(require("d3")); var _reactTippy = require("react-tippy"); var _BarChart2 = require("./BarChart.styles"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* inspired on https://blog.risingstack.com/d3-js-tutorial-bar-charts-with-javascript/ */ const gradients = { default: { start: '#0050ca', end: '#0050ca' }, red: { start: '#f24f45', end: '#ee220d' }, blue: { start: '#6a97e0', end: '#0050ca' } }; class BarChart extends _react.PureComponent { constructor() { super(...arguments); _defineProperty(this, "transitionDuration", 800); _defineProperty(this, "transitionDatumDelay", 100); _defineProperty(this, "state", { tooltipOpen: false, dataIndex: 0 }); _defineProperty(this, "onResize", () => { clearTimeout(this.resizeTimeout); this.resizeTimeout = setTimeout(this.onResizeTimeout, 200); }); _defineProperty(this, "onResizeTimeout", () => { this.updateChart(true); }); _defineProperty(this, "onScroll", e => { if (this.drawn) return; if (this.chartEl.getBoundingClientRect().top < window.innerHeight) { this.initialDrawChart(); } }); _defineProperty(this, "initialDrawChart", () => { this.updateChart(); this.drawn = true; }); _defineProperty(this, "createChart", () => { if (!this.chartEl) return; const { id, data, colorTheme } = this.props; const initData = data.map(datum => _objectSpread(_objectSpread({}, datum), {}, { value: 0 })); const { dataIndex } = this.state; const tooltip = !!data[dataIndex].tooltip; const wrapperWidth = this.chartEl.offsetWidth; const wrapperHeight = 320; const margin = { top: 20, left: 0, right: 40, bottom: 30 }; const width = wrapperWidth - (margin.left + margin.right); const height = wrapperHeight - (margin.top + margin.bottom); const thickness = 6; const hThickness = thickness / 2; this.hThickness = hThickness; // #region x scale const xScale = this.getXScale(data, width); // #endregion x scale // #region y scale const yScale = this.getYScale(data, height); // #endregion y scale this.svg = d3.select(this.chartEl).append('svg').attr('width', wrapperWidth).attr('height', wrapperHeight); this.chart = this.svg.append('g').attr('transform', "translate(".concat(margin.left, ", ").concat(margin.top, ")")); const defs = this.svg.append('defs'); // #region gradient const gradient = defs.append('linearGradient').attr('id', "".concat(id, "-barChart-gradient")).attr('x1', '50%').attr('x2', '50%').attr('y1', '100%').attr('y2', '0%'); gradient.append('stop').attr('class', 'start').attr('offset', '0%').attr('stop-color', gradients[colorTheme].start).attr('stop-opacity', 1); gradient.append('stop').attr('class', 'end').attr('offset', '100%').attr('stop-color', gradients[colorTheme].end).attr('stop-opacity', 1); // #endregion gradient // #region grid x axis this.xGrid = this.chart.append('g').attr('class', 'gridX').attr('transform', "translate(0, ".concat(height, ")")).call(d3.axisBottom().scale(xScale).tickSize(-height, 0, 0).tickFormat('')); // #endregion grid x axis // #region grid y axis this.yGrid = this.chart.append('g').attr('class', 'gridY').attr('transform', "translate(".concat(width, ", 0)")).call(d3.axisRight().scale(yScale).tickSize(-width, 0, 0).ticks(4).tickFormat('')); // #endregion grid y axis const tickValues = xScale.domain().length > 20 ? xScale.domain().filter((datum, index) => !(index % 2)) : xScale.domain(); // #region x axis this.xAxis = this.chart.append('g').attr('class', 'axisX').attr('transform', "translate(0, ".concat(height, ")")).call(d3.axisBottom().scale(xScale).tickValues(tickValues)); // #endregion x axis // #region y axis this.yAxis = this.chart.append('g').attr('class', 'axisY').attr('transform', "translate(".concat(width, ", 0)")).call(d3.axisRight().scale(yScale).ticks(4)); // #endregion y axis // #region bars const chartContext = this; this.chart.selectAll().data(initData).enter().append('path').attr('class', 'bar').style('fill', "url(#".concat(id, "-barChart-gradient)")).attr('transform', "translate(".concat(-hThickness, ", 0)")).each(function bindContext(d, i) { d3.select(this).node().chartContext = chartContext; }).on('mouseenter', tooltip ? this.onMouseEnter : () => {}).on('mouseleave', tooltip ? this.onMouseLeave : () => {}).attr('d', this.getBarDAttrFn(xScale, yScale, height, hThickness, thickness)); // #endregion bars }); _defineProperty(this, "setTooltip", (opened, dataIndex) => { const newState = { tooltipOpen: opened }; if (dataIndex !== undefined) newState.dataIndex = dataIndex; this.setState(newState); }); _defineProperty(this, "onMouseEnter", function onMouseEnter(d, i) { const chartRect = this.chartContext.chartEl.getBoundingClientRect(); const barRect = this.getBoundingClientRect(); const y = barRect.y - chartRect.y; const x = barRect.x - chartRect.x + this.chartContext.hThickness; this.chartContext.tooltipEl.style.left = "".concat(x, "px"); this.chartContext.tooltipEl.style.top = "".concat(y, "px"); this.chartContext.setTooltip(true, i); }); _defineProperty(this, "onMouseLeave", function onMouseLeave(d, i) { this.chartContext.setTooltip(false); }); _defineProperty(this, "updateChart", resizeUpdate => { if (!this.chartEl) return; const { data } = this.props; const durration = this.transitionDuration; const delay = this.transitionDatumDelay; const wrapperWidth = this.chartEl.offsetWidth; const wrapperHeight = 320; const margin = { top: 20, left: 0, right: 40, bottom: 30 }; const width = wrapperWidth - (margin.left + margin.right); const height = wrapperHeight - (margin.top + margin.bottom); const thickness = 6; const hThickness = thickness / 2; // #region x scale const xScale = this.getXScale(data, width); // #endregion x scale // #region y scale const yScale = this.getYScale(data, height); // #endregion y scale // #region root svg this.svg.transition().duration(durration).attr('width', wrapperWidth).attr('height', wrapperHeight); // #endregion root svg // #region grid x axis this.xGrid.transition().duration(durration).attr('transform', "translate(0, ".concat(height, ")")).call(d3.axisBottom().scale(xScale).tickSize(-height, 0, 0).tickFormat('')); // #endregion grid x axis // #region grid y axis this.yGrid.transition().duration(durration).attr('transform', "translate(".concat(width, ", 0)")).call(d3.axisRight().scale(yScale).tickSize(-width, 0, 0).ticks(4).tickFormat('')); // #endregion grid y axis const tickValues = xScale.domain().length > 20 ? xScale.domain().filter((datum, index) => !(index % 2)) : xScale.domain(); // #region x axis this.xAxis.transition().duration(durration).attr('transform', "translate(0, ".concat(height, ")")).call(d3.axisBottom().scale(xScale).tickValues(tickValues)); // #endregion x axis // #region y axis this.yAxis.transition().duration(durration).attr('transform', "translate(".concat(width, ", 0)")).call(d3.axisRight().scale(yScale).ticks(4)); // #endregion y axis // #region bars this.chart.selectAll('.bar').data(data).transition().delay((_, index) => resizeUpdate ? 0 : index * delay).duration(durration).attr('d', this.getBarDAttrFn(xScale, yScale, height, hThickness, thickness)); // #endregion bars }); _defineProperty(this, "getBarDAttrFn", (xScale, yScale, height, hThickness, thickness) => datum => "\n M".concat(xScale(datum.name), ",").concat(yScale(datum.value) + hThickness, "\n a").concat(hThickness, ",").concat(hThickness, " 0 0 1 ").concat(hThickness, ",").concat(-hThickness, "\n a").concat(hThickness, ",").concat(hThickness, " 0 0 1 ").concat(hThickness, ",").concat(hThickness, "\n v").concat(height - yScale(datum.value) - hThickness, "\n h").concat(-thickness, "Z\n ")); _defineProperty(this, "getXScale", (data, width) => d3.scaleBand().range([0, width]).domain(data.map(datum => datum.name)).paddingInner(1).paddingOuter(0.5)); _defineProperty(this, "getYScale", (data, height) => d3.scaleLinear().range([height, 0]).domain([0, d3.max(data, datum => Math.ceil(datum.value / 10) * 10)])); } componentDidMount() { this.createChart(); window.addEventListener('resize', this.onResize); window.addEventListener('scroll', this.onScroll); this.onScroll(); } componentWillUnmount() { clearTimeout(this.resizeTimeout); window.removeEventListener('resize', this.onResize); window.removeEventListener('scroll', this.onScroll); } render() { const { tooltipOpen, dataIndex } = this.state; const { data, title } = this.props; return /*#__PURE__*/_react.default.createElement(_react.Fragment, null, title && /*#__PURE__*/_react.default.createElement(_BarChart2.ChartTitle, null, title), /*#__PURE__*/_react.default.createElement(_BarChart2.Chart, { ref: el => { this.chartEl = el; } }, data[dataIndex].tooltip && /*#__PURE__*/_react.default.createElement(_BarChart2.TooltipWrapper, { ref: el => { this.tooltipEl = el; } }, /*#__PURE__*/_react.default.createElement(_reactTippy.Tooltip, { open: tooltipOpen, position: "top", animation: "fade", html: data[dataIndex].tooltip, theme: "light", arrow: true, duration: 200 })))); } } exports.BarChart = BarChart; BarChart.propTypes = { id: _propTypes.default.string.isRequired, title: _propTypes.default.string, data: _propTypes.default.arrayOf(_propTypes.default.shape({ value: _propTypes.default.number.isRequired, name: _propTypes.default.string, tooltip: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.node]) })).isRequired, colorTheme: _propTypes.default.oneOf(['default', 'red', 'blue']) }; BarChart.defaultProps = { title: '', colorTheme: 'default' }; var _default = exports.default = BarChart;