UNPKG

zmp-react

Version:

Build full featured iOS & Android apps using ZMP & React

384 lines (338 loc) 12.8 kB
import _extends from "@babel/runtime/helpers/extends"; import React, { forwardRef, useRef, useImperativeHandle, useState, useEffect } from 'react'; import { classNames, getExtraAttrs, emit } from '../shared/utils'; import { zmp } from '../shared/zmp'; var AreaChart = /*#__PURE__*/forwardRef(function (props, ref) { var className = props.className, id = props.id, style = props.style, _props$lineChart = props.lineChart, lineChart = _props$lineChart === void 0 ? false : _props$lineChart, _props$datasets = props.datasets, datasets = _props$datasets === void 0 ? [] : _props$datasets, _props$axis = props.axis, axis = _props$axis === void 0 ? false : _props$axis, _props$axisLabels = props.axisLabels, axisLabels = _props$axisLabels === void 0 ? [] : _props$axisLabels, _props$tooltip = props.tooltip, tooltip = _props$tooltip === void 0 ? false : _props$tooltip, _props$legend = props.legend, legend = _props$legend === void 0 ? false : _props$legend, _props$toggleDatasets = props.toggleDatasets, toggleDatasets = _props$toggleDatasets === void 0 ? false : _props$toggleDatasets, _props$width = props.width, width = _props$width === void 0 ? 640 : _props$width, _props$height = props.height, height = _props$height === void 0 ? 320 : _props$height, _props$maxAxisLabels = props.maxAxisLabels, maxAxisLabels = _props$maxAxisLabels === void 0 ? 8 : _props$maxAxisLabels, formatAxisLabelProp = props.formatAxisLabel, formatLegendLabelProp = props.formatLegendLabel, formatTooltipProp = props.formatTooltip, formatTooltipAxisLabel = props.formatTooltipAxisLabel, formatTooltipTotal = props.formatTooltipTotal, formatTooltipDataset = props.formatTooltipDataset, children = props.children; var _useState = useState(null), currentIndex = _useState[0], setCurrentIndex = _useState[1]; var previousIndex = useRef(null); var _useState2 = useState([]), hiddenDatasets = _useState2[0], setHiddenDatasets = _useState2[1]; var extraAttrs = getExtraAttrs(props); var elRef = useRef(null); var svgElRef = useRef(null); var zmpTooltip = useRef(null); var linesOffsets = useRef(null); useImperativeHandle(ref, function () { return { el: elRef.current }; }); var getVisibleLegends = function getVisibleLegends() { if (!maxAxisLabels || axisLabels.length <= maxAxisLabels) return axisLabels; var skipStep = Math.ceil(axisLabels.length / maxAxisLabels); var filtered = axisLabels.filter(function (label, index) { return index % skipStep === 0; }); return filtered; }; var getSummValues = function getSummValues() { var summValues = []; datasets.filter(function (dataset, index) { return !hiddenDatasets.includes(index); }).forEach(function (_ref) { var values = _ref.values; values.forEach(function (value, valueIndex) { if (!summValues[valueIndex]) summValues[valueIndex] = 0; summValues[valueIndex] += value; }); }); return summValues; }; var getChartData = function getChartData() { var data = []; if (!datasets.length) { return data; } var lastValues = datasets[0].values.map(function () { return 0; }); var maxValue = 0; if (lineChart) { datasets.forEach(function (_ref2) { var values = _ref2.values; var datasetMaxValue = Math.max.apply(Math, values); if (datasetMaxValue > maxValue) maxValue = datasetMaxValue; }); } else { maxValue = Math.max.apply(Math, getSummValues()); } datasets.filter(function (dataset, index) { return !hiddenDatasets.includes(index); }).forEach(function (_ref3) { var label = _ref3.label, values = _ref3.values, color = _ref3.color; var points = values.map(function (originalValue, valueIndex) { lastValues[valueIndex] += originalValue; var value = lineChart ? originalValue : lastValues[valueIndex]; var x = valueIndex / (values.length - 1) * width; var y = height - value / maxValue * height; if (lineChart) { return "" + (valueIndex === 0 ? 'M' : 'L') + x + "," + y; } return x + " " + y; }); if (!lineChart) { points.push(width + " " + height + " 0 " + height); } data.push({ label: label, points: points.join(' '), color: color }); }); return data.reverse(); }; var getVerticalLines = function getVerticalLines() { var lines = []; if (!datasets.length) { return lines; } var values = datasets[0].values; values.forEach(function (value, valueIndex) { var x = valueIndex / (values.length - 1) * width; lines.push(x); }); return lines; }; var toggleDataset = function toggleDataset(index) { if (!toggleDatasets) return; if (hiddenDatasets.includes(index)) { hiddenDatasets.splice(hiddenDatasets.indexOf(index), 1); } else { hiddenDatasets.push(index); } setHiddenDatasets([].concat(hiddenDatasets)); }; var formatAxisLabel = function formatAxisLabel(label) { if (formatAxisLabelProp) return formatAxisLabelProp(label); return label; }; var formatLegendLabel = function formatLegendLabel(label) { if (formatLegendLabelProp) return formatLegendLabelProp(label); return label; }; var calcLinesOffsets = function calcLinesOffsets() { var lines = svgElRef.current.querySelectorAll('line'); linesOffsets.current = []; for (var i = 0; i < lines.length; i += 1) { linesOffsets.current.push(lines[i].getBoundingClientRect().left); } }; var formatTooltip = function formatTooltip() { if (currentIndex === null) return ''; var total = 0; var currentValues = datasets.filter(function (dataset, index) { return !hiddenDatasets.includes(index); }).map(function (dataset) { return { color: dataset.color, label: dataset.label, value: dataset.values[currentIndex] }; }); currentValues.forEach(function (dataset) { total += dataset.value; }); if (formatTooltipProp) { return formatTooltipProp({ index: currentIndex, total: total, datasets: currentValues }); } var labelText = formatTooltipAxisLabel ? formatTooltipAxisLabel(axisLabels[currentIndex]) : formatAxisLabel(axisLabels[currentIndex]); if (!labelText) labelText = ''; var totalText = formatTooltipTotal ? formatTooltipTotal(total) : total; // prettier-ignore var datasetsText = currentValues.length > 0 ? "\n <ul class=\"area-chart-tooltip-list\">\n " + currentValues.map(function (_ref4) { var label = _ref4.label, color = _ref4.color, value = _ref4.value; var valueText = formatTooltipDataset ? formatTooltipDataset(label, value, color) : label + ": " + value; return "\n <li><span style=\"background-color: " + color + ";\"></span>" + valueText + "</li>\n "; }).join('') + "\n </ul>" : ''; // prettier-ignore return "\n <div class=\"area-chart-tooltip-label\">" + labelText + "</div>\n <div class=\"area-chart-tooltip-total\">" + totalText + "</div>\n " + datasetsText + "\n "; }; var setTooltip = function setTooltip() { if (!tooltip) return; var hasVisibleDataSets = datasets.filter(function (dataset, index) { return !hiddenDatasets.includes(index); }).length > 0; if (!hasVisibleDataSets) { if (zmpTooltip.current && zmpTooltip.current.hide) zmpTooltip.current.hide(); return; } if (currentIndex !== null && !zmpTooltip.current) { zmpTooltip.current = zmp.tooltip.create({ trigger: 'manual', containerEl: elRef.current, targetEl: svgElRef.current.querySelector("line[data-index=\"" + currentIndex + "\"]"), text: formatTooltip(), cssClass: 'area-chart-tooltip' }); if (zmpTooltip.current && zmpTooltip.current.show) { zmpTooltip.current.show(); } return; } if (!zmpTooltip.current || !zmpTooltip.current.hide || !zmpTooltip.current.show) { return; } if (currentIndex !== null) { zmpTooltip.current.setText(formatTooltip()); zmpTooltip.current.setTargetEl(svgElRef.current.querySelector("line[data-index=\"" + currentIndex + "\"]")); zmpTooltip.current.show(); } else { zmpTooltip.current.hide(); } }; var onMouseEnter = function onMouseEnter() { calcLinesOffsets(); }; var onMouseMove = function onMouseMove(e) { if (!linesOffsets.current) { calcLinesOffsets(); } var currentLeft = e.pageX; if (typeof currentLeft === 'undefined') currentLeft = 0; var distances = linesOffsets.current.map(function (left) { return Math.abs(currentLeft - left); }); var minDistance = Math.min.apply(Math, distances); var closestIndex = distances.indexOf(minDistance); setCurrentIndex(closestIndex); }; var onMouseLeave = function onMouseLeave() { setCurrentIndex(null); }; var attachEvents = function attachEvents() { if (!svgElRef.current) return; svgElRef.current.addEventListener('mouseenter', onMouseEnter); svgElRef.current.addEventListener('mousemove', onMouseMove); svgElRef.current.addEventListener('mouseleave', onMouseLeave); }; var detachEvents = function detachEvents() { if (!svgElRef.current) return; svgElRef.current.removeEventListener('mouseenter', onMouseEnter); svgElRef.current.removeEventListener('mousemove', onMouseMove); svgElRef.current.removeEventListener('mouseleave', onMouseLeave); }; useEffect(function () { if (previousIndex.current === currentIndex) return; previousIndex.current = currentIndex; emit(props, 'select', currentIndex); setTooltip(); }, [currentIndex]); useEffect(function () { attachEvents(); return detachEvents; }); useEffect(function () { return function () { if (zmpTooltip.current && zmpTooltip.current.destroy) { zmpTooltip.current.destroy(); } zmpTooltip.current = null; }; }, []); var classes = classNames('area-chart', className); var chartData = getChartData(); var verticalLines = getVerticalLines(); var visibleLegends = getVisibleLegends(); var LegendItemTag = toggleDatasets ? 'button' : 'span'; var ChartTag = lineChart ? 'path' : 'polygon'; return /*#__PURE__*/React.createElement("div", _extends({ id: id, style: style, className: classes, ref: elRef }, extraAttrs), /*#__PURE__*/React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: width, height: height, viewBox: "0 0 " + width + " " + height, preserveAspectRatio: "none", ref: svgElRef }, chartData.map(function (data, index) { return /*#__PURE__*/React.createElement(ChartTag, { key: ChartTag + "-" + index, fill: lineChart ? undefined : data.color, stroke: lineChart ? data.color : undefined, fillRule: "evenodd", points: lineChart ? undefined : data.points, d: lineChart ? data.points : undefined }); }), verticalLines.map(function (line, index) { return /*#__PURE__*/React.createElement("line", { key: "line-" + index, "data-index": index, fill: "#000", x1: line, y1: 0, x2: line, y2: height, className: classNames({ 'area-chart-current-line': currentIndex === index }) }); })), axis && /*#__PURE__*/React.createElement("div", { className: "area-chart-axis" }, axisLabels.map(function (label, index) { return /*#__PURE__*/React.createElement("span", { key: index }, visibleLegends.includes(label) && /*#__PURE__*/React.createElement("span", null, formatAxisLabel(label))); })), legend && /*#__PURE__*/React.createElement("div", { className: "area-chart-legend" }, datasets.map(function (dataset, index) { return /*#__PURE__*/React.createElement(LegendItemTag, { key: index, className: classNames('area-chart-legend-item', { 'area-chart-legend-item-hidden': hiddenDatasets.includes(index), 'area-chart-legend-button': toggleDatasets }), type: toggleDatasets ? 'button' : undefined, onClick: function onClick() { return toggleDataset(index); } }, /*#__PURE__*/React.createElement("span", { style: { backgroundColor: dataset.color } }), formatLegendLabel(dataset.label)); })), children); }); AreaChart.displayName = 'zmp-area-chart'; export default AreaChart;