UNPKG

react-native-svg-charts

Version:

Customizable charts (Line, Bar, Area, Pie, Circle, Progress) for React Native

398 lines (365 loc) 11.2 kB
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import React, { PureComponent } from 'react'; import { View } from 'react-native'; import PropTypes from 'prop-types'; import Svg from 'react-native-svg'; import * as array from 'd3-array'; import * as scale from 'd3-scale'; import * as shape from 'd3-shape'; import Path from '../animated-path'; class StackedBarGrouped extends PureComponent { constructor(...args) { super(...args); _defineProperty(this, "state", { width: 0, height: 0 }); _defineProperty(this, "coordinatesToPathCommands", (x0, y0, x1, y1, borderRadius, showTopBorder, showBottomBorder) => { const commands = []; commands.push({ marker: 'M', values: [x0, y0] }); if (showTopBorder) { const topLeft1 = [x0 + borderRadius, y0]; const topLeft2 = [x0, y0 + borderRadius]; commands.push({ marker: 'L', values: topLeft1 }); commands.push({ marker: 'C', values: [...topLeft1, x0, y0, ...topLeft2] }); commands.push({ marker: 'L', values: topLeft2 }); } else { commands.push({ marker: 'L', values: [x0, y0] }); } if (showBottomBorder) { const bottomLeft1 = [x0, y1 - borderRadius]; const bottomLeft2 = [x0 + borderRadius, y1]; commands.push({ marker: 'L', values: bottomLeft1 }); commands.push({ marker: 'C', values: [...bottomLeft1, x0, y1, ...bottomLeft2] }); commands.push({ marker: 'L', values: bottomLeft2 }); const bottomRight1 = [x1 - borderRadius, y1]; const bottomRight2 = [x1, y1 - borderRadius]; commands.push({ marker: 'L', values: bottomRight1 }); commands.push({ marker: 'C', values: [...bottomRight1, x1, y1, ...bottomRight2] }); commands.push({ marker: 'L', values: bottomRight2 }); } else { commands.push({ marker: 'L', values: [x0, y1] }); commands.push({ marker: 'L', values: [x1, y1] }); } if (showTopBorder) { const topRight1 = [x1, y0 + borderRadius]; const topRight2 = [x1 - borderRadius, y0]; commands.push({ marker: 'L', values: topRight1 }); commands.push({ marker: 'C', values: [...topRight1, x1, y0, ...topRight2] }); commands.push({ marker: 'L', values: topRight2 }); } else { commands.push({ marker: 'L', values: [x1, y0] }); } commands.push({ marker: 'Z', values: [] }); return commands; }); _defineProperty(this, "commandsToSvgPath", commands => commands.map(command => "".concat(command.marker, " ").concat(command.values.join(','))).join(' ').trim()); } _onLayout(event) { const { nativeEvent: { layout: { height, width } } } = event; this.setState({ height, width }); } calcXScale(domain) { const { horizontal, contentInset: { left = 0, right = 0 }, spacingInner, spacingOuter } = this.props; const { width } = this.state; if (horizontal) { return scale.scaleLinear().domain(domain).range([left, width - right]); } return scale.scaleBand().domain(domain).range([left, width - right]).paddingInner([spacingInner]).paddingOuter([spacingOuter]); } calcYScale(domain) { const { horizontal, contentInset: { top = 0, bottom = 0 }, spacingInner, spacingOuter } = this.props; const { height } = this.state; if (horizontal) { return scale.scaleBand().domain(domain).range([top, height - bottom]).paddingInner([spacingInner]).paddingOuter([spacingOuter]); } return scale.scaleLinear().domain(domain).range([height - bottom, top]); } calcAreas(x, y, series) { const { horizontal, colors, keys, data, borderRadius: initialBorderRadius, innerBarSpace } = this.props; let areas; let barWidth; if (horizontal) { barWidth = y.bandwidth() / data.length; areas = series.map((stack, stackIndex) => { return stack.map((serie, keyIndex) => { return serie.map((entry, entryIndex) => { const leftMargin = series.length > 1 ? innerBarSpace / 2 : 0; const path = shape.area().x0(d => x(d[0])).x1(d => x(d[1])).y((d, _index) => (_index === 0 ? y(entryIndex) + barWidth * stackIndex + leftMargin : y(entryIndex) + barWidth + barWidth * stackIndex) - leftMargin).defined(d => !isNaN(d[0]) && !isNaN(d[1]))([entry, entry]); return { path, color: colors[stackIndex][keyIndex], key: keys[stackIndex][keyIndex] }; }); }); }); } else { barWidth = x.bandwidth() / data.length; areas = series.map((stack, stackIndex) => { return stack.map((serie, keyIndex) => { return serie.map((entry, entryIndex) => { const leftMargin = series.length > 1 ? innerBarSpace / 2 : 0; const x0 = x(entryIndex) + barWidth * stackIndex + leftMargin; const x1 = x(entryIndex) + barWidth + barWidth * stackIndex - leftMargin; const y0 = y(entry[1]); const y1 = y(entry[0]); const barHeight = y1 - y0; const borderRadius = initialBorderRadius * 2 > barHeight ? barHeight / 2 : initialBorderRadius; const showTopBorder = keyIndex === stack.length - 1; const showBottomBorder = keyIndex === 0; const commands = this.coordinatesToPathCommands(x0, y0, x1, y1, borderRadius, showTopBorder, showBottomBorder); return { path: this.commandsToSvgPath(commands), color: colors[stackIndex][keyIndex], key: keys[stackIndex][keyIndex] }; }); }); }); } return array.merge(areas); } calcExtent(values) { const { gridMax, gridMin } = this.props; // One more merge for stacked groups const mergedValues = array.merge(values); return array.extent([...mergedValues, gridMin, gridMax]); } calcIndexes() { const { data } = this.props; // Must return an array with indexes for the number of groups to be shown return data[0].data.map((_, index) => index); } getSeries() { const { data, keys, offset, order, valueAccessor } = this.props; return data.map((obj, index) => shape.stack().keys(keys[index]).value((item, key) => valueAccessor({ item, key })).order(order).offset(offset)(obj.data)); } render() { const { data, animate, animationDuration, style, numberOfTicks, children, horizontal } = this.props; const { height, width } = this.state; if (data.length === 0) { return /*#__PURE__*/React.createElement(View, { style: style }); } const series = this.getSeries(); //double merge arrays to extract just the values const values = array.merge(array.merge(series)); const indexes = this.calcIndexes(values); const extent = this.calcExtent(values); const ticks = array.ticks(extent[0], extent[1], numberOfTicks); const xDomain = horizontal ? extent : indexes; const yDomain = horizontal ? indexes : extent; const x = this.calcXScale(xDomain); const y = this.calcYScale(yDomain); const bandwidth = horizontal ? y.bandwidth() : x.bandwidth(); const stacks = this.calcAreas(x, y, series); const extraProps = { x, y, width, height, ticks, data, bandwidth }; return /*#__PURE__*/React.createElement(View, { style: style }, /*#__PURE__*/React.createElement(View, { style: { flex: 1 }, onLayout: event => this._onLayout(event) }, height > 0 && width > 0 && /*#__PURE__*/React.createElement(Svg, { style: { height, width } }, React.Children.map(children, child => { if (child && child.props.belowChart) { return React.cloneElement(child, extraProps); } return null; }), stacks.map((areas, indexStack) => { const areaIndex = indexStack % data.length; return areas.map((bar, indexArea) => { const keyIndex = indexArea % data[areaIndex].data.length; const key = "".concat(areaIndex, "-").concat(keyIndex, "-").concat(bar.key); const { svg } = data[areaIndex].data[keyIndex][bar.key]; return /*#__PURE__*/React.createElement(Path, _extends({ key: key, fill: bar.color }, svg, { d: bar.path, animate: animate, animationDuration: animationDuration })); }); }), React.Children.map(children, child => { if (child && !child.props.belowChart) { return React.cloneElement(child, extraProps); } return null; })))); } } StackedBarGrouped.propTypes = { data: PropTypes.arrayOf(PropTypes.object), keys: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)).isRequired, colors: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)).isRequired, offset: PropTypes.func, order: PropTypes.func, style: PropTypes.any, spacingInner: PropTypes.number, spacingOuter: PropTypes.number, animate: PropTypes.bool, animationDuration: PropTypes.number, contentInset: PropTypes.shape({ top: PropTypes.number, left: PropTypes.number, right: PropTypes.number, bottom: PropTypes.number }), gridMin: PropTypes.number, gridMax: PropTypes.number, valueAccessor: PropTypes.func, borderRadius: PropTypes.number, innerBarSpace: PropTypes.number }; StackedBarGrouped.defaultProps = { spacingInner: 0.05, spacingOuter: 0.05, offset: shape.stackOffsetNone, order: shape.stackOrderNone, width: 100, height: 100, showZeroAxis: true, contentInset: {}, numberOfTicks: 10, showGrid: true, valueAccessor: ({ item, key }) => item[key], borderRadius: 0, innerBarSpace: 0 }; export default StackedBarGrouped; //# sourceMappingURL=stacked-bar-grouped.js.map