This is the result I want to achieve.
But I can't find a way to add margin between the ReferenceLine label and the bar components.
This is my chart component
<BarChart
width={500}
height={300}
data={data}
>
<XAxis hide dataKey="name" />
{
data.map((entry, index) => (
<ReferenceLine key={`cell-${index}`} strokeWidth={0} x={entry.name} label={entry.name} />
))
}
<Bar dataKey="pv" fill="#8884d8">
{
data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.pv <= 0 ? "#FF645C" : "#29DB92"} />
))
}
</Bar>
</BarChart>
You can see the full example on my sandbox. https://codesandbox.io/s/bar-chart-with-positive-negative-forked-g3kvz5?file=/src/App.tsx:673-1176
Important: This solution is technically not "correct" if you care for the integrity of your data because it modifies the data so every entry does not start at zero, but because in this case we do not want to show an y-axis anyway I think this can be a simple solution:
const transformData = (margin: number) => {
return data.map((item) => {
if (item.pv >= 0) {
return {
name: item.name,
pv: [margin, item.pv + margin]
};
} else {
return {
name: item.name,
pv: [-margin, item.pv - margin]
};
}
});
};
This simple function maps over every entry of the data and depending of the original pv
value returns a new object with the new position of the
corresponding entry. The new pv
value is an array where the first entry is the new starting position and the second one is the end position/length of the entry. The new starting position is ether the value of margin
(When the original pv
is positiv) or -margin
(When the original pv
is negativ). To keep the original length of the entries the second value of the new array is the original pv
+/- the margin
.
The App
component in this example than has to be modified like this:
export default function App() {
const barData = useMemo(() => transformData(1000), []);
return (
<BarChart width={500} height={300} data={barData}>
<XAxis hide dataKey="name" />
{barData.map((entry, index) => (
<ReferenceLine
key={`cell-${index}`}
strokeWidth={0}
x={entry.name}
label={entry.name}
/>
))}
<Bar dataKey="pv" fill="#8884d8">
{barData.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={entry.pv[0] <= 0 ? "#FF645C" : "#29DB92"}
/>
))}
</Bar>
</BarChart>
);
}
Note: Since the new pv
value of each entry is no longer a simple number but an array we need to change the fill
property of each Cell
component from entry.pv <= 0 ? "#FF645C" : "#29DB92"
to entry.pv[0] <= 0 ? "#FF645C" : "#29DB92"
. You also can play with the margin
value to fit your purpose. I choose 1000 here because I thought it looks nice :)
Link to the full code: https://codesandbox.io/s/bar-chart-with-positive-negative-forked-cu6d7q?file=/src/App.tsx:971-1026