"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireWildcard(require("react")); var _charts = require("@elastic/charts"); var _i18n = require("@kbn/i18n"); var _eui = require("@elastic/eui"); var _public = require("@kbn/charts-plugin/public"); var _constants = require("@kbn/visualizations-plugin/common/constants"); var _utils = require("@kbn/visualizations-plugin/common/utils"); var _chartExpressionsCommon = require("@kbn/chart-expressions-common"); var _utils2 = require("../../common/utils"); var _constants2 = require("../../common/constants"); var _expression_renderers = require("../../common/types/expression_renderers"); var _utils3 = require("../utils"); var _chart_split = require("./chart_split"); var _visualization_noresults = require("./visualization_noresults"); var _partition_vis_component = require("./partition_vis_component.styles"); var _filter_out_config = require("../utils/filter_out_config"); var _filter_helpers = require("../utils/filter_helpers"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * 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 PartitionVisComponent = props => { var _visParams$labels$per, _visParams$legendPosi, _visParams$distinctCo, _window$_echDebugStat, _visParams$legendSize, _visParams$maxLegendL; const { columnCellValueActions, visData: originalVisData, visParams: preVisParams, visType, services, syncColors, interactive, overrides, hasOpenedOnAggBasedEditor } = props; const visParams = (0, _react.useMemo)(() => (0, _filter_out_config.filterOutConfig)(visType, preVisParams), [preVisParams, visType]); const chartTheme = props.chartsThemeService.useChartsTheme(); const chartBaseTheme = props.chartsThemeService.useChartsBaseTheme(); const { table: visData, metricAccessor, bucketAccessors } = (0, _react.useMemo)(() => (0, _utils2.consolidateMetricColumns)(originalVisData, visParams.dimensions.buckets, visParams.dimensions.metrics, visParams.metricsToLabels), [originalVisData, visParams.dimensions.buckets, visParams.dimensions.metrics, visParams.metricsToLabels]); const { bucketColumns, metricColumn } = (0, _react.useMemo)(() => (0, _utils3.getColumns)({ metric: metricAccessor, buckets: bucketAccessors }, visData), [bucketAccessors, metricAccessor, visData]); const formatters = (0, _react.useMemo)(() => (0, _utils3.generateFormatters)(visData, services.fieldFormats.deserialize), [services.fieldFormats.deserialize, visData]); const showLegendDefault = (0, _react.useCallback)(() => { var _props$uiState$get, _props$uiState; const showLegendDef = (0, _utils3.shouldShowLegend)(visType, visParams.legendDisplay, bucketColumns); return (_props$uiState$get = (_props$uiState = props.uiState) === null || _props$uiState === void 0 ? void 0 : _props$uiState.get('vis.legendOpen', showLegendDef)) !== null && _props$uiState$get !== void 0 ? _props$uiState$get : showLegendDef; }, [bucketColumns, props.uiState, visParams.legendDisplay, visType]); const [showLegend, setShowLegend] = (0, _react.useState)(() => showLegendDefault()); const showToggleLegendElement = props.uiState !== undefined; const [chartIsLoaded, setChartIsLoaded] = (0, _react.useState)(false); const [containerDimensions, setContainerDimensions] = (0, _react.useState)(); const parentRef = (0, _react.useRef)(null); (0, _react.useEffect)(() => { // chart should be loaded to compute the dimensions // otherwise the height is set to 0 if (parentRef && parentRef.current && chartIsLoaded) { const parentHeight = parentRef.current.getBoundingClientRect().height; const parentWidth = parentRef.current.getBoundingClientRect().width; setContainerDimensions({ width: parentWidth, height: parentHeight }); } }, [chartIsLoaded, parentRef]); (0, _react.useEffect)(() => { const legendShow = showLegendDefault(); setShowLegend(legendShow); }, [showLegendDefault]); const onRenderChange = (0, _react.useCallback)((isRendered = true) => { if (isRendered) { // this requestAnimationFrame call is a temporary fix for https://github.com/elastic/elastic-charts/issues/2124 window.requestAnimationFrame(() => { props.renderComplete(); setChartIsLoaded(true); }); } }, [props]); // handles slice click event const handleSliceClick = (0, _react.useCallback)((clickedLayers, buckets, vData, splitChartDimension, splitChartFormatter) => { const data = (0, _utils3.getFilterClickData)(clickedLayers, buckets, metricColumn.id, vData, originalVisData, visParams.dimensions.metrics.length, splitChartDimension, splitChartFormatter); props.fireEvent({ name: 'filter', data: { data } }); }, [metricColumn.id, originalVisData, props, visParams.dimensions.metrics.length]); // handles legend action event data const getLegendActionEventData = (0, _react.useCallback)(vData => series => { const data = (0, _utils3.getFilterEventData)(vData, series); return { name: 'filter', data: { negate: false, data } }; }, []); const handleLegendAction = (0, _react.useCallback)((event, negate = false) => { props.fireEvent({ ...event, data: { ...event.data, negate } }); }, [props]); const toggleLegend = (0, _react.useCallback)(() => { setShowLegend(value => { var _props$uiState2; const newValue = !value; (_props$uiState2 = props.uiState) === null || _props$uiState2 === void 0 ? void 0 : _props$uiState2.set('vis.legendOpen', newValue); return newValue; }); }, [props.uiState]); const setColor = (0, _react.useCallback)((newColor, seriesLabel) => { var _props$uiState3, _props$uiState4, _props$uiState5, _props$uiState6; const colors = ((_props$uiState3 = props.uiState) === null || _props$uiState3 === void 0 ? void 0 : _props$uiState3.get('vis.colors')) || {}; if (colors[seriesLabel] === newColor || !newColor) { delete colors[seriesLabel]; } else { colors[seriesLabel] = newColor; } (_props$uiState4 = props.uiState) === null || _props$uiState4 === void 0 ? void 0 : _props$uiState4.setSilent('vis.colors', null); (_props$uiState5 = props.uiState) === null || _props$uiState5 === void 0 ? void 0 : _props$uiState5.set('vis.colors', colors); (_props$uiState6 = props.uiState) === null || _props$uiState6 === void 0 ? void 0 : _props$uiState6.emit('reload'); }, [props.uiState]); const getSliceValue = (0, _react.useCallback)((d, metric) => { const value = d[metric.id]; return Number.isFinite(value) && value >= 0 ? value : 0; }, []); const defaultFormatter = services.fieldFormats.deserialize; // formatters const metricFieldFormatter = (0, _utils3.getFormatter)(metricColumn, formatters, defaultFormatter); const { splitColumn, splitRow } = visParams.dimensions; const splitChartFormatter = splitColumn ? (0, _utils3.getFormatter)(typeof splitColumn[0] === 'string' ? (0, _utils.getColumnByAccessor)(splitColumn[0], visData.columns) : splitColumn[0], formatters, defaultFormatter) : splitRow ? (0, _utils3.getFormatter)(typeof splitRow[0] === 'string' ? (0, _utils.getColumnByAccessor)(splitRow[0], visData.columns) : splitRow[0], formatters, defaultFormatter) : undefined; const percentFormatter = services.fieldFormats.deserialize({ id: 'percent', params: { pattern: `0,0.[${'0'.repeat((_visParams$labels$per = visParams.labels.percentDecimals) !== null && _visParams$labels$per !== void 0 ? _visParams$labels$per : _constants2.DEFAULT_PERCENT_DECIMALS)}]%` } }); const isDarkMode = props.chartsThemeService.useDarkMode(); const layers = (0, _react.useMemo)(() => { var _props$uiState7; return (0, _utils3.getLayers)(visType, bucketColumns, visParams, visData, { ...((_props$uiState7 = props.uiState) === null || _props$uiState7 === void 0 ? void 0 : _props$uiState7.get('vis.colors', {})), ...props.visParams.labels.colorOverrides }, visData.rows, props.palettesRegistry, formatters, services.fieldFormats, syncColors, isDarkMode); }, [visType, bucketColumns, visParams, visData, props.uiState, props.visParams.labels.colorOverrides, props.palettesRegistry, formatters, services.fieldFormats, syncColors, isDarkMode]); const legendActions = (0, _react.useMemo)(() => interactive ? (0, _utils3.getLegendActions)(_utils3.canFilter, getLegendActionEventData(visData), handleLegendAction, columnCellValueActions, visParams, visData, services.data.actions, services.fieldFormats) : undefined, [columnCellValueActions, getLegendActionEventData, handleLegendAction, interactive, services.data.actions, services.fieldFormats, visData, visParams]); const rescaleFactor = (0, _react.useMemo)(() => { var _slices$filter; const overallSum = visData.rows.reduce((sum, row) => sum + row[metricColumn.id], 0); const slices = visData.rows.map(row => row[metricColumn.id] / overallSum); const smallSlices = (_slices$filter = slices.filter(value => value < 0.02)) !== null && _slices$filter !== void 0 ? _slices$filter : []; if (smallSlices.length) { // shrink up to 20% to give some room for the linked values return 1 / (1 + Math.min(smallSlices.length * 0.05, 0.2)); } return 1; }, [visData.rows, metricColumn]); const { theme: settingsThemeOverrides = {}, ...settingsOverrides } = (0, _chartExpressionsCommon.getOverridesFor)(overrides, 'settings'); const themeOverrides = (0, _react.useMemo)(() => (0, _utils3.getPartitionTheme)(visType, visParams, chartTheme, containerDimensions, rescaleFactor, hasOpenedOnAggBasedEditor), [visType, visParams, chartTheme, containerDimensions, rescaleFactor, hasOpenedOnAggBasedEditor]); const fixedViewPort = document.getElementById('app-fixed-viewport'); const legendPosition = (_visParams$legendPosi = visParams.legendPosition) !== null && _visParams$legendPosi !== void 0 ? _visParams$legendPosi : _charts.Position.Right; const splitChartColumnAccessor = splitColumn ? (0, _utils3.getSplitDimensionAccessor)(visData.columns, splitColumn[0], formatters, defaultFormatter) : undefined; const splitChartRowAccessor = splitRow ? (0, _utils3.getSplitDimensionAccessor)(visData.columns, splitRow[0], formatters, defaultFormatter) : undefined; const splitChartDimension = splitColumn ? (0, _utils.getColumnByAccessor)(splitColumn[0], visData.columns) : splitRow ? (0, _utils.getColumnByAccessor)(splitRow[0], visData.columns) : undefined; const hasTooltipActions = interactive && bucketAccessors.filter(a => a !== 'metric-name').length > 0; const tooltip = { ...(fixedViewPort ? { boundary: fixedViewPort } : {}), type: visParams.addTooltip ? _charts.TooltipType.Follow : _charts.TooltipType.None, actions: hasTooltipActions ? [{ disabled: selected => selected.length < 1, label: selected => selected.length === 0 ? _i18n.i18n.translate('expressionPartitionVis.tooltipActions.emptyFilterSelection', { defaultMessage: 'Select at least one series to filter' }) : _i18n.i18n.translate('expressionPartitionVis.tooltipActions.filterValues', { defaultMessage: 'Filter {seriesNumber} series', values: { seriesNumber: selected.length } }), onSelect: tooltipSelectedValues => { const cells = (0, _filter_helpers.getMultiFilterCells)(tooltipSelectedValues, bucketColumns, visData); props.fireEvent({ name: 'multiFilter', data: { data: [{ table: visData, cells }] } }); } }] : undefined }; /** * Checks whether data have all zero values. * If so, the no data container is loaded. */ const isAllZeros = (0, _react.useMemo)(() => visData.rows.every(row => row[metricColumn.id] === 0), [visData.rows, metricColumn]); const isEmpty = visData.rows.length === 0; const isMetricEmpty = visData.rows.every(row => !row[metricColumn.id]); /** * Checks whether data have negative values. * If so, the no data container is loaded. */ const hasNegative = (0, _react.useMemo)(() => visData.rows.some(row => { const value = row[metricColumn.id]; return typeof value === 'number' && value < 0; }), [visData.rows, metricColumn]); const flatLegend = !visParams.nestedLegend || (0, _utils3.isLegendFlat)(visType, splitChartDimension); const canShowPieChart = !isEmpty && !isMetricEmpty && !isAllZeros && !hasNegative; const { euiTheme } = (0, _eui.useEuiTheme)(); const chartContainerStyle = showToggleLegendElement ? (0, _partition_vis_component.partitionVisContainerWithToggleStyleFactory)(euiTheme) : _partition_vis_component.partitionVisContainerStyle; const partitionType = (0, _utils3.getPartitionType)(visType); const customLegendSort = (0, _react.useMemo)(() => { if (!showLegend || !flatLegend) { return; } const [bucketColumn] = bucketColumns; if (!bucketColumn.id) { return; } const lookup = {}; visData.rows.forEach((row, i) => { const category = row[bucketColumn.id]; if (!(category in lookup)) { lookup[category] = i; } }); return (a, b) => { if (a.key == null) { return 1; } if (b.key == null) { return -1; } return lookup[a.key] - lookup[b.key]; }; }, [bucketColumns, flatLegend, showLegend, visData.rows]); return /*#__PURE__*/_react.default.createElement("div", { css: chartContainerStyle, "data-test-subj": "partitionVisChart" }, !canShowPieChart ? /*#__PURE__*/_react.default.createElement(_visualization_noresults.VisualizationNoResults, { hasNegativeValues: hasNegative, chartType: visType, renderComplete: onRenderChange }) : /*#__PURE__*/_react.default.createElement("div", { css: _partition_vis_component.partitionVisWrapperStyle, ref: parentRef }, /*#__PURE__*/_react.default.createElement(_utils3.LegendColorPickerWrapperContext.Provider, { value: { legendPosition, setColor, bucketColumns, palette: visParams.palette.name, data: visData.rows, uiState: props.uiState, distinctColors: (_visParams$distinctCo = visParams.distinctColors) !== null && _visParams$distinctCo !== void 0 ? _visParams$distinctCo : false } }, showToggleLegendElement && /*#__PURE__*/_react.default.createElement(_public.LegendToggle, { onClick: toggleLegend, showLegend: showLegend, legendPosition: legendPosition }), /*#__PURE__*/_react.default.createElement(_charts.Chart, (0, _extends2.default)({ size: "100%" }, (0, _chartExpressionsCommon.getOverridesFor)(overrides, 'chart')), /*#__PURE__*/_react.default.createElement(_chart_split.ChartSplit, { splitColumnAccessor: splitChartColumnAccessor, splitRowAccessor: splitChartRowAccessor }), /*#__PURE__*/_react.default.createElement(_charts.Tooltip, tooltip), /*#__PURE__*/_react.default.createElement(_charts.Settings, (0, _extends2.default)({ noResults: /*#__PURE__*/_react.default.createElement(_visualization_noresults.VisualizationNoResults, { chartType: visType, renderComplete: onRenderChange }), debugState: (_window$_echDebugStat = window._echDebugStateFlag) !== null && _window$_echDebugStat !== void 0 ? _window$_echDebugStat : false, showLegend: showLegend !== null && showLegend !== void 0 ? showLegend : (0, _utils3.shouldShowLegend)(visType, visParams.legendDisplay, bucketColumns), legendPosition: legendPosition, legendSize: _constants.LegendSizeToPixels[(_visParams$legendSize = visParams.legendSize) !== null && _visParams$legendSize !== void 0 ? _visParams$legendSize : _constants.DEFAULT_LEGEND_SIZE], legendMaxDepth: visParams.nestedLegend ? undefined : 1, legendColorPicker: props.uiState ? _utils3.LegendColorPickerWrapper : undefined, flatLegend: flatLegend, legendSort: customLegendSort, showLegendExtra: visParams.showValuesInLegend, onElementClick: ([elementEvent]) => { // this cast is safe because we are rendering a partition chart const [layerValues] = elementEvent; handleSliceClick(layerValues, bucketColumns, visData, splitChartDimension, splitChartFormatter); }, legendAction: legendActions, theme: [ // Chart background should be transparent for the usage at Canvas. { background: { color: 'transparent' } }, themeOverrides, chartTheme, { legend: { labelOptions: { maxLines: visParams.truncateLegend ? (_visParams$maxLegendL = visParams.maxLegendLines) !== null && _visParams$maxLegendL !== void 0 ? _visParams$maxLegendL : 1 : 0 } } }, ...(Array.isArray(settingsThemeOverrides) ? settingsThemeOverrides : [settingsThemeOverrides])], baseTheme: chartBaseTheme, onRenderChange: onRenderChange, ariaLabel: props.visParams.ariaLabel, ariaUseDefaultSummary: !props.visParams.ariaLabel }, settingsOverrides)), /*#__PURE__*/_react.default.createElement(_charts.Partition, (0, _extends2.default)({ id: visType, smallMultiples: _chart_split.SMALL_MULTIPLES_ID, data: visData.rows, layout: partitionType, specialFirstInnermostSector: visParams.startFromSecondLargestSlice, valueAccessor: d => getSliceValue(d, metricColumn), percentFormatter: d => percentFormatter.convert(d / 100), valueGetter: !visParams.labels.show || visParams.labels.valuesFormat === _expression_renderers.ValueFormats.VALUE || !visParams.labels.values ? undefined : _expression_renderers.ValueFormats.PERCENT, valueFormatter: d => !visParams.labels.show || !visParams.labels.values ? '' : metricFieldFormatter.convert(d), layers: layers, topGroove: !visParams.labels.show ? 0 : undefined }, (0, _chartExpressionsCommon.getOverridesFor)(overrides, 'partition'))))))); }; // eslint-disable-next-line import/no-default-export var _default = /*#__PURE__*/(0, _react.memo)(PartitionVisComponent); exports.default = _default; module.exports = exports.default;