Reputation: 127
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
Upvotes: 2
Views: 988
Reputation: 3247
The problem is it's not really just a margin change. A bar chart still uses a coordinate system. If you add space around the origin it becomes really a different type of chart, even though the difference is subtle. You effectively need 2 different Y axes for this, to split the negative and positive values. So it's a bit tricky to come up with a "right way" to approach this.
Rechart just uses 1 axis. Given the current implementation, trying to modify the component to do this anyway will be highly prone to bugs if Rechartjs's implementation changes. Changing your data, as suggested in the other answer, is probably the safest approach at the moment. But it's still not a very nice solution.
You could also create 2 separate bar charts. But it's also not nice and adds a lot of risk for mismatches.
I see you already opened an issue on Github, which seems the right place to discuss this in detail. It's a very sensible looking use case that a component library should at least have some kind of answer to. Even if that answer is "no that's deliberately unsupported". But it may very well also be "sure that's easy to add as an option". Some patience for this answer can save you a lot of time and hassle.
Rechart has some pretty extensive documentation, pretty sure they're happy to fill in gaps like this.
Upvotes: 0
Reputation: 1056
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
Upvotes: 2