"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.getDatatableVisualization = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireDefault(require("react")); var _i18n = require("@kbn/i18n"); var _coloring = require("@kbn/coloring"); var _public = require("@kbn/visualizations-plugin/public"); var _chartIcons = require("@kbn/chart-icons"); var _public2 = require("@kbn/expression-xy-plugin/public"); var _common = require("@kbn/expressions-plugin/common"); var _dimension_editor = require("./components/dimension_editor"); var _dimension_editor_addtional_section = require("./components/dimension_editor_addtional_section"); var _summary = require("../../../common/expressions/datatable/summary"); var _toolbar = require("./components/toolbar"); /* * 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; you may not use this file except in compliance with the Elastic License * 2.0. */ const visualizationLabel = _i18n.i18n.translate('xpack.lens.datatable.label', { defaultMessage: 'Table' }); const getDatatableVisualization = ({ paletteService, theme }) => ({ id: 'lnsDatatable', visualizationTypes: [{ id: 'lnsDatatable', icon: _chartIcons.IconChartDatatable, label: visualizationLabel, groupLabel: _i18n.i18n.translate('xpack.lens.datatable.groupLabel', { defaultMessage: 'Tabular' }), sortPriority: 5 }], getVisualizationTypeId() { return 'lnsDatatable'; }, getLayerIds(state) { return [state.layerId]; }, clearLayer(state) { return { ...state, columns: [] }; }, getDescription() { return { icon: _chartIcons.IconChartDatatable, label: visualizationLabel }; }, switchVisualizationType: (_, state) => state, triggers: [_public.VIS_EVENT_TO_TRIGGER.filter, _public.VIS_EVENT_TO_TRIGGER.tableRowContextMenuClick], initialize(addNewLayer, state) { return state || { columns: [], layerId: addNewLayer(), layerType: _public2.LayerTypes.DATA }; }, getSuggestions({ table, state, keptLayerIds }) { if (keptLayerIds.length > 1 || keptLayerIds.length && table.layerId !== keptLayerIds[0] || state && table.changeType === 'unchanged' || table.columns.some(col => col.operation.isStaticValue)) { return []; } const oldColumnSettings = {}; if (state) { state.columns.forEach(column => { oldColumnSettings[column.columnId] = column; }); } const lastTransposedColumnIndex = table.columns.findIndex(c => { var _oldColumnSettings$c$; return !oldColumnSettings[c.columnId] ? false : !((_oldColumnSettings$c$ = oldColumnSettings[c.columnId]) !== null && _oldColumnSettings$c$ !== void 0 && _oldColumnSettings$c$.isTransposed); }); const usesTransposing = state === null || state === void 0 ? void 0 : state.columns.some(c => c.isTransposed); const title = table.changeType === 'unchanged' ? _i18n.i18n.translate('xpack.lens.datatable.suggestionLabel', { defaultMessage: 'As table' }) : _i18n.i18n.translate('xpack.lens.datatable.visualizationOf', { defaultMessage: 'Table {operations}', values: { operations: table.label || table.columns.map(col => col.operation.label).join(_i18n.i18n.translate('xpack.lens.datatable.conjunctionSign', { defaultMessage: ' & ', description: 'A character that can be used for conjunction of multiple enumarated items. Make sure to include spaces around it if needed.' })) } }); const changeType = table.changeType; const changeFactor = changeType === 'reduced' || changeType === 'layers' ? 0.3 : changeType === 'unchanged' ? 0.5 : 1; return [{ title, // table with >= 10 columns will have a score of 0.4, fewer columns reduce score score: Math.min(table.columns.length, 10) / 10 * 0.4 * changeFactor, state: { ...(state || {}), layerId: table.layerId, layerType: _public2.LayerTypes.DATA, columns: table.columns.map((col, columnIndex) => ({ ...(oldColumnSettings[col.columnId] || {}), isTransposed: usesTransposing && columnIndex < lastTransposedColumnIndex, columnId: col.columnId })) }, previewIcon: _chartIcons.IconChartDatatable, // tables are hidden from suggestion bar, but used for drag & drop and chart switching hide: true }]; }, getConfiguration({ state, frame, layerId }) { const { sortedColumns, datasource } = getDataSourceAndSortedColumns(state, frame.datasourceLayers, layerId) || {}; const columnMap = {}; state.columns.forEach(column => { columnMap[column.columnId] = column; }); if (!sortedColumns) { return { groups: [] }; } return { groups: [{ groupId: 'rows', groupLabel: _i18n.i18n.translate('xpack.lens.datatable.breakdownRows', { defaultMessage: 'Rows' }), dimensionEditorGroupLabel: _i18n.i18n.translate('xpack.lens.datatable.breakdownRow', { defaultMessage: 'Row' }), groupTooltip: _i18n.i18n.translate('xpack.lens.datatable.breakdownRows.description', { defaultMessage: 'Split table rows by field. This is recommended for high cardinality breakdowns.' }), layerId: state.layerId, accessors: sortedColumns.filter(c => { var _getOperationForColum, _state$columns$find; return ((_getOperationForColum = datasource.getOperationForColumnId(c)) === null || _getOperationForColum === void 0 ? void 0 : _getOperationForColum.isBucketed) && !((_state$columns$find = state.columns.find(col => col.columnId === c)) !== null && _state$columns$find !== void 0 && _state$columns$find.isTransposed); }).map(accessor => ({ columnId: accessor, triggerIconType: columnMap[accessor].hidden ? 'invisible' : columnMap[accessor].collapseFn ? 'aggregate' : undefined })), supportsMoreColumns: true, filterOperations: op => op.isBucketed, dataTestSubj: 'lnsDatatable_rows', enableDimensionEditor: true, hideGrouping: true, nestingOrder: 1 }, { groupId: 'columns', groupLabel: _i18n.i18n.translate('xpack.lens.datatable.breakdownColumns', { defaultMessage: 'Split metrics by' }), dimensionEditorGroupLabel: _i18n.i18n.translate('xpack.lens.datatable.breakdownColumn', { defaultMessage: 'Split metrics by' }), groupTooltip: _i18n.i18n.translate('xpack.lens.datatable.breakdownColumns.description', { defaultMessage: "Split metric columns by field. It's recommended to keep the number of columns low to avoid horizontal scrolling." }), layerId: state.layerId, accessors: sortedColumns.filter(c => { var _getOperationForColum2, _state$columns$find2; return ((_getOperationForColum2 = datasource.getOperationForColumnId(c)) === null || _getOperationForColum2 === void 0 ? void 0 : _getOperationForColum2.isBucketed) && ((_state$columns$find2 = state.columns.find(col => col.columnId === c)) === null || _state$columns$find2 === void 0 ? void 0 : _state$columns$find2.isTransposed); }).map(accessor => ({ columnId: accessor })), supportsMoreColumns: true, filterOperations: op => op.isBucketed, dataTestSubj: 'lnsDatatable_columns', enableDimensionEditor: true, hideGrouping: true, nestingOrder: 0 }, { groupId: 'metrics', groupLabel: _i18n.i18n.translate('xpack.lens.datatable.metrics', { defaultMessage: 'Metrics' }), dimensionEditorGroupLabel: _i18n.i18n.translate('xpack.lens.datatable.metric', { defaultMessage: 'Metric' }), paramEditorCustomProps: { headingLabel: _i18n.i18n.translate('xpack.lens.datatable.headingLabel', { defaultMessage: 'Value' }) }, layerId: state.layerId, accessors: sortedColumns.filter(c => { var _getOperationForColum3; return !((_getOperationForColum3 = datasource.getOperationForColumnId(c)) !== null && _getOperationForColum3 !== void 0 && _getOperationForColum3.isBucketed); }).map(accessor => { var _columnConfig$palette, _columnConfig$palette2; const columnConfig = columnMap[accessor]; const stops = columnConfig === null || columnConfig === void 0 ? void 0 : (_columnConfig$palette = columnConfig.palette) === null || _columnConfig$palette === void 0 ? void 0 : (_columnConfig$palette2 = _columnConfig$palette.params) === null || _columnConfig$palette2 === void 0 ? void 0 : _columnConfig$palette2.stops; const hasColoring = Boolean((columnConfig === null || columnConfig === void 0 ? void 0 : columnConfig.colorMode) !== 'none' && stops); return { columnId: accessor, triggerIconType: columnConfig !== null && columnConfig !== void 0 && columnConfig.hidden ? 'invisible' : hasColoring ? 'colorBy' : undefined, palette: hasColoring && stops ? stops.map(({ color }) => color) : undefined }; }), supportsMoreColumns: true, filterOperations: op => !op.isBucketed, isMetricDimension: true, requiredMinDimensionCount: 1, dataTestSubj: 'lnsDatatable_metrics', enableDimensionEditor: true }] }; }, setDimension({ prevState, columnId, groupId, previousColumn }) { if (prevState.columns.some(column => column.columnId === columnId || previousColumn && column.columnId === previousColumn)) { return { ...prevState, columns: prevState.columns.map(column => { if (column.columnId === columnId || column.columnId === previousColumn) { return { ...column, columnId, isTransposed: groupId === 'columns' }; } return column; }) }; } return { ...prevState, columns: [...prevState.columns, { columnId, isTransposed: groupId === 'columns' }] }; }, removeDimension({ prevState, columnId }) { var _prevState$sorting; return { ...prevState, columns: prevState.columns.filter(column => column.columnId !== columnId), sorting: ((_prevState$sorting = prevState.sorting) === null || _prevState$sorting === void 0 ? void 0 : _prevState$sorting.columnId) === columnId ? undefined : prevState.sorting }; }, DimensionEditorComponent(props) { return /*#__PURE__*/_react.default.createElement(_dimension_editor.TableDimensionEditor, (0, _extends2.default)({}, props, { paletteService: paletteService })); }, DimensionEditorAdditionalSectionComponent(props) { return /*#__PURE__*/_react.default.createElement(_dimension_editor_addtional_section.TableDimensionEditorAdditionalSection, (0, _extends2.default)({}, props, { paletteService: paletteService })); }, DimensionEditorDataExtraComponent(props) { return /*#__PURE__*/_react.default.createElement(_dimension_editor.TableDimensionDataExtraEditor, (0, _extends2.default)({}, props, { paletteService: paletteService })); }, getSupportedLayers() { return [{ type: _public2.LayerTypes.DATA, label: _i18n.i18n.translate('xpack.lens.datatable.addLayer', { defaultMessage: 'Visualization' }) }]; }, getLayerType(layerId, state) { if ((state === null || state === void 0 ? void 0 : state.layerId) === layerId) { return state.layerType; } }, toExpression(state, datasourceLayers, { title, description } = {}, datasourceExpressionsByLayers = {}) { var _state$sorting, _state$sorting2, _state$headerRowHeigh, _state$rowHeightLines, _state$headerRowHeigh2, _state$paging, _datasourceExpression; const { sortedColumns, datasource } = getDataSourceAndSortedColumns(state, datasourceLayers, state.layerId) || {}; if (sortedColumns !== null && sortedColumns !== void 0 && sortedColumns.length && sortedColumns.filter(c => { var _getOperationForColum4; return !((_getOperationForColum4 = datasource.getOperationForColumnId(c)) !== null && _getOperationForColum4 !== void 0 && _getOperationForColum4.isBucketed); }).length === 0) { return null; } if (!datasourceExpressionsByLayers || Object.keys(datasourceExpressionsByLayers).length === 0) { return null; } const columnMap = {}; state.columns.forEach(column => { columnMap[column.columnId] = column; }); const columns = sortedColumns.filter(columnId => datasource.getOperationForColumnId(columnId)).map(columnId => columnMap[columnId]); const datasourceExpression = datasourceExpressionsByLayers[state.layerId]; const lensCollapseFnAsts = columns.filter(c => c.collapseFn).map(c => (0, _common.buildExpressionFunction)('lens_collapse', { by: columns.filter(col => { var _getOperationForColum5; return col.columnId !== c.columnId && ((_getOperationForColum5 = datasource.getOperationForColumnId(col.columnId)) === null || _getOperationForColum5 === void 0 ? void 0 : _getOperationForColum5.isBucketed); }).map(col => col.columnId), metric: columns.filter(col => { var _getOperationForColum6; return !((_getOperationForColum6 = datasource.getOperationForColumnId(col.columnId)) !== null && _getOperationForColum6 !== void 0 && _getOperationForColum6.isBucketed); }).map(col => col.columnId), fn: [c.collapseFn] }).toAst()); const datatableFnAst = (0, _common.buildExpressionFunction)('lens_datatable', { title: title || '', description: description || '', columns: columns.filter(c => !c.collapseFn).map(column => { var _column$palette, _column$palette2, _column$palette2$para, _column$palette3, _column$palette3$para, _column$palette4, _column$palette4$para, _getOperationForColum7, _getOperationForColum8, _column$summaryLabel; const paletteParams = { ...((_column$palette = column.palette) === null || _column$palette === void 0 ? void 0 : _column$palette.params), // rewrite colors and stops as two distinct arguments colors: (((_column$palette2 = column.palette) === null || _column$palette2 === void 0 ? void 0 : (_column$palette2$para = _column$palette2.params) === null || _column$palette2$para === void 0 ? void 0 : _column$palette2$para.stops) || []).map(({ color }) => color), stops: ((_column$palette3 = column.palette) === null || _column$palette3 === void 0 ? void 0 : (_column$palette3$para = _column$palette3.params) === null || _column$palette3$para === void 0 ? void 0 : _column$palette3$para.name) === 'custom' ? (((_column$palette4 = column.palette) === null || _column$palette4 === void 0 ? void 0 : (_column$palette4$para = _column$palette4.params) === null || _column$palette4$para === void 0 ? void 0 : _column$palette4$para.stops) || []).map(({ stop }) => stop) : [], reverse: false // managed at UI level }; const sortingHint = datasource.getOperationForColumnId(column.columnId).sortingHint; const hasNoSummaryRow = column.summaryRow == null || column.summaryRow === 'none'; const canColor = ((_getOperationForColum7 = datasource.getOperationForColumnId(column.columnId)) === null || _getOperationForColum7 === void 0 ? void 0 : _getOperationForColum7.dataType) === 'number'; const datatableColumnFn = (0, _common.buildExpressionFunction)('lens_datatable_column', { columnId: column.columnId, hidden: column.hidden, oneClickFilter: column.oneClickFilter, width: column.width, isTransposed: column.isTransposed, transposable: !((_getOperationForColum8 = datasource.getOperationForColumnId(column.columnId)) !== null && _getOperationForColum8 !== void 0 && _getOperationForColum8.isBucketed), alignment: column.alignment, colorMode: canColor && column.colorMode ? column.colorMode : 'none', palette: paletteService.get(_coloring.CUSTOM_PALETTE).toExpression(paletteParams), summaryRow: hasNoSummaryRow ? undefined : column.summaryRow, summaryLabel: hasNoSummaryRow ? undefined : (_column$summaryLabel = column.summaryLabel) !== null && _column$summaryLabel !== void 0 ? _column$summaryLabel : (0, _summary.getDefaultSummaryLabel)(column.summaryRow), sortingHint }); return (0, _common.buildExpression)([datatableColumnFn]).toAst(); }), sortingColumnId: ((_state$sorting = state.sorting) === null || _state$sorting === void 0 ? void 0 : _state$sorting.columnId) || '', sortingDirection: ((_state$sorting2 = state.sorting) === null || _state$sorting2 === void 0 ? void 0 : _state$sorting2.direction) || 'none', fitRowToContent: state.rowHeight === 'auto', headerRowHeight: (_state$headerRowHeigh = state.headerRowHeight) !== null && _state$headerRowHeigh !== void 0 ? _state$headerRowHeigh : 'single', rowHeightLines: !state.rowHeight || state.rowHeight === 'single' ? 1 : (_state$rowHeightLines = state.rowHeightLines) !== null && _state$rowHeightLines !== void 0 ? _state$rowHeightLines : 2, headerRowHeightLines: !state.headerRowHeight || state.headerRowHeight === 'single' ? 1 : (_state$headerRowHeigh2 = state.headerRowHeightLines) !== null && _state$headerRowHeigh2 !== void 0 ? _state$headerRowHeigh2 : 2, pageSize: (_state$paging = state.paging) !== null && _state$paging !== void 0 && _state$paging.enabled ? state.paging.size : undefined }).toAst(); return { type: 'expression', chain: [...((_datasourceExpression = datasourceExpression === null || datasourceExpression === void 0 ? void 0 : datasourceExpression.chain) !== null && _datasourceExpression !== void 0 ? _datasourceExpression : []), ...lensCollapseFnAsts, datatableFnAst] }; }, getRenderEventCounters(state) { const events = { color_by_value: false, summary_row: false }; state.columns.forEach(column => { if (column.summaryRow && column.summaryRow !== 'none') { events.summary_row = true; } if (column.colorMode && column.colorMode !== 'none') { events.color_by_value = true; } }); return Object.entries(events).reduce((acc, [key, isActive]) => { if (isActive) { acc.push(`dimension_${key}`); } return acc; }, []); }, ToolbarComponent(props) { return /*#__PURE__*/_react.default.createElement(_toolbar.DataTableToolbar, props); }, onEditAction(state, event) { var _state$paging2; switch (event.data.action) { case 'sort': return { ...state, sorting: { columnId: event.data.columnId, direction: event.data.direction } }; case 'toggle': const toggleColumnId = event.data.columnId; return { ...state, columns: state.columns.map(column => { if (column.columnId === toggleColumnId) { return { ...column, hidden: !column.hidden }; } else { return column; } }) }; case 'resize': const targetWidth = event.data.width; const resizeColumnId = event.data.columnId; return { ...state, columns: state.columns.map(column => { if (column.columnId === resizeColumnId) { return { ...column, width: targetWidth }; } else { return column; } }) }; case 'pagesize': return { ...state, paging: { enabled: ((_state$paging2 = state.paging) === null || _state$paging2 === void 0 ? void 0 : _state$paging2.enabled) || false, size: event.data.size } }; default: return state; } }, getSuggestionFromConvertToLensContext({ suggestions, context }) { const allSuggestions = suggestions; const suggestion = { ...allSuggestions[0], datasourceState: { ...allSuggestions[0].datasourceState, layers: allSuggestions.reduce((acc, s) => { var _s$datasourceState; return { ...acc, ...((_s$datasourceState = s.datasourceState) === null || _s$datasourceState === void 0 ? void 0 : _s$datasourceState.layers) }; }, {}) }, visualizationState: { ...allSuggestions[0].visualizationState, ...context.configuration } }; return suggestion; }, getVisualizationInfo(state) { var _visibleMetricColumns, _visibleMetricColumns2, _visibleMetricColumns3, _visibleMetricColumns4; const visibleMetricColumns = state.columns.filter(c => !c.hidden && c.colorMode && c.colorMode !== 'none'); return { layers: [{ layerId: state.layerId, layerType: state.layerType, chartType: 'table', ...this.getDescription(state), palette: // if multiple columns have color by value, do not show the palette for now: see #154349 visibleMetricColumns.length > 1 ? undefined : (_visibleMetricColumns = visibleMetricColumns[0]) === null || _visibleMetricColumns === void 0 ? void 0 : (_visibleMetricColumns2 = _visibleMetricColumns.palette) === null || _visibleMetricColumns2 === void 0 ? void 0 : (_visibleMetricColumns3 = _visibleMetricColumns2.params) === null || _visibleMetricColumns3 === void 0 ? void 0 : (_visibleMetricColumns4 = _visibleMetricColumns3.stops) === null || _visibleMetricColumns4 === void 0 ? void 0 : _visibleMetricColumns4.map(({ color }) => color), dimensions: state.columns.map(column => { let name = _i18n.i18n.translate('xpack.lens.datatable.metric', { defaultMessage: 'Metric' }); let dimensionType = 'Metric'; if (!column.transposable) { if (column.isTransposed) { name = _i18n.i18n.translate('xpack.lens.datatable.breakdownColumns', { defaultMessage: 'Split metrics by' }); dimensionType = 'split_metrics'; } else { name = _i18n.i18n.translate('xpack.lens.datatable.breakdownRow', { defaultMessage: 'Row' }); dimensionType = 'split_rows'; } } return { dimensionType, id: column.columnId, name }; }) }] }; } }); exports.getDatatableVisualization = getDatatableVisualization; function getDataSourceAndSortedColumns(state, datasourceLayers, layerId) { const datasource = datasourceLayers[state.layerId]; const originalOrder = datasource === null || datasource === void 0 ? void 0 : datasource.getTableSpec().map(({ columnId }) => columnId); // When we add a column it could be empty, and therefore have no order const sortedColumns = Array.from(new Set(originalOrder === null || originalOrder === void 0 ? void 0 : originalOrder.concat(state.columns.map(({ columnId }) => columnId)))); return { datasource, sortedColumns }; }