private static calculateHorizontalLegendItemsWidths()

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;
    }