I have to represent the below array in a chart using Recharts Js library. I want the Y axis to represent the cost property and the X axis to represent the volume property and the chart must be a bar chart (requested by the client). Basically, the bar must grow in height based on their cost but they also have to grow in height based on their volume and that's what I'm struggling with: I can't find a way to display the bars based on their volume too. In this picture it's possible to see the desired result, it was sent me by the client but be aware that the visual representation might not be exact and consistent with the data.
What I've tried: I tried to draw the bars "manually" using the shape prop in the Bar component and setting a width based on the volume value but it's just a weak workaround because the scale of the X axis won't be consistent with the bars. I also tried to set the width with the barSize prop but this only affects the height in case of a horizontal chart
Example of data array:
[
{
volume 1500,
cost: -136.06243,
totalVolumeCost: -204093.645
},
{
volume 1000,
cost: -272.12486,
totalVolumeCost: -294166.97366
}
]
Question: How can I set the bars width in order to display the volume value?
What I got so far: https://codesandbox.io/s/overall-cost-implication-forked-1sumje
I tried to accomplish it with a BarChart, but it doesn't seem possible: You can edit the BarGap, but there is no override for the container of each bar.
An alternative that still uses Recharts is this codesandbox:
https://codesandbox.io/s/area-chart-with-variable-rectangle-widths-h8o780?file=/src/App.tsx
The data format needs some modification, but i've written a loop for that:
//Init edited data data
let dataAdjusted = [{ volume: 0, cost: 0 }];
for (let i = 0; i < data.length; i++) {
let currentData = data[i];
dataAdjusted.push({
volume: dataAdjusted.at(-1).volume,
cost: currentData.cost
});
dataAdjusted.push({
volume: dataAdjusted.at(-1).volume + currentData.volume,
cost: currentData.cost
});
}
It works by supplying the fill parameter of the Area element with a gradient:
<Area dataKey="cost" stroke="#FFFFFF" fill="url(#gradient)" />
The gradient takes the modified data and adds gradient stops for each color.
<linearGradient id="gradient" x1="0" y1="0" x2="100%" y2="0">
{dataAdjusted.map(function (d, index) {
let cost = d.cost;
let volume = d.volume;
let percentage = 100 * (volume / totalVolume);
let fill = "#111111";
if (cost > 0) fill = "#d9f7be";
if (cost > 5) fill = "#b7eb8f";
if (cost < 0) fill = "#ffd8bf";
if (cost <= -5) fill = "#ff9c6e";
return <stop offset={`${percentage}%`} stopColor={fill} />;
})}
</linearGradient>
It still needs some labeling work, and probably some corrections in the gradient function, but it should be a start for your problem.