UNPKG

@semcore/chart

Version:
429 lines (428 loc) 18.4 kB
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _objectSpread from "@babel/runtime/helpers/objectSpread2"; 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 _createSuper from "@babel/runtime/helpers/createSuper"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import { sstyled as _sstyled2 } from "@semcore/core"; import { sstyled as _sstyled } from "@semcore/core"; var _excluded = ["className", "width", "height", "style"]; import React from 'react'; import { Surface } from 'recharts'; import { findAllByType, findChildByType, getPresentationAttributes, isChildrenEqual, validateWidthHeight } from "recharts/es6/util/ReactUtils"; import { intersectionArea, intersectionAreaPath, normalizeSolution, scaleSolution, venn } from '@upsetjs/venn.js'; import { Tooltip } from '../../Tooltip'; import VennArea from './VennArea'; import VennIntersection from './VennIntersection'; import fire from '@semcore/utils/lib/fire'; import assignProps from '@semcore/utils/lib/assignProps'; import { sstyled } from '@semcore/core'; import cn from 'classnames'; /*__reshadow-styles__:"./style/venn-tooltip-label.shadow.css"*/ var styles = ( /*__reshadow_css_start__*/_sstyled.insert( /*__inner_css_start__*/".___STooltipLabel_1te07_gg_{display:inline-flex;justify-content:space-between;width:100%;padding-bottom:var(--intergalactic-spacing-2x, 8px);border-bottom:1px solid var(--intergalactic-border-primary, #c4c7cf);color:var(--intergalactic-text-primary, #191b23);font-size:var(--intergalactic-fs-100, 12px);line-height:var(--intergalactic-lh-100, 133%)}.___STooltipLabelTitle_1te07_gg_{margin-right:var(--intergalactic-spacing-4x, 16px)}.___STooltipLabelPercentage_1te07_gg_{margin-right:var(--intergalactic-spacing-3x, 12px);color:var(--intergalactic-text-secondary, #6c6e79)}.___STooltipLabelValue_1te07_gg_{font-weight:var(--intergalactic-bold, Bold)}" /*__inner_css_end__*/, "1te07_gg_") /*__reshadow_css_end__*/, { "__STooltipLabel": "___STooltipLabel_1te07_gg_", "__STooltipLabelTitle": "___STooltipLabelTitle_1te07_gg_", "__STooltipLabelPercentage": "___STooltipLabelPercentage_1te07_gg_", "__STooltipLabelValue": "___STooltipLabelValue_1te07_gg_" }); /*__reshadow-styles__:"../../style/chart.shadow.css"*/ var chartStyles = ( /*__reshadow_css_start__*/_sstyled2.insert( /*__inner_css_start__*/".___SChart_z2wr8_gg_ .recharts-label{text-anchor:middle;fill:var(--intergalactic-text-hint, #6c6e79)}.___SChart_z2wr8_gg_ .recharts-cartesian-axis-tick-value{fill:var(--intergalactic-chart-grid-text-label, #6c6e79);font-size:12px;line-height:1.1}.___SChart_z2wr8_gg_ .recharts-default-tooltip{padding:12px;margin:0;background-color:var(--intergalactic-bg-primary-neutral, #ffffff);white-space:nowrap;border:1px solid var(--intergalactic-border-secondary, #e0e1e9);box-shadow:var(--intergalactic-box-shadow-popper, 0px 1px 12px 0px rgba(25, 27, 35, 0.15));border-radius:var(--intergalactic-popper-rounded, 6px)}.___SChart_z2wr8_gg_ .recharts-tooltip-item{display:flex;align-items:center;justify-content:space-between;font-size:12px;color:var(--intergalactic-text-primary, #191b23);padding:4px 0}.___SChart_z2wr8_gg_ .recharts-tooltip-label{font-size:12px;color:var(--intergalactic-text-secondary, #6c6e79);margin-top:0;margin-bottom:var(--intergalactic-spacing-2x, 8px)}.___SChart_z2wr8_gg_ .recharts-tooltip-label:empty{margin-bottom:0}.___SChart_z2wr8_gg_ .recharts-tooltip-item-value-wrapper{margin-left:var(--intergalactic-spacing-3x, 12px)}.___SChart_z2wr8_gg_ .recharts-tooltip-item-value{font-weight:var(--intergalactic-bold, Bold)}.___SChart_z2wr8_gg_ .recharts-tooltip-item-color{display:inline-block;margin-right:var(--intergalactic-spacing-2x, 8px);width:8px;height:8px;border-radius:50%}.___SChart_z2wr8_gg_ .recharts-area-dot{fill-opacity:1}" /*__inner_css_end__*/, "z2wr8_gg_") /*__reshadow_css_end__*/, { "__SChart": "___SChart_z2wr8_gg_" }); /** * @deprecated Please, use package `@semcore/ui/d3-chart` instead. Package `@semcore/chart` is deprecated. */ var VennChart = /*#__PURE__*/function (_React$PureComponent) { _inherits(VennChart, _React$PureComponent); var _super = _createSuper(VennChart); function VennChart(props) { var _this; _classCallCheck(this, VennChart); _this = _super.call(this, props); _defineProperty(_assertThisInitialized(_this), "circles", void 0); _defineProperty(_assertThisInitialized(_this), "circlesLayout", void 0); _defineProperty(_assertThisInitialized(_this), "handleMouseEnter", function (nodeProps) { return function (e) { var children = _this.state.children; var tooltip = findChildByType(children, Tooltip); _this.setState({ activeNode: nodeProps, isTooltipActive: !!tooltip }, function () { return fire(_assertThisInitialized(_this), 'onMouseEnter', e, _this.generatePayload()); }); }; }); _defineProperty(_assertThisInitialized(_this), "handleMouseLeave", function (e) { _this.setState({ activeNode: null, isTooltipActive: false }, function () { fire(_assertThisInitialized(_this), 'onMouseLeave', e, _this.generatePayload()); }); }); _defineProperty(_assertThisInitialized(_this), "handleClick", function (e) { fire(_assertThisInitialized(_this), 'onClick', e, _this.generatePayload()); }); _defineProperty(_assertThisInitialized(_this), "dataToCirclesLayoutObj", function () { var actualData = _this.state.actualData; var _this$props = _this.props, width = _this$props.width, height = _this$props.height, padding = _this$props.padding, orientation = _this$props.orientation, orientationOrder = _this$props.orientationOrder, minAreaRadius = _this$props.minAreaRadius; if (actualData.length === 0) { _this.circles = {}; return null; } var circles = venn(actualData); var normalisedCircles = normalizeSolution(circles, orientation, orientationOrder); var scaledCircles = scaleSolution(normalisedCircles, width, height, padding); Object.keys(scaledCircles).forEach(function (key) { var circleRadius = scaledCircles[key].radius; scaledCircles[key].radius = Math.max(minAreaRadius, circleRadius); //@ts-ignore scaledCircles[key].data = getElementDataByKey(actualData, key); }); //@ts-ignore _this.circles = scaledCircles; }); _defineProperty(_assertThisInitialized(_this), "renderCircle", function (circleProps) { var activeNode = _this.state.activeNode; var x = circleProps.x, y = circleProps.y, radius = circleProps.radius, data = circleProps.data, name = circleProps.name, node = circleProps.node; var isNodeActive = activeNode && name === activeNode.name; var nodeProps = _objectSpread(_objectSpread({}, node.props), {}, { cx: x, cy: y, r: radius, key: "venn-area-".concat(name), size: data.size, active: isNodeActive, data: data }); var nodeHandlers = { onMouseEnter: _this.handleMouseEnter(nodeProps), onMouseLeave: _this.handleMouseLeave, onClick: _this.handleClick }; return /*#__PURE__*/React.cloneElement(node, assignProps(nodeProps, nodeHandlers)); }); _defineProperty(_assertThisInitialized(_this), "renderIntersection", function (intersectionProps) { var sets = intersectionProps.sets; var activeNode = _this.state.activeNode; var isNodeActive = (activeNode === null || activeNode === void 0 ? void 0 : activeNode.sets) && stringArrayIsEqual(sets, activeNode.sets); var nodeHandlers = { onMouseEnter: _this.handleMouseEnter(intersectionProps), onMouseLeave: _this.handleMouseLeave, onClick: _this.handleClick }; var nodeProps = _objectSpread({ active: isNodeActive, key: "venn-intersection-".concat(sets.join('-')) }, assignProps(intersectionProps, nodeHandlers)); return /*#__PURE__*/React.createElement(VennIntersection, nodeProps); }); _defineProperty(_assertThisInitialized(_this), "formatTooltipLabel", function (label, payload) { var _ref; var tooltipLabelIntersectionSizeFormatter = _this.props.tooltipLabelIntersectionSizeFormatter; if (!payload || payload.length < 2) { return null; } var totalValue = payload.reduce(function (acc, item) { return acc + item.payload.size; }, 0); var sets = payload.reduce(function (acc, item) { return acc.concat(item.payload.sets); }, []); var _payload$0$data$find = payload[0].data.find(function (dataItem) { return sets.every(function (set) { return dataItem.sets.includes(set); }); }), size = _payload$0$data$find.size; var percent = size / (totalValue / 100); var finalPercentage = percent < 1 ? '< 1' : percent.toFixed(0); var STooltipLabel = 'span'; var STooltipLabelTitle = 'span'; var STooltipLabelPercentage = 'span'; var STooltipLabelValue = 'span'; return _ref = sstyled(styles), /*#__PURE__*/React.createElement(STooltipLabel, _ref.cn("STooltipLabel", {}), /*#__PURE__*/React.createElement(STooltipLabelTitle, _ref.cn("STooltipLabelTitle", {}), label), /*#__PURE__*/React.createElement(STooltipLabelPercentage, _ref.cn("STooltipLabelPercentage", {}), finalPercentage, "%"), /*#__PURE__*/React.createElement(STooltipLabelValue, _ref.cn("STooltipLabelValue", {}), tooltipLabelIntersectionSizeFormatter(size))); }); var _data = props.data, _children = props.children; _this.state = { data: _data, children: _children, actualData: VennChart.getActualData(props), activeNode: null, isTooltipActive: false }; return _this; } _createClass(VennChart, [{ key: "getCirclesLayout", value: function getCirclesLayout() { var children = this.state.children; // tslint:disable-next-line:no-this-assignment var circles = this.circles; var circleItems = findAllByType(children, VennArea); this.circlesLayout = Object.keys(circles).map(function (circle) { var circleName = circles[circle].data.name; var circleNode = circleItems.find(function (_ref3) { var props = _ref3.props; return circleName === props.name; }); return _objectSpread({ name: circleName, node: circleNode }, circles[circle]); }); } }, { key: "renderCircles", value: function renderCircles() { return this.circlesLayout.map(this.renderCircle); } }, { key: "getIntersectionsLayout", value: function getIntersectionsLayout() { var _this2 = this; var actualData = this.state.actualData; var circlesWithIntersections = actualData.filter(function (item) { return item.sets.length > 1; }); return circlesWithIntersections.map(function (intersection) { var sets = intersection.sets, name = intersection.name, size = intersection.size; var circleNodes = sets.map(function (set) { return _this2.circlesLayout.find(function (circle) { return circle.data.sets[0] === set; }); }); var path = intersectionAreaPath(circleNodes); return { d: path, circles: circleNodes, name: name, size: size, sets: sets }; }); } }, { key: "renderIntersections", value: function renderIntersections() { var intersections = this.getIntersectionsLayout(); return intersections.map(this.renderIntersection); } }, { key: "getAreaTooltipCoordinate", value: function getAreaTooltipCoordinate(activeNode) { if (!activeNode || !activeNode.cx || !activeNode.cy) { return null; } return { x: activeNode.cx + activeNode.r, y: activeNode.cy + activeNode.r / 2 }; } }, { key: "getIntersectionTooltipCoordinate", value: function getIntersectionTooltipCoordinate(activeNode) { if (!activeNode || !activeNode.circles) { return null; } var stats = {}; intersectionArea(activeNode.circles, stats); // @ts-ignore var innerPoints = stats.innerPoints; return innerPoints.reduce(function (acc, _ref4) { var x = _ref4.x, y = _ref4.y; if (x > acc.x) { acc.x = x; } if (y > acc.y) { acc.y = y; } return acc; }, { x: 0, y: 0 }); } }, { key: "generatePayload", value: function generatePayload() { var data = this.props.data; var activeNode = this.state.activeNode; if (!activeNode) { return []; } return activeNode.name ? [{ payload: activeNode.data, name: activeNode.name, value: activeNode.size, fill: activeNode.fill, data: data }] : activeNode.circles.map(function (circle) { return { payload: circle.data, // @ts-ignore name: circle.name, value: circle.data.size, // @ts-ignore fill: circle.node.props.fill, data: data }; }); } }, { key: "renderTooltip", value: function renderTooltip() { var children = this.props.children; var tooltipItem = findChildByType(children, Tooltip); if (!tooltipItem) { return null; } var _this$props2 = this.props, width = _this$props2.width, height = _this$props2.height; var _this$state = this.state, isTooltipActive = _this$state.isTooltipActive, activeNode = _this$state.activeNode; var viewBox = { x: 0, y: 0, width: width, height: height }; var coordinate = this.getAreaTooltipCoordinate(activeNode) || this.getIntersectionTooltipCoordinate(activeNode); var payload = isTooltipActive ? this.generatePayload() : []; return /*#__PURE__*/React.cloneElement(tooltipItem, _objectSpread(_objectSpread({ label: 'Overlap', labelFormatter: this.formatTooltipLabel }, tooltipItem.props), {}, { viewBox: viewBox, active: isTooltipActive, coordinate: coordinate, payload: payload })); } }, { key: "render", value: function render() { var _ref2; if (!validateWidthHeight(this)) { return null; } this.dataToCirclesLayoutObj(); // elegant crutch 🤷‍ this.getCirclesLayout(); var _this$props3 = this.props, className = _this$props3.className, width = _this$props3.width, height = _this$props3.height, style = _this$props3.style, other = _objectWithoutProperties(_this$props3, _excluded); var attrs = getPresentationAttributes(other); var SChart = 'div'; return _ref2 = sstyled(chartStyles), /*#__PURE__*/React.createElement(SChart, _ref2.cn("SChart", { "className": cn('recharts-wrapper', className), "style": _objectSpread({ position: 'relative', cursor: 'default', width: width, height: height }, style) }), /*#__PURE__*/React.createElement(Surface, _ref2.cn("Surface", _objectSpread(_objectSpread({}, attrs), {}, { "width": width, "height": height })), this.renderCircles(), this.renderIntersections()), this.renderTooltip()); } }], [{ key: "getActualData", value: function getActualData(props) { var data = props.data, children = props.children; var circleItems = findAllByType(children, VennArea); // Mapping data to children to define actual sets var actualSets = circleItems.reduce(function (acc, circle) { if (circle.props.hidden) { return acc; } var name = circle.props.name; var circleDataItem = data.find(function (item) { return item.name === name; }); if (!circleDataItem || circleDataItem.sets.length > 1) { return acc; } return [].concat(_toConsumableArray(acc), _toConsumableArray(circleDataItem.sets)); }, []); // filtering data by actual sets return data.filter(function (item) { return item.sets.every(function (set) { return actualSets.includes(set); }); }); } }, { key: "getDerivedStateFromProps", value: function getDerivedStateFromProps(props, state) { var children = props.children, data = props.data; var activeNode = state.activeNode, isTooltipActive = state.isTooltipActive; if (data !== state.data || !isChildrenEqual(children, state.children)) { var actualData = VennChart.getActualData(props); return { data: data, children: children, actualData: actualData, activeNode: activeNode, isTooltipActive: isTooltipActive }; } return null; } }]); return VennChart; }(React.PureComponent); _defineProperty(VennChart, "displayName", 'VennChart'); _defineProperty(VennChart, "defaultProps", { padding: 2, orientation: Math.PI / 2, orientationOrder: function orientationOrder(c1, c2) { return c2.radius - c1.radius; }, minAreaRadius: 6, tooltipLabelIntersectionSizeFormatter: function tooltipLabelIntersectionSizeFormatter(v) { return v; } }); export { VennChart as default }; function stringArrayIsEqual(arr1, arr2) { if (arr1.length !== arr2.length) { return false; } return arr1.every(function (item) { return arr2.includes(item); }); } function getElementDataByKey(data, key) { return data.find(function (el) { return el.sets.length === 1 && el.sets[0] === key; }); } //# sourceMappingURL=VennChart.js.map