in src/legend/svgLegend.ts [625:727]
private static calculateHorizontalLegendItemsWidths(
dataPoints: LegendDataPoint[],
availableWidth: number,
iconPadding: number,
fontSize: number,
fontFamily: string,
): LegendItem[] {
let dataPointsLength = dataPoints.length;
// Set the maximum amount of space available to each item. They can use less, but can"t go over this number.
let maxItemWidth = dataPointsLength > 0 ? availableWidth / dataPointsLength | 0 : 0;
let maxItemTextWidth = maxItemWidth - iconPadding;
// Makes sure the amount of space available to each item is at least SVGLegend.MaxTextLength wide.
// If you had many items and/or a narrow amount of available width, the availableTextWidthPerItem would be small, essentially making everything ellipsis.
// This prevents that from happening by giving each item at least SVGLegend.MaxTextLength of space.
if (maxItemTextWidth < SVGLegend.MaxTextLength) {
maxItemTextWidth = SVGLegend.MaxTextLength;
maxItemWidth = maxItemTextWidth + iconPadding;
}
// Make sure the availableWidthPerItem is less than the availableWidth. This lets the long text properly add ellipsis when we"re displaying one item at a time.
if (maxItemWidth > availableWidth) {
maxItemWidth = availableWidth;
maxItemTextWidth = maxItemWidth - iconPadding;
}
let occupiedWidth = 0;
let legendItems: LegendItem[] = [];
// Add legend items until we can"t fit any more (the last one doesn"t fit) or we"ve added all of them
for (let dataPoint of dataPoints) {
let textProperties = SVGLegend.getTextProperties(dataPoint.label, fontSize, fontFamily);
let itemTextWidth = textMeasurementService.measureSvgTextWidth(textProperties);
let desiredWidth = itemTextWidth + iconPadding;
let overMaxWidth = desiredWidth > maxItemWidth;
let actualWidth = overMaxWidth ? maxItemWidth : desiredWidth;
occupiedWidth += actualWidth;
if (occupiedWidth >= availableWidth) {
// Always add at least 1 element
if (legendItems.length === 0) {
legendItems.push({
dataPoint: dataPoint,
textProperties: textProperties,
desiredWidth: desiredWidth,
desiredOverMaxWidth: true,
width: desiredWidth
});
// Set the width to the amount of space we actually have
occupiedWidth = availableWidth;
} else {
// Subtract the width from what was just added since it won"t fit
occupiedWidth -= actualWidth;
}
break;
}
legendItems.push({
dataPoint: dataPoint,
textProperties: textProperties,
desiredWidth: desiredWidth,
desiredOverMaxWidth: overMaxWidth,
width: desiredWidth
});
}
// If there are items at max width, evenly redistribute the extra space to them
let itemsOverMax = legendItems.filter((li: LegendItem) => li.desiredOverMaxWidth);
let numItemsOverMax = itemsOverMax.length;
if (numItemsOverMax > 0) {
let extraWidth = availableWidth - occupiedWidth;
for (let item of itemsOverMax) {
// Divvy up the extra space and add it to the max
// We need to do this calculation in every loop since the remainingWidth may not be changed by the same amount every time
let extraWidthPerItem = extraWidth / numItemsOverMax;
let newMaxItemWidth = maxItemWidth + extraWidthPerItem;
let usedExtraWidth: number;
if (item.desiredWidth <= newMaxItemWidth) {
// If the item doesn"t need all the extra space, it"s not at max anymore
item.desiredOverMaxWidth = false;
usedExtraWidth = item.desiredWidth - maxItemWidth;
} else {
// Otherwise the item is taking up all the extra space so update the actual width to indicate that
item.width = newMaxItemWidth;
usedExtraWidth = newMaxItemWidth - maxItemWidth;
}
extraWidth -= usedExtraWidth;
numItemsOverMax--;
}
}
return legendItems;
}