Skip to content

Instantly share code, notes, and snippets.

@duhseekoh
Last active March 28, 2018 14:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save duhseekoh/07f91617a744b94eb739988c221de1d2 to your computer and use it in GitHub Desktop.
Save duhseekoh/07f91617a744b94eb739988c221de1d2 to your computer and use it in GitHub Desktop.
React Native - StackedBarChart
// Usage of the StackedBarChart.jsx
// @flow
import React, { Component } from 'react';
import {
View,
StyleSheet
} from 'react-native';
import { StackedBarChart, TRAText, TRATextStrong } from 'app/components';
import styleVars from 'app/style/variables';
import Icon from 'react-native-vector-icons/FontAwesome';
type Props = {
barGroups: {
key: string,
values: {
amount: number,
category: string,
},
},
categoryColors: {
[string]: string
},
}
type State = {
chartWidth: number,
};
class SalesStackedBarChart extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
chartWidth: 375
}
}
_onLayout = ({nativeEvent}) => {
this.setState({
chartWidth: nativeEvent.layout.width // allows svg chart to grow based on container element
});
}
render() {
const { barGroups, categoryColors } = this.props;
const { chartWidth } = this.state;
return (
<View style={styles.container} onLayout={this._onLayout}>
<TRAText style={styles.timeSelector}>2016 <Icon name='angle-down' /></TRAText>
<StackedBarChart
barGroups={barGroups}
categoryColors={categoryColors}
chartWidth={chartWidth}
/>
<View style={styles.summaryHolder}>
{this._renderCategorySummary('$456k', 'SOLD', categoryColors)}
{this._renderCategorySummary('$89k', 'CONTRACT', categoryColors)}
{this._renderCategorySummary('$30k', 'APPROACH', categoryColors)}
</View>
</View>
)
}
_renderCategorySummary(amountText, category, categoryColors) {
return (
<View style={styles.categorySummary}>
<TRAText style={styles.categoryTotal}>{amountText}</TRAText>
<TRATextStrong style={[
styles.categoryText,
{color: categoryColors[category]}
]}
>
{category}
</TRATextStrong>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
margin: styleVars.padding.edgeOfScreen
},
timeSelector: {
color: styleVars.color.five,
fontSize: styleVars.fontSize.small
},
summaryHolder: {
marginTop: 10,
flexDirection: 'row'
},
categorySummary: {
flex: 1
},
categoryTotal: {
fontSize: styleVars.fontSize.xlarge,
textAlign: 'center'
},
categoryText: {
fontSize: styleVars.fontSize.medium,
textAlign: 'center'
}
});
export default SalesStackedBarChart;
// @flow
import React from 'react';
import {
StyleSheet
} from 'react-native';
import Svg, {
Rect,
G,
Text
} from 'react-native-svg';
type Props = {
barGroups: {
key: string,
values: {
amount: number,
category: string,
}[],
}[],
categoryColors: {
[string]: string
},
chartHeight?: number,
chartWidth?: number,
keyTextColor?: string,
};
const StackedBarChart = ({barGroups, categoryColors, chartHeight = 300, chartWidth = 375}: Props) => {
const barAreaHeight = chartHeight - 20, // room for key text below bars
barAreaSectionWidth = chartWidth / barGroups.length,
barWidth = barAreaSectionWidth * .8, // bars take up 80% of space
barXPadding = barAreaSectionWidth * .2, // padding takes up 20% of space (between bars)
maxBarGroupValue = _calculateMaxBarGroupValue(barGroups);
let nextX = barXPadding / 2; // first item offset
return (
<Svg height={chartHeight} width={chartWidth}>
{
barGroups.map((barGroup) => {
const x = nextX;
nextX = x + barWidth + barXPadding;
return (
_renderBarGroup(barGroup, x, barAreaHeight, barWidth, maxBarGroupValue, categoryColors)
)
})
}
</Svg>
);
}
function _calculateMaxBarGroupValue(barGroups) {
let maxBarGroupValue = 0;
barGroups.forEach((barGroup) => {
let valueSum = barGroup.values.reduce((accumulator, {amount}) => {
return accumulator + amount;
}, 0);
maxBarGroupValue = valueSum > maxBarGroupValue ?
valueSum :
maxBarGroupValue;
});
return maxBarGroupValue;
}
function _renderBarGroup( {key, values}, x, areaHeight, barWidth, maxBarGroupValue, categoryColors) {
let nextY = areaHeight; //start at bottom
return (
<G key={x} width={150}>
{
values.map( ({amount, category}, idx) => {
const fractionOfMax = amount / maxBarGroupValue;
const height = areaHeight * fractionOfMax;
const y = nextY;
nextY = y - height; // work way up from bottom
// console.log(`y: ${y} height: ${height}`);
return (
<G key={idx}>
<Rect key={idx}
fill={categoryColors[category]} x={x} y={y}
width={barWidth} height={-height}>
</Rect>
</G>
);
}
)
}
<Text
fill={'#999'}
stroke='none'
fontSize='12'
x={x + barWidth/2}
y={areaHeight + 3}
textAnchor='middle'
>
{key}
</Text>
</G>
);
}
const styles = StyleSheet.create({
});
export default StackedBarChart;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment