From a164a91822da0789e02ac143803c49c2f5fdc94a Mon Sep 17 00:00:00 2001 From: Abhinandan Kushwaha Date: Fri, 27 Dec 2024 03:56:46 +0530 Subject: [PATCH] v1.4.49 type refactor & onPress and pointer fixes in Line charts --- docs/LineChart/LineChartProps.md | 126 +++++++++--------- package.json | 4 +- release-notes/release-notes.md | 36 ++++- src/LineChart/index.tsx | 222 +++++++++++++++++++++++++++---- src/index.tsx | 27 ++-- 5 files changed, 313 insertions(+), 102 deletions(-) diff --git a/docs/LineChart/LineChartProps.md b/docs/LineChart/LineChartProps.md index 69d8b7bd..de02c510 100644 --- a/docs/LineChart/LineChartProps.md +++ b/docs/LineChart/LineChartProps.md @@ -2,57 +2,58 @@ ### Basic props -| Prop | Type | Description | Default value | -| ----------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------- | -| data | Array | An item object represents a point in the line chart. It is described in the next table. | \_ | -| data2 | Array | Second set of dataPoint for the second line | \_ | -| data3 | Array | Third set of dataPoint for the third line | \_ | -| data4 | Array | Fourth set of dataPoint for the fourth line | \_ | -| data5 | Array | Fifth set of dataPoint for the third line | \_ | -| dataSet | Array | Array of data sets (used instead of using `data2`, `data3`, `data4` etc) | \_ | -| width | number | Width of the Bar chart | width of the parent | -| height | number | Height of the Bar chart (excluding the bottom label) | 200 | -| overflowTop | number | Extra space at the top of the chart to make room for dataPointText | 0 | -| overflowBottom | number | Extra space at the bottom of the chart to make room for dataPoints or dataPointText | dataPointRadius | -| maxValue | number | Maximum value shown in the Y axis | 200 | -| mostNegativeValue | number | The most 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 | -| negativeStepValue | number | Value of 1 step/section in the Y axis for negative values (in the 4th quadrant) | stepValue | -| negativeStepHeight | number | Height of 1 step/section in the Y axis for negative values (in the 4th quadrant) | stepHeight | -| spacing | number | Distance between 2 consecutive points in the Line chart | 50 | -| adjustToWidth | boolean | When set to true, it auto computes the spacing value to fit the Line chart in the available width | false | -| backgroundColor | ColorValue | Background color of the Bar chart | \_ | -| sectionColors | ColorValue | Background color of the horizontal sections of the chart | backgroundColor | -| customBackground | CustomBackground | An object used to set a custom background component (See the properties of this object below) | \_ | -| scrollref | any | ref object that can be used to control the horizontal ScrollView inside which the chart is rendered | React.useRef() | -| scrollToIndex | number | scroll to a particular index on chart load | \_ | -| disableScroll | boolean | To disable horizontal scroll | false | -| showScrollIndicator | boolean | To show horizontal scroll indicator | false | -| indicatorColor | String | (iOS only) The color of the scroll indicators - ('black', 'white' or 'default') | default | -| nestedScrollEnabled | boolean | Useful when the chart is used inside a horizontal ScrollView (without this, the chart's scrolling is compromised) | false | -| isAnimated | boolean | To show animated Line or Area Chart. Animation occurs when the chart load for the first time | false | -| animateOnDataChange | boolean | To show animation on change in data. A smooth transition takes place between the old and new line | false | -| onDataChangeAnimationDuration | number | Duration (milliseconds) in which the transition animation takes place on a change in data | 400 | -| onPress | Function | The callback function that handles the press event. `item` and `index` are received as props | \_ | -| scrollToEnd | boolean | When set to true, the chart automatically scrolls to the rightmost data point | false | -| scrollAnimation | boolean | When set to true, scroll animation is visible when the chart automatically scrolls to the rightmost data point | true | -| scrollEventThrottle | number | (only for iOS) see https://reactnative.dev/docs/scrollview#scrolleventthrottle-ios | 0 | -| onScroll | Function | callback function called when the chart is scrolled horizontally | \_ | -| onMomentumScrollEnd | Function | callback function called when scroll is completed | \_ | -| initialSpacing | number | distance of the first data point from the Y axis | 20 | -| endSpacing | number | distance/padding left at the end of the line chart | adjustWidth ? 0 : 20 | -| stepChart | boolean | If set true, renders a step chart | false | -| stepChart1 | boolean | If set true, renders a step chart for 1st data set | stepChart | -| stepChart2 | boolean | If set true, renders a step chart for 2nd data set | stepChart | -| stepChart3 | boolean | If set true, renders a step chart for 3rd data set | stepChart | -| stepChart4 | boolean | If set true, renders a step chart for 4th data set | stepChart | -| stepChart5 | boolean | If set true, renders a step chart for 5th data set | stepChart | -| edgePosition | EdgePosition | Used only for stepCharts to specify the edge positions of the steps | EdgePosition.AFTER_DATA_POINT | -| onlyPositive | boolean | when this prop is truthy, it converts negative values to 0 | false | -| onBackgroundPress | Function | Callback function called on pressing the chart body (outside of the are under chart in case of area charts) | \_ | +| Prop | Type | Description | Default value | +| ---------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | +| data | Array | An item object represents a point in the line chart. It is described in the next table. | \_ | +| data2 | Array | Second set of dataPoint for the second line | \_ | +| data3 | Array | Third set of dataPoint for the third line | \_ | +| data4 | Array | Fourth set of dataPoint for the fourth line | \_ | +| data5 | Array | Fifth set of dataPoint for the third line | \_ | +| dataSet | Array | Array of data sets (used instead of using `data2`, `data3`, `data4` etc) | \_ | +| width | number | Width of the Bar chart | width of the parent | +| height | number | Height of the Bar chart (excluding the bottom label) | 200 | +| overflowTop | number | Extra space at the top of the chart to make room for dataPointText | 0 | +| overflowBottom | number | Extra space at the bottom of the chart to make room for dataPoints or dataPointText | dataPointRadius | +| maxValue | number | Maximum value shown in the Y axis | 200 | +| mostNegativeValue | number | The most 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 | +| negativeStepValue | number | Value of 1 step/section in the Y axis for negative values (in the 4th quadrant) | stepValue | +| negativeStepHeight | number | Height of 1 step/section in the Y axis for negative values (in the 4th quadrant) | stepHeight | +| spacing | number | Distance between 2 consecutive points in the Line chart | 50 | +| adjustToWidth | boolean | When set to true, it auto computes the spacing value to fit the Line chart in the available width | false | +| backgroundColor | ColorValue | Background color of the Bar chart | \_ | +| sectionColors | ColorValue | Background color of the horizontal sections of the chart | backgroundColor | +| customBackground | CustomBackground | An object used to set a custom background component (See the properties of this object below) | \_ | +| scrollref | any | ref object that can be used to control the horizontal ScrollView inside which the chart is rendered | React.useRef() | +| scrollToIndex | number | scroll to a particular index on chart load | \_ | +| disableScroll | boolean | To disable horizontal scroll | false | +| showScrollIndicator | boolean | To show horizontal scroll indicator | false | +| indicatorColor | String | (iOS only) The color of the scroll indicators - ('black', 'white' or 'default') | default | +| nestedScrollEnabled | boolean | Useful when the chart is used inside a horizontal ScrollView (without this, the chart's scrolling is compromised) | false | +| isAnimated | boolean | To show animated Line or Area Chart. Animation occurs when the chart load for the first time | false | +| animateOnDataChange | boolean | To show animation on change in data. A smooth transition takes place between the old and new line | false | +| onDataChangeAnimationDuration | number | Duration (milliseconds) in which the transition animation takes place on a change in data | 400 | +| renderDataPointsAfterAnimationEnds | boolean | to render the data points after the animation has ended. Should be used if `onPress` is used in multi-line animated charts | false | +| onPress | Function | The callback function that handles the press event. `item` and `index` are received as props | \_ | +| scrollToEnd | boolean | When set to true, the chart automatically scrolls to the rightmost data point | false | +| scrollAnimation | boolean | When set to true, scroll animation is visible when the chart automatically scrolls to the rightmost data point | true | +| scrollEventThrottle | number | (only for iOS) see https://reactnative.dev/docs/scrollview#scrolleventthrottle-ios | 0 | +| onScroll | Function | callback function called when the chart is scrolled horizontally | \_ | +| onMomentumScrollEnd | Function | callback function called when scroll is completed | \_ | +| initialSpacing | number | distance of the first data point from the Y axis | 20 | +| endSpacing | number | distance/padding left at the end of the line chart | adjustWidth ? 0 : 20 | +| stepChart | boolean | If set true, renders a step chart | false | +| stepChart1 | boolean | If set true, renders a step chart for 1st data set | stepChart | +| stepChart2 | boolean | If set true, renders a step chart for 2nd data set | stepChart | +| stepChart3 | boolean | If set true, renders a step chart for 3rd data set | stepChart | +| stepChart4 | boolean | If set true, renders a step chart for 4th data set | stepChart | +| stepChart5 | boolean | If set true, renders a step chart for 5th data set | stepChart | +| edgePosition | EdgePosition | Used only for stepCharts to specify the edge positions of the steps | EdgePosition.AFTER_DATA_POINT | +| onlyPositive | boolean | when this prop is truthy, it converts negative values to 0 | false | +| onBackgroundPress | Function | Callback function called on pressing the chart body (outside of the are under chart in case of area charts) | \_ | --- @@ -106,14 +107,14 @@ type DataSet = { ```ts type CustomBackground = { - color?: ColorValue - component?: Function - horizontalShift?: number - verticalShift?: number - height?: number - width?: number - widthAdjustment?: number -} + color?: ColorValue; + component?: Function; + horizontalShift?: number; + verticalShift?: number; + height?: number; + width?: number; + widthAdjustment?: number; +}; ``` **Alert!**\ @@ -768,7 +769,8 @@ The `strokeDashArray` property lets us render a dashed/dotted strip along the po #### pointerLabelComponent -`pointerLabelComponent` is a function that returns the component to be rendered as a Label. It takes 3 parameters - +`pointerLabelComponent` is a function that returns the component to be rendered as a Label. It takes 3 parameters - + 1. an array of items 2. secondaryDataItem 3. pointerIndex @@ -836,8 +838,8 @@ To achieve this the `focusEnabled` props must be set to true. In addition, use b | focusedDataPointColor | ColorValue | Color of the data points when focused due to press event | item.dataPointsColor OR dataPointsColor | | focusedDataPointRadius | number | Radius of the data points when focused due to press event | item.dataPointsRadius OR dataPointsRadius | | focusedCustomDataPoint | Function | Custom data point when focused due to press event | item.customDataPoint OR customDataPoint | -| focusTogether | boolean | In case of multi-line charts, Unless `focusTogether` is set to `false`, all the data points at the focused index get focused together | true | -| focusProximity | number | Sets the distance from a data point upto which a press event should result in focusing that data point | Infinity | +| focusTogether | boolean | In case of multi-line charts, Unless `focusTogether` is set to `false`, all the data points at the focused index get focused together | true | +| focusProximity | number | Sets the distance from a data point upto which a press event should result in focusing that data point | Infinity | #### Example of onFocus : diff --git a/package.json b/package.json index 91541c7b..86a1415d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-gifted-charts", - "version": "1.4.48", + "version": "1.4.49", "description": "The most complete library for Bar, Line, Area, Pie, Donut, Stacked Bar and Population Pyramid charts in React Native. Allows 2D, 3D, gradient, animations and live data updates.", "main": "dist/index.js", "files": [ @@ -26,7 +26,7 @@ "registry": "https://registry.npmjs.org/" }, "dependencies": { - "gifted-charts-core": "0.1.49" + "gifted-charts-core": "0.1.50" }, "devDependencies": { "@babel/cli": "^7.24.8", diff --git a/release-notes/release-notes.md b/release-notes/release-notes.md index 749708a0..6f465f13 100644 --- a/release-notes/release-notes.md +++ b/release-notes/release-notes.md @@ -1,3 +1,37 @@ +## 🎉 1.4.49 + +### ✨ Features added- + +1. Added the prop `renderDataPointsAfterAnimationEnds` to Line/Area charts to render the data points after the animation has ended. This flag can be helpful if `onPress` does not work in case of multi-line animated charts. See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/921 + +### 🐛 Bug fixes + +1. Fixed the issue- "getPointerProps starts from 0 index, regardless of where you press and drag on the LineChart". See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/937 +2. Fixed the issue- "Unable to use multi onPress in LineChart". See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/921 and https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/570 +3. Fixed the issue- "LineChart 1st data point does not show pointer label if initialSpacing={0}". See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/925 + +## 🔨 Refactor + +Made the `value` property of data items optional for Bar and Line/Area charts. Added null safety types for internal calculations, while the `` and `` components exposed by the library will be capable of accepting null/undefined values as data items, internally it will be conerted to respective nullSafe type after handling the null values (by interpolatio or replacing with 0). +Below are the exposed types and their corresponding null afe types- + +| Chart Type | Type of `data` prop | Corresponding null safe type | +| ------------------------ | ------------------- | -----------------------------| +| BarChart | barDataItem[] | barDataItemNullSafe[] | +| LineChart | lineDataItem[] | lineDataItemNullSafe[] | +| LineChart with `dataSet` | DataSet[] | DataSetNullSafe[] | + +This refactor fixes the below type-related issues- + +1. https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/787 +2. https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/781 + +--- + +--- + +--- + # 🎉 1.4.48 ## ✨ Features added- @@ -32,7 +66,7 @@ type CustomBackground = { 3. Some fix about vertical lines in Line charts. 4. Fixed the issue- "LineChart - Strip and Points not showing beyond x index 0 for DataSet". See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/911 5. Fixed the issue- "hidePointer doesn't work". See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/932 -6. Fixed the issue- "Custom Data Point in wrong position with non-uniform spacing on data in LineChart" Seehttps://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/923 +6. Fixed the issue- "Custom Data Point in wrong position with non-uniform spacing on data in LineChart" See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/923 7. Fixed the issue- "extrapolateMissingValues not working with dataSet". See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/916 8. Fixed the issue- "The first property setting of BarChart/LineChart RulesConfigArray does not take effect". See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/893 9. Fixed the issue- "areaChart2, areaChart3, areaChart4... props not working". See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/892 diff --git a/src/LineChart/index.tsx b/src/LineChart/index.tsx index ee55db09..b1f0ab79 100644 --- a/src/LineChart/index.tsx +++ b/src/LineChart/index.tsx @@ -46,6 +46,7 @@ import { LineProperties, LineDefaults, pointsWithPaddedRepititions, + lineDataItemNullSafe, } from 'gifted-charts-core'; import BarAndLineChartsWrapper from '../Components/BarAndLineChartsWrapper'; import {StripAndLabel} from '../Components/common/StripAndLabel'; @@ -140,6 +141,7 @@ export const LineChart = (props: LineChartPropsType) => { animationDuration, onDataChangeAnimationDuration, animateTogether, + renderDataPointsAfterAnimationEnds, animateOnDataChange, startIndex1, startIndex2, @@ -645,7 +647,7 @@ export const LineChart = (props: LineChartPropsType) => { key: number, ) => { const getYOrSecondaryY = isSecondary ? getSecondaryY : getY; - return dataForRender.map((item: lineDataItem, index: number) => { + return dataForRender.map((item: lineDataItemNullSafe, index: number) => { if (index < startIndex || index > endIndex) return null; if (item.hideDataPoint) { return null; @@ -712,6 +714,7 @@ export const LineChart = (props: LineChartPropsType) => { if (showValuesAsDataPointsText) { text = originalDataFromProps[index].value; } + return ( {focusEnabled ? ( @@ -995,7 +998,7 @@ export const LineChart = (props: LineChartPropsType) => { dataForRender: any, spacingArray: number[], ) => { - return dataForRender.map((item: lineDataItem, index: number) => { + return dataForRender.map((item: lineDataItemNullSafe, index: number) => { if (item.showVerticalLine) { const x = getX(spacingArray, index); return ( @@ -1284,6 +1287,171 @@ export const LineChart = (props: LineChartPropsType) => { ); }; + const renderDataPointsForEachLine = () => { + if (dataSet && pointsFromSet.length) { + return ( + <> + {dataSet.map((set, index) => { + const { + hideDataPoints, + data, + dataPointsShape, + dataPointsWidth, + dataPointsHeight, + dataPointsColor, + dataPointsRadius, + textColor, + textFontSize, + startIndex, + endIndex, + isSecondary, + } = set; + return renderDataPoints( + hideDataPoints ?? hideDataPoints1, + data, + adjustToOffset(data, -(props.yAxisOffset ?? 0)), + dataPointsShape ?? dataPointsShape1, + dataPointsWidth ?? dataPointsWidth1, + dataPointsHeight ?? dataPointsHeight1, + dataPointsColor ?? dataPointsColor1, + dataPointsRadius ?? dataPointsRadius1, + textColor ?? textColor1, + textFontSize ?? textFontSize1, + startIndex ?? 0, + endIndex ?? set.data.length - 1, + isSecondary, + showValuesAsDataPointsText, + cumulativeSpacingForSet[index], + index, + ); + })} + + ); + } + return ( + <> + {renderDataPoints( + hideDataPoints1, + data, + props.data, + dataPointsShape1, + dataPointsWidth1, + dataPointsHeight1, + dataPointsColor1, + dataPointsRadius1, + textColor1, + textFontSize1, + startIndex1, + endIndex1, + false, + showValuesAsDataPointsText, + cumulativeSpacing1, + 0, + )} + {points2 + ? renderDataPoints( + hideDataPoints2, + data2, + props.data2, + dataPointsShape2, + dataPointsWidth2, + dataPointsHeight2, + dataPointsColor2, + dataPointsRadius2, + textColor2, + textFontSize2, + startIndex2, + endIndex2, + false, + showValuesAsDataPointsText, + cumulativeSpacing2, + 1, + ) + : null} + {points3 + ? renderDataPoints( + hideDataPoints3, + data3, + props.data3, + dataPointsShape3, + dataPointsWidth3, + dataPointsHeight3, + dataPointsColor3, + dataPointsRadius3, + textColor3, + textFontSize3, + startIndex3, + endIndex3, + false, + showValuesAsDataPointsText, + cumulativeSpacing3, + 2, + ) + : null} + {points4 + ? renderDataPoints( + hideDataPoints4, + data4, + props.data4, + dataPointsShape4, + dataPointsWidth4, + dataPointsHeight4, + dataPointsColor4, + dataPointsRadius4, + textColor4, + textFontSize4, + startIndex4, + endIndex4, + false, + showValuesAsDataPointsText, + cumulativeSpacing4, + 3, + ) + : null} + {points5 + ? renderDataPoints( + hideDataPoints5, + data5, + props.data5, + dataPointsShape5, + dataPointsWidth5, + dataPointsHeight5, + dataPointsColor5, + dataPointsRadius5, + textColor5, + textFontSize5, + startIndex5, + endIndex5, + false, + showValuesAsDataPointsText, + cumulativeSpacing5, + 4, + ) + : null} + {secondaryPoints + ? renderDataPoints( + secondaryLineConfig.hideDataPoints, + secondaryData, + props.secondaryData, + secondaryLineConfig.dataPointsShape, + secondaryLineConfig.dataPointsWidth, + secondaryLineConfig.dataPointsHeight, + secondaryLineConfig.dataPointsColor, + secondaryLineConfig.dataPointsRadius, + secondaryLineConfig.textColor, + secondaryLineConfig.textFontSize, + secondaryLineConfig.startIndex, + secondaryLineConfig.endIndex, + true, + secondaryLineConfig.showValuesAsDataPointsText, + cumulativeSpacingSecondary, + 6666, + ) + : null} + + ); + }; + const lineSvgComponent = ( points: any, currentLineThickness: number | undefined, @@ -1455,25 +1623,29 @@ export const LineChart = (props: LineChartPropsType) => { ), ) ?? null} - {/*** !!! Here it's done 5 times intentionally, trying to make it to only 1 breaks things !!! ***/} - {renderDataPoints( - hideDataPoints, - data, - propsData, - dataPointsShape, - dataPointsWidth, - dataPointsHeight, - dataPointsColor, - dataPointsRadius, - textColor, - textFontSize, - startIndex, - endIndex, - isSecondary, - showValuesAsDataPointsText, - spacingArray, - key, - )} + {/*** !!! Here it's done 5 times intentionally, so that onPress works for each line !!! ***/} + {isAnimated && !renderDataPointsAfterAnimationEnds // in this condition onPress won't work properly in case of multi-line, so it's suggested to use either renderDataPointsAfterAnimationEnds prop if you want to use onPress for data points + ? renderDataPoints( + hideDataPoints, + data, + propsData, + dataPointsShape, + dataPointsWidth, + dataPointsHeight, + dataPointsColor, + dataPointsRadius, + textColor, + textFontSize, + startIndex, + endIndex, + isSecondary, + showValuesAsDataPointsText, + spacingArray, + key, + ) + : key === lastLineNumber - 1 + ? renderDataPointsForEachLine() + : null} {showArrow && ( { ) - (pointerRadius || pointerWidth / 2) - 1; - setPointerX(z); + setPointerX(Math.max(0.1, z)); // 0.1 is to avoid pointer going out of the chart, See https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/issues/925 setPointerIndex(factor); y = containerHeight - @@ -1905,11 +2077,11 @@ export const LineChart = (props: LineChartPropsType) => { : containerHeight - (item.dataPointHeight ?? dataPointsHeight1) / 2 + 14 - - (item.value * containerHeight) / maxValue; + ((item.value ?? 0) * containerHeight) / maxValue; const actualStripHeight = currentStripHeight || - (item.value * containerHeight) / maxValue - 2 + overflowTop; + ((item.value ?? 0) * containerHeight) / maxValue - 2 + overflowTop; return ( { return (