"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.GaugeComponent = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireWildcard(require("react")); var _charts = require("@elastic/charts"); var _i18nReact = require("@kbn/i18n-react"); var _public = require("@kbn/charts-plugin/public"); var _chartExpressionsCommon = require("@kbn/chart-expressions-common"); var _utils = require("@kbn/visualizations-plugin/common/utils"); var _common = require("../../common"); var _utils2 = require("./utils"); var _icons = require("./utils/icons"); require("./index.scss"); var _utils3 = require("../../common/utils"); require("./gauge.scss"); 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 TRANSPARENT = `rgba(255,255,255,0)`; function normalizeBands({ colors, stops, range, rangeMax, rangeMin }, { min, max }) { if (!stops.length) { const step = (max - min) / colors.length; return [min, ...colors.map((_, i) => min + (i + 1) * step)]; } let firstRanges = [min]; let lastRanges = [max]; let correctMin = rangeMin; let correctMax = rangeMax; if (range === 'percent') { correctMin = min + rangeMin * ((max - min) / 100); correctMax = min + rangeMax * ((max - min) / 100); } if (correctMin > min && isFinite(correctMin)) { firstRanges = [min, correctMin]; } if (correctMax < max && isFinite(correctMax)) { lastRanges = [correctMax, max]; } if (range === 'percent') { const filteredStops = stops.filter(stop => stop > 0 && stop < 100); return [...firstRanges, ...filteredStops.map(step => min + step * ((max - min) / 100)), ...lastRanges]; } const orderedStops = stops.filter((stop, i) => stop < max && stop > min); return [...firstRanges, ...orderedStops, ...lastRanges]; } const toPercents = (min, max) => v => (v - min) / (max - min); function normalizeBandsLegacy({ colors, stops }, value) { const min = stops[0]; const max = stops[stops.length - 1]; const convertToPercents = toPercents(min, max); const normalizedStops = stops.map(convertToPercents); if (max < value) { normalizedStops.push(convertToPercents(value)); } return normalizedStops; } function actualValueToPercentsLegacy({ stops }, value) { const min = stops[0]; const max = stops[stops.length - 1]; const convertToPercents = toPercents(min, max); return convertToPercents(value); } function getTitle(majorMode, major, fallbackTitle) { if (majorMode === _common.GaugeLabelMajorModes.NONE) { return ''; } if (majorMode === _common.GaugeLabelMajorModes.AUTO) { return fallbackTitle || ''; } return major || fallbackTitle || ''; } const getPreviousSectionValue = (value, bands) => { // bands value is equal to the stop. The purpose of this value is coloring the previous section, which is smaller, then the band. // So, the smaller value should be taken. For the first element -1, for the next - middle value of the previous section. let prevSectionValue = value - 1; const valueIndex = bands.indexOf(value); const prevBand = bands[valueIndex - 1]; const curBand = bands[valueIndex]; if (valueIndex > 0) { prevSectionValue = value - (curBand - prevBand) / 2; } return prevSectionValue; }; function getTicksLabels(baseStops) { const tenPercentRange = (Math.max(...baseStops) - Math.min(...baseStops)) * 0.1; const lastIndex = baseStops.length - 1; return baseStops.filter((stop, i) => { if (i === 0 || i === lastIndex) { return true; } return !(stop - baseStops[i - 1] < tenPercentRange || baseStops[lastIndex] - stop < tenPercentRange); }); } function getTicks(ticksPosition, range, colorBands, percentageMode) { if (ticksPosition === _common.GaugeTicksPositions.HIDDEN) { return []; } if (ticksPosition === _common.GaugeTicksPositions.BANDS && colorBands) { return colorBands && getTicksLabels(colorBands); } } const GaugeComponent = /*#__PURE__*/(0, _react.memo)(({ data, args, uiState, formatFactory, paletteService, chartsThemeService, renderComplete, overrides }) => { var _metricColumn$meta, _metricColumn$meta$pa, _metricColumn$meta2, _ref, _palette$params$stops, _window$_echDebugStat; const { shape: gaugeType, palette, colorMode, labelMinor, labelMajor, labelMajorMode, centralMajor, centralMajorMode, ticksPosition, commonLabel } = args; const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const getColor = (0, _react.useCallback)((value, paletteConfig, bands, percentageMode) => { var _paletteConfig$params, _paletteConfig$params2, _paletteService$get$g, _paletteService$get, _paletteConfig$name; let stops = (_paletteConfig$params = (_paletteConfig$params2 = paletteConfig.params) === null || _paletteConfig$params2 === void 0 ? void 0 : _paletteConfig$params2.stops) !== null && _paletteConfig$params !== void 0 ? _paletteConfig$params : []; if (percentageMode) { stops = bands.map(v => v * 100); } const { min, max } = (0, _utils2.computeMinMax)(paletteConfig, bands); return (_paletteService$get$g = (_paletteService$get = paletteService.get((_paletteConfig$name = paletteConfig === null || paletteConfig === void 0 ? void 0 : paletteConfig.name) !== null && _paletteConfig$name !== void 0 ? _paletteConfig$name : 'custom')).getColorForValue) === null || _paletteService$get$g === void 0 ? void 0 : _paletteService$get$g.call(_paletteService$get, value, { ...paletteConfig.params, stops }, { min, max }); }, [paletteService]); // Legacy chart was not formatting numbers, when was forming overrideColors. // To support the behavior of the color overriding, it is required to skip all the formatting, except percent. const overrideColor = (0, _react.useCallback)((value, bands, formatter) => { var _uiState$get, _formatter$convert, _formatter$convert2; const overrideColors = (_uiState$get = uiState === null || uiState === void 0 ? void 0 : uiState.get('vis.colors')) !== null && _uiState$get !== void 0 ? _uiState$get : {}; const valueIndex = bands.findIndex((band, index, allBands) => { if (index === allBands.length - 1) { return false; } return value >= band && value < allBands[index + 1]; }); if (valueIndex < 0 || valueIndex === bands.length - 1) { return undefined; } const curValue = bands[valueIndex]; const nextValue = bands[valueIndex + 1]; return overrideColors[`${(_formatter$convert = formatter === null || formatter === void 0 ? void 0 : formatter.convert(curValue)) !== null && _formatter$convert !== void 0 ? _formatter$convert : curValue} - ${(_formatter$convert2 = formatter === null || formatter === void 0 ? void 0 : formatter.convert(nextValue)) !== null && _formatter$convert2 !== void 0 ? _formatter$convert2 : nextValue}`]; }, [uiState]); 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(() => { renderComplete(); }); } }, [renderComplete]); const table = data; const accessors = (0, _utils2.getAccessorsFromArgs)(args, table.columns); if (!accessors || !accessors.metric) { // Chart is not ready return null; } const chartTheme = chartsThemeService.useChartsTheme(); const metricColumn = table.columns.find(col => col.id === accessors.metric); const chartData = table.rows.filter(v => typeof v[accessors.metric] === 'number' || Array.isArray(v[accessors.metric])); const row = chartData === null || chartData === void 0 ? void 0 : chartData[0]; const metricValue = args.metric ? (0, _utils2.getValueFromAccessor)(accessors.metric, row) : undefined; const icon = (0, _icons.getIcons)(gaugeType); if (typeof metricValue !== 'number') { return /*#__PURE__*/_react.default.createElement(_public.EmptyPlaceholder, { icon: icon, renderComplete: onRenderChange }); } const goal = accessors.goal ? (0, _utils2.getValueFromAccessor)(accessors.goal, row) : undefined; const min = (0, _utils2.getMinValue)(row, accessors, palette === null || palette === void 0 ? void 0 : palette.params, args.respectRanges); const max = (0, _utils2.getMaxValue)(row, accessors, palette === null || palette === void 0 ? void 0 : palette.params, args.respectRanges); if (min === max) { return /*#__PURE__*/_react.default.createElement(_public.EmptyPlaceholder, { icon: icon, message: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "expressionGauge.renderer.chartCannotRenderEqual", defaultMessage: "Minimum and maximum values may not be equal" }), renderComplete: onRenderChange }); } if (min > max) { return /*#__PURE__*/_react.default.createElement(_public.EmptyPlaceholder, { icon: icon, message: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "expressionGauge.renderer.chartCannotRenderMinGreaterMax", defaultMessage: "Minimum value may not be greater than maximum value" }), renderComplete: onRenderChange }); } const customMetricFormatParams = (0, _utils.isVisDimension)(args.metric) ? args.metric.format : undefined; const tableMetricFormatParams = metricColumn !== null && metricColumn !== void 0 && (_metricColumn$meta = metricColumn.meta) !== null && _metricColumn$meta !== void 0 && (_metricColumn$meta$pa = _metricColumn$meta.params) !== null && _metricColumn$meta$pa !== void 0 && _metricColumn$meta$pa.params ? metricColumn === null || metricColumn === void 0 ? void 0 : (_metricColumn$meta2 = metricColumn.meta) === null || _metricColumn$meta2 === void 0 ? void 0 : _metricColumn$meta2.params : undefined; const defaultMetricFormatParams = { id: 'number', params: { pattern: max - min > 5 ? `0,0` : `0,0.0` } }; const tickFormatter = formatFactory((_ref = customMetricFormatParams !== null && customMetricFormatParams !== void 0 ? customMetricFormatParams : tableMetricFormatParams) !== null && _ref !== void 0 ? _ref : defaultMetricFormatParams); let bands = palette !== null && palette !== void 0 && palette.params ? normalizeBands(palette === null || palette === void 0 ? void 0 : palette.params, { min, max }) : [min, max]; // TODO: format in charts let actualValue = Math.round(Math.min(Math.max(metricValue, min), max) * 1000) / 1000; if (args.percentageMode && palette !== null && palette !== void 0 && palette.params && palette !== null && palette !== void 0 && (_palette$params$stops = palette.params.stops) !== null && _palette$params$stops !== void 0 && _palette$params$stops.length) { bands = normalizeBandsLegacy(palette === null || palette === void 0 ? void 0 : palette.params, actualValue); actualValue = actualValueToPercentsLegacy(palette === null || palette === void 0 ? void 0 : palette.params, actualValue); } const totalTicks = getTicks(ticksPosition, [min, max], bands, args.percentageMode); const ticks = totalTicks && gaugeType === _common.GaugeShapes.CIRCLE ? totalTicks.slice(0, totalTicks.length - 1) : totalTicks; const goalConfig = (0, _utils2.getGoalConfig)(gaugeType); const labelMajorTitle = getTitle(labelMajorMode, labelMajor, metricColumn === null || metricColumn === void 0 ? void 0 : metricColumn.name); // added extra space for nice rendering const majorExtraSpaces = (0, _utils3.isBulletShape)(gaugeType) ? ' ' : ''; const minorExtraSpaces = (0, _utils3.isBulletShape)(gaugeType) ? ' ' : ''; const extraTitles = (0, _utils3.isRoundShape)(gaugeType) ? { centralMinor: tickFormatter.convert(actualValue), centralMajor: getTitle(centralMajorMode, centralMajor, metricColumn === null || metricColumn === void 0 ? void 0 : metricColumn.name) } : {}; return /*#__PURE__*/_react.default.createElement("div", { className: "gauge__wrapper" }, /*#__PURE__*/_react.default.createElement(_charts.Chart, (0, _chartExpressionsCommon.getOverridesFor)(overrides, 'chart'), /*#__PURE__*/_react.default.createElement(_charts.Settings, (0, _extends2.default)({ noResults: /*#__PURE__*/_react.default.createElement(_public.EmptyPlaceholder, { icon: icon, renderComplete: onRenderChange }), debugState: (_window$_echDebugStat = window._echDebugStateFlag) !== null && _window$_echDebugStat !== void 0 ? _window$_echDebugStat : false, theme: [{ background: { color: 'transparent' } }, chartTheme], baseTheme: chartBaseTheme, ariaLabel: args.ariaLabel, ariaUseDefaultSummary: !args.ariaLabel, onRenderChange: onRenderChange }, (0, _chartExpressionsCommon.getOverridesFor)(overrides, 'settings'))), /*#__PURE__*/_react.default.createElement(_charts.Goal, (0, _extends2.default)({ id: "goal", subtype: (0, _utils2.getSubtypeByGaugeType)(gaugeType), base: bands[0], target: goal && goal >= bands[0] && goal <= bands[bands.length - 1] ? goal : undefined, actual: actualValue, tickValueFormatter: ({ value: tickValue }) => tickFormatter.convert(tickValue), tooltipValueFormatter: tooltipValue => tickFormatter.convert(tooltipValue), bands: bands, ticks: ticks, domain: { min, max }, bandFillColor: colorMode === _common.GaugeColorModes.PALETTE ? val => { var _args$palette$params$, _args$palette, _args$palette$params, _getColor; const value = getPreviousSectionValue(val.value, bands); const overridedColor = overrideColor(value, args.percentageMode ? bands : (_args$palette$params$ = (_args$palette = args.palette) === null || _args$palette === void 0 ? void 0 : (_args$palette$params = _args$palette.params) === null || _args$palette$params === void 0 ? void 0 : _args$palette$params.stops) !== null && _args$palette$params$ !== void 0 ? _args$palette$params$ : [], args.percentageMode ? tickFormatter : undefined); if (overridedColor) { return overridedColor; } return args.palette ? (_getColor = getColor(value, args.palette, bands, args.percentageMode)) !== null && _getColor !== void 0 ? _getColor : TRANSPARENT : TRANSPARENT; } : () => TRANSPARENT, labelMajor: labelMajorTitle ? `${labelMajorTitle}${majorExtraSpaces}` : labelMajorTitle, labelMinor: labelMinor ? `${labelMinor}${minorExtraSpaces}` : '' }, extraTitles, goalConfig, (0, _chartExpressionsCommon.getOverridesFor)(overrides, 'gauge')))), commonLabel && /*#__PURE__*/_react.default.createElement("div", { className: "gauge__label" }, commonLabel)); }); // default export required for React.Lazy // eslint-disable-next-line import/no-default-export exports.default = exports.GaugeComponent = GaugeComponent;