Added tooltip feature to bar and Stacked Bar charts

This commit is contained in:
Abhinandan-Kushwaha 2022-05-23 21:20:09 +05:30
parent 1a4e85190b
commit be8da2d29e
5 changed files with 308 additions and 233 deletions

View File

@ -27,6 +27,7 @@
| scrollToEnd | Boolean | When set to true, the chart automatically scrolls to the rightmost bar | false |
| scrollAnimation | Boolean | When set to true, scroll animation is visible when the chart automatically scrolls to the rightmost bar | true |
| initialSpacing | number | distance of the first bar from the Y axis | 40 |
| renderTooltip | Function | tooltip component appearing above the bar when it is pressed, takes item and index as parameters | null |
---

View File

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

View File

@ -65,6 +65,10 @@ type Props = {
barMarginBottom?: number;
onPress?: Function;
xAxisTextNumberOfLines: number;
renderTooltip: Function;
initialSpacing: number;
selectedIndex: number;
setSelectedIndex: Function;
};
type itemType = {
value?: number;
@ -114,6 +118,10 @@ const RenderBars = (props: Props) => {
label,
labelTextStyle,
xAxisTextNumberOfLines,
renderTooltip,
initialSpacing,
selectedIndex,
setSelectedIndex,
} = props;
const barMarginBottom =
@ -318,139 +326,172 @@ const RenderBars = (props: Props) => {
);
};
const barHeight =
(item.value >= 0 && (!isThreeD || isAnimated) && item.topLabelComponent
? (item.topLabelComponentHeight || 30) +
(Math.abs(item.value) * (containerHeight || 200)) / (maxValue || 200)
: (Math.abs(item.value) * (containerHeight || 200)) / (maxValue || 200)) -
barMarginBottom;
return (
<TouchableOpacity
disabled={item.disablePress || props.disablePress}
activeOpacity={props.activeOpacity || 0.2}
onPress={() => {
item.onPress
? item.onPress()
: props.onPress
? props.onPress(item, index)
: null;
}}
style={[
{
// overflow: 'visible',
marginBottom: 60 + barMarginBottom,
width: item.barWidth || props.barWidth || 30,
height:
(item.value >= 0 &&
(!isThreeD || isAnimated) &&
item.topLabelComponent
? (item.topLabelComponentHeight || 30) +
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
: (Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)) - barMarginBottom,
marginRight: spacing,
},
item.value < 0 && {
transform: [
{
translateY:
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200),
},
{rotateZ: '180deg'},
],
},
// !isThreeD && !item.showGradient && !props.showGradient &&
// { backgroundColor: item.frontColor || props.frontColor || 'black' },
side !== 'right' && {zIndex: data.length - index},
]}>
{props.showVerticalLines && (
<View
style={{
zIndex: props.verticalLinesZIndex,
position: 'absolute',
height: (containerHeight || 200) + 15,
width: props.verticalLinesThickness,
bottom: 0,
left: (item.barWidth || props.barWidth || 30) / 2,
backgroundColor: props.verticalLinesColor,
}}
/>
)}
{props.showXAxisIndices && (
<View
style={{
zIndex: 2,
position: 'absolute',
height: props.xAxisIndicesHeight,
width: props.xAxisIndicesWidth,
bottom: 0,
left: (item.barWidth || props.barWidth || 30) / 2,
backgroundColor: props.xAxisIndicesColor,
}}
/>
)}
{isThreeD ? (
isAnimated ? (
<AnimatedBar
barBackgroundPattern={
item.barBackgroundPattern || props.barBackgroundPattern
}
patternId={item.patternId || props.patternId}
topLabelContainerStyle={item.topLabelContainerStyle}
width={item.barWidth || props.barWidth || 30}
sideWidth={
item.sideWidth ||
props.sideWidth ||
(item.barWidth || props.barWidth || 30) / 2
}
side={side || 'left'}
frontColor={item.frontColor || props.frontColor || ''}
sideColor={item.sideColor || props.sideColor || ''}
topColor={item.topColor || props.topColor || ''}
showGradient={item.showGradient || props.showGradient || false}
gradientColor={item.gradientColor || props.gradientColor}
topLabelComponent={item.topLabelComponent}
opacity={opacity || 1}
animationDuration={animationDuration || 800}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200) -
barMarginBottom
}
intactTopLabel={props.intactTopLabel}
horizontal={props.horizontal}
<>
<TouchableOpacity
disabled={item.disablePress || props.disablePress}
activeOpacity={props.activeOpacity || 0.2}
onPress={() => {
if (renderTooltip) {
setSelectedIndex(index);
}
item.onPress
? item.onPress()
: props.onPress
? props.onPress(item, index)
: null;
}}
style={[
{
// overflow: 'visible',
marginBottom: 60 + barMarginBottom,
width: item.barWidth || props.barWidth || 30,
height: barHeight,
marginRight: spacing,
},
item.value < 0 && {
transform: [
{
translateY:
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200),
},
{rotateZ: '180deg'},
],
},
// !isThreeD && !item.showGradient && !props.showGradient &&
// { backgroundColor: item.frontColor || props.frontColor || 'black' },
side !== 'right' && {zIndex: data.length - index},
]}>
{props.showVerticalLines && (
<View
style={{
zIndex: props.verticalLinesZIndex,
position: 'absolute',
height: (containerHeight || 200) + 15,
width: props.verticalLinesThickness,
bottom: 0,
left: (item.barWidth || props.barWidth || 30) / 2,
backgroundColor: props.verticalLinesColor,
}}
/>
) : (
<ThreeDBar
barBackgroundPattern={
item.barBackgroundPattern || props.barBackgroundPattern
}
patternId={item.patternId || props.patternId}
style={{}}
color={''}
topLabelContainerStyle={item.topLabelContainerStyle}
width={item.barWidth || props.barWidth || 30}
sideWidth={
item.sideWidth ||
props.sideWidth ||
(item.barWidth || props.barWidth || 30) / 2
}
side={side || 'left'}
frontColor={item.frontColor || props.frontColor || ''}
sideColor={item.sideColor || props.sideColor || ''}
topColor={item.topColor || props.topColor || ''}
showGradient={item.showGradient || props.showGradient || false}
gradientColor={item.gradientColor || props.gradientColor}
topLabelComponent={item.topLabelComponent || Function}
opacity={opacity || 1}
horizontal={props.horizontal}
intactTopLabel={props.intactTopLabel}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200) -
barMarginBottom
}
value={item.value}
)}
{props.showXAxisIndices && (
<View
style={{
zIndex: 2,
position: 'absolute',
height: props.xAxisIndicesHeight,
width: props.xAxisIndicesWidth,
bottom: 0,
left: (item.barWidth || props.barWidth || 30) / 2,
backgroundColor: props.xAxisIndicesColor,
}}
/>
)
) : item.showGradient || props.showGradient ? (
isAnimated ? (
)}
{isThreeD ? (
isAnimated ? (
<AnimatedBar
barBackgroundPattern={
item.barBackgroundPattern || props.barBackgroundPattern
}
patternId={item.patternId || props.patternId}
topLabelContainerStyle={item.topLabelContainerStyle}
width={item.barWidth || props.barWidth || 30}
sideWidth={
item.sideWidth ||
props.sideWidth ||
(item.barWidth || props.barWidth || 30) / 2
}
side={side || 'left'}
frontColor={item.frontColor || props.frontColor || ''}
sideColor={item.sideColor || props.sideColor || ''}
topColor={item.topColor || props.topColor || ''}
showGradient={item.showGradient || props.showGradient || false}
gradientColor={item.gradientColor || props.gradientColor}
topLabelComponent={item.topLabelComponent}
opacity={opacity || 1}
animationDuration={animationDuration || 800}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200) -
barMarginBottom
}
intactTopLabel={props.intactTopLabel}
horizontal={props.horizontal}
/>
) : (
<ThreeDBar
barBackgroundPattern={
item.barBackgroundPattern || props.barBackgroundPattern
}
patternId={item.patternId || props.patternId}
style={{}}
color={''}
topLabelContainerStyle={item.topLabelContainerStyle}
width={item.barWidth || props.barWidth || 30}
sideWidth={
item.sideWidth ||
props.sideWidth ||
(item.barWidth || props.barWidth || 30) / 2
}
side={side || 'left'}
frontColor={item.frontColor || props.frontColor || ''}
sideColor={item.sideColor || props.sideColor || ''}
topColor={item.topColor || props.topColor || ''}
showGradient={item.showGradient || props.showGradient || false}
gradientColor={item.gradientColor || props.gradientColor}
topLabelComponent={item.topLabelComponent || Function}
opacity={opacity || 1}
horizontal={props.horizontal}
intactTopLabel={props.intactTopLabel}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200) -
barMarginBottom
}
value={item.value}
/>
)
) : item.showGradient || props.showGradient ? (
isAnimated ? (
<Animated2DWithGradient
barBackgroundPattern={props.barBackgroundPattern}
patternId={props.patternId}
barWidth={props.barWidth}
item={item}
opacity={opacity}
animationDuration={animationDuration || 800}
roundedBottom={props.roundedBottom || false}
roundedTop={props.roundedTop || false}
gradientColor={props.gradientColor}
frontColor={props.frontColor || 'black'}
containerHeight={containerHeight}
maxValue={maxValue}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
}
barMarginBottom={barMarginBottom}
cappedBars={props.cappedBars}
capThickness={props.capThickness}
capColor={props.capColor}
capRadius={props.capRadius}
horizontal={props.horizontal}
intactTopLabel={props.intactTopLabel}
barBorderRadius={props.barBorderRadius || 0}
/>
) : (
static2DWithGradient(item)
)
) : isAnimated ? (
<Animated2DWithGradient
barBackgroundPattern={props.barBackgroundPattern}
patternId={props.patternId}
@ -461,6 +502,7 @@ const RenderBars = (props: Props) => {
roundedBottom={props.roundedBottom || false}
roundedTop={props.roundedTop || false}
gradientColor={props.gradientColor}
noGradient
frontColor={props.frontColor || 'black'}
containerHeight={containerHeight}
maxValue={maxValue}
@ -478,70 +520,52 @@ const RenderBars = (props: Props) => {
barBorderRadius={props.barBorderRadius || 0}
/>
) : (
static2DWithGradient(item)
)
) : isAnimated ? (
<Animated2DWithGradient
barBackgroundPattern={props.barBackgroundPattern}
patternId={props.patternId}
barWidth={props.barWidth}
item={item}
opacity={opacity}
animationDuration={animationDuration || 800}
roundedBottom={props.roundedBottom || false}
roundedTop={props.roundedTop || false}
gradientColor={props.gradientColor}
noGradient
frontColor={props.frontColor || 'black'}
containerHeight={containerHeight}
maxValue={maxValue}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
}
barMarginBottom={barMarginBottom}
cappedBars={props.cappedBars}
capThickness={props.capThickness}
capColor={props.capColor}
capRadius={props.capRadius}
horizontal={props.horizontal}
intactTopLabel={props.intactTopLabel}
barBorderRadius={props.barBorderRadius || 0}
/>
) : (
<Animated2DWithGradient
barBackgroundPattern={props.barBackgroundPattern}
patternId={props.patternId}
barWidth={props.barWidth}
item={item}
opacity={opacity}
animationDuration={animationDuration || 800}
roundedBottom={props.roundedBottom || false}
roundedTop={props.roundedTop || false}
gradientColor={props.gradientColor}
noGradient
noAnimation
frontColor={props.frontColor || 'black'}
containerHeight={containerHeight}
maxValue={maxValue}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
}
barMarginBottom={barMarginBottom}
cappedBars={props.cappedBars}
capThickness={props.capThickness}
capColor={props.capColor}
capRadius={props.capRadius}
horizontal={props.horizontal}
intactTopLabel={props.intactTopLabel}
barBorderRadius={props.barBorderRadius || 0}
/>
<Animated2DWithGradient
barBackgroundPattern={props.barBackgroundPattern}
patternId={props.patternId}
barWidth={props.barWidth}
item={item}
opacity={opacity}
animationDuration={animationDuration || 800}
roundedBottom={props.roundedBottom || false}
roundedTop={props.roundedTop || false}
gradientColor={props.gradientColor}
noGradient
noAnimation
frontColor={props.frontColor || 'black'}
containerHeight={containerHeight}
maxValue={maxValue}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
}
barMarginBottom={barMarginBottom}
cappedBars={props.cappedBars}
capThickness={props.capThickness}
capColor={props.capColor}
capRadius={props.capRadius}
horizontal={props.horizontal}
intactTopLabel={props.intactTopLabel}
barBorderRadius={props.barBorderRadius || 0}
/>
)}
{isAnimated
? renderAnimatedLabel(label, labelTextStyle, item.value)
: renderLabel(label, labelTextStyle, item.value)}
</TouchableOpacity>
{renderTooltip && selectedIndex === index && (
<View
style={{
position: 'absolute',
bottom: barHeight + 60,
left:
(item.barWidth || props.barWidth || 30) * index + initialSpacing,
zIndex: 1000,
}}>
{renderTooltip(item, index)}
</View>
)}
{isAnimated
? renderAnimatedLabel(label, labelTextStyle, item.value)
: renderLabel(label, labelTextStyle, item.value)}
</TouchableOpacity>
</>
);
};

View File

@ -39,6 +39,11 @@ type Props = {
barBackgroundPattern?: Function;
patternId?: String;
xAxisTextNumberOfLines: number;
renderTooltip: Function;
initialSpacing: number;
selectedIndex: number;
setSelectedIndex: Function;
activeOpacity: number;
};
type itemType = {
value?: number;
@ -63,6 +68,7 @@ const RenderStackBars = (props: Props) => {
barBackgroundPattern,
patternId,
item,
index,
containerHeight,
maxValue,
spacing,
@ -71,6 +77,11 @@ const RenderStackBars = (props: Props) => {
label,
labelTextStyle,
xAxisTextNumberOfLines,
renderTooltip,
initialSpacing,
selectedIndex,
setSelectedIndex,
activeOpacity,
} = props;
const disablePress = props.disablePress || false;
const renderLabel = (label: String, labelTextStyle: any) => {
@ -123,7 +134,15 @@ const RenderStackBars = (props: Props) => {
const cotainsNegative = item.stacks.some(item => item.value < 0);
return (
<>
<View
<TouchableOpacity
disabled={disablePress}
activeOpacity={activeOpacity}
onPress={() => {
setSelectedIndex(index);
if(item.onPress){
item.onPress();
}
}}
style={[
{
position: 'absolute',
@ -142,7 +161,8 @@ const RenderStackBars = (props: Props) => {
<TouchableOpacity
key={index}
onPress={stackItem.onPress}
disabled={disablePress}
activeOpacity={activeOpacity}
disabled={disablePress || !stackItem.onPress}
style={[
{
position: 'absolute',
@ -187,7 +207,7 @@ const RenderStackBars = (props: Props) => {
/>
</Svg>
)}
</View>
</TouchableOpacity>
{item.topLabelComponent && (
<View
style={[
@ -214,45 +234,59 @@ const RenderStackBars = (props: Props) => {
};
return (
<View
style={[
{
// overflow: 'visible',
marginBottom: 60,
width: item.stacks[0].barWidth || props.barWidth || 30,
height: totalHeight,
marginRight: spacing,
},
]}>
{props.showVerticalLines && (
<>
<View
style={[
{
// overflow: 'visible',
marginBottom: 60,
width: item.stacks[0].barWidth || props.barWidth || 30,
height: totalHeight,
marginRight: spacing,
},
]}>
{props.showVerticalLines && (
<View
style={{
zIndex: props.verticalLinesZIndex,
position: 'absolute',
height: (containerHeight || 200) + 15,
width: props.verticalLinesThickness,
bottom: 0,
left: (item.barWidth || props.barWidth || 30) / 2,
backgroundColor: props.verticalLinesColor,
}}
/>
)}
{props.showXAxisIndices && (
<View
style={{
zIndex: 2,
position: 'absolute',
height: props.xAxisIndicesHeight,
width: props.xAxisIndicesWidth,
bottom: 0,
left: (item.barWidth || props.barWidth || 30) / 2,
backgroundColor: props.xAxisIndicesColor,
}}
/>
)}
{static2DSimple(item)}
{renderLabel(label || '', labelTextStyle)}
</View>
{renderTooltip && selectedIndex === index && (
<View
style={{
zIndex: props.verticalLinesZIndex,
position: 'absolute',
height: (containerHeight || 200) + 15,
width: props.verticalLinesThickness,
bottom: 0,
left: (item.barWidth || props.barWidth || 30) / 2,
backgroundColor: props.verticalLinesColor,
}}
/>
bottom: totalHeight + 60,
left:
(item.barWidth || props.barWidth || 30) * index + initialSpacing,
zIndex: 1000,
}}>
{renderTooltip(item, index)}
</View>
)}
{props.showXAxisIndices && (
<View
style={{
zIndex: 2,
position: 'absolute',
height: props.xAxisIndicesHeight,
width: props.xAxisIndicesWidth,
bottom: 0,
left: (item.barWidth || props.barWidth || 30) / 2,
backgroundColor: props.xAxisIndicesColor,
}}
/>
)}
{static2DSimple(item)}
{renderLabel(label || '', labelTextStyle)}
</View>
</>
);
};

View File

@ -133,6 +133,7 @@ type PropTypes = {
patternId?: String;
barMarginBottom?: number;
onPress?: Function;
renderTooltip?: Function;
};
type lineConfigType = {
initialSpacing?: number;
@ -192,6 +193,7 @@ type itemType = {
export const BarChart = (props: PropTypes) => {
const scrollRef = useRef();
const [points, setPoints] = useState('');
const [selectedIndex, setSelectedIndex] = useState(-1);
const showLine = props.showLine || false;
const initialSpacing =
props.initialSpacing === 0 ? 0 : props.initialSpacing || 40;
@ -1329,6 +1331,11 @@ export const BarChart = (props: PropTypes) => {
{props.hideAxesAndRules !== true && renderHorizSections()}
<ScrollView
ref={scrollRef}
onTouchStart={evt => {
if (props.renderTooltip) {
setSelectedIndex(-1);
}
}}
onContentSizeChange={() => {
if (scrollRef.current && scrollToEnd) {
scrollRef.current.scrollToEnd({animated: scrollAnimation});
@ -1383,7 +1390,7 @@ export const BarChart = (props: PropTypes) => {
xAxisThickness={xAxisThickness}
barWidth={props.barWidth}
opacity={opacity}
disablePress={props.disablePress}
disablePress={item.disablePress || props.disablePress}
rotateLabel={rotateLabel}
showVerticalLines={showVerticalLines}
verticalLinesThickness={verticalLinesThickness}
@ -1408,6 +1415,11 @@ export const BarChart = (props: PropTypes) => {
item.labelTextStyle || props.xAxisLabelTextStyle
}
xAxisTextNumberOfLines={xAxisTextNumberOfLines}
renderTooltip={props.renderTooltip}
initialSpacing={initialSpacing}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
activeOpacity={props.activeOpacity || 0.2}
/>
);
})
@ -1470,6 +1482,10 @@ export const BarChart = (props: PropTypes) => {
}
onPress={props.onPress}
xAxisTextNumberOfLines={xAxisTextNumberOfLines}
renderTooltip={props.renderTooltip}
initialSpacing={initialSpacing}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
/>
))}
</Fragment>