nivo
nivo copied to clipboard
Totals for stacked bars
Is your feature request related to a problem? Please describe. I don't know if there is a way to show the totals of stacked bars as asked in
Describe the solution you'd like Being able to show the total of the portions of the stacked bar over the bars or as the number of the last segment.
Additional context I opened this issue, because there is a closed similar issue (https://github.com/plouc/nivo/issues/951) where the last message says: "Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!"
I personally need a solution to this in a project I'm working and I suspect other people are looking for one too.
I mention @Mulli and @timothyarmes who asked for this feature in the other issue (https://github.com/plouc/nivo/issues/951).
@damianocasula I am still waiting for a solution... Thanks for re-opening
Hi @damianocasula, hi @Mulli. Do you mean something like this?
https://codesandbox.io/s/hopeful-mcclintock-44m0w8?file=/src/App.js
This implementation uses a custom layer. Note the sandbox only works for vertical bar plots, but hopefully you can customize it for your needs.
Hi Tomasz Thanks for taking the time to show the example I shall give it a try
Best Mulli Bahr +972-50-9262025 https://site2goal.co.il
בתאריך שבת, 28 במאי 2022 ב-19:47 מאת Tomasz Konopka < @.***>:
Hi @damianocasula https://github.com/damianocasula, hi @Mulli https://github.com/Mulli. Do you mean something like this?
[image: 2022-05-28-bar-totals] https://user-images.githubusercontent.com/7260190/170834387-752b8ce7-07e8-45c0-bf44-d343444d2b7e.png
https://codesandbox.io/s/hopeful-mcclintock-44m0w8?file=/src/App.js
This implementation uses a custom layer. Note the sandbox only works for vertical bar plots, but hopefully you can customize it for your needs.
— Reply to this email directly, view it on GitHub https://github.com/plouc/nivo/issues/2020#issuecomment-1140295641, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAF25U7EFW6NPEMR2ERKME3VMJE3XANCNFSM5XENTCEA . You are receiving this because you were mentioned.Message ID: @.***>
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!
Converted this solution to typescript (and did some very slight refactoring).
const BarTotalsLayer: BarLayer<{ value: number }> = ({
bars,
xScale,
yScale,
}) => {
const labelOffset = 10;
const labelFontSize = 12;
if (bars.length === 0) return null;
// compute totals for each index/bar
const totals = bars.reduce<Record<number, number>>((acc, bar) => {
console.log(bar.data.indexValue);
const indexValue = bar.data.indexValue;
if (!(indexValue in acc)) {
acc[indexValue] = 0;
}
if (!bar.data.hidden) {
acc[indexValue] += bar.data.value;
}
return acc;
}, {});
const bandwidth = bars[0]?.width;
// place text elements above the bars
const labels = (Object.keys(totals) as any as number[]).map((indexValue) => {
const x = xScale(indexValue) + bandwidth / 2;
const y = yScale(totals[indexValue]) - labelOffset;
return (
<text
key={'total.' + indexValue}
x={x}
y={y}
textAnchor={'middle'}
fontWeight={'bold'}
fontSize={labelFontSize}
>
{totals[indexValue]}
</text>
);
});
return <>{labels}</>;
};
Note: to get the types to work, you also need to install @types/d3-scale
as indicated in https://github.com/plouc/nivo/issues/2173
@tkonopka thank you so muchhhh man 🚀
For anyone having trouble doing this for the <ResponsiveBarCanvas/>
component like I did trying the above solutions, you need to draw directly onto the canvas using the context provided by Nivo. This is what worked for me:
Version of Nivo I used:
"@nivo/bar": "^0.83.0",
"@nivo/core": "^0.83.0",
Import the types from @nivo/bar
import {
ResponsiveBarCanvas,
BarCanvasCustomLayerProps,
BarDatum,
} from '@nivo/bar'
Add the component to the layers prop
layers={['axes', 'bars', BarTotalsLayer]}
const BarTotalsLayer = (
context: CanvasRenderingContext2D,
props: BarCanvasCustomLayerProps<BarDatum>
) => {
const { bars, xScale, yScale } = props
const labelOffset = 5
const labelFontSize = 12
if (bars.length === 0) return null
const totals = bars.reduce<Record<number, number>>((acc, bar) => {
const indexValue = bar.data.indexValue
if (!(indexValue in acc)) {
acc[indexValue as keyof typeof acc] = 0
}
if (!bar.data.hidden) {
acc[indexValue as keyof typeof acc] += bar.data.value!
}
return acc
}, {})
const bandwidth = bars[0]?.width
return (Object.keys(totals) as any as number[]).forEach((indexValue) => {
const x = xScale(indexValue) + bandwidth / 2
const y = yScale(totals[indexValue]) - labelOffset
context.font = `600 ${labelFontSize}px 'IBM Plex Sans', sans-serif`
context.fillStyle = '#575756'
context.textAlign = 'center'
context.fillText(`${totals[indexValue]}`, x, y - labelOffset)
})
}
Of course adjust the styling how you desire
Implemented in https://github.com/plouc/nivo/pull/2525 thanks to @joaopedromatias.