"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.Y_AXIS_LABEL_WIDTH = exports.Y_AXIS_LABEL_PADDING = exports.SwimlaneAnnotationContainer = void 0; var _react = _interopRequireWildcard(require("react")); var _d = _interopRequireDefault(require("d3")); var _d3Scale = require("d3-scale"); var _i18n = require("@kbn/i18n"); var _moment = _interopRequireDefault(require("moment")); var _kibana = require("../contexts/kibana"); 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; you may not use this file except in compliance with the Elastic License * 2.0. */ const Y_AXIS_LABEL_WIDTH = 170; exports.Y_AXIS_LABEL_WIDTH = Y_AXIS_LABEL_WIDTH; const Y_AXIS_LABEL_PADDING = 8; exports.Y_AXIS_LABEL_PADDING = Y_AXIS_LABEL_PADDING; const ANNOTATION_CONTAINER_HEIGHT = 12; const ANNOTATION_MIN_WIDTH = 8; const SwimlaneAnnotationContainer = ({ chartWidth, domain, annotationsData, tooltipService }) => { const canvasRef = _react.default.useRef(null); const { euiTheme } = (0, _kibana.useCurrentThemeVars)(); (0, _react.useEffect)(() => { if (canvasRef.current !== null && Array.isArray(annotationsData)) { const chartElement = _d.default.select(canvasRef.current); chartElement.selectAll('*').remove(); const dimensions = canvasRef.current.getBoundingClientRect(); const startingXPos = Y_AXIS_LABEL_WIDTH; const endingXPos = dimensions.width; const svg = chartElement.append('svg').attr('width', '100%').attr('height', ANNOTATION_CONTAINER_HEIGHT); const xScale = (0, _d3Scale.scaleTime)().domain([domain.min, domain.max]).range([startingXPos, endingXPos]); // Add Annotation y axis label svg.append('text').attr('text-anchor', 'end').attr('class', 'swimlaneAnnotationLabel').text(_i18n.i18n.translate('xpack.ml.explorer.swimlaneAnnotationLabel', { defaultMessage: 'Annotations' })).attr('x', Y_AXIS_LABEL_WIDTH - Y_AXIS_LABEL_PADDING).attr('y', ANNOTATION_CONTAINER_HEIGHT / 2).attr('dominant-baseline', 'middle').style('fill', euiTheme.euiTextSubduedColor).style('font-size', euiTheme.euiFontSizeXS); // Add border svg.append('rect').attr('x', startingXPos).attr('y', 0).attr('height', ANNOTATION_CONTAINER_HEIGHT).attr('width', endingXPos - startingXPos).style('stroke', euiTheme.euiBorderColor).style('fill', 'none').style('stroke-width', 1); // Merging overlapping annotations into bigger blocks let mergedAnnotations = []; const sortedAnnotationsData = [...annotationsData].sort((a, b) => a.timestamp - b.timestamp); if (sortedAnnotationsData.length > 0) { var _sortedAnnotationsDat; let lastEndTime = (_sortedAnnotationsDat = sortedAnnotationsData[0].end_timestamp) !== null && _sortedAnnotationsDat !== void 0 ? _sortedAnnotationsDat : sortedAnnotationsData[0].timestamp; mergedAnnotations = [{ start: sortedAnnotationsData[0].timestamp, end: lastEndTime, annotations: [sortedAnnotationsData[0]] }]; for (let i = 1; i < sortedAnnotationsData.length; i++) { if (sortedAnnotationsData[i].timestamp < lastEndTime) { const itemToMerge = mergedAnnotations.pop(); if (itemToMerge) { const newMergedItem = { ...itemToMerge, end: lastEndTime, annotations: [...itemToMerge.annotations, sortedAnnotationsData[i]] }; mergedAnnotations.push(newMergedItem); } } else { var _sortedAnnotationsDat2; lastEndTime = (_sortedAnnotationsDat2 = sortedAnnotationsData[i].end_timestamp) !== null && _sortedAnnotationsDat2 !== void 0 ? _sortedAnnotationsDat2 : sortedAnnotationsData[i].timestamp; mergedAnnotations.push({ start: sortedAnnotationsData[i].timestamp, end: lastEndTime, annotations: [sortedAnnotationsData[i]] }); } } } // Add annotation marker mergedAnnotations.forEach(d => { const annotationWidth = Math.max(d.end ? xScale(Math.min(d.end, domain.max)) - Math.max(xScale(d.start), startingXPos) : 0, ANNOTATION_MIN_WIDTH); const xPos = d.start >= domain.min ? xScale(d.start) : startingXPos; svg.append('rect').classed('mlAnnotationRect', true) // If annotation is at the end, prevent overflow by shifting it back .attr('x', xPos + annotationWidth >= endingXPos ? endingXPos - annotationWidth : xPos).attr('y', 0).attr('height', ANNOTATION_CONTAINER_HEIGHT).attr('width', annotationWidth).on('mouseover', function () { const tooltipData = []; if (Array.isArray(d.annotations)) { const hasMergedAnnotations = d.annotations.length > 1; if (hasMergedAnnotations) { // @ts-ignore skipping header so it doesn't have other params tooltipData.push({ skipHeader: true }); } d.annotations.forEach(item => { let timespan = (0, _moment.default)(item.timestamp).format('MMMM Do YYYY, HH:mm'); if (typeof item.end_timestamp !== 'undefined') { timespan += ` - ${(0, _moment.default)(item.end_timestamp).format(hasMergedAnnotations ? 'HH:mm' : 'MMMM Do YYYY, HH:mm')}`; } if (hasMergedAnnotations) { var _item$_id; tooltipData.push({ label: timespan, value: `${item.annotation}`, seriesIdentifier: { key: 'anomaly_timeline', specId: (_item$_id = item._id) !== null && _item$_id !== void 0 ? _item$_id : `${item.annotation}-${item.timestamp}-label` }, valueAccessor: 'annotation' }); } else { var _item$_id2, _item$_id3; tooltipData.push({ label: `${item.annotation}`, seriesIdentifier: { key: 'anomaly_timeline', specId: (_item$_id2 = item._id) !== null && _item$_id2 !== void 0 ? _item$_id2 : `${item.annotation}-${item.timestamp}-label` }, valueAccessor: 'label' }, { label: `${timespan}`, seriesIdentifier: { key: 'anomaly_timeline', specId: (_item$_id3 = item._id) !== null && _item$_id3 !== void 0 ? _item$_id3 : `${item.annotation}-${item.timestamp}-ts` }, valueAccessor: 'time' }); } if (item.partition_field_name !== undefined && item.partition_field_value !== undefined) { tooltipData.push({ label: `${item.partition_field_name}: ${item.partition_field_value}`, seriesIdentifier: { key: 'anomaly_timeline', specId: item._id ? `${item._id}-partition` : `${item.partition_field_name}-${item.partition_field_value}-label` }, valueAccessor: 'partition' }); } }); } // @ts-ignore we don't need all the fields for tooltip to show tooltipService.show(tooltipData, this); }).on('mouseout', () => tooltipService.hide()); }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [chartWidth, domain, annotationsData, tooltipService]); return /*#__PURE__*/_react.default.createElement("div", { ref: canvasRef }); }; exports.SwimlaneAnnotationContainer = SwimlaneAnnotationContainer;