Allowed negative values for Line and Area charts

This commit is contained in:
abhinandan-kushwaha 2022-03-08 03:22:31 +05:30
parent 450043ff83
commit 2d1b834f3c
5 changed files with 196 additions and 72 deletions

29
App.js
View File

@ -548,17 +548,6 @@ const App = () => {
</View>
);
};
const barData = [
{
value: 15,
label: 'Mon',
},
{value: 30, label: 'Tue'},
{value: 26, label: 'Wed'},
{value: 40, label: 'Thu'},
{value: 16, label: 'Wed'},
{value: 40, label: 'Thu'},
];
const styleObject = {
marginLeft: -95,
@ -600,6 +589,18 @@ const App = () => {
{value: 190},
]);
const barData = [
{
value: 15,
label: 'Mon',
},
{value: 30, label: 'Tue'},
{value: -23, label: 'Wed'},
{value: 40, label: 'Thu'},
{value: -16, label: 'Wed'},
{value: 40, label: 'Thu'},
];
return (
<View
style={{
@ -607,8 +608,8 @@ const App = () => {
// marginLeft: 46,
marginLeft: 20,
}}>
{/* <BarChart backgroundColor={'green'} data={barData} /> */}
<BarChart
<BarChart yAxisSide='right' width={260} scrollToEnd curved data={barData} />
{/* <BarChart
// isAnimated={true}
isThreeD={true}
data={dtt}
@ -634,7 +635,7 @@ const App = () => {
// noOfSections={4}
// innerCircleBorderColor={'gray'}
// showTextBackground={true}
/>
/> */}
<TouchableOpacity
onPress={()=>{dtt[1].value+=20;setDtt([...dtt])}}>
<Text>Press me</Text>

View File

@ -10,7 +10,9 @@
| width | number | Width of the Bar chart | width of the parent |
| height | number | Height of the Bar chart (excluding the bottom label) | 200 |
| maxValue | number | Maximum value shown in the Y axis | 200 |
| minValue | number | Minimum negative value shown in the Y axis (to be used only if the data set has negative values too) | \_ |
| noOfSections | number | Number of sections in the Y axis | 10 |
| noOfSectionsBelowXAxis | number | Number of sections in the Y axis below X axis (in case the data set has negative values too) | 0 |
| stepValue | number | Value of 1 step/section in the Y axis | 20 |
| stepHeight | number | Height of 1 step/section in the Y axis | 20 |
| spacing | number | Distance between 2 consecutive bars in the Bar chart | 20 |

View File

@ -1,6 +1,6 @@
{
"name": "react-native-gifted-charts",
"version": "1.0.5",
"version": "1.0.6",
"description": "The most complete library for Bar, Line, Area, Pie and Donut charts in React Native. Allows 2D, 3D, gradient, animations and live data updates.",
"main": "src/index.tsx",
"files": [

View File

@ -235,7 +235,8 @@ export const BarChart = (props: PropTypes) => {
const labelsExtraHeight = props.labelsExtraHeight || 0;
let totalWidth = spacing;
let maxItem = 0, minItem = 0;
let maxItem = 0,
minItem = 0;
if (props.stackData) {
props.stackData.forEach(stackItem => {
// console.log('stackItem', stackItem);
@ -247,7 +248,7 @@ export const BarChart = (props: PropTypes) => {
if (stackSum > maxItem) {
maxItem = stackSum;
}
if(stackSum < minItem){
if (stackSum < minItem) {
minItem = stackSum;
}
totalWidth +=
@ -259,7 +260,7 @@ export const BarChart = (props: PropTypes) => {
if (item.value > maxItem) {
maxItem = item.value;
}
if(item.value < minItem){
if (item.value < minItem) {
minItem = item.value;
}
totalWidth +=
@ -273,7 +274,7 @@ export const BarChart = (props: PropTypes) => {
maxItem = maxItem + (10 - (maxItem % 10));
maxItem /= 10 * (props.roundToDigits || 1);
maxItem = parseFloat(maxItem.toFixed(props.roundToDigits || 1));
if(minItem !== 0){
if (minItem !== 0) {
minItem *= 10 * (props.roundToDigits || 1);
minItem = minItem - (10 + (minItem % 10));
minItem /= 10 * (props.roundToDigits || 1);
@ -281,8 +282,8 @@ export const BarChart = (props: PropTypes) => {
}
} else {
maxItem = maxItem + (10 - (maxItem % 10));
if(minItem !== 0){
minItem = minItem - (10 + (minItem % 10))
if (minItem !== 0) {
minItem = minItem - (10 + (minItem % 10));
}
}
@ -290,7 +291,8 @@ export const BarChart = (props: PropTypes) => {
const minValue = props.minValue || minItem;
const stepValue = props.stepValue || maxValue / noOfSections;
const noOfSectionsBelowXAxis = props.noOfSectionsBelowXAxis || (-minValue / stepValue);
const noOfSectionsBelowXAxis =
props.noOfSectionsBelowXAxis || -minValue / stepValue;
const disableScroll = props.disableScroll || false;
const showScrollIndicator = props.showScrollIndicator || false;
const initialSpacing =
@ -537,7 +539,7 @@ export const BarChart = (props: PropTypes) => {
: value.toString(),
});
}
if(noOfSectionsBelowXAxis){
if (noOfSectionsBelowXAxis) {
for (let i = 1; i <= noOfSectionsBelowXAxis; i++) {
let value = stepValue * -i;
if (props.showFractionalValues || props.roundToDigits) {
@ -545,9 +547,10 @@ export const BarChart = (props: PropTypes) => {
}
horizSectionsBelow.push({
value: props.yAxisLabelTexts
? props.yAxisLabelTexts[noOfSectionsBelowXAxis - i] ?? value.toString()
: value.toString(),
})
? props.yAxisLabelTexts[noOfSectionsBelowXAxis - i] ??
value.toString()
: value.toString(),
});
}
}
@ -602,7 +605,7 @@ export const BarChart = (props: PropTypes) => {
? props.width || totalWidth
: props.width || totalWidth + 11,
},
yAxisSide === 'right' && {transform:[{rotateY:'180deg'}]}
yAxisSide === 'right' && {transform: [{rotateY: '180deg'}]},
]}>
<View
style={[
@ -628,13 +631,16 @@ export const BarChart = (props: PropTypes) => {
style={[
yAxisTextStyle,
index === noOfSections && {marginBottom: stepHeight / -2},
horizontal ? {
transform: [
{rotate: '270deg'},
{translateY: yAxisAtTop ? 0 : 50},
],
}:
yAxisSide === 'right' && {transform:[{rotateY:'180deg'}]}
horizontal
? {
transform: [
{rotate: '270deg'},
{translateY: yAxisAtTop ? 0 : 50},
],
}
: yAxisSide === 'right' && {
transform: [{rotateY: '180deg'}],
},
]}>
{label}
</Text>
@ -745,7 +751,8 @@ export const BarChart = (props: PropTypes) => {
? props.width || totalWidth
: props.width || totalWidth + 11,
},
index===0&&{marginTop:stepHeight/2}
yAxisSide === 'right' && {transform: [{rotateY: '180deg'}]},
index === 0 && {marginTop: stepHeight / 2},
]}>
<View
style={[
@ -759,10 +766,10 @@ export const BarChart = (props: PropTypes) => {
transform: [{translateX: totalWidth + yAxisThickness}],
},
{
height: index===0?stepHeight*1.5:stepHeight,
height: index === 0 ? stepHeight * 1.5 : stepHeight,
width: yAxisLabelWidth,
},
index===0&&{marginTop:-stepHeight/2}
index === 0 && {marginTop: -stepHeight / 2},
]}>
{!hideYAxisText ? (
<Text
@ -777,16 +784,16 @@ export const BarChart = (props: PropTypes) => {
{translateY: yAxisAtTop ? 0 : 50},
],
},
yAxisSide === 'right' && {
transform: [{rotateY: '180deg'}],
},
]}>
{label}
</Text>
) : null}
</View>
<View
style={[
styles.leftPart,
{backgroundColor: backgroundColor},
]}>
style={[styles.leftPart, {backgroundColor: backgroundColor}]}>
{hideRules ? null : (
<Rule
config={{
@ -803,7 +810,7 @@ export const BarChart = (props: PropTypes) => {
)}
</View>
</View>
)
);
})}
</>
);
@ -1109,14 +1116,15 @@ export const BarChart = (props: PropTypes) => {
{props.hideAxesAndRules !== true && renderHorizSections()}
<ScrollView
ref={scrollRef}
onContentSizeChange={()=>{
if(scrollRef.current && scrollToEnd){
onContentSizeChange={() => {
if (scrollRef.current && scrollToEnd) {
scrollRef.current.scrollToEnd({animated: scrollAnimation});
}
}}
style={[
{
marginLeft: yAxisSide === 'right' ? -yAxisLabelWidth+10 : yAxisLabelWidth,
marginLeft:
yAxisSide === 'right' ? -yAxisLabelWidth + 10 : yAxisLabelWidth,
position: 'absolute',
bottom: stepHeight * -0.5 - 60 + xAxisThickness,
},
@ -1127,9 +1135,11 @@ export const BarChart = (props: PropTypes) => {
contentContainerStyle={[
{
// backgroundColor: 'yellow',
height: containerHeight + 130 + horizSectionsBelow.length * stepHeight,
height:
containerHeight + 130 + horizSectionsBelow.length * stepHeight,
paddingLeft: initialSpacing,
paddingBottom:horizSectionsBelow.length * stepHeight + labelsExtraHeight,
paddingBottom:
horizSectionsBelow.length * stepHeight + labelsExtraHeight,
alignItems: 'flex-end',
},
!props.width && {width: totalWidth},
@ -1224,4 +1234,4 @@ export const BarChart = (props: PropTypes) => {
</ScrollView>
</View>
);
};
};

View File

@ -33,6 +33,7 @@ type propTypes = {
height?: number;
noOfSections?: number;
maxValue?: number;
minValue?: number;
stepHeight?: number;
stepValue?: number;
spacing?: number;
@ -192,6 +193,7 @@ type propTypes = {
yAxisLabelSuffix?: String;
scrollToEnd?: Boolean;
scrollAnimation?: Boolean;
noOfSectionsBelowXAxis?: number;
};
type referenceConfigType = {
thickness: number;
@ -280,7 +282,6 @@ export const LineChart = (props: propTypes) => {
const yAxisLabelSuffix = props.yAxisLabelSuffix || '';
const yAxisSide = props.yAxisSide || 'left';
if (!initialData) {
initialData = [...data];
animations = initialData.map(item => new Animated.Value(item.value));
@ -456,15 +457,39 @@ export const LineChart = (props: propTypes) => {
const xAxisColor = props.xAxisColor || 'black';
let totalWidth = initialSpacing;
let maxItem = 0;
let maxItem = 0,
minItem = 0;
data.forEach((item: itemType) => {
if (item.value > maxItem) {
maxItem = item.value;
}
if (item.value < minItem) {
minItem = item.value;
}
totalWidth += spacing;
});
const maxValue = props.maxValue || maxItem || 10;
if (props.showFractionalValues || props.roundToDigits) {
maxItem *= 10 * (props.roundToDigits || 1);
maxItem = maxItem + (10 - (maxItem % 10));
maxItem /= 10 * (props.roundToDigits || 1);
maxItem = parseFloat(maxItem.toFixed(props.roundToDigits || 1));
if (minItem !== 0) {
minItem *= 10 * (props.roundToDigits || 1);
minItem = minItem - (10 + (minItem % 10));
minItem /= 10 * (props.roundToDigits || 1);
minItem = parseFloat(minItem.toFixed(props.roundToDigits || 1));
}
} else {
maxItem = maxItem + (10 - (maxItem % 10));
if (minItem !== 0) {
minItem = minItem - (10 + (minItem % 10));
}
}
const maxValue = props.maxValue || maxItem;
const minValue = props.minValue || minItem;
useEffect(() => {
// console.log('comes here............')
@ -767,18 +792,12 @@ export const LineChart = (props: propTypes) => {
xAxisThickness,
]);
if (props.showFractionalValues || props.roundToDigits) {
maxItem *= 10 * (props.roundToDigits || 1);
maxItem = maxItem + (10 - (maxItem % 10));
maxItem /= 10 * (props.roundToDigits || 1);
maxItem = parseFloat(maxItem.toFixed(props.roundToDigits || 1));
} else {
maxItem = maxItem + (10 - (maxItem % 10));
}
const horizSections = [{value: '0'}];
const horizSectionsBelow = [];
const stepHeight = props.stepHeight || containerHeight / noOfSections;
const stepValue = props.stepValue || maxValue / noOfSections;
const noOfSectionsBelowXAxis =
props.noOfSectionsBelowXAxis || -minValue / stepValue;
const thickness1 = props.thickness1;
const thickness2 = props.thickness2;
const thickness3 = props.thickness3;
@ -939,6 +958,20 @@ export const LineChart = (props: propTypes) => {
: value.toString(),
});
}
if (noOfSectionsBelowXAxis) {
for (let i = 1; i <= noOfSectionsBelowXAxis; i++) {
let value = stepValue * -i;
if (props.showFractionalValues || props.roundToDigits) {
value = parseFloat(value.toFixed(props.roundToDigits || 1));
}
horizSectionsBelow.push({
value: props.yAxisLabelTexts
? props.yAxisLabelTexts[noOfSectionsBelowXAxis - i] ??
value.toString()
: value.toString(),
});
}
}
const renderLabel = (
index: number,
@ -1078,7 +1111,7 @@ export const LineChart = (props: propTypes) => {
{
width: (props.width ? props.width : totalWidth) + 15,
},
yAxisSide === 'right' && {transform:[{rotateY:'180deg'}]}
yAxisSide === 'right' && {transform: [{rotateY: '180deg'}]},
]}>
<View
style={[
@ -1095,7 +1128,9 @@ export const LineChart = (props: propTypes) => {
ellipsizeMode={'clip'}
style={[
yAxisTextStyle,
yAxisSide === 'right' && {transform:[{rotateY:'180deg'}]},
yAxisSide === 'right' && {
transform: [{rotateY: '180deg'}],
},
index === noOfSections && {
marginBottom: stepHeight / -2,
},
@ -1190,6 +1225,70 @@ export const LineChart = (props: propTypes) => {
</View>
);
})}
{horizSectionsBelow.map((sectionItems, index) => {
let label = getLabel(sectionItems.value);
if (hideOrigin && index === horizSections.length - 1) {
label = '';
}
return (
<View
key={index}
style={[
styles.horizBar,
{
width: (props.width ? props.width : totalWidth) + 15,
},
index === 0 && {marginTop: stepHeight / 2},
yAxisSide === 'right' && {transform: [{rotateY: '180deg'}]},
]}>
<View
style={[
styles.leftLabel,
{
borderRightWidth: yAxisThickness,
borderColor: yAxisColor,
marginLeft: 1,
},
{
height: index === 0 ? stepHeight * 1.5 : stepHeight,
width: yAxisLabelWidth,
},
index === 0 && {marginTop: -stepHeight / 2},
]}>
{!hideYAxisText ? (
<Text
numberOfLines={1}
ellipsizeMode={'clip'}
style={[
yAxisTextStyle,
index === 0 && {marginBottom: stepHeight / -2},
yAxisSide === 'right' && {
transform: [{rotateY: '180deg'}],
},
]}>
{label}
</Text>
) : null}
</View>
<View
style={[styles.leftPart, {backgroundColor: backgroundColor}]}>
{hideRules ? null : (
<Rule
config={{
thickness: rulesThickness,
color: rulesColor,
width: (props.width || totalWidth) + 11,
dashWidth: dashWidth,
dashGap: dashGap,
type: rulesType,
}}
/>
)}
</View>
</View>
);
})}
</>
);
};
@ -1481,11 +1580,11 @@ export const LineChart = (props: propTypes) => {
<View
style={{
position: 'absolute',
height: containerHeight + 10,
height: containerHeight + 10 + horizSectionsBelow.length * stepHeight,
bottom: 60, //stepHeight * -0.5 + xAxisThickness,
width: totalWidth,
zIndex: -1,
// backgroundColor: 'rgba(200,150,150,0.1)'
// backgroundColor: 'rgba(200,150,150,0.6)'
}}>
<Svg>
<Path
@ -1587,7 +1686,7 @@ export const LineChart = (props: propTypes) => {
<Animated.View
style={{
position: 'absolute',
height: containerHeight + 10,
height: containerHeight + 10 + horizSectionsBelow.length * stepHeight,
bottom: 60, //stepHeight * -0.5 + xAxisThickness,
width: animatedWidth,
zIndex: -1,
@ -1678,31 +1777,43 @@ export const LineChart = (props: propTypes) => {
);
};
console.log('horizSectionsBelow -- >', horizSectionsBelow);
return (
<View style={[styles.container, {height: containerHeight}, yAxisSide === 'right' && {marginLeft: yAxisLabelWidth + yAxisThickness }]}>
<View
style={[
styles.container,
{height: containerHeight + horizSectionsBelow.length * stepHeight},
yAxisSide === 'right' && {marginLeft: yAxisLabelWidth + yAxisThickness},
]}>
{props.hideAxesAndRules !== true && renderHorizSections()}
{/* {sectionsOverlay()} */}
<ScrollView
horizontal
contentContainerStyle={[
{
height: containerHeight + 130,
width: totalWidth -20,
height:
containerHeight + 130 + horizSectionsBelow.length * stepHeight,
width: totalWidth - 20,
paddingBottom: horizSectionsBelow.length * stepHeight,
// backgroundColor: 'yellow'
},
!props.width && {width: totalWidth -20},
!props.width && {width: totalWidth - 20},
]}
scrollEnabled={!disableScroll}
ref={scrollRef}
onContentSizeChange={()=>{
if(scrollRef.current && scrollToEnd){
onContentSizeChange={() => {
if (scrollRef.current && scrollToEnd) {
scrollRef.current.scrollToEnd({animated: scrollAnimation});
}
}}
showsHorizontalScrollIndicator={showScrollIndicator}
style={[
{
marginLeft: yAxisSide === 'right' ? -yAxisLabelWidth - yAxisThickness + 6 : yAxisLabelWidth + yAxisThickness,
marginLeft:
yAxisSide === 'right'
? -yAxisLabelWidth - yAxisThickness + 6
: yAxisLabelWidth + yAxisThickness,
position: 'absolute',
bottom: stepHeight * -0.5 - 60, //stepHeight * -0.5 + xAxisThickness,
paddingRight: 100,