Skip to content

Commit

Permalink
fix bug #6342: legend line wrap issues (#6363)
Browse files Browse the repository at this point in the history
Co-authored-by: Nelson Liu <[email protected]>
Co-authored-by: Daniel Chang <[email protected]>
Co-authored-by: Jaehwan Ryu <[email protected]>
  • Loading branch information
4 people authored Feb 3, 2025
1 parent 7a2bedc commit 2d84222
Showing 1 changed file with 33 additions and 17 deletions.
50 changes: 33 additions & 17 deletions packages/client/hmi-client/src/services/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,39 +408,48 @@ export function createHistogramChart(dataset: Record<string, any>[], options: Hi
}

/* This function estimates the legend width, because if it's too we will have to draw it with columns since there's no linewrap */
function estimateLegendWidth(items: string[], fontSize: number): number {
function estimateLegendWidth(items: string[], fontSize: number) {
// Approximate width of each character (assuming monospace-like proportions)
const charWidth = fontSize * 0.3;
const charWidth = fontSize * 0.4;

// Account for symbol width, padding, and spacing between items
const symbolWidth = fontSize * 2; // Symbol + padding
const itemSpacing = fontSize * 2; // Space between items
const symbolWidth = fontSize * 3; // Symbol + padding
const itemSpacing = fontSize * 4; // Space between items

// Calculate total width
return items.reduce((totalWidth, item) => {
const totalWidth = items.reduce((acc, item) => {
const itemWidth = item.length * charWidth + symbolWidth;
return totalWidth + itemWidth + itemSpacing;
return acc + itemWidth + itemSpacing;
}, 0);

const maxItemWidth = Math.max(...items.map((item) => item.length)) * charWidth;

return {
totalWidth,
maxItemWidth
};
}

function calculateLegendColumns(
isCompact: boolean,
estimatedWidth: number,
estimatedWidth: { totalWidth: number; maxItemWidth: number },
chartWidth: number,
numItems: number | undefined
): number | undefined {
// account for left-padding from chart width
if (!isCompact) chartWidth -= 100;

if (isCompact || !numItems) {
return isCompact ? 1 : undefined;
}

if (estimatedWidth <= chartWidth) {
if (estimatedWidth.totalWidth <= chartWidth) {
return undefined; // Everything fits in one row
}

const avgItemWidth = (estimatedWidth / numItems) * 0.85; // Reduce by 15% since our estimation seems high
const maxItemWidth = estimatedWidth.maxItemWidth;

// Calculate how many columns can fit without any extra buffer
const maxColumns = Math.floor(chartWidth / avgItemWidth);
const maxColumns = Math.floor(chartWidth / maxItemWidth);

// Use as many columns as we can fit, up to the number of items
return Math.max(1, Math.min(maxColumns, numItems));
Expand Down Expand Up @@ -527,11 +536,11 @@ export function createForecastChart(
};

const isCompact = options.width < 200;
const legendFontSize = isCompact ? 8 : 12;
const legendLabelFontSize = isCompact ? 8 : 12;

// Estimate total legend width
const legendItems = getAllLegendItems();
const estimatedWidth = estimateLegendWidth(legendItems, legendFontSize);
const estimatedWidth = estimateLegendWidth(legendItems, legendLabelFontSize);

const legendProperties = {
title: null,
Expand All @@ -541,9 +550,9 @@ export function createForecastChart(
direction: isCompact ? 'vertical' : 'horizontal',
symbolStrokeWidth: isCompact ? 2 : 4,
symbolSize: 200,
labelFontSize: isCompact ? 8 : 12,
labelFontSize: legendLabelFontSize,
labelOffset: isCompact ? 2 : 4,
labelLimit: isCompact ? 100 : 250,
labelLimit: isCompact ? 120 : 320,
columnPadding: 16,
symbolType: 'stroke',
offset: isCompact ? 8 : 16,
Expand Down Expand Up @@ -1132,6 +1141,11 @@ export function createQuantilesForecastChart(
}

const isCompact = options.width < 200;
const legendLabelFontSize = isCompact ? 8 : 12;

// Get all unique legend items
const legendItems = variables.map((v) => translationMap?.[v] ?? v);
const estimatedWidth = estimateLegendWidth(legendItems, legendLabelFontSize);

const legendProperties = {
title: null,
Expand All @@ -1141,10 +1155,12 @@ export function createQuantilesForecastChart(
direction: isCompact ? 'vertical' : 'horizontal',
symbolStrokeWidth: isCompact ? 2 : 4,
symbolSize: 200,
labelFontSize: isCompact ? 8 : 12,
labelFontSize: legendLabelFontSize,
labelOffset: isCompact ? 2 : 4,
labelLimit: isCompact ? 100 : 250,
labelLimit: isCompact ? 120 : 320,
columnPadding: 16,
// Add columns if legend would overflow
columns: calculateLegendColumns(isCompact, estimatedWidth, options.width, legendItems.length),
...options.legendProperties
};

Expand Down

0 comments on commit 2d84222

Please sign in to comment.