"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSeriesProps = exports.getSeriesName = exports.getMetaFromSeriesId = exports.getFormattedTablesByLayers = exports.getFormattedTable = exports.getFormattedRow = exports.generateSeriesId = void 0; exports.hasMultipleLayersWithSplits = hasMultipleLayersWithSplits; var _charts = require("@elastic/charts"); var _utils = require("@kbn/visualizations-plugin/common/utils"); var _layer_types_guards = require("../../common/utils/layer_types_guards"); var _constants = require("../../common/constants"); var _state = require("./state"); var _format = require("./format"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ const isPrimitive = value => value != null && typeof value !== 'object'; const getFormattedRow = (row, columns, columnsFormatters, xAccessor, splitColumnAccessor, splitRowAccessor, xScaleType) => columns.reduce((formattedInfo, { id }) => { const record = formattedInfo.row[id]; if (record != null && ( // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level !isPrimitive(record) || id === xAccessor && xScaleType === 'ordinal' || id === splitColumnAccessor || id === splitRowAccessor)) { return { row: { ...formattedInfo.row, [id]: columnsFormatters[id].convert(record) }, formattedColumns: { ...formattedInfo.formattedColumns, [id]: true } }; } return formattedInfo; }, { row, formattedColumns: {} }); exports.getFormattedRow = getFormattedRow; const getFormattedTable = (table, formatFactory, xAccessor, splitColumnAccessor, splitRowAccessor, accessors, xScaleType) => { const columnsFormatters = table.columns.reduce((formatters, { id, meta }) => { const accessor = accessors.find(a => (0, _utils.getAccessorByDimension)(a, table.columns) === id); return { ...formatters, [id]: formatFactory(accessor ? (0, _format.getFormat)(table.columns, accessor) : meta.params) }; }, {}); const formattedTableInfo = { rows: [], formattedColumns: {} }; for (const row of table.rows) { const formattedRowInfo = getFormattedRow(row, table.columns, columnsFormatters, xAccessor ? (0, _utils.getAccessorByDimension)(xAccessor, table.columns) : undefined, splitColumnAccessor ? (0, _utils.getAccessorByDimension)(splitColumnAccessor, table.columns) : undefined, splitRowAccessor ? (0, _utils.getAccessorByDimension)(splitRowAccessor, table.columns) : undefined, xScaleType); formattedTableInfo.rows.push(formattedRowInfo.row); formattedTableInfo.formattedColumns = { ...formattedTableInfo.formattedColumns, ...formattedRowInfo.formattedColumns }; } return { table: { ...table, rows: formattedTableInfo.rows }, formattedColumns: formattedTableInfo.formattedColumns }; }; exports.getFormattedTable = getFormattedTable; const getFormattedTablesByLayers = (layers, formatFactory, splitColumnAccessor, splitRowAccessor) => layers.reduce((formattedDatatables, { layerId, table, xAccessor, splitAccessors = [], accessors, xScaleType }) => ({ ...formattedDatatables, [layerId]: getFormattedTable(table, formatFactory, xAccessor, splitColumnAccessor, splitRowAccessor, [xAccessor, ...splitAccessors, ...accessors, splitColumnAccessor, splitRowAccessor].filter(a => a !== undefined), xScaleType) }), {}); exports.getFormattedTablesByLayers = getFormattedTablesByLayers; function getSplitValues(splitAccessorsMap, splitAccessors, alreadyFormattedColumns, columns, splitAccessorsFormats) { if (splitAccessorsMap.size < 0) { return []; } return [...splitAccessorsMap].reduce((acc, [splitAccessor, value]) => { const split = splitAccessors.find(accessor => (0, _utils.getAccessorByDimension)(accessor, columns) === splitAccessor); if (split) { const splitColumnId = (0, _utils.getAccessorByDimension)(split, columns); const splitFormatter = splitAccessorsFormats[splitColumnId].formatter; return [...acc, alreadyFormattedColumns[splitColumnId] ? value : splitFormatter.convert(value)]; } return acc; }, []); } const getSeriesName = (data, { splitAccessors, accessorsCount, columns, splitAccessorsFormats, alreadyFormattedColumns, columnToLabelMap, multipleLayersWithSplits }, titles) => { var _ref, _columnToLabelMap$key, _titles$yTitles; // For multiple y series, the name of the operation is used on each, either: // * Key - Y name // * Formatted value - Y name const splitValues = getSplitValues(data.splitAccessors, splitAccessors, alreadyFormattedColumns, columns, splitAccessorsFormats); const key = data.seriesKeys[data.seriesKeys.length - 1]; const yAccessorTitle = (_ref = (_columnToLabelMap$key = columnToLabelMap[key]) !== null && _columnToLabelMap$key !== void 0 ? _columnToLabelMap$key : titles === null || titles === void 0 ? void 0 : (_titles$yTitles = titles.yTitles) === null || _titles$yTitles === void 0 ? void 0 : _titles$yTitles[key]) !== null && _ref !== void 0 ? _ref : null; if (accessorsCount > 1 || multipleLayersWithSplits) { if (splitValues.length === 0) { return yAccessorTitle; } return `${splitValues.join(' - ')}${yAccessorTitle ? ' - ' + yAccessorTitle : ''}`; } return splitValues.length > 0 ? splitValues.join(' - ') : yAccessorTitle; }; exports.getSeriesName = getSeriesName; const getPointConfig = ({ xAccessor, markSizeAccessor, emphasizeFitting, showPoints, pointsRadius }) => ({ visible: showPoints !== undefined ? showPoints : !xAccessor || markSizeAccessor !== undefined, radius: pointsRadius !== undefined ? pointsRadius : xAccessor && !emphasizeFitting ? 5 : 0, fill: markSizeAccessor ? _charts.ColorVariant.Series : undefined }); const getFitLineConfig = () => ({ visible: true, stroke: _charts.ColorVariant.Series, opacity: 1, dash: [] }); const getLineConfig = ({ showLines, lineWidth }) => ({ strokeWidth: lineWidth, visible: showLines }); const getColor = (series, { layer, colorAssignments, paletteService, syncColors, getSeriesNameFn }, uiState, singleTable) => { var _getSeriesNameFn; const overwriteColor = (0, _state.getSeriesColor)(layer, series.yAccessor); if (overwriteColor !== null) { return overwriteColor; } const name = ((_getSeriesNameFn = getSeriesNameFn(series)) === null || _getSeriesNameFn === void 0 ? void 0 : _getSeriesNameFn.toString()) || ''; const overwriteColors = uiState !== null && uiState !== void 0 && uiState.get ? uiState.get('vis.colors', {}) : {}; if (Object.keys(overwriteColors).includes(name)) { return overwriteColors[name]; } const colorAssignment = colorAssignments[layer.palette.name]; const seriesLayers = [{ name, totalSeriesAtDepth: colorAssignment.totalSeriesCount, rankAtDepth: colorAssignment.getRank(singleTable ? 'commonLayerId' : layer.layerId, name) }]; return paletteService.get(layer.palette.name).getCategoricalColor(seriesLayers, { maxDepth: 1, behindText: false, totalSeries: colorAssignment.totalSeriesCount, syncColors }, layer.palette.params); }; const EMPTY_ACCESSOR = '-'; const SPLIT_CHAR = ':'; const SPLIT_Y_ACCESSORS = '|'; const generateSeriesId = ({ layerId }, splitColumnIds, accessor, xColumnId) => [layerId, xColumnId !== null && xColumnId !== void 0 ? xColumnId : EMPTY_ACCESSOR, accessor !== null && accessor !== void 0 ? accessor : EMPTY_ACCESSOR, ...splitColumnIds].join(SPLIT_CHAR); exports.generateSeriesId = generateSeriesId; const getMetaFromSeriesId = seriesId => { const [layerId, xAccessor, yAccessors, ...splitAccessors] = seriesId.split(SPLIT_CHAR); return { layerId, xAccessor: xAccessor === EMPTY_ACCESSOR ? undefined : xAccessor, yAccessors: yAccessors.split(SPLIT_Y_ACCESSORS), splitAccessor: splitAccessors[0] === EMPTY_ACCESSOR ? undefined : splitAccessors }; }; exports.getMetaFromSeriesId = getMetaFromSeriesId; function hasMultipleLayersWithSplits(layers) { return layers.filter(l => { var _l$splitAccessors; return (0, _layer_types_guards.isDataLayer)(l) && (((_l$splitAccessors = l.splitAccessors) === null || _l$splitAccessors === void 0 ? void 0 : _l$splitAccessors.length) || 0) > 0; }).length > 1; } const getSeriesProps = ({ layer, titles = {}, accessor, chartHasMoreThanOneBarSeries, colorAssignments, formatFactory, columnToLabelMap, paletteService, syncColors, yAxis, xAxis, timeZone, emphasizeFitting, fillOpacity, formattedDatatableInfo, defaultXScaleType, fieldFormats, uiState, allYAccessors, singleTable, multipleLayersWithSplits }) => { var _layer$splitAccessors, _table$columns$find, _table$columns$find$m, _layer$xScaleType, _yAxis$extent, _xAxis$extent; const { table, isStacked, markSizeAccessor } = layer; const isPercentage = layer.isPercentage; let stackMode = isPercentage ? _constants.AxisModes.PERCENTAGE : undefined; if (yAxis !== null && yAxis !== void 0 && yAxis.mode) { stackMode = (yAxis === null || yAxis === void 0 ? void 0 : yAxis.mode) === _constants.AxisModes.NORMAL ? undefined : yAxis === null || yAxis === void 0 ? void 0 : yAxis.mode; } const scaleType = (yAxis === null || yAxis === void 0 ? void 0 : yAxis.scaleType) || _charts.ScaleType.Linear; const isBarChart = layer.seriesType === _constants.SeriesTypes.BAR; const xColumnId = layer.xAccessor !== undefined ? (0, _utils.getAccessorByDimension)(layer.xAccessor, table.columns) : undefined; const splitColumnIds = ((_layer$splitAccessors = layer.splitAccessors) === null || _layer$splitAccessors === void 0 ? void 0 : _layer$splitAccessors.map(splitAccessor => { return (0, _utils.getAccessorByDimension)(splitAccessor, table.columns); })) || []; const enableHistogramMode = layer.isHistogram && (isStacked || !splitColumnIds.length) && (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); const formatter = table === null || table === void 0 ? void 0 : (_table$columns$find = table.columns.find(column => column.id === (Array.isArray(accessor) ? accessor[0] : accessor))) === null || _table$columns$find === void 0 ? void 0 : (_table$columns$find$m = _table$columns$find.meta) === null || _table$columns$find$m === void 0 ? void 0 : _table$columns$find$m.params; const markSizeColumnId = markSizeAccessor ? (0, _utils.getAccessorByDimension)(markSizeAccessor, table.columns) : undefined; const markFormatter = formatFactory(markSizeAccessor ? (0, _format.getFormat)(table.columns, markSizeAccessor) : undefined); // what if row values are not primitive? That is the case of, for instance, Ranges // remaps them to their serialized version with the formatHint metadata // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on const { table: formattedTable, formattedColumns } = formattedDatatableInfo; // For date histogram chart type, we're getting the rows that represent intervals without data. // To not display them in the legend, they need to be filtered out. let rows = formattedTable.rows.filter(row => !(xColumnId && row[xColumnId] === undefined) && !(splitColumnIds.some(splitColumnId => row[splitColumnId] === undefined) && (Array.isArray(accessor) ? accessor.some(a => row[a] === undefined) : row[accessor] === undefined))); const emptyX = { unifiedX: '' }; if (!xColumnId) { rows = rows.map(row => ({ ...row, ...emptyX })); } const getSeriesNameFn = d => { return getSeriesName(d, { splitAccessors: layer.splitAccessors || [], accessorsCount: singleTable ? allYAccessors.length : layer.accessors.length, alreadyFormattedColumns: formattedColumns, columns: formattedTable.columns, splitAccessorsFormats: fieldFormats[layer.layerId].splitSeriesAccessors, columnToLabelMap, multipleLayersWithSplits }, titles); }; return { splitSeriesAccessors: splitColumnIds.length ? splitColumnIds : [], stackAccessors: isStacked ? [xColumnId || 'unifiedX'] : [], id: generateSeriesId(layer, splitColumnIds.length ? splitColumnIds : [EMPTY_ACCESSOR], Array.isArray(accessor) ? accessor.join(SPLIT_Y_ACCESSORS) : accessor, xColumnId), xAccessor: xColumnId || 'unifiedX', yAccessors: Array.isArray(accessor) ? accessor : [accessor], markSizeAccessor: markSizeColumnId, markFormat: value => markFormatter.convert(value), data: rows, xScaleType: xColumnId ? (_layer$xScaleType = layer.xScaleType) !== null && _layer$xScaleType !== void 0 ? _layer$xScaleType : defaultXScaleType : 'ordinal', yScaleType: (formatter === null || formatter === void 0 ? void 0 : formatter.id) === 'bytes' && scaleType === _charts.ScaleType.Linear ? _charts.ScaleType.LinearBinary : scaleType, color: series => getColor(series, { layer, colorAssignments, paletteService, getSeriesNameFn, syncColors }, uiState, singleTable), groupId: yAxis === null || yAxis === void 0 ? void 0 : yAxis.groupId, enableHistogramMode, stackMode, timeZone, areaSeriesStyle: { point: getPointConfig({ xAccessor: xColumnId, markSizeAccessor: markSizeColumnId, emphasizeFitting, showPoints: layer.showPoints, pointsRadius: layer.pointsRadius }), ...(fillOpacity && { area: { opacity: fillOpacity } }), ...(emphasizeFitting && { fit: { area: { opacity: fillOpacity || 0.5 }, line: getFitLineConfig() } }), line: getLineConfig({ showLines: layer.showLines, lineWidth: layer.lineWidth }) }, lineSeriesStyle: { point: getPointConfig({ xAccessor: xColumnId, markSizeAccessor: markSizeColumnId, emphasizeFitting, showPoints: layer.showPoints, pointsRadius: layer.pointsRadius }), ...(emphasizeFitting && { fit: { line: getFitLineConfig() } }), line: getLineConfig({ lineWidth: layer.lineWidth, showLines: layer.showLines }) }, name(d) { return getSeriesNameFn(d); }, yNice: Boolean(yAxis === null || yAxis === void 0 ? void 0 : (_yAxis$extent = yAxis.extent) === null || _yAxis$extent === void 0 ? void 0 : _yAxis$extent.niceValues), xNice: Boolean(xAxis === null || xAxis === void 0 ? void 0 : (_xAxis$extent = xAxis.extent) === null || _xAxis$extent === void 0 ? void 0 : _xAxis$extent.niceValues) }; }; exports.getSeriesProps = getSeriesProps;