import 'vendor/flot/jquery.flot'; import $ from 'jquery'; import { isNumber } from 'lodash'; import { PanelCtrl } from 'app/angular/panel/panel_ctrl'; import { config } from 'app/core/config'; import { CoreEvents } from 'app/types'; export class ThresholdManager { plot: any; placeholder: any; height: any; thresholds: any; needsCleanup = false; hasSecondYAxis: any; constructor(private panelCtrl: PanelCtrl) {} getHandleHtml(handleIndex: any, model: { colorMode: string }, valueStr: any) { let stateClass = model.colorMode; if (model.colorMode === 'custom') { stateClass = 'critical'; } return `
${valueStr}
`; } initDragging(evt: any) { const handleElem = $(evt.currentTarget).parents('.alert-handle-wrapper'); const handleIndex = $(evt.currentTarget).data('handleIndex'); let lastY: number | null = null; let posTop: number; const plot = this.plot; const panelCtrl = this.panelCtrl; const model = this.thresholds[handleIndex]; function dragging(evt: any) { if (lastY === null) { lastY = evt.clientY; } else { const diff = evt.clientY - lastY; posTop = posTop + diff; lastY = evt.clientY; handleElem.css({ top: posTop + diff }); } } function stopped() { // calculate graph level let graphValue = plot.c2p({ left: 0, top: posTop }).y; graphValue = parseInt(graphValue.toFixed(0), 10); model.value = graphValue; handleElem.off('mousemove', dragging); document.removeEventListener('mouseup', stopped); // trigger digest and render panelCtrl.$scope.$apply(() => { panelCtrl.render(); panelCtrl.events.emit(CoreEvents.thresholdChanged, { threshold: model, handleIndex: handleIndex, }); }); } lastY = null; posTop = handleElem.position().top; handleElem.on('mousemove', dragging); document.addEventListener('mouseup', stopped); } cleanUp() { this.placeholder.find('.alert-handle-wrapper').remove(); this.needsCleanup = false; } renderHandle(handleIndex: number, defaultHandleTopPos: number) { const model = this.thresholds[handleIndex]; // alerting defines if (!model.visible && (this.panelCtrl as any).alert) { return; } const value = model.value; let valueStr = value; let handleTopPos = 0; // handle no value if (!isNumber(value)) { valueStr = ''; handleTopPos = defaultHandleTopPos; } else { const valueCanvasPos = this.plot.p2c({ x: 0, y: value }); handleTopPos = Math.round(Math.min(Math.max(valueCanvasPos.top, 0), this.height) - 6); } const handleElem = $(this.getHandleHtml(handleIndex, model, valueStr)); this.placeholder.append(handleElem); handleElem.toggleClass('alert-handle-wrapper--no-value', valueStr === ''); handleElem.css({ top: handleTopPos }); } shouldDrawHandles() { // @ts-ignore return !this.hasSecondYAxis && this.panelCtrl.editingThresholds && this.panelCtrl.panel.thresholds.length > 0; } prepare(elem: JQuery, data: any[]) { this.hasSecondYAxis = false; for (let i = 0; i < data.length; i++) { if (data[i].yaxis > 1) { this.hasSecondYAxis = true; break; } } if (this.shouldDrawHandles()) { const thresholdMargin = this.panelCtrl.panel.thresholds.length > 1 ? '220px' : '110px'; elem.css('margin-right', thresholdMargin); } else if (this.needsCleanup) { elem.css('margin-right', '0'); } } draw(plot: any) { this.thresholds = this.panelCtrl.panel.thresholds; this.plot = plot; this.placeholder = plot.getPlaceholder(); if (this.needsCleanup) { this.cleanUp(); } if (!this.shouldDrawHandles()) { return; } this.height = plot.height(); if (this.thresholds.length > 0) { this.renderHandle(0, 10); } if (this.thresholds.length > 1) { this.renderHandle(1, this.height - 30); } this.placeholder.off('mousedown', '.alert-handle'); this.placeholder.on('mousedown', '.alert-handle', this.initDragging.bind(this)); this.needsCleanup = true; } addFlotOptions(options: any, panel: any) { if (!panel.thresholds || panel.thresholds.length === 0) { return; } let gtLimit = Infinity; let ltLimit = -Infinity; let i, threshold, other; for (i = 0; i < panel.thresholds.length; i++) { threshold = panel.thresholds[i]; if (!isNumber(threshold.value)) { continue; } let limit; switch (threshold.op) { case 'gt': { limit = gtLimit; // if next threshold is less then op and greater value, then use that as limit if (panel.thresholds.length > i + 1) { other = panel.thresholds[i + 1]; if (other.value > threshold.value) { limit = other.value; ltLimit = limit; } } break; } case 'lt': { limit = ltLimit; // if next threshold is less then op and greater value, then use that as limit if (panel.thresholds.length > i + 1) { other = panel.thresholds[i + 1]; if (other.value < threshold.value) { limit = other.value; gtLimit = limit; } } break; } } let fillColor, lineColor; switch (threshold.colorMode) { case 'critical': { fillColor = 'rgba(234, 112, 112, 0.12)'; lineColor = 'rgba(237, 46, 24, 0.60)'; break; } case 'warning': { fillColor = 'rgba(235, 138, 14, 0.12)'; lineColor = 'rgba(247, 149, 32, 0.60)'; break; } case 'ok': { fillColor = 'rgba(11, 237, 50, 0.090)'; lineColor = 'rgba(6,163,69, 0.60)'; break; } case 'custom': { fillColor = threshold.fillColor; lineColor = threshold.lineColor; break; } } // fill if (threshold.fill) { if (threshold.yaxis === 'right' && this.hasSecondYAxis) { options.grid.markings.push({ y2axis: { from: threshold.value, to: limit }, color: config.theme.visualization.getColorByName(fillColor), }); } else { options.grid.markings.push({ yaxis: { from: threshold.value, to: limit }, color: config.theme.visualization.getColorByName(fillColor), }); } } if (threshold.line) { if (threshold.yaxis === 'right' && this.hasSecondYAxis) { options.grid.markings.push({ y2axis: { from: threshold.value, to: threshold.value }, color: config.theme.visualization.getColorByName(lineColor), }); } else { options.grid.markings.push({ yaxis: { from: threshold.value, to: threshold.value }, color: config.theme.visualization.getColorByName(lineColor), }); } } } } }