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 | | 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 | | 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 | | 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", "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.", "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", "main": "src/index.tsx",
"files": [ "files": [

View File

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

View File

@ -39,6 +39,11 @@ type Props = {
barBackgroundPattern?: Function; barBackgroundPattern?: Function;
patternId?: String; patternId?: String;
xAxisTextNumberOfLines: number; xAxisTextNumberOfLines: number;
renderTooltip: Function;
initialSpacing: number;
selectedIndex: number;
setSelectedIndex: Function;
activeOpacity: number;
}; };
type itemType = { type itemType = {
value?: number; value?: number;
@ -63,6 +68,7 @@ const RenderStackBars = (props: Props) => {
barBackgroundPattern, barBackgroundPattern,
patternId, patternId,
item, item,
index,
containerHeight, containerHeight,
maxValue, maxValue,
spacing, spacing,
@ -71,6 +77,11 @@ const RenderStackBars = (props: Props) => {
label, label,
labelTextStyle, labelTextStyle,
xAxisTextNumberOfLines, xAxisTextNumberOfLines,
renderTooltip,
initialSpacing,
selectedIndex,
setSelectedIndex,
activeOpacity,
} = props; } = props;
const disablePress = props.disablePress || false; const disablePress = props.disablePress || false;
const renderLabel = (label: String, labelTextStyle: any) => { const renderLabel = (label: String, labelTextStyle: any) => {
@ -123,7 +134,15 @@ const RenderStackBars = (props: Props) => {
const cotainsNegative = item.stacks.some(item => item.value < 0); const cotainsNegative = item.stacks.some(item => item.value < 0);
return ( return (
<> <>
<View <TouchableOpacity
disabled={disablePress}
activeOpacity={activeOpacity}
onPress={() => {
setSelectedIndex(index);
if(item.onPress){
item.onPress();
}
}}
style={[ style={[
{ {
position: 'absolute', position: 'absolute',
@ -142,7 +161,8 @@ const RenderStackBars = (props: Props) => {
<TouchableOpacity <TouchableOpacity
key={index} key={index}
onPress={stackItem.onPress} onPress={stackItem.onPress}
disabled={disablePress} activeOpacity={activeOpacity}
disabled={disablePress || !stackItem.onPress}
style={[ style={[
{ {
position: 'absolute', position: 'absolute',
@ -187,7 +207,7 @@ const RenderStackBars = (props: Props) => {
/> />
</Svg> </Svg>
)} )}
</View> </TouchableOpacity>
{item.topLabelComponent && ( {item.topLabelComponent && (
<View <View
style={[ style={[
@ -214,45 +234,59 @@ const RenderStackBars = (props: Props) => {
}; };
return ( return (
<View <>
style={[ <View
{ style={[
// overflow: 'visible', {
marginBottom: 60, // overflow: 'visible',
width: item.stacks[0].barWidth || props.barWidth || 30, marginBottom: 60,
height: totalHeight, width: item.stacks[0].barWidth || props.barWidth || 30,
marginRight: spacing, height: totalHeight,
}, marginRight: spacing,
]}> },
{props.showVerticalLines && ( ]}>
{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 <View
style={{ style={{
zIndex: props.verticalLinesZIndex,
position: 'absolute', position: 'absolute',
height: (containerHeight || 200) + 15, bottom: totalHeight + 60,
width: props.verticalLinesThickness, left:
bottom: 0, (item.barWidth || props.barWidth || 30) * index + initialSpacing,
left: (item.barWidth || props.barWidth || 30) / 2, zIndex: 1000,
backgroundColor: props.verticalLinesColor, }}>
}} {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; patternId?: String;
barMarginBottom?: number; barMarginBottom?: number;
onPress?: Function; onPress?: Function;
renderTooltip?: Function;
}; };
type lineConfigType = { type lineConfigType = {
initialSpacing?: number; initialSpacing?: number;
@ -192,6 +193,7 @@ type itemType = {
export const BarChart = (props: PropTypes) => { export const BarChart = (props: PropTypes) => {
const scrollRef = useRef(); const scrollRef = useRef();
const [points, setPoints] = useState(''); const [points, setPoints] = useState('');
const [selectedIndex, setSelectedIndex] = useState(-1);
const showLine = props.showLine || false; const showLine = props.showLine || false;
const initialSpacing = const initialSpacing =
props.initialSpacing === 0 ? 0 : props.initialSpacing || 40; props.initialSpacing === 0 ? 0 : props.initialSpacing || 40;
@ -1329,6 +1331,11 @@ export const BarChart = (props: PropTypes) => {
{props.hideAxesAndRules !== true && renderHorizSections()} {props.hideAxesAndRules !== true && renderHorizSections()}
<ScrollView <ScrollView
ref={scrollRef} ref={scrollRef}
onTouchStart={evt => {
if (props.renderTooltip) {
setSelectedIndex(-1);
}
}}
onContentSizeChange={() => { onContentSizeChange={() => {
if (scrollRef.current && scrollToEnd) { if (scrollRef.current && scrollToEnd) {
scrollRef.current.scrollToEnd({animated: scrollAnimation}); scrollRef.current.scrollToEnd({animated: scrollAnimation});
@ -1383,7 +1390,7 @@ export const BarChart = (props: PropTypes) => {
xAxisThickness={xAxisThickness} xAxisThickness={xAxisThickness}
barWidth={props.barWidth} barWidth={props.barWidth}
opacity={opacity} opacity={opacity}
disablePress={props.disablePress} disablePress={item.disablePress || props.disablePress}
rotateLabel={rotateLabel} rotateLabel={rotateLabel}
showVerticalLines={showVerticalLines} showVerticalLines={showVerticalLines}
verticalLinesThickness={verticalLinesThickness} verticalLinesThickness={verticalLinesThickness}
@ -1408,6 +1415,11 @@ export const BarChart = (props: PropTypes) => {
item.labelTextStyle || props.xAxisLabelTextStyle item.labelTextStyle || props.xAxisLabelTextStyle
} }
xAxisTextNumberOfLines={xAxisTextNumberOfLines} 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} onPress={props.onPress}
xAxisTextNumberOfLines={xAxisTextNumberOfLines} xAxisTextNumberOfLines={xAxisTextNumberOfLines}
renderTooltip={props.renderTooltip}
initialSpacing={initialSpacing}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
/> />
))} ))}
</Fragment> </Fragment>