"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.isCellValueSupported = exports.isBucketed = exports.getHeatmapVisualization = exports.filterOperationsAxis = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireDefault(require("react")); var _i18n = require("@kbn/i18n"); var _i18nReact = require("@kbn/i18n-react"); var _charts = require("@elastic/charts"); var _chartIcons = require("@kbn/chart-icons"); var _coloring = require("@kbn/coloring"); var _public = require("@kbn/visualizations-plugin/public"); var _public2 = require("@kbn/expression-xy-plugin/public"); var _common = require("@kbn/expressions-plugin/common"); var _suggestions = require("./suggestions"); var _constants = require("./constants"); var _toolbar_component = require("./toolbar_component"); var _dimension_editor = require("./dimension_editor"); var _utils = require("./utils"); /* * 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 groupLabelForHeatmap = _i18n.i18n.translate('xpack.lens.heatmapVisualization.heatmapGroupLabel', { defaultMessage: 'Magnitude' }); function getAxisName(axis) { const vertical = _i18n.i18n.translate('xpack.lens.heatmap.verticalAxisLabel', { defaultMessage: 'Vertical axis' }); const horizontal = _i18n.i18n.translate('xpack.lens.heatmap.horizontalAxisLabel', { defaultMessage: 'Horizontal axis' }); if (axis === 'x') { return horizontal; } return vertical; } const isBucketed = op => op.isBucketed && op.scale === 'ordinal'; exports.isBucketed = isBucketed; const isNumericMetric = op => op.dataType === 'number' && !op.isStaticValue; const filterOperationsAxis = op => isBucketed(op) || op.scale === 'interval'; exports.filterOperationsAxis = filterOperationsAxis; const isCellValueSupported = op => { return !isBucketed(op) && (op.scale === 'ordinal' || op.scale === 'ratio') && isNumericMetric(op); }; exports.isCellValueSupported = isCellValueSupported; function getInitialState() { return { shape: _constants.CHART_SHAPES.HEATMAP, legend: { isVisible: true, position: _charts.Position.Right, maxLines: 1, type: _constants.LEGEND_FUNCTION }, gridConfig: { type: _constants.HEATMAP_GRID_FUNCTION, isCellLabelVisible: false, isYAxisLabelVisible: true, isXAxisLabelVisible: true, isYAxisTitleVisible: true, isXAxisTitleVisible: true } }; } function computePaletteParams(params) { return { ...params, // rewrite colors and stops as two distinct arguments colors: ((params === null || params === void 0 ? void 0 : params.stops) || []).map(({ color }) => color), stops: (params === null || params === void 0 ? void 0 : params.name) === 'custom' ? ((params === null || params === void 0 ? void 0 : params.stops) || []).map(({ stop }) => stop) : [], reverse: false // managed at UI level }; } const getHeatmapVisualization = ({ paletteService, theme }) => ({ id: _constants.LENS_HEATMAP_ID, visualizationTypes: [{ id: 'heatmap', icon: _chartIcons.IconChartHeatmap, label: _i18n.i18n.translate('xpack.lens.heatmapVisualization.heatmapLabel', { defaultMessage: 'Heat map' }), groupLabel: groupLabelForHeatmap, showExperimentalBadge: false, sortPriority: 1 }], getVisualizationTypeId(state) { return state.shape; }, getLayerIds(state) { return [state.layerId]; }, clearLayer(state) { const newState = { ...state }; delete newState.valueAccessor; delete newState.xAccessor; delete newState.yAccessor; return newState; }, switchVisualizationType: (visualizationTypeId, state) => { return { ...state, shape: visualizationTypeId }; }, getDescription(state) { return _constants.CHART_NAMES.heatmap; }, initialize(addNewLayer, state, mainPalette) { return state || { layerId: addNewLayer(), layerType: _public2.LayerTypes.DATA, title: 'Empty Heatmap chart', ...getInitialState() }; }, getSuggestions: _suggestions.getSuggestions, triggers: [_public.VIS_EVENT_TO_TRIGGER.filter, _public.VIS_EVENT_TO_TRIGGER.brush], getConfiguration({ state, frame, layerId }) { var _frame$activeData, _activePalette$params; const datasourceLayer = frame.datasourceLayers[layerId]; const originalOrder = datasourceLayer === null || datasourceLayer === void 0 ? void 0 : datasourceLayer.getTableSpec().map(({ columnId }) => columnId); if (!originalOrder) { return { groups: [] }; } const { displayStops, activePalette } = (0, _utils.getSafePaletteParams)(paletteService, (_frame$activeData = frame.activeData) === null || _frame$activeData === void 0 ? void 0 : _frame$activeData[state.layerId], state.valueAccessor, state !== null && state !== void 0 && state.palette && state.palette.accessor === state.valueAccessor ? state.palette : undefined); return { groups: [{ layerId: state.layerId, groupId: _constants.GROUP_ID.X, groupLabel: getAxisName(_constants.GROUP_ID.X), accessors: state.xAccessor ? [{ columnId: state.xAccessor }] : [], filterOperations: filterOperationsAxis, supportsMoreColumns: !state.xAccessor, requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_xDimensionPanel' }, { layerId: state.layerId, groupId: _constants.GROUP_ID.Y, groupLabel: getAxisName(_constants.GROUP_ID.Y), accessors: state.yAccessor ? [{ columnId: state.yAccessor }] : [], filterOperations: filterOperationsAxis, supportsMoreColumns: !state.yAccessor, requiredMinDimensionCount: 0, isBreakdownDimension: true, dataTestSubj: 'lnsHeatmap_yDimensionPanel' }, { layerId: state.layerId, groupId: _constants.GROUP_ID.CELL, groupLabel: _i18n.i18n.translate('xpack.lens.heatmap.cellValueLabel', { defaultMessage: 'Cell value' }), paramEditorCustomProps: { headingLabel: _i18n.i18n.translate('xpack.lens.heatmap.headingLabel', { defaultMessage: 'Value' }) }, accessors: state.valueAccessor ? [ // When data is not available and the range type is numeric, return a placeholder while refreshing displayStops.length && (frame.activeData || (activePalette === null || activePalette === void 0 ? void 0 : (_activePalette$params = activePalette.params) === null || _activePalette$params === void 0 ? void 0 : _activePalette$params.rangeType) !== 'number') ? { columnId: state.valueAccessor, triggerIconType: 'colorBy', palette: displayStops.map(({ color }) => color) } : { columnId: state.valueAccessor, triggerIconType: 'none' }] : [], filterOperations: isCellValueSupported, isMetricDimension: true, supportsMoreColumns: !state.valueAccessor, enableDimensionEditor: true, requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_cellPanel' }] }; }, setDimension({ prevState, layerId, columnId, groupId, previousColumn }) { const update = {}; if (groupId === _constants.GROUP_ID.X) { update.xAccessor = columnId; } if (groupId === _constants.GROUP_ID.Y) { update.yAccessor = columnId; } if (groupId === _constants.GROUP_ID.CELL) { update.valueAccessor = columnId; } return { ...prevState, ...update }; }, removeDimension({ prevState, layerId, columnId }) { const update = { ...prevState }; if (prevState.valueAccessor === columnId) { delete update.valueAccessor; } if (prevState.xAccessor === columnId) { delete update.xAccessor; } if (prevState.yAccessor === columnId) { delete update.yAccessor; } return update; }, DimensionEditorComponent(props) { return /*#__PURE__*/_react.default.createElement(_dimension_editor.HeatmapDimensionEditor, (0, _extends2.default)({}, props, { paletteService: paletteService })); }, ToolbarComponent(props) { return /*#__PURE__*/_react.default.createElement(_toolbar_component.HeatmapToolbar, props); }, getSupportedLayers() { return [{ type: _public2.LayerTypes.DATA, label: _i18n.i18n.translate('xpack.lens.heatmap.addLayer', { defaultMessage: 'Visualization' }) }]; }, getLayerType(layerId, state) { if ((state === null || state === void 0 ? void 0 : state.layerId) === layerId) { return state.layerType; } }, toExpression(state, datasourceLayers, attributes, datasourceExpressionsByLayers = {}) { var _state$gridConfig$isY, _state$gridConfig$isX, _state$xAccessor, _state$yAccessor, _state$valueAccessor, _state$palette, _state$palette$params, _state$palette2, _state$palette3, _datasourceExpression; const datasource = datasourceLayers[state.layerId]; const datasourceExpression = datasourceExpressionsByLayers[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 if (!originalOrder || !state.valueAccessor) { return null; } const legendFn = (0, _common.buildExpressionFunction)('heatmap_legend', { isVisible: state.legend.isVisible, position: state.legend.position, legendSize: state.legend.legendSize }); const gridConfigFn = (0, _common.buildExpressionFunction)('heatmap_grid', { // grid strokeWidth: state.gridConfig.strokeWidth, strokeColor: state.gridConfig.strokeColor, // cells isCellLabelVisible: state.gridConfig.isCellLabelVisible, // Y-axis isYAxisLabelVisible: state.gridConfig.isYAxisLabelVisible, isYAxisTitleVisible: (_state$gridConfig$isY = state.gridConfig.isYAxisTitleVisible) !== null && _state$gridConfig$isY !== void 0 ? _state$gridConfig$isY : false, yTitle: state.gridConfig.yTitle, // X-axis isXAxisLabelVisible: state.gridConfig.isXAxisLabelVisible, isXAxisTitleVisible: (_state$gridConfig$isX = state.gridConfig.isXAxisTitleVisible) !== null && _state$gridConfig$isX !== void 0 ? _state$gridConfig$isX : false, xTitle: state.gridConfig.xTitle }); const heatmapFn = (0, _common.buildExpressionFunction)('heatmap', { xAccessor: (_state$xAccessor = state.xAccessor) !== null && _state$xAccessor !== void 0 ? _state$xAccessor : '', yAccessor: (_state$yAccessor = state.yAccessor) !== null && _state$yAccessor !== void 0 ? _state$yAccessor : '', valueAccessor: (_state$valueAccessor = state.valueAccessor) !== null && _state$valueAccessor !== void 0 ? _state$valueAccessor : '', lastRangeIsRightOpen: (_state$palette = state.palette) !== null && _state$palette !== void 0 && (_state$palette$params = _state$palette.params) !== null && _state$palette$params !== void 0 && _state$palette$params.continuity ? ['above', 'all'].includes(state.palette.params.continuity) : true, palette: (_state$palette2 = state.palette) !== null && _state$palette2 !== void 0 && _state$palette2.params ? paletteService.get(_coloring.CUSTOM_PALETTE).toExpression(computePaletteParams((_state$palette3 = state.palette) === null || _state$palette3 === void 0 ? void 0 : _state$palette3.params)) : paletteService.get(_constants.DEFAULT_PALETTE_NAME).toExpression(), legend: (0, _common.buildExpression)([legendFn]), gridConfig: (0, _common.buildExpression)([gridConfigFn]) }); return { type: 'expression', chain: [...((_datasourceExpression = datasourceExpression === null || datasourceExpression === void 0 ? void 0 : datasourceExpression.chain) !== null && _datasourceExpression !== void 0 ? _datasourceExpression : []), heatmapFn.toAst()] }; }, toPreviewExpression(state, datasourceLayers, datasourceExpressionsByLayers = {}) { var _state$xAccessor2, _state$yAccessor2, _state$valueAccessor2, _state$palette4, _state$palette5, _datasourceExpression2; const datasource = datasourceLayers[state.layerId]; const datasourceExpression = datasourceExpressionsByLayers[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 if (!originalOrder || !state.valueAccessor) { return null; } const legendFn = (0, _common.buildExpressionFunction)('heatmap_legend', { isVisible: false, position: 'right' }); const gridConfigFn = (0, _common.buildExpressionFunction)('heatmap_grid', { // grid strokeWidth: 1, // cells isCellLabelVisible: false, // Y-axis isYAxisLabelVisible: false, isYAxisTitleVisible: false, yTitle: state.gridConfig.yTitle, // X-axis isXAxisLabelVisible: false, isXAxisTitleVisible: false, xTitle: state.gridConfig.xTitle }); const heatmapFn = (0, _common.buildExpressionFunction)('heatmap', { xAccessor: (_state$xAccessor2 = state.xAccessor) !== null && _state$xAccessor2 !== void 0 ? _state$xAccessor2 : '', yAccessor: (_state$yAccessor2 = state.yAccessor) !== null && _state$yAccessor2 !== void 0 ? _state$yAccessor2 : '', valueAccessor: (_state$valueAccessor2 = state.valueAccessor) !== null && _state$valueAccessor2 !== void 0 ? _state$valueAccessor2 : '', legend: (0, _common.buildExpression)([legendFn]), gridConfig: (0, _common.buildExpression)([gridConfigFn]), palette: (_state$palette4 = state.palette) !== null && _state$palette4 !== void 0 && _state$palette4.params ? paletteService.get(_coloring.CUSTOM_PALETTE).toExpression(computePaletteParams((_state$palette5 = state.palette) === null || _state$palette5 === void 0 ? void 0 : _state$palette5.params)) : paletteService.get(_constants.DEFAULT_PALETTE_NAME).toExpression() }); return { type: 'expression', chain: [...((_datasourceExpression2 = datasourceExpression === null || datasourceExpression === void 0 ? void 0 : datasourceExpression.chain) !== null && _datasourceExpression2 !== void 0 ? _datasourceExpression2 : []), heatmapFn.toAst()] }; }, getUserMessages(state, { frame }) { if (!state.yAccessor && !state.xAccessor && !state.valueAccessor) { // nothing configured yet return []; } const errors = []; if (!state.xAccessor) { errors.push({ severity: 'error', fixableInEditor: true, displayLocations: [{ id: 'visualization' }], shortMessage: _i18n.i18n.translate('xpack.lens.heatmapVisualization.missingXAccessorShortMessage', { defaultMessage: 'Missing Horizontal axis.' }), longMessage: _i18n.i18n.translate('xpack.lens.heatmapVisualization.missingXAccessorLongMessage', { defaultMessage: 'Configuration for the horizontal axis is missing.' }) }); } let warnings = []; if (state !== null && state !== void 0 && state.layerId && frame.activeData && state.valueAccessor) { const rows = frame.activeData[state.layerId] && frame.activeData[state.layerId].rows; if (rows) { const hasArrayValues = rows.some(row => Array.isArray(row[state.valueAccessor])); const datasource = frame.datasourceLayers[state.layerId]; const operation = datasource === null || datasource === void 0 ? void 0 : datasource.getOperationForColumnId(state.valueAccessor); warnings = hasArrayValues ? [{ severity: 'warning', fixableInEditor: true, displayLocations: [{ id: 'toolbar' }], shortMessage: '', longMessage: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.lens.heatmapVisualization.arrayValuesWarningMessage", defaultMessage: "{label} contains array values. Your visualization may not render as expected.", values: { label: /*#__PURE__*/_react.default.createElement("strong", null, operation === null || operation === void 0 ? void 0 : operation.label) } }) }] : []; } } return [...errors, ...warnings]; }, 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, frame) { var _frame$activeData2, _frame$activeData3; const dimensions = []; if (state.xAccessor) { dimensions.push({ id: state.xAccessor, name: getAxisName(_constants.GROUP_ID.X), dimensionType: 'x' }); } if (state.yAccessor) { dimensions.push({ id: state.yAccessor, name: getAxisName(_constants.GROUP_ID.Y), dimensionType: 'y' }); } if (state.valueAccessor) { dimensions.push({ id: state.valueAccessor, name: _i18n.i18n.translate('xpack.lens.heatmap.cellValueLabel', { defaultMessage: 'Cell value' }), dimensionType: 'value' }); } const { displayStops } = (0, _utils.getSafePaletteParams)(paletteService, // When the active data comes from the embeddable side it might not have been indexed by layerId // rather using a "default" key (frame === null || frame === void 0 ? void 0 : (_frame$activeData2 = frame.activeData) === null || _frame$activeData2 === void 0 ? void 0 : _frame$activeData2[state.layerId]) || (frame === null || frame === void 0 ? void 0 : (_frame$activeData3 = frame.activeData) === null || _frame$activeData3 === void 0 ? void 0 : _frame$activeData3.default), state.valueAccessor, state !== null && state !== void 0 && state.palette && state.palette.accessor === state.valueAccessor ? state.palette : undefined); return { layers: [{ layerId: state.layerId, layerType: state.layerType, chartType: state.shape, ...this.getDescription(state), dimensions, palette: displayStops.length ? displayStops.map(({ color }) => color) : undefined }] }; } }); exports.getHeatmapVisualization = getHeatmapVisualization;