chartHelper.js

"use strict";
/**
 * Datart
 *
 * Copyright 2021
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeChartAndViewComputedField = exports.findPathByNameInMeta = exports.getMinAndMaxNumber = exports.round = exports.getAllColumnInMeta = exports.compareSelectedItems = exports.getSelectedItemStyles = exports.setRuntimeDateLevelFieldsInChartConfig = exports.clearRuntimeDateLevelFieldsInChartConfig = exports.getRuntimeComputedFields = exports.getRuntimeDateLevelFields = exports.getDrillableRows = exports.getAutoFunnelTopPosition = exports.setOptionsByAxisLabelOverflow = exports.hadAxisLabelOverflowConfig = exports.getIntervalShow = exports.isMatchRequirement = exports.getColorizeGroupSeriesColumns = exports.getExtraSeriesDataFormat = exports.getExtraSeriesRowData = exports.getGridStyleWithLegend = exports.getGridStyle = exports.getScatterSymbolSizeFn = exports.valueFormatter = exports.getSeriesTooltips4Rectangular = exports.getSeriesTooltips4Polar2 = exports.getSeriesTooltips4Rectangular2 = exports.getSeriesTooltips4Scatter = exports.getDataColumnMaxAndMin2 = exports.getDataColumnMaxAndMin = exports.getUnusedHeaderRows = exports.getColumnRenderName = exports.getValueByColumnKey = exports.transformToObjectArray = exports.transformToDataSet = exports.getNameTextStyle = exports.getAxisTick = exports.getSplitLine = exports.getAxisLabel = exports.getAxisLine = exports.getReference2 = exports.getReference = exports.getValue = exports.getLegendOrientAndPositions = exports.getStyles = exports.getStyleValueByGroup = exports.getSettingValue = exports.getStyleValue = exports.getDefaultThemeColor = exports.toFormattedValue = void 0;
exports.hasAggregationFunction = exports.filterCurrentUsedComputedFields = exports.createDateLevelComputedFieldForConfigComputedFields = void 0;
const echarts_default_theme_json_1 = __importDefault(require("app/assets/theme/echarts_default_theme.json"));
const constants_1 = require("app/constants");
const ChartDataSet_1 = require("app/models/ChartDataSet");
const ChartDrillOption_1 = require("app/models/ChartDrillOption");
const utils_1 = require("app/pages/ChartWorkbenchPage/components/ChartOperationPanel/components/ChartDataViewPanel/components/utils");
const constant_1 = require("app/pages/ChartWorkbenchPage/slice/constant");
const mutation_1 = require("app/utils/mutation");
const globalConstants_1 = require("globalConstants");
const moment_1 = __importDefault(require("moment"));
const debugger_1 = require("utils/debugger");
const utils_2 = require("utils/utils");
const object_1 = require("utils/object");
const internalChartHelper_1 = require("./internalChartHelper");
const number_1 = require("./number");
/**
 * [中文] 获取格式聚合数据
 * </br>
 * [EN] Gets format aggregate data
 *
 * @example
 * const format = {
 *   percentage: {
 *     decimalPlaces: 2,
 *   },
 *   type: "percentage",
 * }
 * const formattedData = toFormattedValue('1', format);
 * console.log(formattedData); // '100.00%';
 * @export
 * @param {(number | string)} [value]
 * @param {FormatFieldAction} [format]
 * @return {*}
 */
function toFormattedValue(value, format) {
    if (value === null || value === undefined) {
        return '-';
    }
    if (!format || format.type === constants_1.FieldFormatType.Default) {
        return value;
    }
    if (!format.type) {
        return value;
    }
    const { type: formatType } = format;
    if (typeof value === 'string' &&
        formatType !== constants_1.FieldFormatType.Date &&
        (!value || isNaN(+value))) {
        return value;
    }
    const config = format[formatType];
    if (!config) {
        return value;
    }
    let formattedValue;
    switch (formatType) {
        case constants_1.FieldFormatType.Numeric:
            const numericConfig = config;
            formattedValue = (0, object_1.pipe)(unitFormater, decimalPlacesFormater, numericFormater)(value, numericConfig);
            break;
        case constants_1.FieldFormatType.Currency:
            const currencyConfig = config;
            formattedValue = (0, object_1.pipe)(currencyFormater)(value, currencyConfig);
            break;
        case constants_1.FieldFormatType.Percentage:
            const percentageConfig = config;
            formattedValue = (0, object_1.pipe)(percentageFormater)(value, percentageConfig);
            break;
        case constants_1.FieldFormatType.Scientific:
            const scientificNotationConfig = config;
            formattedValue = (0, object_1.pipe)(scientificNotationFormater)(value, scientificNotationConfig);
            break;
        case constants_1.FieldFormatType.Date:
            const dateConfig = config;
            formattedValue = (0, object_1.pipe)(dateFormater)(value, dateConfig);
            break;
        default:
            formattedValue = value;
            break;
    }
    return formattedValue;
}
exports.toFormattedValue = toFormattedValue;
function decimalPlacesFormater(value, config) {
    if ((0, object_1.isEmpty)(config?.decimalPlaces)) {
        return value;
    }
    if (isNaN(value)) {
        return value;
    }
    if (config?.decimalPlaces < 0 || config?.decimalPlaces > 100) {
        return value;
    }
    return (+value).toFixed(config?.decimalPlaces);
}
function unitFormater(value, config) {
    if ((0, object_1.isEmpty)(config?.unitKey)) {
        return value;
    }
    if (isNaN(+value)) {
        return value;
    }
    const realUnit = globalConstants_1.NumericUnitDescriptions.get(config?.unitKey)?.[0] || 1;
    return +value / realUnit;
}
function numericFormater(value, config) {
    if (isNaN(+value)) {
        return value;
    }
    const valueWithPrefixs = [
        config?.prefix || '',
        thousandSeperatorFormater(value, config),
        globalConstants_1.NumericUnitDescriptions.get(config?.unitKey || globalConstants_1.NumberUnitKey.None)?.[1],
        config?.suffix || '',
    ].join('');
    return valueWithPrefixs;
}
function thousandSeperatorFormater(value, config) {
    if (isNaN(+value) || !config?.useThousandSeparator) {
        return value;
    }
    const parts = value.toString().split('.');
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    const formatted = parts.join('.');
    return formatted;
}
function currencyFormater(value, config) {
    if (isNaN(+value)) {
        return value;
    }
    const realUnit = globalConstants_1.NumericUnitDescriptions.get(config?.unitKey)?.[0] || 1;
    return `${new Intl.NumberFormat('zh-CN', {
        style: 'currency',
        currency: config?.currency || 'CNY',
        minimumFractionDigits: config?.decimalPlaces,
        maximumFractionDigits: config?.decimalPlaces,
        useGrouping: config?.useThousandSeparator,
    }).format(value / realUnit)} ${globalConstants_1.NumericUnitDescriptions.get(config?.unitKey || globalConstants_1.NumberUnitKey.None)?.[1]}`;
}
function percentageFormater(value, config) {
    if (isNaN(+value)) {
        return value;
    }
    let fractionDigits = 0;
    if (!(0, object_1.isEmpty)(config?.decimalPlaces) &&
        +config?.decimalPlaces >= 0 &&
        +config?.decimalPlaces <= 20) {
        fractionDigits = +config?.decimalPlaces;
    }
    return `${(+value * 100).toFixed(fractionDigits)}%`;
}
function scientificNotationFormater(value, config) {
    if (isNaN(+value)) {
        return value;
    }
    let fractionDigits = 0;
    if (!(0, object_1.isEmpty)(config?.decimalPlaces) &&
        +config?.decimalPlaces >= 0 &&
        +config?.decimalPlaces <= 20) {
        fractionDigits = +config?.decimalPlaces;
    }
    return (+value).toExponential(fractionDigits);
}
function dateFormater(value, config) {
    if (isNaN(+value) || (0, object_1.isEmpty)(config?.format)) {
        return value;
    }
    return (0, moment_1.default)(value).format(config?.format);
}
/**
 * [中文] 获取系统默认颜色
 * </br>
 * [EN] Gets an array of default colors
 *
 * @example
 * const colorList = getDefaultThemeColor();
 * console.log(colorList); // ["#298ffe","#dae9ff","#fe705a","#ffdcdc","#751adb","#8663d7","#15AD31","#FAD414","#E62412"]
 *
 * @export
 * @return {string[]} default color array
 */
function getDefaultThemeColor() {
    return echarts_default_theme_json_1.default.color;
}
exports.getDefaultThemeColor = getDefaultThemeColor;
/**
 * [中文] 使用路径语法获取配置信息,此方法已过时,请参考方法getStyles
 * </br>
 * [EN] Get config info by value path, please use getStyles instead
 *
 * @deprecated This function will be removed in next version, please use @see {@link getStyles} instread
 * @param {ChartStyleConfig[]} styleConfigs
 * @param {string[]} paths
 * @return {*}  {*}
 */
function getStyleValue(styleConfigs, paths) {
    return getValue(styleConfigs, paths);
}
exports.getStyleValue = getStyleValue;
/**
 * [中文] 使用路径语法获取配置信息,此方法已过时,请参考方法getStyles
 * </br>
 * [EN] Get setting config info by value path, please use getStyles instead
 *
 * @deprecated This function will be removed in next version, please use @see {@link getStyles} instread
 * @export
 * @param {ChartStyleConfig[]} configs
 * @param {string} path
 * @param {string} targetKey
 * @return {*}
 */
function getSettingValue(configs, path, targetKey) {
    return getValue(configs, path.split('.'), targetKey);
}
exports.getSettingValue = getSettingValue;
/**
 * [中文] 使用路径语法获取配置信息,此方法已过时,请参考方法getStyles
 * </br>
 * [EN] Get setting config info by value path, please use getStyles instead
 *
 * @deprecated This function will be removed in next version, please use @see {@link getStyles} instead
 * @export
 * @param {ChartStyleConfig[]} styles
 * @param {string} groupPath
 * @param {string} childPath
 * @return {*}
 */
function getStyleValueByGroup(styles, groupPath, childPath) {
    const childPaths = childPath.split('.');
    return getValue(styles, [groupPath, ...childPaths]);
}
exports.getStyleValueByGroup = getStyleValueByGroup;
/**
 * [中文] 通过数组路径语法,获取对应的配置的值集合
 * </br>
 * [EN] Get config style values
 *
 * @example
 *
 * const styleConfigs = [
 *       {
 *        key: 'label',
 *        rows: [
 *           { key: 'color', value: 'red' },
 *           { key: 'font', value: 'sans-serif' },
 *         ],
 *       },
 *     ];
 * const [color, font] = getStyles(styleConfigs, ['label'], ['color', 'font']);
 * console.log(color); // red
 * console.log(font); // sans-serif
 *
 * @param {Array<ChartStyleConfig>} configs required
 * @param {Array<string>} parentKeyPaths required
 * @param {Array<string>} childTargetKeys required
 * @return {*} array of child keys with the same order
 */
function getStyles(configs, parentKeyPaths, childTargetKeys) {
    const rows = getValue(configs, parentKeyPaths, 'rows');
    if (!rows) {
        return Array(childTargetKeys.length).fill(undefined);
    }
    return childTargetKeys.map(k => getValue(rows, [k]));
}
exports.getStyles = getStyles;
/**
 * [中文] 将图例位置配置转换为 ECharts 图例的排列方向与定位样式
 * </br>
 * [EN] Convert legend position option to ECharts legend orient and positions
 *
 * @param {string} legendPos required
 *        one of 'top' | 'bottom' | 'left' | 'right' |
 *        'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
 * @param {Object} [options] optional
 * @param {number} [options.top] optional
 *        pin the legend to a fixed top offset instead of center anchoring
 *        (used by charts that compute the offset dynamically, e.g. funnel)
 * @return {{ orient: string; positions: object }}
 */
function getLegendOrientAndPositions(legendPos, options) {
    const top = options?.top;
    let positions = {};
    let orient = '';
    switch (legendPos) {
        case 'top':
            orient = 'horizontal';
            positions = { left: 'center', top: top ?? 8, height: 32 };
            break;
        case 'bottom':
            orient = 'horizontal';
            positions = { left: 'center', bottom: 8, height: 32 };
            break;
        case 'left':
            orient = 'vertical';
            positions =
                top != null
                    ? { left: 8, top, bottom: 24, width: 96 }
                    : { left: 8, top: 'middle' };
            break;
        case 'topLeft':
            orient = 'horizontal';
            positions = { top: top ?? 8, left: 8 };
            break;
        case 'topRight':
            orient = 'horizontal';
            positions = { top: top ?? 8, right: 8 };
            break;
        case 'bottomLeft':
            orient = 'horizontal';
            positions = { bottom: 8, left: 8 };
            break;
        case 'bottomRight':
            orient = 'horizontal';
            positions = { bottom: 8, right: 8 };
            break;
        default:
            orient = 'vertical';
            positions =
                top != null
                    ? { right: 8, top, bottom: 24, width: 96 }
                    : { right: 8, top: 'middle' };
            break;
    }
    return { orient, positions };
}
exports.getLegendOrientAndPositions = getLegendOrientAndPositions;
/**
 * [中文] 通过数组路径语法,获取对应的配置信息
 * </br>
 * [EN] Get style config value base function with default target key
 *
 * @example
 *
 * const styleConfigs = [
 *       {
 *        key: 'label',
 *        rows: [
 *           { key: 'color', value: 'red' },
 *           { key: 'font', value: 'sans-serif' },
 *         ],
 *       },
 *     ];
 * const colorValue = getValue(styleConfigs, ['label', 'color']);
 * console.log(colorValue); // red
 *
 * @param {Array<ChartStyleConfig>} configs
 * @param {Array<string>} keyPaths
 * @param {string} [targetKey='value']
 * @return {*}
 */
function getValue(configs, keyPaths, targetKey = 'value') {
    let iterators = configs || [];
    while (!(0, object_1.isEmptyArray)(iterators)) {
        const key = keyPaths?.shift();
        const group = iterators?.find(sc => sc.key === key);
        if (!group) {
            return undefined;
        }
        if ((0, object_1.isEmptyArray)(keyPaths)) {
            return group[targetKey];
        }
        iterators = group.rows || [];
    }
}
exports.getValue = getValue;
/**
 * @deprecated to be remove in next release
 *
 * @export
 * @param {*} settingConfigs
 * @param {*} dataColumns
 * @param {*} dataConfig
 * @param {*} isHorizonDisplay
 * @return {*}
 */
function getReference(settingConfigs, dataColumns, dataConfig, isHorizonDisplay) {
    const referenceTabs = getValue(settingConfigs, ['reference', 'panel', 'configuration'], 'rows');
    return {
        markLine: getMarkLine(referenceTabs, dataColumns, dataConfig, isHorizonDisplay),
        markArea: getMarkArea(referenceTabs, dataColumns, isHorizonDisplay),
    };
}
exports.getReference = getReference;
function getReference2(settingConfigs, dataSetRows, dataConfig, isHorizonDisplay) {
    const referenceTabs = getValue(settingConfigs, ['reference', 'panel', 'configuration'], 'rows');
    return {
        markLine: getMarkLine2(referenceTabs, dataSetRows, dataConfig, isHorizonDisplay),
        markArea: getMarkArea2(referenceTabs, dataSetRows, dataConfig, isHorizonDisplay),
    };
}
exports.getReference2 = getReference2;
/**
 * @deprecated to be remove in next release
 *
 * @export
 * @param {*} settingConfigs
 * @param {*} dataColumns
 * @param {*} dataConfig
 * @param {*} isHorizonDisplay
 * @return {*}
 */
function getMarkLine(refTabs, dataColumns, dataConfig, isHorizonDisplay) {
    const markLineData = refTabs
        ?.reduce((acc, cur) => {
        const markLineConfigs = cur?.rows?.filter(r => r.key === 'markLine');
        acc.push(...markLineConfigs);
        return acc;
    }, [])
        .map(ml => {
        return getMarkLineData(ml, dataColumns, 'valueType', 'constantValue', 'metric', dataConfig, isHorizonDisplay);
    })
        .filter(Boolean);
    return {
        data: markLineData,
    };
}
/**
 * @deprecated to be remove in next release
 *
 * @export
 * @param {*} settingConfigs
 * @param {*} dataColumns
 * @param {*} dataConfig
 * @param {*} isHorizonDisplay
 * @return {*}
 */
function getMarkLineData(mark, dataColumns, valueTypeKey, constantValueKey, metricKey, dataConfig, isHorizonDisplay) {
    const name = mark.label;
    const valueKey = isHorizonDisplay ? 'xAxis' : 'yAxis';
    const show = getSettingValue(mark.rows, 'showLabel', 'value');
    const enableMarkLine = getSettingValue(mark.rows, 'enableMarkLine', 'value');
    const position = getSettingValue(mark.rows, 'position', 'value');
    const font = getSettingValue(mark.rows, 'font', 'value');
    const lineStyle = getSettingValue(mark.rows, 'lineStyle', 'value');
    const valueType = getSettingValue(mark.rows, valueTypeKey, 'value');
    const metricUid = getSettingValue(mark.rows, metricKey, 'value');
    const metr = getValueByColumnKey(dataConfig);
    const metricDatas = dataConfig.uid === metricUid ? dataColumns.map(d => +d[metr]) : [];
    const constantValue = getSettingValue(mark.rows, constantValueKey, 'value');
    let yAxis = 0;
    switch (valueType) {
        case 'constant':
            yAxis = constantValue;
            break;
        case 'average':
            yAxis = (0, object_1.meanValue)(metricDatas);
            break;
        case 'max':
            yAxis = Math.max(...metricDatas);
            break;
        case 'min':
            yAxis = Math.min(...metricDatas);
            break;
    }
    if (!enableMarkLine) {
        return null;
    }
    return {
        [valueKey]: yAxis,
        name,
        label: {
            show,
            position,
            ...font,
        },
        lineStyle,
    };
}
function getMarkLine2(refTabs, dataSetRows, dataConfig, isHorizonDisplay) {
    const markLineData = refTabs
        ?.reduce((acc, cur) => {
        const markLineConfigs = cur?.rows?.filter(r => r.key === 'markLine');
        return acc.concat(markLineConfigs);
    }, [])
        .map(ml => {
        return getMarkLineData2(ml, dataSetRows, 'valueType', 'constantValue', 'metric', dataConfig, isHorizonDisplay);
    })
        .filter(Boolean);
    return {
        data: markLineData,
    };
}
function getMarkLineData2(mark, dataSetRows, valueTypeKey, constantValueKey, metricKey, dataConfig, isHorizonDisplay) {
    if (!mark)
        return null;
    const name = mark.label;
    const valueKey = isHorizonDisplay ? 'xAxis' : 'yAxis';
    const [show, enableMarkLine, position, font, lineStyle, valueType, metricUid, constantValue,] = getStyles([mark], ['markLine'], [
        'showLabel',
        'enableMarkLine',
        'position',
        'font',
        'lineStyle',
        valueTypeKey,
        metricKey,
        constantValueKey,
    ]);
    if (!enableMarkLine) {
        return null;
    }
    const metricDatas = dataConfig.uid === metricUid
        ? dataSetRows.map(d => +d.getCell(dataConfig))
        : [];
    let yAxis = 0;
    switch (valueType) {
        case 'constant':
            yAxis = constantValue;
            break;
        case 'average':
            yAxis = (0, object_1.meanValue)(metricDatas);
            break;
        case 'max':
            yAxis = Math.max(...metricDatas);
            break;
        case 'min':
            yAxis = Math.min(...metricDatas);
            break;
    }
    return {
        [valueKey]: yAxis,
        name,
        label: {
            show,
            position,
            ...font,
        },
        lineStyle,
    };
}
function getMarkAreaData2(mark, dataSetRows, valueTypeKey, constantValueKey, metricKey, dataConfig, isHorizonDisplay) {
    if (!mark)
        return null;
    const valueKey = isHorizonDisplay ? 'xAxis' : 'yAxis';
    const [metric, show, enableMarkArea, position, font, borderStyle, opacity, backgroundColor, valueType, constantValue,] = getStyles([mark], ['markArea'], [
        metricKey,
        'showLabel',
        'enableMarkArea',
        'position',
        'font',
        'borderStyle',
        'opacity',
        'backgroundColor',
        valueTypeKey,
        constantValueKey,
    ]);
    const name = mark.value;
    const metricDatas = dataConfig.uid === metric
        ? dataSetRows.map(d => +d.getCell(dataConfig))
        : [];
    let yAxis = 0;
    switch (valueType) {
        case 'constant':
            yAxis = constantValue;
            break;
        case 'average':
            yAxis = (0, object_1.meanValue)(metricDatas);
            break;
        case 'max':
            yAxis = Math.max(...metricDatas);
            break;
        case 'min':
            yAxis = Math.min(...metricDatas);
            break;
    }
    if (!enableMarkArea || !Number.isFinite(yAxis) || Number.isNaN(yAxis)) {
        return null;
    }
    return {
        [valueKey]: yAxis,
        name,
        label: {
            show,
            position,
            ...font,
        },
        itemStyle: {
            opacity,
            color: backgroundColor,
            borderColor: borderStyle.color,
            borderWidth: borderStyle.width,
            borderType: borderStyle.type,
        },
    };
}
/**
 * @deprecated to be remove in next release
 *
 * @export
 * @param {*} settingConfigs
 * @param {*} dataColumns
 * @param {*} dataConfig
 * @param {*} isHorizonDisplay
 * @return {*}
 */
function getMarkAreaData(mark, dataColumns, valueTypeKey, constantValueKey, metricKey, isHorizonDisplay) {
    const valueKey = isHorizonDisplay ? 'xAxis' : 'yAxis';
    const show = getSettingValue(mark.rows, 'showLabel', 'value');
    const enableMarkArea = getSettingValue(mark.rows, 'enableMarkArea', 'value');
    const position = getSettingValue(mark.rows, 'position', 'value');
    const font = getSettingValue(mark.rows, 'font', 'value');
    const borderStyle = getSettingValue(mark.rows, 'borderStyle', 'value');
    const opacity = getSettingValue(mark.rows, 'opacity', 'value');
    const backgroundColor = getSettingValue(mark.rows, 'backgroundColor', 'value');
    const name = mark.value;
    const valueType = getSettingValue(mark.rows, valueTypeKey, 'value');
    const metric = getSettingValue(mark.rows, metricKey, 'value');
    const metricDatas = dataColumns.map(d => +d[metric]);
    const constantValue = getSettingValue(mark.rows, constantValueKey, 'value');
    let yAxis = 0;
    switch (valueType) {
        case 'constant':
            yAxis = constantValue;
            break;
        case 'average':
            yAxis = (0, object_1.meanValue)(metricDatas);
            break;
        case 'max':
            yAxis = Math.max(...metricDatas);
            break;
        case 'min':
            yAxis = Math.min(...metricDatas);
            break;
    }
    if (!enableMarkArea) {
        return null;
    }
    return {
        [valueKey]: yAxis,
        name,
        label: {
            show,
            position,
            ...font,
        },
        itemStyle: {
            opacity,
            color: backgroundColor,
            borderColor: borderStyle.color,
            borderWidth: borderStyle.width,
            borderType: borderStyle.type,
        },
    };
}
/**
 * @deprecated to be remove in next release
 *
 * @export
 * @param {*} settingConfigs
 * @param {*} dataColumns
 * @param {*} dataConfig
 * @param {*} isHorizonDisplay
 * @return {*}
 */
function getMarkArea(refTabs, dataColumns, isHorizonDisplay) {
    const refAreas = refTabs?.reduce((acc, cur) => {
        const markLineConfigs = cur?.rows?.filter(r => r.key === 'markArea');
        acc.push(...markLineConfigs);
        return acc;
    }, []);
    return {
        data: refAreas
            ?.map(mark => {
            const markAreaData = ['start', 'end']
                .map(prefix => {
                return getMarkAreaData(mark, dataColumns, `${prefix}ValueType`, `${prefix}ConstantValue`, `${prefix}Metric`, isHorizonDisplay);
            })
                .filter(Boolean);
            return markAreaData;
        })
            .filter(m => Boolean(m?.length)),
    };
}
function getMarkArea2(refTabs, dataSetRows, dataConfig, isHorizonDisplay) {
    const refAreas = refTabs?.reduce((acc, cur) => {
        const markLineConfigs = cur?.rows?.filter(r => r.key === 'markArea');
        return acc.concat(markLineConfigs);
    }, []);
    return {
        data: refAreas
            ?.map(mark => {
            const markAreaData = ['start', 'end']
                .map(prefix => {
                return getMarkAreaData2(mark, dataSetRows, `${prefix}ValueType`, `${prefix}ConstantValue`, `${prefix}Metric`, dataConfig, isHorizonDisplay);
            })
                .filter(Boolean);
            return markAreaData;
        })
            .filter(m => m?.length === 2),
    };
}
function getAxisLine(show, lineStyle) {
    return {
        show,
        lineStyle,
    };
}
exports.getAxisLine = getAxisLine;
function getAxisLabel(show, font, interval = null, rotate = null, overflow = null) {
    return {
        show,
        interval,
        rotate,
        overflow,
        ...font,
    };
}
exports.getAxisLabel = getAxisLabel;
function getSplitLine(show, lineStyle) {
    return {
        show,
        lineStyle,
    };
}
exports.getSplitLine = getSplitLine;
function getAxisTick(show, lineStyle) {
    return {
        show,
        lineStyle,
    };
}
exports.getAxisTick = getAxisTick;
function getNameTextStyle(fontFamily, fontSize, color) {
    return {
        fontFamily,
        fontSize,
        color,
    };
}
exports.getNameTextStyle = getNameTextStyle;
/**
 * [中文] 将服务端返回数据转换为ChartDataSet模型
 * </br>
 * [EN] Create ChartDataSet Model with sorted values
 *
 * @export
 * @template T
 * @param {T[][]} [datas]
 * @param {ChartDatasetMeta[]} [metas]
 * @param {ChartDataConfig[]} [dataConfigs]
 * @return {*}  {IChartDataSet<T>}
 */
function transformToDataSet(datas, metas, dataConfigs) {
    const fields = (dataConfigs || []).flatMap(config => config.rows || []);
    const ds = new ChartDataSet_1.ChartDataSet(datas || [], metas || [], fields || []);
    ds.sortBy(dataConfigs || []);
    return ds;
}
exports.transformToDataSet = transformToDataSet;
/**
 * [中文] 将服务端返回数据转换为一维对象数组结构, 已过时,请使用transformToDataSet
 * </br>
 * [EN] transform dataset to object array, please use transformToDataSet instead
 *
 * @deprecated should use DataSet model, @see {@link transformToDataSet}
 * @description
 * Support:
 *  1. Case Insensitive to get value
 *  2. More util helper
 * @example
 *
 * const columns = [
 *      ['r1-c1-v', 'r1-c2-v'],
 *      ['r2-c1-v', 'r2-c2-v'],
 *    ];
 * const metas = [{ name: 'name' }, { name: 'age' }];
 * const datas = transformToObjectArray(columns, metas);
 * console.log(datas); // [{"name":"r1-c1-v","age":"r1-c2-v2"},{"name":"r2-c1-v","age":"r2-c2-v"}]
 *
 * @export
 * @param {string[][]} [columns]
 * @param {ChartDatasetMeta[]} [metas]
 * @return {*}
 */
function transformToObjectArray(columns, metas) {
    if (!columns || !metas) {
        return [];
    }
    return debugger_1.Debugger.instance.measure('transformToObjectArray', () => {
        const result = Array.apply(null, Array(columns.length));
        for (let j = 0, outerLength = result.length; j < outerLength; j++) {
            let objCol = {};
            for (let i = 0, innerLength = metas.length; i < innerLength; i++) {
                const key = metas?.[i]?.name?.[0];
                if (!!key) {
                    objCol[key] = columns[j][i];
                }
            }
            result[j] = objCol;
        }
        return result;
    }, false);
}
exports.transformToObjectArray = transformToObjectArray;
function getValueByColumnKey(field) {
    if (!field) {
        return '';
    }
    if (!field.aggregate) {
        return field.colName;
    }
    return `${field.aggregate}(${field.colName})`;
}
exports.getValueByColumnKey = getValueByColumnKey;
/**
 * [中文] 获取字段的图表显示名称
 * </br>
 * [EN] Get data field render name by alias, colName and aggregate
 *
 * @export
 * @param {ChartDataSectionField} [field]
 * @return {string}
 */
function getColumnRenderName(field) {
    if (!field) {
        return '[unknown]';
    }
    if (field.alias?.name) {
        return field.alias.name;
    }
    return (0, internalChartHelper_1.getColumnRenderOriginName)(field);
}
exports.getColumnRenderName = getColumnRenderName;
function getUnusedHeaderRows(allRows, originalRows) {
    const oldFlattenedColNames = originalRows
        .flatMap(row => (0, internalChartHelper_1.flattenHeaderRowsWithoutGroupRow)(row))
        .map(r => r.colName);
    return (allRows || []).reduce((acc, cur) => {
        if (!oldFlattenedColNames.includes(cur.colName)) {
            acc.push(cur);
        }
        return acc;
    }, []);
}
exports.getUnusedHeaderRows = getUnusedHeaderRows;
/**
 * @deprecated to be remove in next release
 *
 * @export
 * @param {[]} dataset
 * @param {ChartDataSectionField} [config]
 * @return {*}
 */
function getDataColumnMaxAndMin(dataset, config) {
    if (!config || !dataset?.length) {
        return { min: 0, max: 100 };
    }
    const datas = dataset.map(row => row[getValueByColumnKey(config)]);
    const min = Number.isNaN(Math.min(...datas)) ? 0 : Math.min(...datas);
    const max = Number.isNaN(Math.max(...datas)) ? 100 : Math.max(...datas);
    return { min, max };
}
exports.getDataColumnMaxAndMin = getDataColumnMaxAndMin;
function getDataColumnMaxAndMin2(chartDataSetRows, config) {
    if (!config || !chartDataSetRows?.length) {
        return { min: 0, max: 100 };
    }
    const datas = (chartDataSetRows || []).map(row => Number(row.getCell(config)));
    const min = Number.isNaN(Math.min(...datas)) ? 0 : Math.min(...datas);
    const max = Number.isNaN(Math.max(...datas)) ? 100 : Math.max(...datas);
    return { min, max };
}
exports.getDataColumnMaxAndMin2 = getDataColumnMaxAndMin2;
function getSeriesTooltips4Scatter(params, tooltipItemConfigs, start) {
    const dataValues = params?.[0]?.value;
    return tooltipItemConfigs.map((config, index) => valueFormatter(config, dataValues?.[!!start ? start + index : index]));
}
exports.getSeriesTooltips4Scatter = getSeriesTooltips4Scatter;
function getSeriesTooltips4Rectangular2(chartDataSet, tooltipParam, groupConfigs, colorConfigs, aggConfigs, infoConfigs, sizeConfigs) {
    if (tooltipParam?.componentType !== 'series') {
        return '';
    }
    const aggConfigName = tooltipParam?.data?.name || tooltipParam?.seriesName;
    const row = tooltipParam?.data?.rowData || {};
    const tooltips = []
        .concat(groupConfigs || [])
        .concat(colorConfigs || [])
        .concat(aggConfigs.filter(agg => getColumnRenderName(agg) === aggConfigName) ||
        [])
        .concat(sizeConfigs || [])
        .concat(infoConfigs || [])
        .map(config => valueFormatter(config, row?.[chartDataSet.getFieldOriginKey(config)]));
    return tooltips.join('<br />');
}
exports.getSeriesTooltips4Rectangular2 = getSeriesTooltips4Rectangular2;
function getSeriesTooltips4Polar2(chartDataSet, tooltipParam, groupConfigs, colorConfigs, aggConfigs, infoConfigs, sizeConfigs) {
    const row = tooltipParam?.data?.rowData || {};
    const tooltips = []
        .concat(groupConfigs || [])
        .concat(colorConfigs || [])
        .concat(aggConfigs || [])
        .concat(sizeConfigs || [])
        .concat(infoConfigs || [])
        .map(config => valueFormatter(config, row?.[chartDataSet.getFieldOriginKey(config)]));
    return tooltips.join('<br />');
}
exports.getSeriesTooltips4Polar2 = getSeriesTooltips4Polar2;
function getSeriesTooltips4Rectangular(params, groupConfigs, aggConfigs, dataColumns) {
    if (!aggConfigs?.length) {
        return [];
    }
    if (!groupConfigs?.length) {
        return aggConfigs.map(config => valueFormatter(config, dataColumns?.[0]?.[getValueByColumnKey(config)]));
    }
    if (groupConfigs?.[0]) {
        const groupConfig = groupConfigs?.[0];
        const dataRow = dataColumns.find(dc => dc[getValueByColumnKey(groupConfig)] === params?.[0]?.axisValue);
        return aggConfigs.map(config => valueFormatter(config, dataRow?.[getValueByColumnKey(config)]));
    }
    return [];
}
exports.getSeriesTooltips4Rectangular = getSeriesTooltips4Rectangular;
/**
 * [中文] 获取字段的Tooltip显示名称和内容
 * </br>
 * [EN] Get chart render string with field name and value
 * @example
 * const config = {
 *   aggregate: "SUM"
 *   colName: 'name',
 *   type: 'STRING',
 *   category: 'field',
 *   uid: '123456',
 * }
 * const formatValue = valueFormatter(config, '示例');
 * console.log(formatValue) // SUM(name): 示例
 * @export
 * @param {ChartDataSectionField} [config]
 * @param {number} [value]
 * @return {string}
 */
function valueFormatter(config, value) {
    return `${getColumnRenderName(config)}: ${toFormattedValue(value, config?.format)}`;
}
exports.valueFormatter = valueFormatter;
function getScatterSymbolSizeFn(valueIndex, max, min, cycleRatio) {
    min = Math.min(0, min);
    const scaleRatio = cycleRatio || 1;
    const defaultScatterPointPixelSize = 10;
    const distance = max - min === 0 ? 100 : max - min;
    return function (val) {
        return Math.max(3, ((val?.[valueIndex] - min) / distance) *
            scaleRatio *
            defaultScatterPointPixelSize *
            2);
    };
}
exports.getScatterSymbolSizeFn = getScatterSymbolSizeFn;
function getGridStyle(styles) {
    const [containLabel, left, right, bottom, top] = getStyles(styles, ['margin'], ['containLabel', 'marginLeft', 'marginRight', 'marginBottom', 'marginTop']);
    return { left, right, bottom, top, containLabel };
}
exports.getGridStyle = getGridStyle;
function convertMarginToPx(value, base) {
    if (typeof value === 'number')
        return value;
    if (typeof value === 'string' && value?.endsWith('%') && base) {
        return (Number(value.replace('%', '')) / 100) * base;
    }
    return 0;
}
/**
 * [中文] 估算图例实际渲染高度(像素)
 * </br>
 * [EN] Estimate the rendered height (px) of the legend
 */
function getLegendEstimatedHeight(legend, chartWidth) {
    if (legend.orient !== 'horizontal') {
        return 0;
    }
    const names = legend.data || [];
    // scroll legend never wraps into multiple rows, it pages horizontally
    if (legend.type === 'scroll' || !names.length) {
        return legend.height || 32;
    }
    const font = legend.textStyle;
    const textWidth = (name) => (0, utils_2.getTextWidth)(String(name), font?.fontWeight || undefined, font?.fontSize != null ? String(font.fontSize) : undefined, font?.fontFamily || undefined);
    // 25 = default itemWidth, 5 = icon-text spacing, 10 = itemGap / padding
    const totalWidth = names.reduce((width, name) => width + 25 + 5 + textWidth(name), 0) +
        Math.max(names.length - 1, 0) * 10 +
        10;
    const usableWidth = Math.max((chartWidth ?? 600) - 16, 100);
    const rows = Math.max(1, Math.ceil(totalWidth / usableWidth));
    // the configured height may be smaller than the wrapped content, ECharts
    // still renders extra rows beyond the fixed height, take the larger one
    return Math.max(legend.height ?? 0, rows * 24 + 8);
}
/**
 * [中文] 估算图例单项的最大渲染宽度(像素),系列名未设置别名时会带上
 * 聚合函数前缀(如 SUM(xxx)),按渲染的完整名称测量
 * </br>
 * [EN] Estimate the max rendered width (px) of a legend item. The full
 * rendered series name is measured, including the aggregate prefix
 * (e.g. SUM(xxx)) when the field has no alias
 */
function getLegendEstimatedWidth(legend, chartWidth) {
    const names = legend.data || [];
    if (!names.length) {
        return 96;
    }
    const font = legend.textStyle;
    const textWidth = (name) => (0, utils_2.getTextWidth)(String(name), font?.fontWeight || undefined, font?.fontSize != null ? String(font.fontSize) : undefined, font?.fontFamily || undefined);
    // 25 = default itemWidth, 5 = icon-text spacing, 10 = padding
    const maxItemWidth = Math.max(...names.map(name => 25 + 5 + textWidth(name)));
    return Math.min(maxItemWidth + 10, Math.max((chartWidth ?? 600) * 0.5, 96));
}
/**
 * [中文] 获取绘图区网格样式,并根据图例位置与尺寸自动预留空间,避免图例与图表主区重叠
 * </br>
 * [EN] Get grid style and reserve space for the legend automatically
 * to avoid overlapping between legend and chart main area
 *
 * @param {Array<ChartStyleConfig>} styles required
 * @param {LegendStyle} legend required the final legend style of the chart
 * @param {Object} [chartSize] optional { width, height } from echarts instance,
 *        used to resolve percentage margins for comparison
 * @return {GridStyle}
 */
function getGridStyleWithLegend(styles, legend, chartSize) {
    const grid = getGridStyle(styles);
    if (!legend?.show) {
        return grid;
    }
    const RESERVE_GAP = 8;
    // axis name (e.g. rotated y-axis name) is rendered outside the grid and
    // not covered by containLabel, reserve extra space for its zone
    // (default nameGap 15 + name text height ~14)
    const AXIS_NAME_ALLOWANCE = 30;
    // when the canvas size is unknown (e.g. plugin charts calling dHelper
    // without chartSize), percentage margins cannot be resolved to px,
    // keep them untouched instead of overriding with the reserve value
    const reserveMoreThan = (margin, base, reserve) => {
        if (typeof margin === 'string' && margin.endsWith('%') && !base) {
            return margin;
        }
        return Math.max(convertMarginToPx(margin, base), reserve);
    };
    const legendHeight = getLegendEstimatedHeight(legend, chartSize?.width);
    if (legend.orient === 'horizontal') {
        if (typeof legend.top === 'number') {
            grid.top = reserveMoreThan(grid.top, chartSize?.height, legend.top + legendHeight + RESERVE_GAP);
        }
        else if (typeof legend.bottom === 'number') {
            grid.bottom = reserveMoreThan(grid.bottom, chartSize?.height, legend.bottom + legendHeight + RESERVE_GAP + AXIS_NAME_ALLOWANCE);
        }
    }
    else if (legend.orient === 'vertical') {
        const legendWidth = legend.width || getLegendEstimatedWidth(legend, chartSize?.width);
        if (typeof legend.left === 'number') {
            grid.left = reserveMoreThan(grid.left, chartSize?.width, legend.left + legendWidth + RESERVE_GAP + AXIS_NAME_ALLOWANCE);
        }
        else if (typeof legend.right === 'number') {
            grid.right = reserveMoreThan(grid.right, chartSize?.width, legend.right + legendWidth + RESERVE_GAP + AXIS_NAME_ALLOWANCE);
        }
    }
    return grid;
}
exports.getGridStyleWithLegend = getGridStyleWithLegend;
// TODO(Stephen): to be used chart DataSetRow model for all charts
function getExtraSeriesRowData(data) {
    if (data instanceof ChartDataSet_1.ChartDataSetRow) {
        return {
            // NOTE: row data should be case sensitive except for data chart
            rowData: data?.convertToCaseSensitiveObject(),
        };
    }
    return {
        rowData: data,
    };
}
exports.getExtraSeriesRowData = getExtraSeriesRowData;
function getExtraSeriesDataFormat(format) {
    return {
        format,
    };
}
exports.getExtraSeriesDataFormat = getExtraSeriesDataFormat;
function getColorizeGroupSeriesColumns(chartDataSet, groupConfig) {
    return Object.entries(chartDataSet.groupBy(groupConfig)).map(([k, v]) => {
        let a = {};
        a[k] = v;
        return a;
    });
}
exports.getColorizeGroupSeriesColumns = getColorizeGroupSeriesColumns;
/**
 * [中文] 是否满足当前meta中标识的限制要求,以满足图表绘制
 * </br>
 * [EN] Check if current config with required fields match the chart basic requirement of meta info.
 *
 * @example
 *
 *  const meta = {
 *      requirements: [
 *        {
 *          group: [1, 999],
 *          aggregate: [1, 999],
 *        },
 *      ],
 *    };
 *    const config = {
 *     datas: [
 *        {
 *         type: 'group',
 *          required: true,
 *          rows: [
 *            {
 *              colName: 'category',
 *            },
 *          ],
 *        },
 *        {
 *          type: 'aggregate',
 *          required: true,
 *          rows: [
 *            {
 *              colName: 'amount',
 *            },
 *          ],
 *        },
 *      ],
 *    };
 *  const isMatch = isMatchRequirement(meta, config);
 *  console.log(isMatch); // true;
 *
 * @export
 * @param {ChartMetadata} meta
 * @param {ChartConfig} config
 * @return {boolean}
 */
function isMatchRequirement(meta, config) {
    const dataConfigs = config.datas || [];
    const groupedFieldConfigs = (0, internalChartHelper_1.getRequiredGroupedSections)(dataConfigs).flatMap(config => config.rows || []);
    const aggregateFieldConfigs = (0, internalChartHelper_1.getRequiredAggregatedSections)(dataConfigs).flatMap(config => config.rows || []);
    const requirements = meta.requirements || [];
    return requirements.some(r => {
        const group = r?.[constants_1.ChartDataSectionType.Group];
        const aggregate = r?.[constants_1.ChartDataSectionType.Aggregate];
        return ((0, internalChartHelper_1.isInRange)(group, groupedFieldConfigs.length) &&
            (0, internalChartHelper_1.isInRange)(aggregate, aggregateFieldConfigs.length));
    });
}
exports.isMatchRequirement = isMatchRequirement;
// 获取是否展示刻度
const getIntervalShow = (interval) => interval !== 'auto' && interval !== null;
exports.getIntervalShow = getIntervalShow;
// 判断overflow 条件是否已生效
function hadAxisLabelOverflowConfig(options, horizon = false) {
    if (!options)
        return false;
    const axisName = !horizon ? 'xAxis' : 'yAxis';
    const axisLabelOpts = options[axisName]?.[0]?.axisLabel;
    if (!axisLabelOpts)
        return false;
    const { overflow, interval, show } = axisLabelOpts;
    return !!(show && overflow && (0, exports.getIntervalShow)(interval));
}
exports.hadAxisLabelOverflowConfig = hadAxisLabelOverflowConfig;
// 处理溢出情况
function setOptionsByAxisLabelOverflow(config) {
    const { chart, xAxis, yAxis, grid, series, horizon = false } = config;
    const commonOpts = {
        grid,
        xAxis,
        yAxis,
        series,
    };
    // 如果是x轴需要截断,则取x轴数据
    const axisOpts = (!horizon ? xAxis : yAxis);
    const axisName = !horizon ? 'xAxis' : 'yAxis';
    const data = axisOpts.data || [];
    const dataLength = data.length;
    // 拿到截断配置
    const overflow = axisOpts.axisLabel?.overflow;
    const show = axisOpts.axisLabel?.show;
    // 是否展示刻度,非刻度使用默认样式
    const showInterval = (0, exports.getIntervalShow)(axisOpts.axisLabel?.interval);
    // 不展示刻度
    if (!show)
        return commonOpts;
    // 数据为空
    if (!dataLength)
        return commonOpts;
    commonOpts[axisName].axisLabel.hideOverlap = true;
    commonOpts[axisName].axisLabel.overflow = overflow;
    // 如果overflow为截断,则使用每段刻度来响应tooltip
    // 不破坏原有展示逻辑
    if (showInterval && overflow === 'truncate') {
        commonOpts[axisName].axisPointer = {
            show: true,
            type: 'shadow',
        };
    }
    // 获取x/y轴在model上的信息
    // @ts-ignore
    const axisModel = chart.getModel()?.getComponent(axisName);
    // 处理 每个刻度宽度
    const setWidth = width => {
        // 水平图表使用默认宽度
        if (horizon)
            return 40;
        return parseInt(String((width - dataLength * 8) / dataLength));
    };
    // model 渲染未完成的兼容性方案,一般只在图表初始化阶段,还没有拿到model。
    // 一般只会运行一次
    // 拿到model后就可使用更加精确的坐标轴宽高度等信息,所以处理可以略粗略
    const handlerWhenChartUnFinished = () => {
        commonOpts[axisName].axisLabel.width = showInterval
            ? setWidth((0, internalChartHelper_1.getAxisLengthByConfig)(config))
            : void 0;
        return commonOpts;
    };
    // model未获取到,原因: 未渲染完成
    if (!axisModel) {
        handlerWhenChartUnFinished();
        return commonOpts;
    }
    // @ts-ignore
    const axisView = chart.getViewOfComponentModel(axisModel);
    const axisRect = axisView?.group?.getBoundingRect();
    if (!axisRect) {
        handlerWhenChartUnFinished();
        return commonOpts;
    }
    commonOpts[axisName].axisLabel.width = showInterval
        ? setWidth(axisRect.width)
        : void 0;
    return commonOpts;
}
exports.setOptionsByAxisLabelOverflow = setOptionsByAxisLabelOverflow;
const getAutoFunnelTopPosition = (config) => {
    const { chart, height, sort, legendPos } = config;
    if (legendPos !== 'left' && legendPos !== 'right')
        return 8;
    if (!height)
        return 16;
    // 升序
    if (sort === 'ascending')
        return 16;
    const chartHeight = chart.getHeight();
    if (!chartHeight)
        return 16;
    // 24 marginBottom
    return chartHeight - 24 - height;
};
exports.getAutoFunnelTopPosition = getAutoFunnelTopPosition;
/**
 * Get Fields when data section is drillable
 *
 * @param {ChartDataConfig[]} configs
 * @param {ChartDrillOption} option
 * @return {*}  {ChartDataSectionField[]}
 */
const getDrillableRows = (configs, option) => {
    return configs
        ?.filter(c => c.type === constants_1.ChartDataSectionType.Group)
        .flatMap(config => {
        if (Boolean(config.drillable)) {
            if (!option ||
                option?.mode === ChartDrillOption_1.DrillMode.Normal ||
                !option?.getCurrentFields()) {
                return config.rows?.[0] || [];
            }
            return (config.rows?.filter(f => !option?.getCurrentFields() ||
                Boolean(option?.getCurrentFields()?.some(df => df.uid === f.uid))) || []);
        }
        return config.rows || [];
    });
};
exports.getDrillableRows = getDrillableRows;
const getRuntimeDateLevelFields = (rows) => {
    const _rows = (0, object_1.CloneValueDeep)(rows);
    _rows?.forEach((v, i) => {
        const symbolData = v?.[constants_1.RUNTIME_DATE_LEVEL_KEY];
        if (symbolData) {
            _rows[i] = symbolData;
        }
    });
    return _rows;
};
exports.getRuntimeDateLevelFields = getRuntimeDateLevelFields;
/**
 * Merging runtime date level into computed fields
 */
const getRuntimeComputedFields = (dateLevelComputedFields, replacedConfig, computedFields, isRuntime) => {
    let _computedFields = computedFields ? (0, object_1.CloneValueDeep)(computedFields) : [];
    if (isRuntime && replacedConfig?.field) {
        const index = (0, exports.getRuntimeDateLevelFields)(_computedFields).findIndex(v => v.name === replacedConfig?.colName);
        const replacedConfigIndex = dateLevelComputedFields.findIndex(v => v.field === replacedConfig?.field);
        _computedFields = (0, mutation_1.updateBy)(_computedFields, draft => {
            const dateLevelConfig = dateLevelComputedFields[replacedConfigIndex];
            if (dateLevelConfig) {
                draft[index][constants_1.RUNTIME_DATE_LEVEL_KEY] = {
                    category: dateLevelConfig.category,
                    name: dateLevelConfig.colName,
                    type: dateLevelConfig.type,
                    expression: dateLevelConfig.expression,
                };
            }
        });
    }
    else {
        if (dateLevelComputedFields.length) {
            const expressionList = [];
            _computedFields.forEach(v => {
                if (v.category === constants_1.ChartDataViewFieldCategory.DateLevelComputedField) {
                    expressionList.push(v.expression);
                }
            });
            dateLevelComputedFields.forEach(v => {
                if (!expressionList.includes(v.expression)) {
                    _computedFields = (0, mutation_1.updateBy)(_computedFields, draft => {
                        draft.push({
                            category: v.category,
                            name: v.colName,
                            type: v.type,
                            expression: v.expression,
                        });
                    });
                }
            });
        }
        if (replacedConfig) {
            _computedFields = _computedFields.filter(v => v.name !== replacedConfig.colName);
        }
    }
    return _computedFields;
};
exports.getRuntimeComputedFields = getRuntimeComputedFields;
const clearRuntimeDateLevelFieldsInChartConfig = (config) => {
    return (0, mutation_1.updateBy)(config, draft => {
        if (draft?.datas) {
            const index = draft.datas.findIndex(v => v.type === constants_1.ChartDataSectionType.Group);
            const groupRows = draft.datas[index]?.rows;
            groupRows?.forEach((v, i) => {
                if (groupRows[i]) {
                    delete groupRows[i][constants_1.RUNTIME_DATE_LEVEL_KEY];
                }
            });
        }
    });
};
exports.clearRuntimeDateLevelFieldsInChartConfig = clearRuntimeDateLevelFieldsInChartConfig;
const setRuntimeDateLevelFieldsInChartConfig = (config) => {
    return (0, mutation_1.updateBy)(config, draft => {
        if (draft?.datas) {
            const index = draft.datas.findIndex(v => v.type === constants_1.ChartDataSectionType.Group);
            const groupRows = draft.datas[index]?.rows;
            groupRows?.forEach((v, i) => {
                const runtimeDateLevel = groupRows[i][constants_1.RUNTIME_DATE_LEVEL_KEY];
                if (groupRows[i].uid === runtimeDateLevel?.uid) {
                    groupRows[i] = runtimeDateLevel;
                }
            });
        }
    });
};
exports.setRuntimeDateLevelFieldsInChartConfig = setRuntimeDateLevelFieldsInChartConfig;
/**
 * Get common selected styles
 *
 * @param {string | number} comIndex
 * @param {string | number} dcIndex
 * @param {SelectedItem[]} selectionList
 * @param {[x: string]: any} [itemStyle = {}]
 * @return {itemStyle: [x: string]: any} itemStyle
 */
const getSelectedItemStyles = (comIndex, dcIndex, selectionList, itemStyle = {}) => {
    if (selectionList.length) {
        const selectionConfig = selectionList.find(v => v.index === comIndex + ',' + dcIndex);
        return {
            itemStyle: Object.assign(itemStyle, selectionConfig ? {} : { opacity: 0.5 }),
        };
    }
    return {
        itemStyle,
    };
};
exports.getSelectedItemStyles = getSelectedItemStyles;
/**
 * Comparing old and new selectedItems
 *
 * @param {SelectedItem[]} newSelectedItems
 * @param {SelectedItem[]} [oldSelectedItems]
 * @return {boolean}
 */
const compareSelectedItems = (newSelectedItems, oldSelectedItems) => {
    if (newSelectedItems.length !== oldSelectedItems?.length) {
        return true;
    }
    else if (newSelectedItems.length === oldSelectedItems.length &&
        newSelectedItems.length) {
        return !!newSelectedItems.filter(v => {
            const item = oldSelectedItems.find(oldItem => oldItem.index === v.index);
            return !item ||
                Object.values(item.data.rowData).length !==
                    Object.values(v.data.rowData).length
                ? true
                : false;
        }).length;
    }
    return false;
};
exports.compareSelectedItems = compareSelectedItems;
function getAllColumnInMeta(meta) {
    return (meta?.reduce((arr, cur) => {
        return cur.children ? arr.concat(cur.children) : arr.concat([cur]);
    }, []) || []);
}
exports.getAllColumnInMeta = getAllColumnInMeta;
/**
 * Get precision number.
 *
 * @param {number | string} x
 * @param {number} [precision]
 * @return {number}  x
 */
function round(x, precision) {
    if (precision == null) {
        precision = 10;
    }
    precision = Math.min(Math.max(0, precision), 20);
    x = (+x).toFixed(precision);
    return +x;
}
exports.round = round;
/**
 * Get min and max number.
 *
 * @param {ChartDataSectionField[]} configs
 * @param {IChartDataSet<string>} [chartDataset]
 * @return {[number, number]}  minAndMax
 */
function getMinAndMaxNumber(configs, chartDataset) {
    const datas = configs
        .reduce((acc, cur) => acc.concat(chartDataset.map(dc => Number(dc.getCell(cur)))), [])
        .filter(number_1.isNumber);
    return [Math.min(0, ...datas), Math.max(0, ...datas)];
}
exports.getMinAndMaxNumber = getMinAndMaxNumber;
function findPathByNameInMeta(meta, colName) {
    return getAllColumnInMeta(meta)?.find(v => v.name === colName);
}
exports.findPathByNameInMeta = findPathByNameInMeta;
function mergeChartAndViewComputedField(viewComputer, chartComputer) {
    viewComputer = viewComputer || [];
    return (0, object_1.UniqWith)(viewComputer.concat(chartComputer || []), (a, b) => a?.name === b?.name);
}
exports.mergeChartAndViewComputedField = mergeChartAndViewComputedField;
function createDateLevelComputedFieldForConfigComputedFields(meta, computedFields) {
    if (!meta) {
        return [];
    }
    const dateFields = getAllColumnInMeta(meta)?.filter(v => v.type === constants_1.DataViewFieldType.DATE) ||
        [];
    const allDateLevelComputedFields = [];
    const notDateLevelComputedFields = computedFields?.filter(field => field.category !== constants_1.ChartDataViewFieldCategory.DateLevelComputedField) || [];
    dateFields.forEach(field => {
        constant_1.DATE_LEVELS.forEach(v => {
            allDateLevelComputedFields.push({
                category: constants_1.ChartDataViewFieldCategory.DateLevelComputedField,
                name: field.name + globalConstants_1.DATE_LEVEL_DELIMITER + v.expression,
                type: field.type,
                expression: `${v.expression}(${(0, utils_1.FieldTemplate)(field.path)})`,
            });
        });
    });
    return allDateLevelComputedFields.concat(notDateLevelComputedFields);
}
exports.createDateLevelComputedFieldForConfigComputedFields = createDateLevelComputedFieldForConfigComputedFields;
function filterCurrentUsedComputedFields(chartConfig, computedFields) {
    const fieldsNameList = (chartConfig?.datas || [])
        .flatMap(config => config.rows || [])
        .flatMap(row => row?.colName || []);
    const currentUsedDateComputedFields = computedFields?.filter(field => field.category === constants_1.ChartDataViewFieldCategory.DateLevelComputedField &&
        fieldsNameList.includes(field.name)) || [];
    const notDateLevelComputedFields = computedFields?.filter(field => field.category !== constants_1.ChartDataViewFieldCategory.DateLevelComputedField) || [];
    return notDateLevelComputedFields.concat(currentUsedDateComputedFields);
}
exports.filterCurrentUsedComputedFields = filterCurrentUsedComputedFields;
function hasAggregationFunction(exp) {
    return [
        constants_1.AggregateFieldActionType.Avg,
        constants_1.AggregateFieldActionType.Count,
        constants_1.AggregateFieldActionType.Count_Distinct,
        constants_1.AggregateFieldActionType.Max,
        constants_1.AggregateFieldActionType.Min,
        constants_1.AggregateFieldActionType.Median,
        constants_1.AggregateFieldActionType.Quartile1,
        constants_1.AggregateFieldActionType.Quartile3,
        constants_1.AggregateFieldActionType.Sum,
    ].some(agg => new RegExp(`${agg}\\(`, 'i').test(exp || ''));
}
exports.hasAggregationFunction = hasAggregationFunction;