From 2d1b834f3c1d2f3a08efafd45deaa2f273454b40 Mon Sep 17 00:00:00 2001 From: abhinandan-kushwaha Date: Tue, 8 Mar 2022 03:22:31 +0530 Subject: [PATCH] Allowed negative values for Line and Area charts --- App.js | 29 +++--- docs/LineChart/LineChartProps.md | 2 + package.json | 2 +- src/BarChart/index.tsx | 76 ++++++++------- src/LineChart/index.tsx | 159 ++++++++++++++++++++++++++----- 5 files changed, 196 insertions(+), 72 deletions(-) diff --git a/App.js b/App.js index 4e39b13..b2f30bc 100644 --- a/App.js +++ b/App.js @@ -548,17 +548,6 @@ const App = () => { ); }; - 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 ( { // marginLeft: 46, marginLeft: 20, }}> - {/* */} - + {/* { // noOfSections={4} // innerCircleBorderColor={'gray'} // showTextBackground={true} - /> + /> */} {dtt[1].value+=20;setDtt([...dtt])}}> Press me diff --git a/docs/LineChart/LineChartProps.md b/docs/LineChart/LineChartProps.md index 72bda9c..6697404 100644 --- a/docs/LineChart/LineChartProps.md +++ b/docs/LineChart/LineChartProps.md @@ -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 | diff --git a/package.json b/package.json index 2bafea6..fa0358d 100644 --- a/package.json +++ b/package.json @@ -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": [ diff --git a/src/BarChart/index.tsx b/src/BarChart/index.tsx index e770310..43e90dc 100644 --- a/src/BarChart/index.tsx +++ b/src/BarChart/index.tsx @@ -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'}]}, ]}> { 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} @@ -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}, ]}> { 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 ? ( { {translateY: yAxisAtTop ? 0 : 50}, ], }, + yAxisSide === 'right' && { + transform: [{rotateY: '180deg'}], + }, ]}> {label} ) : null} + style={[styles.leftPart, {backgroundColor: backgroundColor}]}> {hideRules ? null : ( { )} - ) + ); })} ); @@ -1109,14 +1116,15 @@ export const BarChart = (props: PropTypes) => { {props.hideAxesAndRules !== true && renderHorizSections()} { - 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) => { ); -}; \ No newline at end of file +}; diff --git a/src/LineChart/index.tsx b/src/LineChart/index.tsx index 97e020b..6fdb72b 100644 --- a/src/LineChart/index.tsx +++ b/src/LineChart/index.tsx @@ -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'}]}, ]}> { 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) => { ); })} + + {horizSectionsBelow.map((sectionItems, index) => { + let label = getLabel(sectionItems.value); + if (hideOrigin && index === horizSections.length - 1) { + label = ''; + } + return ( + + + {!hideYAxisText ? ( + + {label} + + ) : null} + + + {hideRules ? null : ( + + )} + + + ); + })} ); }; @@ -1481,11 +1580,11 @@ export const LineChart = (props: propTypes) => { { { ); }; + console.log('horizSectionsBelow -- >', horizSectionsBelow); + return ( - + {props.hideAxesAndRules !== true && renderHorizSections()} {/* {sectionsOverlay()} */} { - 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,