I want to dynamically allow the labels on the bar charts depending on the width of the bars - if it gets to thin the labels are not looking good and often overlap. However I don't really want to mess with some low-level SVG element manipulation and nivo has to use something to calculate the width of the bars; I could use the same thing to determine if the labels will fit. I'm expecting something from d3 directly, but I don't know.
So I went through the nivo's source code and after some searching I found what I was looking for (sort of) here:
const bandwidth = (indexScale.bandwidth() - innerPadding * (keys.length - 1)) / keys.length
This is the version for the grouped bar chart (as opposed to stacked, but in that case it's simply = indexScale.bandwidth()
), where innerPadding
is the padding between the bars in the group and keys
are the keys used for a single group. The indexScale
, however, is an internal object created with (I'm simplifying) createBandScale
, which you can see here:
export const createBandScale = <Input extends StringValue>(
{ round = true }: ScaleBandSpec,
data: ComputedSerieAxis<Input>,
size: number,
axis: ScaleAxis
) => {
const scale = scaleBand<Input>()
.range(axis === 'x' ? [0, size] : [size, 0])
.domain(data.all)
.round(round)
return castBandScale<Input>(scale)
}
This function is available through @nivo/scales
, so I could use it for my purposes. However, after doing some testing I discovered that my results were off - the computations seem to ignore the padding between the groups and so the width I was calculating was greater than the actual bar width; I don't know why that is, perhaps this is considered somewhere further along the line, but for my purposes I just needed to create a BandScale
like this:
const bandScale = createBandScale(
{ round: false },
{ all: xAxis },
totalWidth,
"x"
).paddingInner(padding) // << this is the important part
The only challenge left was figuring out the total width of the SVG element and fortunately nivo's codebase has this kind of hook, but it is not a part of the package, so I ended up adding it to my own code - here is the hook.