dynamically resizes horizontal xychart bar

Co-authored-by: pranavm2109 <mishrap@dickinson.edu>
This commit is contained in:
Shahir Ahmed
2025-04-02 23:00:12 -04:00
parent f4c08a0c6f
commit 2798e27b1e
2 changed files with 36 additions and 28 deletions

View File

@@ -101,42 +101,50 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram
if (chartConfig.showDataLabel) { if (chartConfig.showDataLabel) {
if (chartConfig.chartOrientation === 'horizontal') { if (chartConfig.chartOrientation === 'horizontal') {
// Append a temporary group to measure the widths of the texts // Factor to approximate each character's width.
const tempGroup = svg.append('g').attr('class', 'temp-label-group'); const charWidthFactor = 0.7;
// Append texts temporarily to measure their widths
const tempTexts = tempGroup // Filter out bars that have zero width or height.
.selectAll('text') const validItems = shape.data
.data(labelData) .map((d, i) => ({ data: d, label: labelData[i].toString() }))
.enter() .filter((item) => item.data.width > 0 && item.data.height > 0);
.append('text')
.attr('font-size', (data, i) => shape.data[i].height * 0.7) // Helper function to check if the text fits horizontally with a 10px right margin.
.text((d) => d); function fitsHorizontally(item: BarItem, fontSize: number): boolean {
// Measure widths and determine the font size & actual widths const { data, label } = item;
const measured = tempTexts.nodes().map((node, i) => { // Approximate the text width.
const bbox = node.getBBox(); const textWidth: number = fontSize * label.length * charWidthFactor;
return { // The available width is the bar's width minus a 10px right margin.
width: bbox.width, return textWidth <= data.width - 10;
height: bbox.height, }
fontSize: shape.data[i].height * 0.7,
}; // For each valid bar, start with an initial candidate font size (70% of the bar's height),
// then reduce it until the text fits horizontally.
const candidateFontSizes = validItems.map((item) => {
const { data } = item;
let fontSize = data.height * 0.7;
// Decrease fontSize until the text fits horizontally.
while (!fitsHorizontally(item, fontSize) && fontSize > 0) {
fontSize -= 1;
}
return fontSize;
}); });
const uniformFontSize = Math.floor(Math.min(...measured.map((m) => m.fontSize)));
const longestTextWidth = Math.max(...measured.map((m) => m.width)); // Choose the smallest candidate font size across all valid bars for uniformity.
// Clean up temp texts const uniformFontSize = Math.floor(Math.min(...candidateFontSizes));
tempGroup.remove();
shapeGroup shapeGroup
.selectAll('text') .selectAll('text')
.data(shape.data) .data(validItems)
.enter() .enter()
.append('text') .append('text')
.attr('x', (data) => data.x + data.width - longestTextWidth - 5) .attr('x', (item) => item.data.x + item.data.width - 10)
.attr('y', (data) => data.y + data.height / 2 + 0.2 * data.height) .attr('y', (item) => item.data.y + item.data.height / 2)
.attr('text-anchor', 'start') .attr('text-anchor', 'end')
.attr('dominant-baseline', 'middle') .attr('dominant-baseline', 'middle')
.attr('fill', 'black') .attr('fill', 'black')
.attr('font-size', `${uniformFontSize}px`) .attr('font-size', `${uniformFontSize}px`)
.text((data, index) => labelData[index]); .text((item) => item.label);
} else { } else {
const yOffset = 10; const yOffset = 10;

View File

@@ -1231,7 +1231,7 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file)
showDataLabel: showDataLabel:
description: Should show the data label on the chart description: Should show the data label on the chart
type: boolean type: boolean
default: true default: false
showTitle: showTitle:
description: Should show the chart title description: Should show the chart title
type: boolean type: boolean