mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-19 07:19:41 +02:00
dynamically resizes horizontal xychart bar
Co-authored-by: pranavm2109 <mishrap@dickinson.edu>
This commit is contained in:
@@ -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;
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user