Source: muze-legend/src/legend/legend-helper.js

import { makeElement, numberInterpolator } from 'muze-utils';

import {
    SCALE_FUNCTIONS,
    WIDTH,
    HEIGHT,
    LEFT,
    RIGHT,
    TOP,
    BOTTOM
} from '../enums/constants';

/**
 *
 *
 * @param {*} scale
 *
 */
export const getScaleInfo = (scale) => {
    const scaleType = scale.constructor.type();
    const domain = scale.uniqueValues();
    const steps = scale.config().stops || 1;
    const scaleFn = SCALE_FUNCTIONS[scaleType];

    return { scaleType, domain, steps, scaleFn };
};

/**
 *
 *
 * @param {*} domain
 * @param {*} steps
 *
 */
export const getInterpolatedData = (domain, steps) => {
    const domainForLegend = [];
    const interpolatedFn = numberInterpolator()(domain[0], domain[1]);

    for (let i = 0; i <= steps; i++) {
        domainForLegend[i] = interpolatedFn(i / steps);
    }
    return domainForLegend;
};

/**
 *
 *
 * @param {*} container
 * @param {*} text
 * @param {*} measurement
 * @param {*} classPrefix
 */
export const titleCreator = (container, title, measurement, config) => {
    const titleContainer = makeElement(container, 'table', [1], `${config.classPrefix}-legend-title`)
            .style(WIDTH, `${measurement.width}px`)
            .style(HEIGHT, `${measurement.height}px`)
            .style('border-bottom', `${measurement.border}px ${config.borderStyle} ${config.borderColor}`)
            .style('text-align', title.orientation instanceof Function ?
                    title.orientation(config.position) : title.orientation);
    return makeElement(titleContainer, 'td', [1], `${config.classPrefix}-legend-title-text`)
                    .style(WIDTH, `${measurement.width}px`)
                    .style(HEIGHT, '100%')
                    .style('padding', `${measurement.padding}px`)
                    .text(title.text)
                    .node();
};

                                /**
 *
 *
 * @param {*} data
 * @param {*} prop
 * @param {*} labelManager
 *
 */
export const getMaxMeasures = (data, prop, labelManager) => {
    let maxHeight = -Infinity;
    let maxWidth = -Infinity;

    data.forEach((item) => {
        const value = prop ? item[prop] : item;
        const space = labelManager.getOriSize(value);
        maxHeight = Math.max(space.height + 2, maxHeight);
        maxWidth = Math.max(space.width + 2, maxWidth);
    });

    return { height: maxHeight, width: maxWidth };
};

/**
 *
 *
 * @param {*} data
 * @param {*} prop
 * @param {*} labelManager
 *
 */
export const getItemMeasures = (data, prop, labelManager, formatter) => {
    const space = [];

    data.forEach((item, index) => {
        const value = prop ? item[prop] : item;
        const { height, width } = labelManager.getOriSize(formatter(value));
        space[index] = { height: height + 1, width: width + 1 };
    });
    return space;
};

/**
 *
 *
 * @param {*} textOrientation
 * @param {*} effPadding
 * @param {*} titleSpace
 *
 * @memberof Legend
 */
export const computeItemSpaces = (config, measures, data) => {
    let totalHeight = 0;
    let totalWidth = 0;
    let maxItemSpaces = {
        width: 0, height: 0
    };
    const {
        effPadding,
        titleWidth,
        labelSpaces,
        titleHeight,
        maxWidth
    } = measures;
    const {
        item,
        align
    } = config;
    const {
        icon,
        text
    } = item;
    const textOrientation = text.orientation;
    const itemSpaces = [];
    const iconSpaces = [];
    let maxIconWidth = 0;
    labelSpaces.forEach((labelSpace, i) => {
        const itemSpace = { width: 0, height: 0 };
        const iconSpace = { width: 0, height: 0 };
        const datum = data[i] || {};
            // Compute each legend item height/width
        if (textOrientation === LEFT || textOrientation === RIGHT) {
            // Get label, icon and item widths
            labelSpace.width += effPadding;
            iconSpace.width = (datum.size ? 2 * Math.sqrt(datum.size / Math.PI) : icon.width) + effPadding;
            maxIconWidth = Math.max(iconSpace.width, maxIconWidth);
            itemSpace.width = labelSpace.width + maxIconWidth;

            // Get label, icon and item heights
            labelSpace.height = Math.max(labelSpace.height, icon.height) + effPadding;
            iconSpace.height = labelSpace.height;
            itemSpace.height = labelSpace.height;
        } else {
            // Get label, icon and item widths
            labelSpace.width = Math.max(labelSpace.width, datum.size ? 2 * Math.sqrt(datum.size / Math.PI)
            : icon.width) + effPadding;
            iconSpace.width = labelSpace.width;
            itemSpace.width = labelSpace.width;
            maxIconWidth = Math.max(iconSpace.width, maxIconWidth);

            // Get label, icon and item heights
            labelSpace.height += effPadding;
            iconSpace.height = icon.height + effPadding;
            itemSpace.height = labelSpace.height + iconSpace.height;
        }
        // Compute height and width of legend for each alignment
        if (align === 'horizontal') {
            totalHeight = Math.max(totalHeight, itemSpace.height);
        } else {
            totalHeight += itemSpace.height;
            totalWidth = Math.max(totalWidth, itemSpace.width, titleWidth) + effPadding;
        }
        maxItemSpaces = {
            width: Math.max(itemSpace.width, maxItemSpaces.width),
            height: Math.max(itemSpace.height, maxItemSpaces.height)
        };
        itemSpaces.push(itemSpace);
        iconSpaces.push(iconSpace);
    });
    itemSpaces.forEach((itemSpace, i) => {
        if (align === 'horizontal') {
            itemSpace.height = totalHeight;
            iconSpaces[i].width = maxIconWidth;
            if (textOrientation === LEFT || textOrientation === RIGHT) {
                labelSpaces[i].height = totalHeight;
                iconSpaces[i].height = totalHeight;
                itemSpaces[i].width = labelSpaces[i].width + maxIconWidth;
            } else {
                labelSpaces[i].width = maxIconWidth;
                itemSpaces[i].width = maxIconWidth;
                labelSpaces[i].width = maxIconWidth;
            }
            totalWidth = Math.max(totalWidth + itemSpaces[i].width);
        } else {
            itemSpace.width = Math.max(totalWidth, maxWidth);
            if (textOrientation === TOP || textOrientation === BOTTOM) {
                labelSpaces[i].width = totalWidth;
                iconSpaces[i].width = totalWidth;
                maxIconWidth = totalWidth;
            } else {
                iconSpaces[i].width = maxIconWidth;
                itemSpaces[i].width = labelSpaces[i].width + maxIconWidth;
                labelSpaces[i].width = maxItemSpaces.width - maxIconWidth;
                totalWidth = Math.max(totalWidth, itemSpace.width) + effPadding;
            }
        }
    });
    totalWidth = Math.max(totalWidth, titleWidth);
    totalHeight += titleHeight + effPadding;

    return { totalHeight, totalWidth, itemSpaces, iconSpaces, maxItemSpaces, maxIconWidth };
};

/**
 *
 *
 * @param {*} type
 * @param {*} scaleInfo
 * @param {*} domainInfo
 *
 */
export const getDomainBounds = (type, scaleInfo, domainInfo) => {
    const {
        scaleFn,
        scaleType,
        scale
    } = scaleInfo;
    const {
        domain,
        domainBounds,
        domainLeg,
        steps
    } = domainInfo;
    const ele = domain[type === 'lower' ? 0 : domain.length - 1];
    const step = steps[type === 'lower' ? 0 : steps.length - 1];

    return {
        [scaleType]: scaleType === 'size' ? scale[scaleFn](ele) * scale.getScaleFactor() : scale[scaleFn](ele),
        value: domainBounds[type],
        id: type === 'lower' ? 0 : domainLeg.length + 2,
        range: [ele, step]
    };
};