Background:
I am trying to add a tooltip to a react-native-svg chart following this tutorial.
The link to the tutorial: Link
Current Code Implementation:
import React, {useState} from 'react';
import {View, Text, Dimensions} from 'react-native';
import {LineChart} from 'react-native-chart-kit';
import {Rect, Text as TextSVG, Svg} from 'react-native-svg';
const Charts = () => {
let [tooltipPos, setTooltipPos] = useState({
x: 0,
y: 0,
visible: false,
value: 0,
});
return (
<View>
<LineChart
data={{
labels: ['January', 'February', 'March', 'April', 'May', 'June'],
datasets: [
{
data: [100, 110, 90, 130, 80, 103],
},
],
}}
width={Dimensions.get('window').width}
height={250}
yAxisLabel="$"
yAxisSuffix="k"
yAxisInterval={1}
chartConfig={{
backgroundColor: 'white',
backgroundGradientFrom: '#fbfbfb',
backgroundGradientTo: '#fbfbfb',
decimalPlaces: 2,
color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
style: {
borderRadius: 0,
},
propsForDots: {
r: '6',
strokeWidth: '0',
stroke: '#fbfbfb',
},
}}
bezier
style={{
marginVertical: 8,
borderRadius: 6,
}}
decorator={() => {
return tooltipPos.visible ? (
<View>
<Svg>
<Rect
x={tooltipPos.x - 15}
y={tooltipPos.y + 10}
width="40"
height="30"
fill="black"
/>
<MaterialCommunityIcons
name="run"
size={32}
color="rgb(67, 67, 67)"
/>
<TextSVG
x={tooltipPos.x + 5}
y={tooltipPos.y + 30}
fill="white"
fontSize="16"
fontWeight="bold"
textAnchor="middle">
{tooltipPos.value}
</TextSVG>
</Svg>
</View>
) : null;
}}
onDataPointClick={(data) => {
let isSamePoint = tooltipPos.x === data.x && tooltipPos.y === data.y;
isSamePoint
? setTooltipPos((previousState) => {
return {
...previousState,
value: data.value,
visible: !previousState.visible,
};
})
: setTooltipPos({
x: data.x,
value: data.value,
y: data.y,
visible: true,
});
}}
/>
</View>
);
};
Question:
I want to add the icon(Running icon) as seen on the image above, to be next to the tool-tip text.
The Icon, then the text inside the rectangle filled in Black. When I try to position it,, it shows up on the extreme top left for some reason. How do I position it?
You can use the ForeignObject component from react-native-svg and change your decorator to something like this:
decorator={() => {
return tooltipPos.visible ? (
<ForeignObject x={tooltipPos.x} y={tooltipPos.y}>
<View
style={{
width: 70,
flexDirection: 'row',
backgroundColor: 'black',
}}>
<MaterialCommunityIcons
name="run"
size={32}
color="rgb(67, 67, 67)"
/>
<Text
style={{
color: 'white',
fontSize: 16,
fontWeight: 'bold',
textAnchor: 'middle',
}}>
{tooltipPos.value}
</Text>
</View>
</ForeignObject>
) : null;
}}
The problem with what you had before is that the react-native-svg Text and Rect components use x and y coordinates and your icon doesn't, so the positioning will be off.
The benefit of the approach shown above is that you only have to specify the x and y props of the ForeignObject. Everything inside the ForeignObject can be regular views, positioned as you normally would (https://github.com/react-native-community/react-native-svg#foreignobject).
I've chosen the tooltipPos.x and tooltipPos.y for the x and y prop values of the ForeignObject respectively, but you could add an offset if necessary.
Be sure to import ForeignObject from react-native-svg.
Related
I'm trying to create a histogram with Value, Id and Group code. I need to group the Ids based on the group code and put the labels on top. I'm also trying to add a horizontal scroll bar. Can you help me with this?
I tried to do like in this picture but I couldn't.
My data is in the form of this object
{
id: 1,
value: 0.16882,
group_id: 'group_1',
fill: 'red',
}
const App = () => {
const sharedAxisStyles = {
tickLabels: {
fontSize: 10
},
axisLabel: {
padding: 39,
fontSize: 12,
},
grid: {
stroke: "#546E7A",
strokeWidth: 0.1,
}
};
return (
<VictoryChart
domainPadding={{
x: 20,
}}
containerComponent={(
<VictoryZoomContainer
allowZoom={false}
zoomDomain={{
x: [0, 22],
}}
/>
)}
>
<VictoryLabel
x={225}
y={30}
textAnchor="middle"
text="Group code"
/>
<VictoryBar
data={data}
x={(x) => `id_${x.id}`}
y="value"
barWidth={15.8}
style={{
data: {
fill: ({datum}) => datum.fill,
}
}}
/>
<VictoryAxis
tickCount={22}
tickLabelComponent={<VictoryLabel
angle={-90}
textAnchor="end"
style={[
{ fontSize: 3, fontWeight: 'bold', fill: '#78909C' },
]}
/>}
/>
<VictoryAxis
dependentAxis
label="Value"
tickFormat={(t) => {
return (
(t).toFixed(2)
);
}}
style={sharedAxisStyles}
/>
</VictoryChart>
);
};
When I write these codes, the output I get is like this:
you will need to use VictoryGroup : https://formidable.com/open-source/victory/docs/victory-group/
And use labels to add title for each group https://formidable.com/open-source/victory/docs/victory-group/#labels
What I am trying to do is the following:
But I can't manage to get it working. I was able to get the data to read in from 2 different data sources but it somehow does not render the second line properly on the x-axis.
Here is the example code:
import React from "react";
import { render } from "react-dom";
import { LineChart, Line, XAxis, YAxis, ReferenceLine } from "recharts";
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
const data = [];
const maxBudget = 300;
for (let i = 0; i < 20; i++) {
let d = {
day: i,
value: Math.random() * (maxBudget + 50) + 100
};
data.push(d);
}
const testline = [{ x: 0, y: 300 }, { x: 20, y: 0 }]
const App = () => (
<div style={styles}>
<LineChart
width={500}
height={300}
margin={{ top: 5, right: 20, bottom: 5, left: 0 }}
>
<Line type="monotone" data={data} dataKey="value" stroke="#8884d8" dot={false} />
<Line type="linear" data={testline} dataKey="y" stroke="#FF3333" dot={false} strokeWidth={2} />
<XAxis dataKey="day" type="number" tickCount={11} />
<YAxis />
<ReferenceLine
y={maxBudget}
label={{
position: "center",
value: "Max budget"
}}
strokeDasharray="5 5"
/>
</LineChart>
</div>
);
render(<App />, document.getElementById("root"));
ok I m stupid. I just found it out by myself. Sometimes you don't see the wood for the trees for days :(.
The x value must be the same as defined in the XAxis so I had to change x:0 to day:0:
const testline = [{ day: 0, y: 300 }, { day: 20, y: 0 }]
Now it works
Current Output:
Expected Output:
Current Code Implementation:
import React from 'react'
import { LineChart, XAxis, YAxis } from 'react-native-svg-charts'
import { View, Text } from 'react-native'
import { Line, Circle, G, Rect, } from 'react-native-svg'
export default class Chart extends React.PureComponent {
handleFill = (value) => {
if (value>100){
return "rgba(190, 30, 45, 0.5)"
}
else if (value>80 && value<=100){
return "yellow"
}
else{
return "#CCE6D0"
}
}
render() {
const data = [
...
];
const values = data.map(a => a.value);
const days = data.map(a => a.day);
const axesSvg = { fontSize: 10, fill: 'grey' };
const verticalContentInset = { top: 10, bottom: 10 }
const xAxisHeight = 30
const Decorator = ({x, y, data}) => {
return values.map((value, index) => (
<View>
<View style={{marginBottom:50}}>
<Rect
x={x(index)-1.75}
y={y(value+8)}
width="4"
height="40"
fill={this.handleFill(value)}
/>
</View>
<Circle
key={index}
cx={x(index)}
cy={y(value)}
r={2}
stroke={'#639123'}
fill={'#606060'}
/>
</View>
));
};
return (
<View>
<Text style={{fontSize:10}}>BPM</Text>
<View style={{ height: 200, flexDirection: 'row' }}>
<YAxis
...
/>
<View style={{ flex: 1, marginLeft: 10 }}>
<LineChart
...
>
<Decorator />
</LineChart>
<XAxis
....
/>
</View>
</View>
</View>
)
}
}
I am using the react-native-svg-charts library to render a line chart with custom handlers.
As seen in the code, the handler consists of a circle and a rect, which simply do not show up. Could this be a library version problem? I have updated both react-native-svg and react-native-svg-charts to the latest version. What am I missing? Any ideas?
I've gotten it to work like this:
import React from 'react';
import {LineChart, XAxis, YAxis} from 'react-native-svg-charts';
import {View, Text} from 'react-native';
import {Svg, Line, Circle, G, Rect} from 'react-native-svg';
export default class CustomLineChartOne extends React.PureComponent {
handleFill = (value) => {
if (value > 100) {
return 'rgba(190, 30, 45, 0.5)';
} else if (value > 80 && value <= 100) {
return 'yellow';
} else {
return '#CCE6D0';
}
};
render() {
const xAxisLabels = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24];
const Decorator = ({x, y, data}) => {
return data.map((value, index) => (
<View>
<Rect
x={x(index) - 1.75}
y={y(value + 8)}
width="4"
height="40"
rx="2"
fill={this.handleFill(value)}
/>
<Circle
key={index}
cx={x(index)}
cy={y(value)}
r={2}
stroke={'#639123'}
fill={'#606060'}
/>
</View>
));
};
return (
<View
style={{
height: 200,
flexDirection: 'row',
padding: 20,
}}>
<YAxis
data={data}
contentInset={{top: 20, bottom: 20}}
svg={{
fill: 'black',
fontSize: 10,
}}
/>
<View style={{flex: 1}}>
<LineChart
style={{flex: 1}}
data={data}
svg={{stroke: 'rgb(134, 65, 244)'}}
contentInset={{top: 10, bottom: 10, left: 10, right: 10}}>
<Decorator />
</LineChart>
<XAxis
style={{marginHorizontal: -10}}
data={data}
formatLabel={(value, index) => xAxisLabels[index]}
contentInset={{left: 10, right: 10}}
svg={{fontSize: 10, fill: 'black'}}
/>
</View>
</View>
);
}
}
The important parts to look at here are how the data and xAxisLabels are defined, how the labels are formatted and how data is mapped over inside the decorator to create the bars and circles.
It's not entirely clear what the exact problem was with your code as you've left out some important parts, but I think the problem was mainly related to how you had set up your data.
The result looks like this:
I want to use <RNPickerSelect./> with a <TextInput/> in a single row. So, when I make the Parent flexDirection: row, I see only the arrow and no text. Even if I remove the <TextInput/>, I don't see any text in the Picker.
import React, { Component } from 'react';
import {
StyleSheet,
View,
} from 'react-native';
import RNPickerSelect from 'react-native-picker-select';
type Props = {}
const countryCode = [
{
label: '+91',
value: '+91',
},
{
label: '+1',
value: '+1',
},
{
label: '+2',
value: '+2',
},
];
export default class PickerTest extends Component<Props> {
constructor() {
super()
this.state = {
phoneNumber: "",
countryCode: ""
}
}
render() {
return (
<View style={{flexDirection:'row'}}>
<View paddingVertical={5}>
{/* and hiding the InputAccessoryView on iOS */}
<RNPickerSelect
placeholder={{}}
items={countryCode}
onValueChange={value => {
this.setState({
countryCode: value,
});
}}
InputAccessoryView={() => null}
style={pickerSelectStyles}
value={this.state.countryCode}
/>
</View>
</View>
);
}
}
const pickerSelectStyles = StyleSheet.create({
inputIOS: {
fontSize: 16,
paddingVertical: 12,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: 'gray',
borderRadius: 4,
color: 'black',
paddingRight: 30, // to ensure the text is never behind the icon
},
inputAndroid: {
fontSize: 16,
paddingHorizontal: 10,
paddingVertical: 8,
borderWidth: 0.5,
borderColor: 'purple',
borderRadius: 8,
color: 'black',
paddingRight: 30, // to ensure the text is never behind the icon
},
});
On running the above app, I get something like this
As you can see the picker is not showing the text.
Below are the configuration I am using
react-native-picker-select version: 6.3.3
react-native version: 0.61.2
react version: 16.9.0
this is an upstream issue: https://snack.expo.io/HkygCqhsr
options:
useNativeAndroidPickerStyle prop
set width and height with inputAndroid style prop
Add the atribute "pickerProps" to the RNPickerSelect with the option overflow: 'hidden'
<RNPickerSelect style={styles.selectContainer}
...
pickerProps={{ style: { height: 214, overflow: 'hidden' } }}
...
/>
I'm using nivo responsive-bar to put a plot in my app. I've been asked to add a line showing the cumulative total on the same graph. Is there any way to do this? From the documentation I'm struggling to work out how to do it.
Here's the code I have so far (I've obfuscated the axis label and function names for security reasons!)
class CChart extends React.Component {
render = () => {
return (
<div style={{ height: '40em' }}>
<h3 className='text-center'>C</h3>
<ResponsiveBar
margin={{ top: 20, right: 0, bottom: 60, left: 80 }}
data={this.props.results}
indexBy='year'
colorBy={() => '#1f77b4'}
keys={['C']}
enableLabel={false}
axisLeft={{
legend: 'C(millions)',
legendPosition: 'center',
legendOffset: -60,
tickSize: 5,
tickPadding: 15,
format: v => Math.floor(v / 1000000).toLocaleString()
}}
axisBottom={{
legend: 'Year',
legendPosition: 'center',
legendOffset: 50,
tickSize: 5,
tickPadding: 15,
tickValues: this.props.results.map(x => x.year).filter(x => x % 5 === 0)
}}
theme={{
axis: {
fontSize: '1em',
legendFontSize: '1.2em'
}
}}
tooltipFormat={value => value.toLocaleString()}
animate={false}
markers={[
{ axis: 'y', value: 0 }
]}
/>
</div>
)
}
static propTypes = {
results: PropTypes.arrayOf(PropTypes.shape({
year: PropTypes.string.isRequired,
C: PropTypes.number.isRequired
})).isRequired
}
static mapStateToProps = state => ({
results: getCResults(state)
})
}
According to the #plouc (the founder of the Nivo library), as of September, 2018 you should be able to compose graphs.
https://github.com/plouc/nivo/issues/139
(scroll down until Sept 12)
It is not possible currently (https://github.com/plouc/nivo/issues/139)