mirror of
https://github.com/timberjoegithub/grafana-statusmap.git
synced 2026-07-21 23:42:03 +00:00
feat: use grafana-toolkit to build a plugin
- fix linter warnings - use yarn: remove Gruntfile.js and package-lock.json ++
This commit is contained in:
+157
-120
@@ -1,11 +1,12 @@
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import d3 from 'd3';
|
||||
import * as d3ScaleChromatic from './libs/d3-scale-chromatic/index';
|
||||
import {contextSrv} from 'app/core/core';
|
||||
import {tickStep} from 'app/core/utils/ticks';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import { StatusHeatmapCtrl } from "./module";
|
||||
import * as d3 from 'd3';
|
||||
import { d3ScaleChromatic } from './d3/d3-scale-chromatic';
|
||||
import { contextSrv } from 'grafana/app/core/core';
|
||||
import { tickStep } from 'grafana/app/core/utils/ticks';
|
||||
import coreModule from 'grafana/app/core/core_module';
|
||||
|
||||
import { StatusHeatmapCtrl } from './module';
|
||||
import { PanelEvents } from './libs/grafana/events/index';
|
||||
|
||||
const LEGEND_STEP_WIDTH = 2;
|
||||
@@ -32,7 +33,7 @@ coreModule.directive('optionsColorLegend', function() {
|
||||
let legendWidth = Math.floor(legendElem.outerWidth());
|
||||
|
||||
if (panel.color.mode === 'spectrum') {
|
||||
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||
let colorScheme = _.find(ctrl.colorSchemes, { value: panel.color.colorScheme });
|
||||
let colorScale = getColorScale(colorScheme, legendWidth);
|
||||
drawSimpleColorLegend(elem, colorScale);
|
||||
} else if (panel.color.mode === 'opacity') {
|
||||
@@ -40,7 +41,7 @@ coreModule.directive('optionsColorLegend', function() {
|
||||
drawSimpleOpacityLegend(elem, colorOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -52,7 +53,7 @@ coreModule.directive('statusHeatmapLegend', function() {
|
||||
restrict: 'E',
|
||||
template: '<div class="status-heatmap-color-legend"><svg width="100px" height="6px"></svg></div>',
|
||||
link: function(scope, elem, attrs) {
|
||||
let ctrl:StatusHeatmapCtrl = scope.ctrl;
|
||||
let ctrl: StatusHeatmapCtrl = scope.ctrl;
|
||||
let panel = scope.ctrl.panel;
|
||||
|
||||
render();
|
||||
@@ -85,7 +86,7 @@ coreModule.directive('statusHeatmapLegend', function() {
|
||||
}
|
||||
|
||||
if (panel.color.mode === 'spectrum') {
|
||||
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||
let colorScheme = _.find(ctrl.colorSchemes, { value: panel.color.colorScheme });
|
||||
drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minValue);
|
||||
} else if (panel.color.mode === 'opacity') {
|
||||
let colorOptions = panel.color;
|
||||
@@ -96,37 +97,39 @@ coreModule.directive('statusHeatmapLegend', function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
function drawColorLegend(elem, colorScheme, rangeFrom: number, rangeTo:number, maxValue: number, minValue:number) {
|
||||
function drawColorLegend(elem, colorScheme, rangeFrom: number, rangeTo: number, maxValue: number, minValue: number) {
|
||||
let legendElem = $(elem).find('svg');
|
||||
let legend: any = d3.select(legendElem.get(0));
|
||||
clearLegend(elem);
|
||||
|
||||
let legendWidth = Math.floor(legendElem.outerWidth()) - 30; // narrow legendWidth by 30px to get space for first and last tick values
|
||||
let legendHeight = legendElem.attr("height");
|
||||
let legendWidth = Math.floor(legendElem.outerWidth()) - 30; // narrow legendWidth by 30px to get space for first and last tick values
|
||||
let legendHeight = legendElem.attr('height');
|
||||
|
||||
let rangeStep = (rangeTo - rangeFrom) / (legendWidth/LEGEND_STEP_WIDTH);
|
||||
let rangeStep = (rangeTo - rangeFrom) / (legendWidth / LEGEND_STEP_WIDTH);
|
||||
// width in pixels in legend space of unit segment in range space
|
||||
// rangeStep * witdhFactor == width in pixels of one rangeStep
|
||||
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
||||
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
||||
|
||||
let colorScale = getColorScale(colorScheme, maxValue, minValue);
|
||||
legend.selectAll(".status-heatmap-color-legend-rect")
|
||||
legend
|
||||
.selectAll('.status-heatmap-color-legend-rect')
|
||||
.data(valuesRange)
|
||||
.enter().append("rect")
|
||||
.enter()
|
||||
.append('rect')
|
||||
// translate from range space into pixels
|
||||
// and shift all rectangles to the right by 10
|
||||
.attr("x", d => ((d - rangeFrom) * widthFactor)+10)
|
||||
.attr("y", 0)
|
||||
.attr('x', d => (d - rangeFrom) * widthFactor + 10)
|
||||
.attr('y', 0)
|
||||
// rectangles are slightly overlaped to prevent gaps
|
||||
.attr("width", LEGEND_STEP_WIDTH+1)
|
||||
.attr("height", legendHeight)
|
||||
.attr("stroke-width", 0)
|
||||
.attr("fill", d => colorScale(d));
|
||||
.attr('width', LEGEND_STEP_WIDTH + 1)
|
||||
.attr('height', legendHeight)
|
||||
.attr('stroke-width', 0)
|
||||
.attr('fill', d => colorScale(d));
|
||||
|
||||
drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth);
|
||||
}
|
||||
@@ -136,29 +139,31 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue
|
||||
let legend = d3.select(legendElem.get(0));
|
||||
clearLegend(elem);
|
||||
|
||||
let legendWidth = Math.floor(legendElem.outerWidth()) - 30; // narrow legendWidth by 30px to get space for first and last tick values
|
||||
let legendHeight = legendElem.attr("height");
|
||||
let legendWidth = Math.floor(legendElem.outerWidth()) - 30; // narrow legendWidth by 30px to get space for first and last tick values
|
||||
let legendHeight = legendElem.attr('height');
|
||||
|
||||
let rangeStep = (rangeTo - rangeFrom) / (legendWidth/LEGEND_STEP_WIDTH);
|
||||
let rangeStep = (rangeTo - rangeFrom) / (legendWidth / LEGEND_STEP_WIDTH);
|
||||
// width in pixels in legend space of unit segment in range space
|
||||
// rangeStep * witdhFactor == width in pixels of one rangeStep
|
||||
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
||||
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
||||
|
||||
let opacityScale = getOpacityScale(options, maxValue, minValue);
|
||||
legend.selectAll(".status-heatmap-opacity-legend-rect")
|
||||
legend
|
||||
.selectAll('.status-heatmap-opacity-legend-rect')
|
||||
.data(valuesRange)
|
||||
.enter().append("rect")
|
||||
.enter()
|
||||
.append('rect')
|
||||
// translate from range space into pixels
|
||||
// and shift all rectangles to the right by 10
|
||||
.attr("x", d => d * widthFactor+10)
|
||||
.attr("y", 0)
|
||||
.attr('x', d => d * widthFactor + 10)
|
||||
.attr('y', 0)
|
||||
// rectangles are slightly overlaped to prevent gaps
|
||||
.attr("width", LEGEND_STEP_WIDTH+1)
|
||||
.attr("height", legendHeight)
|
||||
.attr("stroke-width", 0)
|
||||
.attr("fill", options.cardColor)
|
||||
.style("opacity", d => opacityScale(d));
|
||||
.attr('width', LEGEND_STEP_WIDTH + 1)
|
||||
.attr('height', legendHeight)
|
||||
.attr('stroke-width', 0)
|
||||
.attr('fill', options.cardColor)
|
||||
.style('opacity', d => opacityScale(d));
|
||||
|
||||
drawLegendValues(elem, opacityScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth);
|
||||
}
|
||||
@@ -173,51 +178,62 @@ function drawDiscreteColorLegend(elem, colorOptions, discreteExtraSeries) {
|
||||
let valuesNumber = thresholds.length;
|
||||
|
||||
// graph width as a fallback
|
||||
const $heatmap = $(elem).parent().parent().parent().find('.statusmap-panel');
|
||||
const graphWidthAttr = $heatmap.find('svg').attr("width");
|
||||
let graphWidth = parseInt(graphWidthAttr);
|
||||
|
||||
const $heatmap = $(elem)
|
||||
.parent()
|
||||
.parent()
|
||||
.parent()
|
||||
.find('.statusmap-panel');
|
||||
const graphWidthAttr = $heatmap.find('svg').attr('width');
|
||||
let graphWidth = parseInt(graphWidthAttr, 10);
|
||||
|
||||
// calculate max width of tooltip and use it as width for each item
|
||||
let textWidth:number[] = [];
|
||||
legend.selectAll(".hidden-texts")
|
||||
let textWidth: number[] = [];
|
||||
legend
|
||||
.selectAll('.hidden-texts')
|
||||
.data(tooltips)
|
||||
.enter().append("text")
|
||||
.attr("class", "axis tick hidden-texts")
|
||||
.attr("font-family", "sans-serif")
|
||||
.enter()
|
||||
.append('text')
|
||||
.attr('class', 'axis tick hidden-texts')
|
||||
.attr('font-family', 'sans-serif')
|
||||
.text(d => d)
|
||||
.each(function(d,i) {
|
||||
.each(function(d, i) {
|
||||
let thisWidth = this.getBBox().width;
|
||||
textWidth.push(thisWidth);
|
||||
});
|
||||
legend.selectAll(".hidden-texts").remove();
|
||||
legend.selectAll('.hidden-texts').remove();
|
||||
|
||||
let legendWidth = Math.floor(_.min([
|
||||
graphWidth - 30,
|
||||
(_.max(textWidth)! + 3) * valuesNumber,
|
||||
])!);
|
||||
legendElem.attr("width", legendWidth);
|
||||
let legendWidth = Math.floor(_.min([graphWidth - 30, (_.max(textWidth)! + 3) * valuesNumber])!);
|
||||
legendElem.attr('width', legendWidth);
|
||||
|
||||
let legendHeight = legendElem.attr("height");
|
||||
let legendHeight = legendElem.attr('height');
|
||||
|
||||
let itemWidth = Math.floor(legendWidth / valuesNumber);
|
||||
let itemWidth = Math.floor(legendWidth / valuesNumber);
|
||||
let valuesRange = d3.range(valuesNumber); // from 0 to valuesNumber-1
|
||||
|
||||
legend.selectAll(".status-heatmap-color-legend-rect")
|
||||
legend
|
||||
.selectAll('.status-heatmap-color-legend-rect')
|
||||
.data(valuesRange)
|
||||
.enter().append("rect")
|
||||
.attr("x", d => d*itemWidth)
|
||||
.attr("y", 0)
|
||||
.attr("width", itemWidth + 1) // Overlap rectangles to prevent gaps
|
||||
.attr("height", legendHeight)
|
||||
.attr("stroke-width", 0)
|
||||
.attr("fill", d => discreteExtraSeries.getDiscreteColor(d));
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('x', d => d * itemWidth)
|
||||
.attr('y', 0)
|
||||
.attr('width', itemWidth + 1) // Overlap rectangles to prevent gaps
|
||||
.attr('height', legendHeight)
|
||||
.attr('stroke-width', 0)
|
||||
.attr('fill', d => discreteExtraSeries.getDiscreteColor(d));
|
||||
|
||||
drawDiscreteLegendValues(elem, colorOptions, legendWidth);
|
||||
}
|
||||
|
||||
|
||||
function drawLegendValues(elem, colorScale, rangeFrom: number, rangeTo: number, maxValue: number, minValue: number, legendWidth: number) {
|
||||
function drawLegendValues(
|
||||
elem,
|
||||
colorScale,
|
||||
rangeFrom: number,
|
||||
rangeTo: number,
|
||||
maxValue: number,
|
||||
minValue: number,
|
||||
legendWidth: number
|
||||
) {
|
||||
let legendElem = $(elem).find('svg');
|
||||
let legend = d3.select(legendElem.get(0));
|
||||
|
||||
@@ -225,25 +241,31 @@ function drawLegendValues(elem, colorScale, rangeFrom: number, rangeTo: number,
|
||||
return;
|
||||
}
|
||||
|
||||
let legendValueScale = d3.scaleLinear()
|
||||
let legendValueScale = d3
|
||||
.scaleLinear()
|
||||
.domain([rangeFrom, rangeTo])
|
||||
.range([0, legendWidth]);
|
||||
|
||||
let ticks = buildLegendTicks(rangeFrom, rangeTo, maxValue, minValue);
|
||||
let xAxis = d3.axisBottom(legendValueScale)
|
||||
let xAxis = d3
|
||||
.axisBottom(legendValueScale)
|
||||
.tickValues(ticks)
|
||||
.tickSize(2);
|
||||
|
||||
let colorRect = legendElem.find(":first-child");
|
||||
let colorRect = legendElem.find(':first-child');
|
||||
let posY = getSvgElemHeight(legendElem) + 2;
|
||||
let posX = getSvgElemX(colorRect);
|
||||
|
||||
d3.select(legendElem.get(0)).append("g")
|
||||
.attr("class", "axis")
|
||||
.attr("transform", "translate(" + posX + "," + posY + ")")
|
||||
d3.select(legendElem.get(0))
|
||||
.append('g')
|
||||
.attr('class', 'axis')
|
||||
.attr('transform', 'translate(' + posX + ',' + posY + ')')
|
||||
.call(xAxis);
|
||||
|
||||
legend.select(".axis").select(".domain").remove();
|
||||
legend
|
||||
.select('.axis')
|
||||
.select('.domain')
|
||||
.remove();
|
||||
}
|
||||
|
||||
function drawDiscreteLegendValues(elem, colorOptions, legendWidth) {
|
||||
@@ -257,10 +279,11 @@ function drawDiscreteLegendValues(elem, colorOptions, legendWidth) {
|
||||
}
|
||||
|
||||
let valuesNumber = thresholds.length;
|
||||
let rangeStep = Math.floor(legendWidth / valuesNumber);
|
||||
let rangeStep = Math.floor(legendWidth / valuesNumber);
|
||||
//let valuesRange = d3.range(0, legendWidth, rangeStep);
|
||||
|
||||
let legendValueScale = d3.scaleLinear()
|
||||
let legendValueScale = d3
|
||||
.scaleLinear()
|
||||
.domain([0, valuesNumber])
|
||||
.range([0, legendWidth]);
|
||||
|
||||
@@ -271,34 +294,39 @@ function drawDiscreteLegendValues(elem, colorOptions, legendWidth) {
|
||||
thresholdTooltips.push(thresholds[i].tooltip);
|
||||
}
|
||||
|
||||
let xAxis = d3.axisBottom(legendValueScale)
|
||||
let xAxis = d3
|
||||
.axisBottom(legendValueScale)
|
||||
.tickValues(d3.range(0, valuesNumber, 1)) //thresholdValues)
|
||||
.tickSize(2)
|
||||
.tickFormat((t) => {
|
||||
.tickFormat(t => {
|
||||
let i = Math.floor(t.valueOf());
|
||||
let v = thresholdTooltips[i];
|
||||
if (v != undefined) {
|
||||
return ""+v;
|
||||
if (v !== undefined) {
|
||||
return '' + v;
|
||||
} else {
|
||||
v = thresholdValues[i];
|
||||
if (v != undefined) {
|
||||
return ""+v;
|
||||
if (v !== undefined) {
|
||||
return '' + v;
|
||||
} else {
|
||||
return "n/a";
|
||||
return 'n/a';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let colorRect = legendElem.find(":first-child");
|
||||
let colorRect = legendElem.find(':first-child');
|
||||
let posY = getSvgElemHeight(legendElem) + 2;
|
||||
let posX = getSvgElemX(colorRect) + Math.floor(rangeStep/2);
|
||||
let posX = getSvgElemX(colorRect) + Math.floor(rangeStep / 2);
|
||||
|
||||
d3.select(legendElem.get(0)).append("g")
|
||||
.attr("class", "axis")
|
||||
.attr("transform", "translate(" + posX + "," + posY + ")")
|
||||
d3.select(legendElem.get(0))
|
||||
.append('g')
|
||||
.attr('class', 'axis')
|
||||
.attr('transform', 'translate(' + posX + ',' + posY + ')')
|
||||
.call(xAxis);
|
||||
|
||||
legend.select(".axis").select(".domain").remove();
|
||||
legend
|
||||
.select('.axis')
|
||||
.select('.domain')
|
||||
.remove();
|
||||
}
|
||||
|
||||
function drawSimpleColorLegend(elem, colorScale) {
|
||||
@@ -307,20 +335,22 @@ function drawSimpleColorLegend(elem, colorScale) {
|
||||
clearLegend(elem);
|
||||
|
||||
let legendWidth = Math.floor(legendElem.outerWidth());
|
||||
let legendHeight = legendElem.attr("height");
|
||||
let legendHeight = legendElem.attr('height');
|
||||
|
||||
if (legendWidth) {
|
||||
let valuesRange = d3.range(0, legendWidth, LEGEND_STEP_WIDTH);
|
||||
|
||||
legend.selectAll(".status-heatmap-color-legend-rect")
|
||||
legend
|
||||
.selectAll('.status-heatmap-color-legend-rect')
|
||||
.data(valuesRange)
|
||||
.enter().append("rect")
|
||||
.attr("x", d => d)
|
||||
.attr("y", 0)
|
||||
.attr("width", LEGEND_STEP_WIDTH + 1) // Overlap rectangles to prevent gaps
|
||||
.attr("height", legendHeight)
|
||||
.attr("stroke-width", 0)
|
||||
.attr("fill", d => colorScale(d));
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('x', d => d)
|
||||
.attr('y', 0)
|
||||
.attr('width', LEGEND_STEP_WIDTH + 1) // Overlap rectangles to prevent gaps
|
||||
.attr('height', legendHeight)
|
||||
.attr('stroke-width', 0)
|
||||
.attr('fill', d => colorScale(d));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,36 +360,40 @@ function drawSimpleOpacityLegend(elem, options) {
|
||||
clearLegend(elem);
|
||||
|
||||
let legendWidth = Math.floor(legendElem.outerWidth());
|
||||
let legendHeight = legendElem.attr("height");
|
||||
let legendHeight = legendElem.attr('height');
|
||||
|
||||
if (legendWidth) {
|
||||
let legendOpacityScale;
|
||||
if (options.colorScale === 'linear') {
|
||||
legendOpacityScale = d3.scaleLinear()
|
||||
.domain([0, legendWidth])
|
||||
.range([0, 1]);
|
||||
legendOpacityScale = d3
|
||||
.scaleLinear()
|
||||
.domain([0, legendWidth])
|
||||
.range([0, 1]);
|
||||
} else if (options.colorScale === 'sqrt') {
|
||||
legendOpacityScale = d3.scalePow().exponent(options.exponent)
|
||||
.domain([0, legendWidth])
|
||||
.range([0, 1]);
|
||||
legendOpacityScale = d3
|
||||
.scalePow()
|
||||
.exponent(options.exponent)
|
||||
.domain([0, legendWidth])
|
||||
.range([0, 1]);
|
||||
}
|
||||
|
||||
let valuesRange = d3.range(0, legendWidth, LEGEND_STEP_WIDTH);
|
||||
|
||||
legend.selectAll(".status-heatmap-opacity-legend-rect")
|
||||
legend
|
||||
.selectAll('.status-heatmap-opacity-legend-rect')
|
||||
.data(valuesRange)
|
||||
.enter().append("rect")
|
||||
.attr("x", d => d)
|
||||
.attr("y", 0)
|
||||
.attr("width", LEGEND_STEP_WIDTH+1)
|
||||
.attr("height", legendHeight)
|
||||
.attr("stroke-width", 0)
|
||||
.attr("fill", options.cardColor)
|
||||
.style("opacity", d => legendOpacityScale(d));
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('x', d => d)
|
||||
.attr('y', 0)
|
||||
.attr('width', LEGEND_STEP_WIDTH + 1)
|
||||
.attr('height', legendHeight)
|
||||
.attr('stroke-width', 0)
|
||||
.attr('fill', options.cardColor)
|
||||
.style('opacity', d => legendOpacityScale(d));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function clearLegend(elem) {
|
||||
let legendElem = $(elem).find('svg');
|
||||
legendElem.empty();
|
||||
@@ -367,8 +401,8 @@ function clearLegend(elem) {
|
||||
|
||||
function getColorScale(colorScheme, maxValue, minValue = 0) {
|
||||
let colorInterpolator = d3ScaleChromatic[colorScheme.value];
|
||||
let colorScaleInverted = colorScheme.invert === 'always' ||
|
||||
(colorScheme.invert === 'dark' && !contextSrv.user.lightTheme);
|
||||
let colorScaleInverted =
|
||||
colorScheme.invert === 'always' || (colorScheme.invert === 'dark' && !contextSrv.user.lightTheme);
|
||||
|
||||
let start = colorScaleInverted ? maxValue : minValue;
|
||||
let end = colorScaleInverted ? minValue : maxValue;
|
||||
@@ -379,13 +413,16 @@ function getColorScale(colorScheme, maxValue, minValue = 0) {
|
||||
function getOpacityScale(options, maxValue, minValue = 0) {
|
||||
let legendOpacityScale;
|
||||
if (options.colorScale === 'linear') {
|
||||
legendOpacityScale = d3.scaleLinear()
|
||||
.domain([minValue, maxValue])
|
||||
.range([0, 1]);
|
||||
legendOpacityScale = d3
|
||||
.scaleLinear()
|
||||
.domain([minValue, maxValue])
|
||||
.range([0, 1]);
|
||||
} else if (options.colorScale === 'sqrt') {
|
||||
legendOpacityScale = d3.scalePow().exponent(options.exponent)
|
||||
.domain([minValue, maxValue])
|
||||
.range([0, 1]);
|
||||
legendOpacityScale = d3
|
||||
.scalePow()
|
||||
.exponent(options.exponent)
|
||||
.domain([minValue, maxValue])
|
||||
.range([0, 1]);
|
||||
}
|
||||
return legendOpacityScale;
|
||||
}
|
||||
@@ -412,7 +449,7 @@ function buildLegendTicks(rangeFrom, rangeTo, maxValue, minValue) {
|
||||
let range = rangeTo - rangeFrom;
|
||||
let tickStepSize = tickStep(rangeFrom, rangeTo, 3);
|
||||
let ticksNum = Math.round(range / tickStepSize);
|
||||
let ticks:any = [];
|
||||
let ticks: any = [];
|
||||
|
||||
for (let i = 0; i < ticksNum; i++) {
|
||||
let current = tickStepSize * i + rangeFrom;
|
||||
|
||||
Reference in New Issue
Block a user