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