How to use linear gradient in rechart - javascript

I have to create this graph in React. I am using the rechart npm package to do so. But I am not able to get the corner radiuses and the linear gradient. I am open to use any other library if needed.
Image
What I have tried?
I have used rechart Pie component to achieve this. Here is the code
import React from 'react';
import { PieChart, Pie, Sector } from 'recharts';
import getDevice from '../../styles/devices';
const renderActiveShape = (props) => {
const { cx, cy, startAngle, endAngle, payload, innerRadius, outerRadius, z, cornerRadius } = props;
console.log(startAngle, endAngle)
return (
<g>
<text x={cx} y={cy - 20} dy={8} fontSize={z ? '16px' : "24px"} textAnchor="middle" fill="#001233" fontWeight="bold">
{/* {parseFloat(payload.value / 1000).toFixed(1)}K */}
{payload.value < 1000 ? payload.value : `${parseFloat(payload.value / 1000).toFixed(1)}K`}
</text>
<text x={cx} y={cy + 5} dy={8} fontSize="12px" textAnchor="middle" fill="#5C677D">
{payload.name}
</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius + 20}
startAngle={startAngle}
endAngle={endAngle}
fill={"#12A4ED"}
cornerRadius={payload.name === 'Deposit' ? cornerRadius : 0}
/>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius - 18}
outerRadius={innerRadius - 10}
startAngle={startAngle}
endAngle={endAngle}
fill={"#7674740f"}
/>
{/* <path d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`} stroke={fill} fill="none" />
<circle cx={ex} cy={ey} r={2} fill={fill} stroke="none" /> */}
{/* <text x={ex + (cos >= 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill="#333">{`${value}%`}</text> */}
</g>
);
};
export default function Chart({ data }) {
// const [activeIndex, setActiveIndex] = useState(1)
// const onPieEnter = (_, index) => {
// setActiveIndex(index)
// };
const { isMobile } = getDevice()
return (
<PieChart width={400} height={350}>
<Pie
activeIndex={1}
activeShape={renderActiveShape}
data={data}
cx={200}
innerRadius={isMobile ? 60 : 80}
outerRadius={isMobile ? 100 : 120}
cornerRadius={isMobile ? 5 : 10}
fill="#7DC762"
dataKey="value"
z={isMobile}
>
</Pie>
</PieChart>
);
}
But what I get by using this is
Can anyone help how to achieve this? Any help is appreciated.

To add gradient color to your chart, you can do the following:
This is just an example:
<PieChart width={400} height={350}>
{/* This will allow to add color based on your need, so update it accordingly */}
<defs>
<linearGradient id="colorUv" x1="1" y1="1" x2="0" y2="0">
<stop offset="30%" stopColor="#6584FF" stopOpacity={0.5} />
<stop offset="95%" stopColor="#FFFFFF" stopOpacity={0.5} />
</linearGradient>
</defs>
<Pie
activeIndex={1}
activeShape={renderActiveShape}
data={data}
cx={200}
innerRadius={isMobile ? 60 : 80}
outerRadius={isMobile ? 100 : 120}
cornerRadius={isMobile ? 5 : 10}
fill="url(#colorUv)"//Add the id 'colorUv' which is used in linearGradient
dataKey="value"
z={isMobile}
></Pie>
</PieChart>

Related

How to only show one label using Labellist in Recharts?

I am using Recharts and using labellist to add label on top of every stacked bar chart, but instead of only one label for every stacked chart it is creating a label for stack (every stack) and my custom render events looks like:
const renderCustomizedEvent = (props) => {
const { x, y, width, height, value } = props;
const radius = 20;
if (!value) return null;
return (
<g>
<rect
x={x + width / 2 - 30}
y={y - radius - 10}
width="60"
height="20"
style={{ strokeWidth: 3, fill: "#203557" }}
/>
<text
x={x + width / 2}
y={y - radius}
fill="#fff"
textAnchor="middle"
dominantBaseline="middle"
style={{ fontSize: "10px" }}
>
data
</text>
</g>
);
};
and my data looks like:
const data = [
{
Date: "2022-12",
Electricity: 220.129804,
Gas: 90,
Net_Carbon_Impact: 90,
EventType: "Event_Gas"
},
{
Date: "2022-11",
Electricity: 220.129804,
Gas: 0.023072,
Net_Carbon_Impact: 220.15287600000002
}
]
<ResponsiveContainer width={width} height={400}>
<ComposedChart
data={data}
stackOffset="sign"
margin={{ left: 20, bottom: 60 }}
barSize={40}
barCategoryGap={40}
>
<CartesianGrid color="#E1E2E1" vertical={false} />
<XAxis
dataKey="Date"
axisLine={false}
tickLine={false}
scale="band"
tick={customizedGroupTick}
interval={0}
/>
<YAxis
tickLine={false}
color="#E1E2E1"
tickSize={1}
tickCount={uniquelabels.length + 1}
label={{ value: "tCO2-e", angle: -90, position: "insideLeft" }}
tickFormatter={(e) => {
return Math.abs(e) >= 1000000000
? `${e / 1000000000}b`
: Math.abs(e) >= 1000000
? `${e / 1000000}m`
: Math.abs(e) >= 1000
? `${e / 1000}k`
: e;
}}
/>
<Tooltip content={customizedTooltip} cursor={false} />
<Legend
layout="horizontal"
verticalAlign="top"
content={renderLegend}
align="left"
/>
{uniquelabels.map((item: string) => (
<Bar
key={item}
dataKey={item}
stackId="a"
fill={renderColor(item)}
width="30px"
>
<LabelList
position="top"
dataKey="EventType"
content={renderCustomizedEvent}
/>
</Bar>
))}
<Line
strokeWidth={3}
type="monotone"
dataKey="Net_Carbon_Impact"
stroke="#FFE81C"
dot={{ strokeWidth: 8, fill: "#FFE81C" }}
/>
</ComposedChart>
</ResponsiveContainer>
how it looks like now is like the picture attached, and as it is shown in the picture also Jan2023 has two events (Gas and Electricity(is is hidden behind the bar chart)) but i only want one of them which is gas. any help will be appreciate. Thank you in advance

Customize ReChart bar chart labels and colors for each Bar

I have a rechart bar chart that I would like to have the Labels like this...
And I want the colors to be like this...
The problem is that I can't seem to get them both at the same time. The second image is the closest I can get but it is bringing in the value instead of the name hence the int(85) instead of string(85%) and it is also not angles.
Here is my code. I commented out the part that allows for angled labels because it wont work when is there..
import { BarChart, Bar, LabelList, Cell } from "recharts";
import { colors } from "../../Colors/colors";
const data = [
{
name: `${85}%`,
uv: 85,
},
{
name: `${95}%`,
uv: 95,
},
{
name: `${80}%`,
uv: 80,
},
];
export default function MyBarChart(): JSX.Element {
return (
<BarChart width={150} height={400} data={data}>
<Bar
dataKey="uv"
fill={colors.blueTheme[0]}
radius={8}
label={{ position: "insideBottom", fill: "black" }}
name="name"
>
{/* <LabelList
dataKey="name"
position="insideBottom"
angle={270}
offset={25}
/> */}
{colors.blueTheme.map((entry, index) => (
<Cell key={`cell-${index}`} fill={colors.blueTheme[index % 20]} />
))}
</Bar>
</BarChart>
);
}
The colors.tsx
export const colors = {
blueTheme: ["#85A5FF", "#ADC6FF", "#D6E4FF"],
};
How can I get the angled labels with the color differences simultaneously?
I figured it out...
<BarChart width={150} height={400} data={data}>
<Bar dataKey="uv" radius={8}>
<LabelList
dataKey="name"
position="insideBottom"
angle={270}
offset={25}
fill="black"
/>
{colors.blueTheme.map((entry, index) => (
<Cell key={`cell-${index}`} fill={colors.blueTheme[index % 20]} />
))}
</Bar>
</BarChart>
If you update the data and add the color each bar needs to have, as follows:
const data = [
{
name: `${85}%`,
uv: 85,
color: "#85A5FF" // you may pass a value directly as string
},
{
name: `${95}%`,
uv: 95,
color: colors.blueTheme[1] // or colors.blueTheme by index
},
{
name: `${80}%`,
uv: 80,
color: colors.blueTheme[getIndex()] // or some custom logic to evaluate index
},
];
You can update the code as follows and it will work:
export default function MyBarChart(): JSX.Element {
return (
<BarChart width={300} height={400} data={data}>
<Bar
dataKey="uv"
fill="#85A5FF"
radius={8}
label={{
position: "insideBottom",
angle: -60,
fill: "black",
offset: 25
}}
>
<LabelList dataKey="name" />
{data.map((entry, index) => (
<Cell fill={entry.color} key={`cell-${index}`} />
))}
</Bar>
</BarChart>
);
}
Working example:
https://codesandbox.io/embed/bar-chart-with-customized-event-forked-4ygf7i?fontsize=14&hidenavigation=1&theme=dark

Are there alternatives to querySelectorAll in React?

I'm trying to refactor some javascript codes using React. What I'm trying to do is -> I have some elements which have the same className and I want to have control over each element. For this, in javascript I could just use the queryselectorAll and forEach functions, like
<Svg>
<circle cx="100" cy="60" r="20" class="human"></circle>
<line x1="100" y1="80" x2="100" y2="120" class="human"></line>
<line x1="70" x2="100" y1="75" y2="95" class="human"></line>
<line x1="130" x2="100" y1="75" y2="95" class="human"></line>
<line x1="70" x2="100" y1="140" y2="120" class="human"></line>
<line x1="130" x2="100" y1="140" y2="120" class="human"></line>
</Svg>
const wrongLetters = [];
window.addEventListener('keydown", (e) => {
if(e.keyCode >= 65 && e.keyCode <= 90) {
wrongLetters.push(e.key)
}
}
const humans = documentSelectorAll(".human")
humans.forEach((human, index) => {
if (index < wrongLetters.length) {
human.style.display = "block";
}
if (6 === wrongLetters.length) {
popUpContainer.style.display = "flex";
comment.innerText = "You have lost";
}
}); }
However, I assume it'd be different in React even if I change 'class' to 'className'? For this, I've already searched some relevant posts and tried to use 'ref' but still don't know exactly what to do in this case.
Any advice would be appreciated.
+edit/ What I'm trying to do is every time I add an element to 'wrongLetters' array, each element in Svg tag will be displayed. Each Svg element is set to 'display: none' as default in javascript
Put the wrongLetters into state that gets changed instead of pushing to the array. Then, in React, analyze that state to determine how many elements need to be hidden. Since this can encompass all of the functionality you're looking for, there's no need to be able to access the individual DOM elements with useRef or querySelectorAll:
const App = () => {
const [pressed, setPressed] = React.useState([]);
React.useEffect(() => {
window.addEventListener('keydown', (e) => {
if (!pressed.includes(e.keyCode) && e.keyCode >= 65 && e.keyCode <= 90) {
setPressed(pressed => [...pressed, e.keyCode]);
}
});
}, []);
return (
<svg>
<circle style={{display: pressed.length > 5 ? 'block' : 'none'}} cx="100" cy="60" r="20" stroke="black"></circle>
<line style={{display: pressed.length > 4 ? 'block' : 'none'}} x1="100" y1="80" x2="100" y2="120" stroke="black"></line>
<line style={{display: pressed.length > 3 ? 'block' : 'none'}} x1="70" x2="100" y1="75" y2="95" stroke="black"></line>
<line style={{display: pressed.length > 2 ? 'block' : 'none'}} x1="130" x2="100" y1="75" y2="95" stroke="black"></line>
<line style={{display: pressed.length > 1 ? 'block' : 'none'}} x1="70" x2="100" y1="140" y2="120" stroke="black"></line>
<line style={{display: pressed.length > 0 ? 'block' : 'none'}} x1="130" x2="100" y1="140" y2="120" stroke="black"></line>
</svg>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
line {
color: black;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
That's the React way of doing things - use state when you can; avoid accessing/changing the DOM directly, just make the render reflect the state, and change the state when needed.
As I said in my comment, in react you'd have a component that renders those elements, updating them or rendering different elements according to changes in application or component state. You very, very rarely need to access the DOM directly in react.
Here's a rough implementation of the sort of thing I mean. There's a live/working copy of it on codesandbox:
import React, { useEffect } from "react";
import "./styles.css";
// an array of the svg elements; the component below renders
// a subset of these according to number of attempts.
// i'm not sure this is how i'd do this in the real world
// but it's good enough for this demo.
const segments = [
<circle cx="100" cy="60" r="20" class="human"></circle>,
<line x1="100" y1="80" x2="100" y2="120" class="human"></line>,
<line x1="70" x2="100" y1="75" y2="95" class="human"></line>,
<line x1="130" x2="100" y1="75" y2="95" class="human"></line>,
<line x1="70" x2="100" y1="140" y2="120" class="human"></line>,
<line x1="130" x2="100" y1="140" y2="120" class="human"></line>
];
export default function App() {
// track the number of attempts in component state
const [attempts, setAttempts] = React.useState(0);
// handler for keydown events that for this demo
// doesn't do anything besides increment the number of attempts
const onKeyDown = React.useCallback(() => setAttempts(attempts + 1), [
attempts
]);
useEffect(() => {
// when the component mounts, add the keydown listener
window.addEventListener("keydown", onKeyDown);
// when the component unmounts remove the listener
return () => window.removeEventListener("keydown", onKeyDown);
}, [onKeyDown]);
// get the visible subset of segments based on number of attempts
const visibleSegments = segments.slice(0, attempts);
// return the new markup
return (
<div className="App">
<svg viewBox="0 0 200 200" style={{ width: "300px" }}>
{visibleSegments}
</svg>
{attempts >= segments.length && (
<div>
<div>You Lose or whatever</div>
<button onClick={() => setAttempts(0)}>Reset</button>
</div>
)}
</div>
);
}
Brother if you want to access the DOM in React JS tou have to create ref using createRef() Hooks and then you can able to access the Dom in react like javascript
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
you can acess the node by calling this.myRef.current method
const node = this.myRef.current;
You can add the same ref in multiple html element and when u called this.myRef.current then all the element you put the ref you can access...

Recharts won't automatically calculate YAxis ticks

I'm trying to add a chart using recharts with the latest exchange rates of some currencies. Data is shown correctly, but the chart always starts at 0 and goes to a bit above the max value.
The chart is correct, however it doesn't need to start at 0, because doing this, it is almost a line.
Here is the picture of the chart:
I'd like that recharts could calculate automatically the ticks, so it would begin a little bit below the minimum value from the data and finish a little bit above the maximum value.
Here is my code:
import React, { useEffect, useState } from "react";
import { StyledCurrencyChart } from "./styles";
import {
AreaChart,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Area,
ResponsiveContainer
} from "recharts";
import useExchangeRateProvider from "../../hooks/useExchangeRateProvider";
import api from "../../services/api";
import theme from "../../styles/customMuiTheme";
import moment from "moment";
function Chart({ data }) {
return (
<ResponsiveContainer width="100%" height={200}>
<AreaChart
width="100%"
height={250}
data={data}
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
>
<defs>
<linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
<stop
offset="5%"
stopColor={theme.palette.secondary.main}
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor={theme.palette.secondary.main}
stopOpacity={0}
/>
</linearGradient>
</defs>
<XAxis
dataKey="date"
tickFormatter={formatDate}
style={{ fill: "#ffffff" }}
/>
<YAxis tickFormatter={formatRate} style={{ fill: "#ffffff" }} />
<CartesianGrid
strokeDasharray="3 3"
fill="rgba(255, 255, 255, 0.3)"
/>
<Tooltip />
<Area
type="monotone"
dataKey="rate"
stroke={theme.palette.secondary.main}
fillOpacity={1}
fill="url(#colorUv)"
/>
</AreaChart>
</ResponsiveContainer>
);
}
// function to format date
function formatDate(tickItem) {
return moment(tickItem).format("MMM Do YY");
}
// function to format rate
function formatRate(tickItem) {
return parseFloat(tickItem).toLocaleString("en-US");
}
export default function CurrencyChart() {
// selected country
const exchangeRateProvider = useExchangeRateProvider();
const country = exchangeRateProvider.state.exchangeRateProvider.country;
// state
const [values, setValues] = useState({
loading: true,
error: false,
data: {}
});
// update chart on country change
useEffect(() => {
async function updateChart() {
try {
const { data } = await api.get(
`/public/rates/history/${country}`
);
setValues({ loading: false, error: false, data });
} catch (e) {
setValues({ loading: false, error: true, data: {} });
}
}
updateChart();
}, [country]);
return (
<StyledCurrencyChart>
<Chart data={values.data} />
</StyledCurrencyChart>
);
}
How can I achieve it? I tried messing around with interval and ticks props under the <YAxis>, but I couldn't make it work.
Thanks in advance
Use the yAxis domain prop:
<YAxis type="number" domain={[0, 1000]}/> // set to whatever you want [yMix, yMax]
For calculating automatically you can use something like these
<YAxis type="number" domain={['dataMin', 'dataMax']} />
<YAxis type="number" domain={[0, 'dataMax']} />
<YAxis type="number" domain={['auto', 'auto']} />
Please make sure to use integer numbers for the values, If you use a string as a value for YAxis it can not recognize the max value correctly.

Dynamic tag name with JSX and React Native

This question is inspired by this question: Dynamic tag name in jsx and React but this time it is for React Native.
I am trying to render a <TouchableOpacity> button for iOS and <TouchableNativeFeedback> button for Android, with no success.
I tried the following code:
render() {
const { element } = this.props;
const ButtonElementName = Platform.OS === 'ios' ? 'TouchableOpacity' : 'TouchableNativeFeedback';
return (
<ButtonElementName style={styles.sectionEntryContainer}>
<View style={styles.sectionDayContainer}>
<LinearGradient
style={styles.gradientStyle}
start={{x: 0, y: 0}} end={{x: 1, y: 0}}
colors={COLOR_MAP[element.item.data.mood].colors}
>
<Text style={styles.sectionDayText}>{ moment(element.item.day, 'D').format('Do') }</Text>
</LinearGradient>
</View>
<View style={styles.sectionDayDescriptionContainer}>
<Text style={styles.sectionDayText}>{ element.item.data.optionalDescription }</Text>
</View>
</ButtonElementName>
);
}
but get the following error when trying it on the iOS Simulator:
Invariant Violation: View config not found for name TouchableOpacity
How would I go about doing this without creating a HOC?
You shouldn't reference the element by it's name, but directly by class
import {
TouchableNativeFeedback,
TouchableOpacity
} from 'react-native';
const Element = Platform.OS === 'ios' ? TouchableOpacity : TouchableNativeFeedback;
class SomeComponent extends React.Component {
render() {
const { element } = this.props;
return (
<Element style={styles.sectionEntryContainer}>
<View style={styles.sectionDayContainer}>
<LinearGradient
style={styles.gradientStyle}
start={{x: 0, y: 0}} end={{x: 1, y: 0}}
colors={COLOR_MAP[element.item.data.mood].colors}
>
<Text style={styles.sectionDayText}>{ moment(element.item.day, 'D').format('Do') }</Text>
</LinearGradient>
</View>
<View style={styles.sectionDayDescriptionContainer}>
<Text style={styles.sectionDayText}>{ element.item.data.optionalDescription }</Text>
</View>
</Element>
);
}
}

Categories