react-native-svg-charts
Version:
Customizable charts (Line, Bar, Area, Pie, Circle, Progress) for React Native
277 lines (249 loc) • 7.42 kB
JavaScript
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 * as array from 'd3-array';
import * as scale from 'd3-scale';
import * as shape from 'd3-shape';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import { View } from 'react-native';
import Svg from 'react-native-svg';
import Path from '../animated-path';
class BarChart extends PureComponent {
constructor(...args) {
super(...args);
_defineProperty(this, "state", {
width: 0,
height: 0
});
}
static extractDataPoints(data, keys, order = shape.stackOrderNone, offset = shape.stackOffsetNone) {
const series = shape.stack().keys(keys).order(order).offset(offset)(data); //double merge arrays to extract just the values
return array.merge(array.merge(series));
}
_onLayout(event) {
const {
nativeEvent: {
layout: {
height,
width
}
}
} = event;
this.setState({
height,
width
});
}
calcXScale(domain) {
const {
data
} = this.props;
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]);
} // use index as domain identifier instead of value since
// domain must be same length as number of bars
// same value can occur at several places in data
return scale.scaleBand().domain(data.map((_, index) => index)).range([left, width - right]).paddingInner([spacingInner]).paddingOuter([spacingOuter]);
}
calcYScale(domain) {
const {
data
} = this.props;
const {
horizontal,
contentInset: {
top = 0,
bottom = 0
},
spacingInner,
spacingOuter
} = this.props;
const {
height
} = this.state;
if (horizontal) {
return scale.scaleBand().domain(data.map((_, index) => index)).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
} = this.props;
if (horizontal) {
return array.merge(series.map((serie, keyIndex) => {
return serie.map((entry, entryIndex) => {
const path = shape.area().x0(d => x(d[0])).x1(d => x(d[1])).y((d, _index) => _index === 0 ? y(entryIndex) : y(entryIndex) + y.bandwidth()).defined(d => !isNaN(d[0]) && !isNaN(d[1]))([entry, entry]);
return {
path,
color: colors[keyIndex],
key: keys[keyIndex]
};
});
}));
}
return array.merge(series.map((serie, keyIndex) => {
return serie.map((entry, entryIndex) => {
const path = shape.area().y0(d => y(d[0])).y1(d => y(d[1])).x((d, _index) => _index === 0 ? x(entryIndex) : x(entryIndex) + x.bandwidth()).defined(d => !isNaN(d[0]) && !isNaN(d[1]))([entry, entry]);
return {
path,
color: colors[keyIndex],
key: keys[keyIndex]
};
});
}));
}
calcExtent(values) {
const {
gridMax,
gridMin
} = this.props;
return array.extent([...values, gridMin, gridMax]);
}
calcIndexes(values) {
return values.map((_, index) => index);
}
getSeries() {
const {
data,
keys,
offset,
order,
valueAccessor
} = this.props;
return shape.stack().keys(keys).value((item, key) => valueAccessor({
item,
key
})).order(order).offset(offset)(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 areas = 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;
}), areas.map((bar, index) => {
const keyIndex = index % data.length;
const key = "".concat(keyIndex, "-").concat(bar.key);
const {
svg
} = 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;
}))));
}
}
BarChart.propTypes = {
data: PropTypes.arrayOf(PropTypes.object),
keys: PropTypes.arrayOf(PropTypes.string).isRequired,
colors: 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
};
BarChart.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]
};
export default BarChart;
//# sourceMappingURL=stacked-bar-chart.js.map