mirror of
https://github.com/timberjoegithub/grafana-statusmap.git
synced 2026-07-22 15:53:09 +00:00
status-heatmap-plugin for Grafana
- fixes for 5.1.3 - nulls as empty
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
.jshintrc
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
module.exports = (grunt) => {
|
||||||
|
require('load-grunt-tasks')(grunt);
|
||||||
|
|
||||||
|
grunt.loadNpmTasks('grunt-execute');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||||
|
|
||||||
|
grunt.initConfig({
|
||||||
|
|
||||||
|
clean: ['dist'],
|
||||||
|
|
||||||
|
copy: {
|
||||||
|
src_to_dist: {
|
||||||
|
cwd: 'src',
|
||||||
|
expand: true,
|
||||||
|
src: ['**/*', '!**/*.js', '!**/*.scss'],
|
||||||
|
dest: 'dist'
|
||||||
|
},
|
||||||
|
pluginDef: {
|
||||||
|
expand: true,
|
||||||
|
src: ['plugin.json', 'README.md'],
|
||||||
|
dest: 'dist'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
rebuild_all: {
|
||||||
|
files: ['src/**/*', 'plugin.json'],
|
||||||
|
tasks: ['default'],
|
||||||
|
options: {
|
||||||
|
spawn: false,
|
||||||
|
livereload: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
babel: {
|
||||||
|
options: {
|
||||||
|
sourceMap: true,
|
||||||
|
presets: ['es2015'],
|
||||||
|
plugins: ['transform-es2015-modules-systemjs', 'transform-es2015-for-of', 'transform-class-properties', 'transform-object-rest-spread'],
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
files: [{
|
||||||
|
cwd: 'src',
|
||||||
|
expand: true,
|
||||||
|
src: ['*.js', '**/*.js'],
|
||||||
|
dest: 'dist',
|
||||||
|
ext: '.js'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
sass: {
|
||||||
|
dist: {
|
||||||
|
files: {
|
||||||
|
'dist/css/status-heatmap.css': 'src/css/status-heatmap.scss'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel', 'sass']);
|
||||||
|
// grunt.registerTask('clean', ['clean']);
|
||||||
|
// grunt.registerTask('watch', ['watch']);
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "status-hetamap",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Status Heatmap panel plugin for grafana",
|
||||||
|
"main": "README.md",
|
||||||
|
"scripts": {
|
||||||
|
"build": "grunt"
|
||||||
|
},
|
||||||
|
"author": "Flant JSC",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/flant/grafana-status-heatmap.git"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel": "^6.23.0",
|
||||||
|
"babel-eslint": "^7.2.3",
|
||||||
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
|
"babel-plugin-transform-es2015-modules-systemjs": "^6.24.1",
|
||||||
|
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||||
|
"babel-preset-es2015": "^6.24.1",
|
||||||
|
"grunt": "^1.0.1",
|
||||||
|
"grunt-babel": "^6.0.0",
|
||||||
|
"grunt-contrib-clean": "^1.1.0",
|
||||||
|
"grunt-contrib-copy": "^1.0.0",
|
||||||
|
"grunt-contrib-sass": "^1.0.0",
|
||||||
|
"grunt-contrib-uglify": "^3.0.1",
|
||||||
|
"grunt-contrib-watch": "^1.0.0",
|
||||||
|
"grunt-execute": "^0.2.2",
|
||||||
|
"grunt-systemjs-builder": "^1.0.0",
|
||||||
|
"load-grunt-tasks": "^3.5.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"d3": "^4.11.0",
|
||||||
|
"d3-scale-chromatic": "^1.1.1",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"moment": "^2.18.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
import angular from 'angular';
|
||||||
|
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';
|
||||||
|
|
||||||
|
let mod = angular.module('grafana.directives');
|
||||||
|
|
||||||
|
const MIN_LEGEND_STEPS = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color legend for heatmap editor.
|
||||||
|
*/
|
||||||
|
mod.directive('optionsColorLegend', function() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
template: '<div class="status-heatmap-color-legend"><svg width="16.8rem" height="24px"></svg></div>',
|
||||||
|
link: function(scope, elem, attrs) {
|
||||||
|
let ctrl = scope.ctrl;
|
||||||
|
let panel = scope.ctrl.panel;
|
||||||
|
|
||||||
|
render();
|
||||||
|
|
||||||
|
ctrl.events.on('render', function() {
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
let legendElem = $(elem).find('svg');
|
||||||
|
let legendWidth = Math.floor(legendElem.outerWidth());
|
||||||
|
|
||||||
|
if (panel.color.mode === 'spectrum') {
|
||||||
|
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||||
|
let colorScale = getColorScale(colorScheme, legendWidth);
|
||||||
|
drawSimpleColorLegend(elem, colorScale);
|
||||||
|
} else if (panel.color.mode === 'opacity') {
|
||||||
|
let colorOptions = panel.color;
|
||||||
|
drawSimpleOpacityLegend(elem, colorOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heatmap legend with scale values.
|
||||||
|
*/
|
||||||
|
mod.directive('statusHeatmapLegend', function() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
template: '<div class="status-heatmap-color-legend"><svg width="100px" height="6px"></svg></div>',
|
||||||
|
link: function(scope, elem, attrs) {
|
||||||
|
let ctrl = scope.ctrl;
|
||||||
|
let panel = scope.ctrl.panel;
|
||||||
|
|
||||||
|
render();
|
||||||
|
ctrl.events.on('render', function() {
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
clearLegend(elem);
|
||||||
|
if (!_.isEmpty(ctrl.cardsData) && !_.isEmpty(ctrl.cardsData.cards)) {
|
||||||
|
let rangeFrom = ctrl.cardsData.minValue;
|
||||||
|
let rangeTo = ctrl.cardsData.maxValue;
|
||||||
|
let maxValue = panel.color.max || rangeTo;
|
||||||
|
let minValue = panel.color.min || rangeFrom;
|
||||||
|
|
||||||
|
if (panel.color.mode === 'spectrum') {
|
||||||
|
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;
|
||||||
|
drawOpacityLegend(elem, colorOptions, rangeFrom, rangeTo, maxValue, minValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minValue) {
|
||||||
|
let legendElem = $(elem).find('svg');
|
||||||
|
let legend = d3.select(legendElem.get(0));
|
||||||
|
clearLegend(elem);
|
||||||
|
|
||||||
|
let legendWidth = Math.floor(legendElem.outerWidth()) - 30;
|
||||||
|
let legendHeight = legendElem.attr("height");
|
||||||
|
|
||||||
|
let rangeStep = 1;
|
||||||
|
if (rangeTo - rangeFrom > legendWidth) {
|
||||||
|
rangeStep = Math.floor((rangeTo - rangeFrom) / legendWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rangeStep * MIN_LEGEND_STEPS > rangeTo) {
|
||||||
|
rangeStep = rangeTo / MIN_LEGEND_STEPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
.data(valuesRange)
|
||||||
|
.enter().append("rect")
|
||||||
|
.attr("x", d => d * widthFactor)
|
||||||
|
.attr("y", 0)
|
||||||
|
.attr("width", rangeStep * widthFactor + 1) // Overlap rectangles to prevent gaps
|
||||||
|
.attr("height", legendHeight)
|
||||||
|
.attr("stroke-width", 0)
|
||||||
|
.attr("fill", d => colorScale(d));
|
||||||
|
|
||||||
|
drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue) {
|
||||||
|
let legendElem = $(elem).find('svg');
|
||||||
|
let legend = d3.select(legendElem.get(0));
|
||||||
|
clearLegend(elem);
|
||||||
|
|
||||||
|
let legendWidth = Math.floor(legendElem.outerWidth()) - 30;
|
||||||
|
let legendHeight = legendElem.attr("height");
|
||||||
|
|
||||||
|
let rangeStep = 10;
|
||||||
|
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
||||||
|
|
||||||
|
if (rangeStep * MIN_LEGEND_STEPS > rangeTo) {
|
||||||
|
rangeStep = rangeTo / MIN_LEGEND_STEPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
||||||
|
|
||||||
|
let opacityScale = getOpacityScale(options, maxValue, minValue);
|
||||||
|
legend.selectAll(".status-heatmap-opacity-legend-rect")
|
||||||
|
.data(valuesRange)
|
||||||
|
.enter().append("rect")
|
||||||
|
.attr("x", d => d * widthFactor)
|
||||||
|
.attr("y", 0)
|
||||||
|
.attr("width", rangeStep * widthFactor)
|
||||||
|
.attr("height", legendHeight)
|
||||||
|
.attr("stroke-width", 0)
|
||||||
|
.attr("fill", options.cardColor)
|
||||||
|
.style("opacity", d => opacityScale(d));
|
||||||
|
|
||||||
|
drawLegendValues(elem, opacityScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth) {
|
||||||
|
let legendElem = $(elem).find('svg');
|
||||||
|
let legend = d3.select(legendElem.get(0));
|
||||||
|
|
||||||
|
if (legendWidth <= 0 || legendElem.get(0).childNodes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let legendValueScale = d3.scaleLinear()
|
||||||
|
.domain([0, rangeTo])
|
||||||
|
.range([0, legendWidth]);
|
||||||
|
|
||||||
|
let ticks = buildLegendTicks(0, rangeTo, maxValue, minValue);
|
||||||
|
let xAxis = d3.axisBottom(legendValueScale)
|
||||||
|
.tickValues(ticks)
|
||||||
|
.tickSize(2);
|
||||||
|
|
||||||
|
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 + ")")
|
||||||
|
.call(xAxis);
|
||||||
|
|
||||||
|
legend.select(".axis").select(".domain").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSimpleColorLegend(elem, colorScale) {
|
||||||
|
let legendElem = $(elem).find('svg');
|
||||||
|
clearLegend(elem);
|
||||||
|
|
||||||
|
let legendWidth = Math.floor(legendElem.outerWidth());
|
||||||
|
let legendHeight = legendElem.attr("height");
|
||||||
|
|
||||||
|
if (legendWidth) {
|
||||||
|
let valuesNumber = Math.floor(legendWidth / 2);
|
||||||
|
let rangeStep = Math.floor(legendWidth / valuesNumber);
|
||||||
|
let valuesRange = d3.range(0, legendWidth, rangeStep);
|
||||||
|
|
||||||
|
let legend = d3.select(legendElem.get(0));
|
||||||
|
var legendRects = legend.selectAll(".status-heatmap-color-legend-rect").data(valuesRange);
|
||||||
|
|
||||||
|
legendRects.enter().append("rect")
|
||||||
|
.attr("x", d => d)
|
||||||
|
.attr("y", 0)
|
||||||
|
.attr("width", rangeStep + 1) // Overlap rectangles to prevent gaps
|
||||||
|
.attr("height", legendHeight)
|
||||||
|
.attr("stroke-width", 0)
|
||||||
|
.attr("fill", d => colorScale(d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSimpleOpacityLegend(elem, options) {
|
||||||
|
let legendElem = $(elem).find('svg');
|
||||||
|
clearLegend(elem);
|
||||||
|
|
||||||
|
let legend = d3.select(legendElem.get(0));
|
||||||
|
let legendWidth = Math.floor(legendElem.outerWidth());
|
||||||
|
let legendHeight = legendElem.attr("height");
|
||||||
|
|
||||||
|
if (legendWidth) {
|
||||||
|
let legendOpacityScale;
|
||||||
|
if (options.colorScale === 'linear') {
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rangeStep = 10;
|
||||||
|
let valuesRange = d3.range(0, legendWidth, rangeStep);
|
||||||
|
var legendRects = legend.selectAll(".status-heatmap-opacity-legend-rect").data(valuesRange);
|
||||||
|
|
||||||
|
legendRects.enter().append("rect")
|
||||||
|
.attr("x", d => d)
|
||||||
|
.attr("y", 0)
|
||||||
|
.attr("width", rangeStep)
|
||||||
|
.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColorScale(colorScheme, maxValue, minValue = 0) {
|
||||||
|
let colorInterpolator = d3ScaleChromatic[colorScheme.value];
|
||||||
|
let colorScaleInverted = colorScheme.invert === 'always' ||
|
||||||
|
(colorScheme.invert === 'dark' && !contextSrv.user.lightTheme);
|
||||||
|
|
||||||
|
let start = colorScaleInverted ? maxValue : minValue;
|
||||||
|
let end = colorScaleInverted ? minValue : maxValue;
|
||||||
|
|
||||||
|
return d3.scaleSequential(colorInterpolator).domain([start, end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOpacityScale(options, maxValue, minValue = 0) {
|
||||||
|
let legendOpacityScale;
|
||||||
|
if (options.colorScale === 'linear') {
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
return legendOpacityScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSvgElemX(elem) {
|
||||||
|
let svgElem = elem.get(0);
|
||||||
|
if (svgElem && svgElem.x && svgElem.x.baseVal) {
|
||||||
|
return svgElem.x.baseVal.value;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSvgElemHeight(elem) {
|
||||||
|
let svgElem = elem.get(0);
|
||||||
|
if (svgElem && svgElem.height && svgElem.height.baseVal) {
|
||||||
|
return svgElem.height.baseVal.value;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLegendTicks(rangeFrom, rangeTo, maxValue, minValue) {
|
||||||
|
let range = rangeTo - rangeFrom;
|
||||||
|
let tickStepSize = tickStep(rangeFrom, rangeTo, 3);
|
||||||
|
let ticksNum = Math.round(range / tickStepSize);
|
||||||
|
let ticks = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < ticksNum; i++) {
|
||||||
|
let current = tickStepSize * i;
|
||||||
|
// Add user-defined min and max if it had been set
|
||||||
|
if (isValueCloseTo(minValue, current, tickStepSize)) {
|
||||||
|
ticks.push(minValue);
|
||||||
|
continue;
|
||||||
|
} else if (minValue < current) {
|
||||||
|
ticks.push(minValue);
|
||||||
|
}
|
||||||
|
if (isValueCloseTo(maxValue, current, tickStepSize)) {
|
||||||
|
ticks.push(maxValue);
|
||||||
|
continue;
|
||||||
|
} else if (maxValue < current) {
|
||||||
|
ticks.push(maxValue);
|
||||||
|
}
|
||||||
|
ticks.push(tickStepSize * i);
|
||||||
|
}
|
||||||
|
if (!isValueCloseTo(maxValue, rangeTo, tickStepSize)) {
|
||||||
|
ticks.push(maxValue);
|
||||||
|
}
|
||||||
|
ticks.push(rangeTo);
|
||||||
|
ticks = _.sortBy(_.uniq(ticks));
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValueCloseTo(val, valueTo, step) {
|
||||||
|
let diff = Math.abs(val - valueTo);
|
||||||
|
return diff < step * 0.3;
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
$font-size-sm: 12px !default;
|
||||||
|
|
||||||
|
|
||||||
|
.status-heatmap-canvas-wrapper {
|
||||||
|
// position: relative;
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-heatmap-panel {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.axis .tick {
|
||||||
|
text {
|
||||||
|
fill: #52545c;
|
||||||
|
color: #52545c;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
line {
|
||||||
|
opacity: 0.4;
|
||||||
|
stroke: #767980;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This hack prevents mouseenter/mouseleave events get fired too often
|
||||||
|
svg {
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
rect {
|
||||||
|
pointer-events: visiblePainted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-heatmap-tooltip {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: $font-size-sm;
|
||||||
|
background-color: #dde4ed;
|
||||||
|
color: #52545c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-heatmap-histogram rect {
|
||||||
|
fill: #767980;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-heatmap-crosshair {
|
||||||
|
line {
|
||||||
|
stroke: darken(red,15%);
|
||||||
|
stroke-width: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-heatmap-selection {
|
||||||
|
stroke-width: 1;
|
||||||
|
fill: rgba(102, 102, 102, 0.4);
|
||||||
|
stroke: rgba(102, 102, 102, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-heatmap-legend-wrapper {
|
||||||
|
// @include clearfix();
|
||||||
|
margin: 0 10px;
|
||||||
|
//padding-top: 10px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
height: 24px;
|
||||||
|
float: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-heatmap-legend-values {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.axis .tick {
|
||||||
|
text {
|
||||||
|
fill: #52545c;
|
||||||
|
color: #52545c;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
line {
|
||||||
|
opacity: 0.4;
|
||||||
|
stroke: #767980;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain {
|
||||||
|
opacity: 0.4;
|
||||||
|
stroke: #767980;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path d="M-0.2-0.2v100.4h100.4V-0.2H-0.2z M13.7,1.4H24v10H13.7V1.4z M13.7,13.9H24v10H13.7V13.9z M13.7,26.3H24v10H13.7V26.3z
|
||||||
|
M13.7,38.8H24v10H13.7V38.8z M11.7,98.5H1.3v-10h10.4V98.5z M11.7,86.1H1.3v-10h10.4V86.1z M11.7,73.6H1.3v-10h10.4V73.6z
|
||||||
|
M11.7,61.2H1.3v-10h10.4V61.2z M11.7,48.8H1.3v-10h10.4V48.8z M11.7,36.3H1.3v-10h10.4V36.3z M11.7,23.9H1.3v-10h10.4V23.9z
|
||||||
|
M11.7,11.4H1.3v-10h10.4V11.4z M24,98.5H13.7v-10H24V98.5z M24,86.1H13.7v-10H24V86.1z M24,73.6H13.7v-10H24V73.6z M24.1,61.2
|
||||||
|
H13.8v-10h10.4V61.2z M36.5,98.5H26.1v-10h10.4V98.5z M36.5,86.1H26.1v-10h10.4V86.1z M36.5,73.6H26.1v-10h10.4V73.6z M36.5,61.2
|
||||||
|
H26.1v-10h10.4V61.2z M36.5,48.8H26.1v-10h10.4V48.8z M36.5,36.3H26.1v-10h10.4V36.3z M36.5,23.9H26.1v-10h10.4V23.9z M36.5,11.4
|
||||||
|
H26.1v-10h10.4V11.4z M48.9,98.5H38.5v-10h10.4V98.5z M48.9,86.1H38.5v-10h10.4V86.1z M48.9,73.6H38.5v-10h10.4V73.6z M48.9,61.2
|
||||||
|
H38.5v-10h10.4V61.2z M48.9,48.8H38.5v-10h10.4V48.8z M48.9,36.3H38.5v-10h10.4V36.3z M48.9,23.9H38.5v-10h10.4V23.9z M48.9,11.4
|
||||||
|
H38.5v-10h10.4V11.4z M50.9,26.3h10.4v10H50.9V26.3z M51,63.6h10.4v10H51V63.6z M61.3,98.5H51v-10h10.4V98.5z M61.3,86.1H51v-10
|
||||||
|
h10.4V86.1z M61.3,61.2H51v-10h10.4V61.2z M61.3,48.8H51v-10h10.4V48.8z M61.3,23.9H51v-10h10.4V23.9z M61.3,11.4H51v-10h10.4V11.4
|
||||||
|
z M73.8,98.5H63.4v-10h10.4V98.5z M73.8,86.1H63.4v-10h10.4V86.1z M73.8,73.6H63.4v-10h10.4V73.6z M73.8,61.2H63.4v-10h10.4V61.2z
|
||||||
|
M73.8,48.8H63.4v-10h10.4V48.8z M73.8,36.3H63.4v-10h10.4V36.3z M73.8,23.9H63.4v-10h10.4V23.9z M73.8,11.4H63.4v-10h10.4V11.4z
|
||||||
|
M86.2,98.5H75.8v-10h10.4V98.5z M86.2,86.1H75.8v-10h10.4V86.1z M86.2,73.6H75.8v-10h10.4V73.6z M86.2,61.2H75.8v-10h10.4V61.2z
|
||||||
|
M86.2,48.8H75.8v-10h10.4V48.8z M86.2,36.3H75.8v-10h10.4V36.3z M86.2,23.9H75.8v-10h10.4V23.9z M86.2,11.4H75.8v-10h10.4V11.4z
|
||||||
|
M98.6,98.5H88.3v-10h10.4V98.5z M98.6,86.1H88.3v-10h10.4V86.1z M98.6,73.6H88.3v-10h10.4V73.6z M98.6,61.2H88.3v-10h10.4V61.2z
|
||||||
|
M98.6,48.8H88.3v-10h10.4V48.8z M98.6,36.3H88.3v-10h10.4V36.3z M98.6,23.9H88.3v-10h10.4V23.9z M98.6,11.4H88.3v-10h10.4V11.4z"
|
||||||
|
/>
|
||||||
|
<rect x="26.1" y="1.4" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="26.1" y="13.9" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="26.1" y="26.3" fill="#B2DBB9" width="10.4" height="10"/>
|
||||||
|
<rect x="26.1" y="38.8" fill="#B2DBB9" width="10.4" height="10"/>
|
||||||
|
<rect x="26.1" y="88.5" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="26.1" y="51.2" fill="#E3F1DC" width="10.4" height="10"/>
|
||||||
|
<rect x="13.8" y="51.2" fill="#E3F1DC" width="10.4" height="10"/>
|
||||||
|
<rect x="26.1" y="63.6" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="26.1" y="76.1" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="1.3" y="1.4" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="1.3" y="13.9" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="1.3" y="26.3" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="1.3" y="38.8" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="1.3" y="88.5" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="1.3" y="51.2" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="1.3" y="63.6" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="1.3" y="76.1" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="13.7" y="1.4" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="13.7" y="13.9" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="13.7" y="26.3" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="13.7" y="38.8" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="13.7" y="88.5" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="13.7" y="63.6" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="13.7" y="76.1" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="38.5" y="1.4" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="38.5" y="13.9" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="38.5" y="26.3" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="38.5" y="38.8" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="38.5" y="88.5" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="38.5" y="51.2" fill="#B2DBB9" width="10.4" height="10"/>
|
||||||
|
<rect x="38.5" y="63.6" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="38.5" y="76.1" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="51" y="1.4" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="51" y="13.9" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="51" y="38.8" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="51" y="88.5" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="51" y="51.2" fill="#E3F1DC" width="10.4" height="10"/>
|
||||||
|
<rect x="51" y="76.1" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="63.4" y="1.4" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="63.4" y="13.9" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="63.4" y="26.3" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="50.9" y="26.3" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="63.4" y="38.8" fill="#B2DBB9" width="10.4" height="10"/>
|
||||||
|
<rect x="63.4" y="88.5" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="63.4" y="51.2" fill="#E3F1DC" width="10.4" height="10"/>
|
||||||
|
<rect x="63.4" y="63.6" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="63.4" y="76.1" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="75.8" y="1.4" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="75.8" y="13.9" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="75.8" y="26.3" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="75.8" y="38.8" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="75.8" y="88.5" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="75.8" y="51.2" fill="#E3F1DC" width="10.4" height="10"/>
|
||||||
|
<rect x="51" y="63.6" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="75.8" y="63.6" fill="#60C0CB" width="10.4" height="10"/>
|
||||||
|
<rect x="75.8" y="76.1" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="88.3" y="13.9" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="88.3" y="26.3" fill="#39A4CB" width="10.4" height="10"/>
|
||||||
|
<rect x="88.3" y="38.8" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="88.3" y="88.5" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="88.3" y="51.2" fill="#8CD0BC" width="10.4" height="10"/>
|
||||||
|
<rect x="88.3" y="63.6" fill="#1A83BD" width="10.4" height="10"/>
|
||||||
|
<rect x="88.3" y="76.1" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
<rect x="88.3" y="1.4" fill="#0062A7" width="10.4" height="10"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
|
export default colors("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666");
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
|
export default colors("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666");
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
|
export default colors("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928");
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
|
export default colors("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2");
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
|
export default colors("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc");
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
|
export default colors("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999");
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
|
export default colors("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3");
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
|
export default colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f");
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
export default function(specifier) {
|
||||||
|
var n = specifier.length / 6 | 0, colors = new Array(n), i = 0;
|
||||||
|
while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"d8b365f5f5f55ab4ac",
|
||||||
|
"a6611adfc27d80cdc1018571",
|
||||||
|
"a6611adfc27df5f5f580cdc1018571",
|
||||||
|
"8c510ad8b365f6e8c3c7eae55ab4ac01665e",
|
||||||
|
"8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e",
|
||||||
|
"8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e",
|
||||||
|
"8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e",
|
||||||
|
"5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30",
|
||||||
|
"5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"91cf60ffffbffc8d59",
|
||||||
|
"1a9641a6d96afdae61d7191c",
|
||||||
|
"1a9641a6d96affffbffdae61d7191c",
|
||||||
|
"1a985091cf60d9ef8bfee08bfc8d59d73027",
|
||||||
|
"1a985091cf60d9ef8bffffbffee08bfc8d59d73027",
|
||||||
|
"1a985066bd63a6d96ad9ef8bfee08bfdae61f46d43d73027",
|
||||||
|
"1a985066bd63a6d96ad9ef8bffffbffee08bfdae61f46d43d73027",
|
||||||
|
"0068371a985066bd63a6d96ad9ef8bfee08bfdae61f46d43d73027a50026",
|
||||||
|
"0068371a985066bd63a6d96ad9ef8bffffbffee08bfdae61f46d43d73027a50026"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"af8dc3f7f7f77fbf7b",
|
||||||
|
"7b3294c2a5cfa6dba0008837",
|
||||||
|
"7b3294c2a5cff7f7f7a6dba0008837",
|
||||||
|
"762a83af8dc3e7d4e8d9f0d37fbf7b1b7837",
|
||||||
|
"762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837",
|
||||||
|
"762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837",
|
||||||
|
"762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837",
|
||||||
|
"40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b",
|
||||||
|
"40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"e9a3c9f7f7f7a1d76a",
|
||||||
|
"d01c8bf1b6dab8e1864dac26",
|
||||||
|
"d01c8bf1b6daf7f7f7b8e1864dac26",
|
||||||
|
"c51b7de9a3c9fde0efe6f5d0a1d76a4d9221",
|
||||||
|
"c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221",
|
||||||
|
"c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221",
|
||||||
|
"c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221",
|
||||||
|
"8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419",
|
||||||
|
"8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"998ec3f7f7f7f1a340",
|
||||||
|
"5e3c99b2abd2fdb863e66101",
|
||||||
|
"5e3c99b2abd2f7f7f7fdb863e66101",
|
||||||
|
"542788998ec3d8daebfee0b6f1a340b35806",
|
||||||
|
"542788998ec3d8daebf7f7f7fee0b6f1a340b35806",
|
||||||
|
"5427888073acb2abd2d8daebfee0b6fdb863e08214b35806",
|
||||||
|
"5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806",
|
||||||
|
"2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08",
|
||||||
|
"2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"ef8a62f7f7f767a9cf",
|
||||||
|
"ca0020f4a58292c5de0571b0",
|
||||||
|
"ca0020f4a582f7f7f792c5de0571b0",
|
||||||
|
"b2182bef8a62fddbc7d1e5f067a9cf2166ac",
|
||||||
|
"b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac",
|
||||||
|
"b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac",
|
||||||
|
"b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac",
|
||||||
|
"67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061",
|
||||||
|
"67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"ef8a62ffffff999999",
|
||||||
|
"ca0020f4a582bababa404040",
|
||||||
|
"ca0020f4a582ffffffbababa404040",
|
||||||
|
"b2182bef8a62fddbc7e0e0e09999994d4d4d",
|
||||||
|
"b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d",
|
||||||
|
"b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d",
|
||||||
|
"b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d",
|
||||||
|
"67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a",
|
||||||
|
"67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"fc8d59ffffbf91bfdb",
|
||||||
|
"d7191cfdae61abd9e92c7bb6",
|
||||||
|
"d7191cfdae61ffffbfabd9e92c7bb6",
|
||||||
|
"d73027fc8d59fee090e0f3f891bfdb4575b4",
|
||||||
|
"d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4",
|
||||||
|
"d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4",
|
||||||
|
"d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4",
|
||||||
|
"a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695",
|
||||||
|
"a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"fc8d59ffffbf91cf60",
|
||||||
|
"d7191cfdae61a6d96a1a9641",
|
||||||
|
"d7191cfdae61ffffbfa6d96a1a9641",
|
||||||
|
"d73027fc8d59fee08bd9ef8b91cf601a9850",
|
||||||
|
"d73027fc8d59fee08bffffbfd9ef8b91cf601a9850",
|
||||||
|
"d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850",
|
||||||
|
"d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850",
|
||||||
|
"a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837",
|
||||||
|
"a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"fc8d59ffffbf99d594",
|
||||||
|
"d7191cfdae61abdda42b83ba",
|
||||||
|
"d7191cfdae61ffffbfabdda42b83ba",
|
||||||
|
"d53e4ffc8d59fee08be6f59899d5943288bd",
|
||||||
|
"d53e4ffc8d59fee08bffffbfe6f59899d5943288bd",
|
||||||
|
"d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd",
|
||||||
|
"d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd",
|
||||||
|
"9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2",
|
||||||
|
"9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
Vendored
+36
@@ -0,0 +1,36 @@
|
|||||||
|
export {default as schemeAccent} from "./categorical/Accent";
|
||||||
|
export {default as schemeDark2} from "./categorical/Dark2";
|
||||||
|
export {default as schemePaired} from "./categorical/Paired";
|
||||||
|
export {default as schemePastel1} from "./categorical/Pastel1";
|
||||||
|
export {default as schemePastel2} from "./categorical/Pastel2";
|
||||||
|
export {default as schemeSet1} from "./categorical/Set1";
|
||||||
|
export {default as schemeSet2} from "./categorical/Set2";
|
||||||
|
export {default as schemeSet3} from "./categorical/Set3";
|
||||||
|
export {default as interpolateBrBG, scheme as schemeBrBG} from "./diverging/BrBG";
|
||||||
|
export {default as interpolatePRGn, scheme as schemePRGn} from "./diverging/PRGn";
|
||||||
|
export {default as interpolatePiYG, scheme as schemePiYG} from "./diverging/PiYG";
|
||||||
|
export {default as interpolatePuOr, scheme as schemePuOr} from "./diverging/PuOr";
|
||||||
|
export {default as interpolateRdBu, scheme as schemeRdBu} from "./diverging/RdBu";
|
||||||
|
export {default as interpolateRdGy, scheme as schemeRdGy} from "./diverging/RdGy";
|
||||||
|
export {default as interpolateRdYlBu, scheme as schemeRdYlBu} from "./diverging/RdYlBu";
|
||||||
|
export {default as interpolateRdYlGn, scheme as schemeRdYlGn} from "./diverging/RdYlGn";
|
||||||
|
export {default as interpolateGnYlRd, scheme as schemeGnYlRd} from "./diverging/GnYlRd";
|
||||||
|
export {default as interpolateSpectral, scheme as schemeSpectral} from "./diverging/Spectral";
|
||||||
|
export {default as interpolateBuGn, scheme as schemeBuGn} from "./sequential-multi/BuGn";
|
||||||
|
export {default as interpolateBuPu, scheme as schemeBuPu} from "./sequential-multi/BuPu";
|
||||||
|
export {default as interpolateGnBu, scheme as schemeGnBu} from "./sequential-multi/GnBu";
|
||||||
|
export {default as interpolateOrRd, scheme as schemeOrRd} from "./sequential-multi/OrRd";
|
||||||
|
export {default as interpolatePuBuGn, scheme as schemePuBuGn} from "./sequential-multi/PuBuGn";
|
||||||
|
export {default as interpolatePuBu, scheme as schemePuBu} from "./sequential-multi/PuBu";
|
||||||
|
export {default as interpolatePuRd, scheme as schemePuRd} from "./sequential-multi/PuRd";
|
||||||
|
export {default as interpolateRdPu, scheme as schemeRdPu} from "./sequential-multi/RdPu";
|
||||||
|
export {default as interpolateYlGnBu, scheme as schemeYlGnBu} from "./sequential-multi/YlGnBu";
|
||||||
|
export {default as interpolateYlGn, scheme as schemeYlGn} from "./sequential-multi/YlGn";
|
||||||
|
export {default as interpolateYlOrBr, scheme as schemeYlOrBr} from "./sequential-multi/YlOrBr";
|
||||||
|
export {default as interpolateYlOrRd, scheme as schemeYlOrRd} from "./sequential-multi/YlOrRd";
|
||||||
|
export {default as interpolateBlues, scheme as schemeBlues} from "./sequential-single/Blues";
|
||||||
|
export {default as interpolateGreens, scheme as schemeGreens} from "./sequential-single/Greens";
|
||||||
|
export {default as interpolateGreys, scheme as schemeGreys} from "./sequential-single/Greys";
|
||||||
|
export {default as interpolatePurples, scheme as schemePurples} from "./sequential-single/Purples";
|
||||||
|
export {default as interpolateReds, scheme as schemeReds} from "./sequential-single/Reds";
|
||||||
|
export {default as interpolateOranges, scheme as schemeOranges} from "./sequential-single/Oranges";
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
import d3 from 'd3';
|
||||||
|
|
||||||
|
export default function(scheme) {
|
||||||
|
return d3.interpolateRgbBasis(scheme[scheme.length - 1]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"e5f5f999d8c92ca25f",
|
||||||
|
"edf8fbb2e2e266c2a4238b45",
|
||||||
|
"edf8fbb2e2e266c2a42ca25f006d2c",
|
||||||
|
"edf8fbccece699d8c966c2a42ca25f006d2c",
|
||||||
|
"edf8fbccece699d8c966c2a441ae76238b45005824",
|
||||||
|
"f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824",
|
||||||
|
"f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"e0ecf49ebcda8856a7",
|
||||||
|
"edf8fbb3cde38c96c688419d",
|
||||||
|
"edf8fbb3cde38c96c68856a7810f7c",
|
||||||
|
"edf8fbbfd3e69ebcda8c96c68856a7810f7c",
|
||||||
|
"edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b",
|
||||||
|
"f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b",
|
||||||
|
"f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"e0f3dba8ddb543a2ca",
|
||||||
|
"f0f9e8bae4bc7bccc42b8cbe",
|
||||||
|
"f0f9e8bae4bc7bccc443a2ca0868ac",
|
||||||
|
"f0f9e8ccebc5a8ddb57bccc443a2ca0868ac",
|
||||||
|
"f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e",
|
||||||
|
"f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e",
|
||||||
|
"f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"fee8c8fdbb84e34a33",
|
||||||
|
"fef0d9fdcc8afc8d59d7301f",
|
||||||
|
"fef0d9fdcc8afc8d59e34a33b30000",
|
||||||
|
"fef0d9fdd49efdbb84fc8d59e34a33b30000",
|
||||||
|
"fef0d9fdd49efdbb84fc8d59ef6548d7301f990000",
|
||||||
|
"fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000",
|
||||||
|
"fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"ece7f2a6bddb2b8cbe",
|
||||||
|
"f1eef6bdc9e174a9cf0570b0",
|
||||||
|
"f1eef6bdc9e174a9cf2b8cbe045a8d",
|
||||||
|
"f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d",
|
||||||
|
"f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b",
|
||||||
|
"fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b",
|
||||||
|
"fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"ece2f0a6bddb1c9099",
|
||||||
|
"f6eff7bdc9e167a9cf02818a",
|
||||||
|
"f6eff7bdc9e167a9cf1c9099016c59",
|
||||||
|
"f6eff7d0d1e6a6bddb67a9cf1c9099016c59",
|
||||||
|
"f6eff7d0d1e6a6bddb67a9cf3690c002818a016450",
|
||||||
|
"fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450",
|
||||||
|
"fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"e7e1efc994c7dd1c77",
|
||||||
|
"f1eef6d7b5d8df65b0ce1256",
|
||||||
|
"f1eef6d7b5d8df65b0dd1c77980043",
|
||||||
|
"f1eef6d4b9dac994c7df65b0dd1c77980043",
|
||||||
|
"f1eef6d4b9dac994c7df65b0e7298ace125691003f",
|
||||||
|
"f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f",
|
||||||
|
"f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"fde0ddfa9fb5c51b8a",
|
||||||
|
"feebe2fbb4b9f768a1ae017e",
|
||||||
|
"feebe2fbb4b9f768a1c51b8a7a0177",
|
||||||
|
"feebe2fcc5c0fa9fb5f768a1c51b8a7a0177",
|
||||||
|
"feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177",
|
||||||
|
"fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177",
|
||||||
|
"fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"f7fcb9addd8e31a354",
|
||||||
|
"ffffccc2e69978c679238443",
|
||||||
|
"ffffccc2e69978c67931a354006837",
|
||||||
|
"ffffccd9f0a3addd8e78c67931a354006837",
|
||||||
|
"ffffccd9f0a3addd8e78c67941ab5d238443005a32",
|
||||||
|
"ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32",
|
||||||
|
"ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"edf8b17fcdbb2c7fb8",
|
||||||
|
"ffffcca1dab441b6c4225ea8",
|
||||||
|
"ffffcca1dab441b6c42c7fb8253494",
|
||||||
|
"ffffccc7e9b47fcdbb41b6c42c7fb8253494",
|
||||||
|
"ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84",
|
||||||
|
"ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84",
|
||||||
|
"ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"fff7bcfec44fd95f0e",
|
||||||
|
"ffffd4fed98efe9929cc4c02",
|
||||||
|
"ffffd4fed98efe9929d95f0e993404",
|
||||||
|
"ffffd4fee391fec44ffe9929d95f0e993404",
|
||||||
|
"ffffd4fee391fec44ffe9929ec7014cc4c028c2d04",
|
||||||
|
"ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04",
|
||||||
|
"ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"ffeda0feb24cf03b20",
|
||||||
|
"ffffb2fecc5cfd8d3ce31a1c",
|
||||||
|
"ffffb2fecc5cfd8d3cf03b20bd0026",
|
||||||
|
"ffffb2fed976feb24cfd8d3cf03b20bd0026",
|
||||||
|
"ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026",
|
||||||
|
"ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026",
|
||||||
|
"ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"deebf79ecae13182bd",
|
||||||
|
"eff3ffbdd7e76baed62171b5",
|
||||||
|
"eff3ffbdd7e76baed63182bd08519c",
|
||||||
|
"eff3ffc6dbef9ecae16baed63182bd08519c",
|
||||||
|
"eff3ffc6dbef9ecae16baed64292c62171b5084594",
|
||||||
|
"f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594",
|
||||||
|
"f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"e5f5e0a1d99b31a354",
|
||||||
|
"edf8e9bae4b374c476238b45",
|
||||||
|
"edf8e9bae4b374c47631a354006d2c",
|
||||||
|
"edf8e9c7e9c0a1d99b74c47631a354006d2c",
|
||||||
|
"edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32",
|
||||||
|
"f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32",
|
||||||
|
"f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"f0f0f0bdbdbd636363",
|
||||||
|
"f7f7f7cccccc969696525252",
|
||||||
|
"f7f7f7cccccc969696636363252525",
|
||||||
|
"f7f7f7d9d9d9bdbdbd969696636363252525",
|
||||||
|
"f7f7f7d9d9d9bdbdbd969696737373525252252525",
|
||||||
|
"fffffff0f0f0d9d9d9bdbdbd969696737373525252252525",
|
||||||
|
"fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"fee6cefdae6be6550d",
|
||||||
|
"feeddefdbe85fd8d3cd94701",
|
||||||
|
"feeddefdbe85fd8d3ce6550da63603",
|
||||||
|
"feeddefdd0a2fdae6bfd8d3ce6550da63603",
|
||||||
|
"feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04",
|
||||||
|
"fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04",
|
||||||
|
"fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"efedf5bcbddc756bb1",
|
||||||
|
"f2f0f7cbc9e29e9ac86a51a3",
|
||||||
|
"f2f0f7cbc9e29e9ac8756bb154278f",
|
||||||
|
"f2f0f7dadaebbcbddc9e9ac8756bb154278f",
|
||||||
|
"f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486",
|
||||||
|
"fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486",
|
||||||
|
"fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import colors from "../colors";
|
||||||
|
import ramp from "../ramp";
|
||||||
|
|
||||||
|
export var scheme = new Array(3).concat(
|
||||||
|
"fee0d2fc9272de2d26",
|
||||||
|
"fee5d9fcae91fb6a4acb181d",
|
||||||
|
"fee5d9fcae91fb6a4ade2d26a50f15",
|
||||||
|
"fee5d9fcbba1fc9272fb6a4ade2d26a50f15",
|
||||||
|
"fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d",
|
||||||
|
"fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d",
|
||||||
|
"fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d"
|
||||||
|
).map(colors);
|
||||||
|
|
||||||
|
export default ramp(scheme);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<div class="status-heatmap-wrapper">
|
||||||
|
<div class="status-heatmap-canvas-wrapper">
|
||||||
|
|
||||||
|
<div class="datapoints-warning" ng-if="ctrl.dataWarning">
|
||||||
|
<span class="small" bs-tooltip="ctrl.dataWarning.tip">{{ctrl.dataWarning.title}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status-heatmap-panel" ng-dblclick="ctrl.zoomOut()"></div>
|
||||||
|
</div>
|
||||||
|
<div class="status-heatmap-legend-wrapper" ng-if="ctrl.panel.legend.show">
|
||||||
|
<status-heatmap-legend></status-heatmap-legend>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import './color_legend';
|
||||||
|
import { StatusHeatmapCtrl } from './status_heatmap_ctrl';
|
||||||
|
|
||||||
|
export {
|
||||||
|
StatusHeatmapCtrl as PanelCtrl
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
|
||||||
|
export class StatusHeatmapOptionsEditorCtrl {
|
||||||
|
constructor($scope) {
|
||||||
|
$scope.editor = this;
|
||||||
|
this.panelCtrl = $scope.ctrl;
|
||||||
|
this.panel = this.panelCtrl.panel;
|
||||||
|
this.unitFormats = kbn.getUnitFormats();
|
||||||
|
|
||||||
|
this.panelCtrl.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
setUnitFormat(subItem) {
|
||||||
|
this.panel.data.unitFormat = subItem.value;
|
||||||
|
this.panelCtrl.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function statusHeatmapOptionsEditor() {
|
||||||
|
'use strict';
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: true,
|
||||||
|
templateUrl: 'public/plugins/status-heatmap-panel/partials/options_editor.html',
|
||||||
|
controller: StatusHeatmapOptionsEditorCtrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<div class="editor-row">
|
||||||
|
<div class="section gf-form-group">
|
||||||
|
<h5 class="section-heading">Colors</h5>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-9">Mode</label>
|
||||||
|
<div class="gf-form-select-wrapper width-8">
|
||||||
|
<select class="input-small gf-form-input" ng-model="ctrl.panel.color.mode" ng-options="s for s in ctrl.colorModes" ng-change="ctrl.render()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="ctrl.panel.color.mode === 'opacity'">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-9">Color</label>
|
||||||
|
<span class="gf-form-label">
|
||||||
|
<color-picker color="ctrl.panel.color.cardColor" onChange="ctrl.onCardColorChange"></color-picker>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-9">Scale</label>
|
||||||
|
<div class="gf-form-select-wrapper width-8">
|
||||||
|
<select class="input-small gf-form-input" ng-model="ctrl.panel.color.colorScale" ng-options="s for s in ctrl.opacityScales" ng-change="ctrl.render()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form" ng-if="ctrl.panel.color.colorScale === 'sqrt'">
|
||||||
|
<label class="gf-form-label width-9">Exponent</label>
|
||||||
|
<input type="number" class="gf-form-input width-8" placeholder="auto" data-placement="right" bs-tooltip="''" ng-model="ctrl.panel.color.exponent" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="ctrl.panel.color.mode === 'spectrum'">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-9">Scheme</label>
|
||||||
|
<div class="gf-form-select-wrapper width-8">
|
||||||
|
<select class="input-small gf-form-input" ng-model="ctrl.panel.color.colorScheme" ng-options="s.value as s.name for s in ctrl.colorSchemes" ng-change="ctrl.render()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<options-color-legend></options-color-legend>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section gf-form-group">
|
||||||
|
<h5 class="section-heading">Color scale</h5>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-8">Min</label>
|
||||||
|
<input type="number" ng-model="ctrl.panel.color.min" class="gf-form-input width-5" placeholder="auto" data-placement="right" bs-tooltip="''" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-8">Max</label>
|
||||||
|
<input type="number" ng-model="ctrl.panel.color.max" class="gf-form-input width-5" placeholder="auto" data-placement="right" bs-tooltip="''" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section gf-form-group">
|
||||||
|
<h5 class="section-heading">Legend</h5>
|
||||||
|
<gf-form-switch class="gf-form" label-class="width-8"
|
||||||
|
label="Show legend"
|
||||||
|
checked="ctrl.panel.legend.show" on-change="ctrl.render()">
|
||||||
|
</gf-form-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section gf-form-group">
|
||||||
|
<h5 class="section-heading">Buckets</h5>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-8">Space</label>
|
||||||
|
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right" bs-tooltip="''" ng-model="ctrl.panel.cards.cardPadding" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-8">Round</label>
|
||||||
|
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right" bs-tooltip="''" ng-model="ctrl.panel.cards.cardRound" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section gf-form-group">
|
||||||
|
<h5 class="section-heading">Tooltip</h5>
|
||||||
|
<gf-form-switch class="gf-form" label-class="width-8"
|
||||||
|
label="Show tooltip"
|
||||||
|
checked="ctrl.panel.tooltip.show" on-change="ctrl.render()">
|
||||||
|
</gf-form-switch>
|
||||||
|
<div ng-if="ctrl.panel.tooltip.show">
|
||||||
|
<gf-form-switch class="gf-form" label-class="width-8"
|
||||||
|
label="Histogram"
|
||||||
|
checked="ctrl.panel.tooltip.showHistogram" on-change="ctrl.render()">
|
||||||
|
</gf-form-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section gf-form-group">
|
||||||
|
<h5 class="section-heading">Null value</h5>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-7">Null value</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input max-width-9" ng-model="ctrl.panel.nullPointMode" ng-options="f for f in ['null', 'null as zero']" ng-change="ctrl.render()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"type": "panel",
|
||||||
|
"name": "Status Heatmap",
|
||||||
|
"id": "status-heatmap-panel",
|
||||||
|
|
||||||
|
"info": {
|
||||||
|
"description": "Status Heatmap panel plugin for grafana",
|
||||||
|
"author": {
|
||||||
|
"name": "Flant JSC",
|
||||||
|
"url": "http://flant.com"
|
||||||
|
},
|
||||||
|
"keywords": ["status", "heatmap", "panel"],
|
||||||
|
"version": "0.0.1",
|
||||||
|
"updated": "2017-11-01",
|
||||||
|
"logos": {
|
||||||
|
"small": "img/logo.svg",
|
||||||
|
"large": "img/logo.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"grafanaVersion": "5.1.x",
|
||||||
|
"plugins": [ ]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,620 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import moment from 'moment';
|
||||||
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
import {appEvents, contextSrv} from 'app/core/core';
|
||||||
|
import {tickStep, getScaledDecimals, getFlotTickSize} from 'app/core/utils/ticks';
|
||||||
|
import d3 from 'd3';
|
||||||
|
import * as d3ScaleChromatic from './libs/d3-scale-chromatic/index';
|
||||||
|
import {StatusHeatmapTooltip} from './tooltip';
|
||||||
|
|
||||||
|
let MIN_CARD_SIZE = 1,
|
||||||
|
CARD_PADDING = 1,
|
||||||
|
CARD_ROUND = 0,
|
||||||
|
DATA_RANGE_WIDING_FACTOR = 1.2,
|
||||||
|
DEFAULT_X_TICK_SIZE_PX = 100,
|
||||||
|
DEFAULT_Y_TICK_SIZE_PX = 50,
|
||||||
|
X_AXIS_TICK_PADDING = 10,
|
||||||
|
Y_AXIS_TICK_PADDING = 5,
|
||||||
|
MIN_SELECTION_WIDTH = 2;
|
||||||
|
|
||||||
|
export default function link(scope, elem, attrs, ctrl) {
|
||||||
|
let data, cardsData, timeRange, panel, heatmap;
|
||||||
|
|
||||||
|
// $heatmap is JQuery object, but heatmap is D3
|
||||||
|
let $heatmap = elem.find('.status-heatmap-panel');
|
||||||
|
let tooltip = new StatusHeatmapTooltip($heatmap, scope);
|
||||||
|
|
||||||
|
let width, height,
|
||||||
|
yScale, xScale,
|
||||||
|
chartWidth, chartHeight,
|
||||||
|
chartTop, chartBottom,
|
||||||
|
yAxisWidth, xAxisHeight,
|
||||||
|
cardPadding, cardRound,
|
||||||
|
cardWidth, cardHeight,
|
||||||
|
colorScale, opacityScale,
|
||||||
|
mouseUpHandler;
|
||||||
|
|
||||||
|
let yOffset = 0;
|
||||||
|
|
||||||
|
let selection = {
|
||||||
|
active: false,
|
||||||
|
x1: -1,
|
||||||
|
x2: -1
|
||||||
|
};
|
||||||
|
|
||||||
|
let padding = {left: 0, right: 0, top: 0, bottom: 0},
|
||||||
|
margin = {left: 25, right: 15, top: 10, bottom: 20},
|
||||||
|
dataRangeWidingFactor = DATA_RANGE_WIDING_FACTOR;
|
||||||
|
|
||||||
|
ctrl.events.on('render', () => {
|
||||||
|
render();
|
||||||
|
// ctrl.renderingCompleted();
|
||||||
|
});
|
||||||
|
|
||||||
|
function setElementHeight() {
|
||||||
|
try {
|
||||||
|
var height = ctrl.height || panel.height || ctrl.row.height;
|
||||||
|
if (_.isString(height)) {
|
||||||
|
height = parseInt(height.replace('px', ''), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
height -= panel.legend.show ? 28 : 11; // bottom padding and space for legend
|
||||||
|
|
||||||
|
$heatmap.css('height', height + 'px');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) { // IE throws errors sometimes
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getYAxisWidth(elem) {
|
||||||
|
let axis_text = elem.selectAll(".axis-y text").nodes();
|
||||||
|
let max_text_width = _.max(_.map(axis_text, text => {
|
||||||
|
// Use SVG getBBox method
|
||||||
|
return text.getBBox().width;
|
||||||
|
}));
|
||||||
|
|
||||||
|
return max_text_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getXAxisHeight(elem) {
|
||||||
|
let axis_line = elem.select(".axis-x line");
|
||||||
|
if (!axis_line.empty()) {
|
||||||
|
let axis_line_position = parseFloat(elem.select(".axis-x line").attr("y2"));
|
||||||
|
let canvas_width = parseFloat(elem.attr("height"));
|
||||||
|
return canvas_width - axis_line_position;
|
||||||
|
} else {
|
||||||
|
// Default height
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addXAxis() {
|
||||||
|
scope.xScale = xScale = d3.scaleTime()
|
||||||
|
.domain([timeRange.from, timeRange.to])
|
||||||
|
.range([0, chartWidth]);
|
||||||
|
|
||||||
|
let ticks = chartWidth / DEFAULT_X_TICK_SIZE_PX;
|
||||||
|
let grafanaTimeFormatter = grafanaTimeFormat(ticks, timeRange.from, timeRange.to);
|
||||||
|
let timeFormat;
|
||||||
|
let dashboardTimeZone = ctrl.dashboard.getTimezone();
|
||||||
|
if (dashboardTimeZone === 'utc') {
|
||||||
|
timeFormat = d3.utcFormat(grafanaTimeFormatter);
|
||||||
|
} else {
|
||||||
|
timeFormat = d3.timeFormat(grafanaTimeFormatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
let xAxis = d3.axisBottom(xScale)
|
||||||
|
.ticks(ticks)
|
||||||
|
.tickFormat(timeFormat)
|
||||||
|
.tickPadding(X_AXIS_TICK_PADDING)
|
||||||
|
.tickSize(chartHeight);
|
||||||
|
|
||||||
|
let posY = chartTop;
|
||||||
|
let posX = yAxisWidth;
|
||||||
|
|
||||||
|
heatmap.append("g")
|
||||||
|
.attr("class", "axis axis-x")
|
||||||
|
.attr("transform", "translate(" + posX + "," + posY + ")")
|
||||||
|
.call(xAxis);
|
||||||
|
|
||||||
|
// Remove horizontal line in the top of axis labels (called domain in d3)
|
||||||
|
heatmap.select(".axis-x").select(".domain").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addYAxis() {
|
||||||
|
let ticks = _.map(data, d => d.target);
|
||||||
|
|
||||||
|
// Set default Y min and max if no data
|
||||||
|
if (_.isEmpty(data)) {
|
||||||
|
ticks = [''];
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = [];
|
||||||
|
let step = chartHeight / ticks.length;
|
||||||
|
range.push(chartHeight - yOffset);
|
||||||
|
for (let i = 1; i < ticks.length; i++) {
|
||||||
|
range.push(chartHeight - step * i - yOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('yRange', range, yOffset);
|
||||||
|
|
||||||
|
scope.yScale = yScale = d3.scaleOrdinal()
|
||||||
|
.domain(ticks)
|
||||||
|
.range(range);
|
||||||
|
|
||||||
|
let yAxis = d3.axisLeft(yScale)
|
||||||
|
.tickValues(ticks)
|
||||||
|
.tickSizeInner(0 - width)
|
||||||
|
.tickPadding(Y_AXIS_TICK_PADDING);
|
||||||
|
|
||||||
|
heatmap.append("g")
|
||||||
|
.attr("class", "axis axis-y")
|
||||||
|
.call(yAxis);
|
||||||
|
|
||||||
|
// Calculate Y axis width first, then move axis into visible area
|
||||||
|
let posY = margin.top;
|
||||||
|
let posX = getYAxisWidth(heatmap) + Y_AXIS_TICK_PADDING;
|
||||||
|
heatmap.select(".axis-y").attr("transform", "translate(" + posX + "," + posY + ")");
|
||||||
|
|
||||||
|
// Remove vertical line in the right of axis labels (called domain in d3)
|
||||||
|
heatmap.select(".axis-y").select(".domain").remove();
|
||||||
|
heatmap.select(".axis-y").selectAll(".tick line").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wide Y values range and anjust to bucket size
|
||||||
|
function wideYAxisRange(min, max, tickInterval) {
|
||||||
|
let y_widing = (max * (dataRangeWidingFactor - 1) - min * (dataRangeWidingFactor - 1)) / 2;
|
||||||
|
let y_min, y_max;
|
||||||
|
|
||||||
|
if (tickInterval === 0) {
|
||||||
|
y_max = max * dataRangeWidingFactor;
|
||||||
|
y_min = min - min * (dataRangeWidingFactor - 1);
|
||||||
|
tickInterval = (y_max - y_min) / 2;
|
||||||
|
} else {
|
||||||
|
y_max = Math.ceil((max + y_widing) / tickInterval) * tickInterval;
|
||||||
|
y_min = Math.floor((min - y_widing) / tickInterval) * tickInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't wide axis below 0 if all values are positive
|
||||||
|
if (min >= 0 && y_min < 0) {
|
||||||
|
y_min = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {y_min, y_max};
|
||||||
|
}
|
||||||
|
|
||||||
|
function tickValueFormatter(decimals, scaledDecimals = null) {
|
||||||
|
let format = panel.yAxis.format;
|
||||||
|
return function(value) {
|
||||||
|
return kbn.valueFormats[format](value, decimals, scaledDecimals);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixYAxisTickSize() {
|
||||||
|
heatmap.select(".axis-y")
|
||||||
|
.selectAll(".tick line")
|
||||||
|
.attr("x2", chartWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAxes() {
|
||||||
|
addYAxis();
|
||||||
|
|
||||||
|
yAxisWidth = getYAxisWidth(heatmap) + Y_AXIS_TICK_PADDING;
|
||||||
|
chartWidth = width - yAxisWidth - margin.right;
|
||||||
|
// fixYAxisTickSize();
|
||||||
|
//
|
||||||
|
addXAxis();
|
||||||
|
xAxisHeight = getXAxisHeight(heatmap);
|
||||||
|
|
||||||
|
if (!panel.yAxis.show) {
|
||||||
|
heatmap.select(".axis-y").selectAll("line").style("opacity", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!panel.xAxis.show) {
|
||||||
|
heatmap.select(".axis-x").selectAll("line").style("opacity", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHeatmapCanvas() {
|
||||||
|
let heatmap_elem = $heatmap[0];
|
||||||
|
|
||||||
|
width = Math.floor($heatmap.width()) - padding.right;
|
||||||
|
height = Math.floor($heatmap.height()) - padding.bottom;
|
||||||
|
|
||||||
|
chartHeight = height - margin.top - margin.bottom - yOffset;
|
||||||
|
chartTop = margin.top;
|
||||||
|
chartBottom = chartTop + chartHeight;
|
||||||
|
|
||||||
|
cardPadding = panel.cards.cardPadding !== null ? panel.cards.cardPadding : CARD_PADDING;
|
||||||
|
cardRound = panel.cards.cardRound !== null ? panel.cards.cardRound : CARD_ROUND;
|
||||||
|
|
||||||
|
if (heatmap) {
|
||||||
|
heatmap.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
heatmap = d3.select(heatmap_elem)
|
||||||
|
.append("svg")
|
||||||
|
.attr("width", width)
|
||||||
|
.attr("height", height);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHeatmap() {
|
||||||
|
addHeatmapCanvas();
|
||||||
|
|
||||||
|
let maxValue = panel.color.max || cardsData.maxValue;
|
||||||
|
let minValue = panel.color.min || cardsData.minValue;
|
||||||
|
|
||||||
|
colorScale = getColorScale(maxValue, minValue);
|
||||||
|
setOpacityScale(maxValue);
|
||||||
|
setCardSize();
|
||||||
|
|
||||||
|
addAxes();
|
||||||
|
|
||||||
|
let cards = heatmap.selectAll(".status-heatmap-card").data(cardsData.cards);
|
||||||
|
cards.append("title");
|
||||||
|
cards = cards.enter().append("rect")
|
||||||
|
.attr("value", c => c.value)
|
||||||
|
.attr("xVal", c => c.x)
|
||||||
|
.attr("x", getCardX)
|
||||||
|
.attr("width", getCardWidth)
|
||||||
|
.attr("yVal", c => c.y)
|
||||||
|
.attr("y", getCardY)
|
||||||
|
.attr("height", getCardHeight)
|
||||||
|
.attr("rx", cardRound)
|
||||||
|
.attr("ry", cardRound)
|
||||||
|
.attr("class", "bordered status-heatmap-card")
|
||||||
|
.style("fill", getCardColor)
|
||||||
|
.style("stroke", getCardColor)
|
||||||
|
.style("stroke-width", 0)
|
||||||
|
.style("opacity", getCardOpacity);
|
||||||
|
|
||||||
|
let $cards = $heatmap.find(".status-heatmap-card");
|
||||||
|
$cards.on("mouseenter", (event) => {
|
||||||
|
tooltip.mouseOverBucket = true;
|
||||||
|
highlightCard(event);
|
||||||
|
let current_card = d3.select(event.target);
|
||||||
|
tooltip.show(event, current_card.attr('xVal'), current_card.attr('yVal'), current_card.attr('value'));
|
||||||
|
})
|
||||||
|
.on("mouseleave", (event) => {
|
||||||
|
tooltip.mouseOverBucket = false;
|
||||||
|
resetCardHighLight(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightCard(event) {
|
||||||
|
let color = d3.select(event.target).style("fill");
|
||||||
|
let highlightColor = d3.color(color).darker(2);
|
||||||
|
let strokeColor = d3.color(color).brighter(4);
|
||||||
|
let current_card = d3.select(event.target);
|
||||||
|
tooltip.originalFillColor = color;
|
||||||
|
current_card.style("fill", highlightColor)
|
||||||
|
.style("stroke", strokeColor)
|
||||||
|
.style("stroke-width", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetCardHighLight(event) {
|
||||||
|
d3.select(event.target).style("fill", tooltip.originalFillColor)
|
||||||
|
.style("stroke", tooltip.originalFillColor)
|
||||||
|
.style("stroke-width", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColorScale(maxValue, minValue = 0) {
|
||||||
|
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||||
|
let colorInterpolator = d3ScaleChromatic[colorScheme.value];
|
||||||
|
let colorScaleInverted = colorScheme.invert === 'always' ||
|
||||||
|
(colorScheme.invert === 'dark' && !contextSrv.user.lightTheme);
|
||||||
|
|
||||||
|
if (maxValue == minValue)
|
||||||
|
maxValue = minValue + 1;
|
||||||
|
|
||||||
|
let start = colorScaleInverted ? maxValue : minValue;
|
||||||
|
let end = colorScaleInverted ? minValue : maxValue;
|
||||||
|
|
||||||
|
return d3.scaleSequential(colorInterpolator).domain([start, end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOpacityScale(maxValue) {
|
||||||
|
if (panel.color.colorScale === 'linear') {
|
||||||
|
opacityScale = d3.scaleLinear()
|
||||||
|
.domain([0, maxValue])
|
||||||
|
.range([0, 1]);
|
||||||
|
} else if (panel.color.colorScale === 'sqrt') {
|
||||||
|
opacityScale = d3.scalePow().exponent(panel.color.exponent)
|
||||||
|
.domain([0, maxValue])
|
||||||
|
.range([0, 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCardSize() {
|
||||||
|
let xGridSize = Math.floor(chartWidth / cardsData.xBucketSize);
|
||||||
|
let yGridSize = Math.floor(chartHeight / cardsData.yBucketSize);
|
||||||
|
|
||||||
|
cardWidth = xGridSize - cardPadding * 2;
|
||||||
|
cardHeight = yGridSize ? yGridSize - cardPadding * 2 : 0;
|
||||||
|
|
||||||
|
yOffset = cardHeight / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCardX(d) {
|
||||||
|
let x;
|
||||||
|
if (xScale(d.x) < 0) {
|
||||||
|
// Cut card left to prevent overlay
|
||||||
|
x = yAxisWidth + cardPadding;
|
||||||
|
} else {
|
||||||
|
x = xScale(d.x) + yAxisWidth + cardPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCardWidth(d) {
|
||||||
|
let w;
|
||||||
|
if (xScale(d.x) < 0) {
|
||||||
|
// Cut card left to prevent overlay
|
||||||
|
let cutted_width = xScale(d.x) + cardWidth;
|
||||||
|
w = cutted_width > 0 ? cutted_width : 0;
|
||||||
|
} else if (xScale(d.x) + cardWidth > chartWidth) {
|
||||||
|
// Cut card right to prevent overlay
|
||||||
|
w = chartWidth - xScale(d.x) - cardPadding;
|
||||||
|
} else {
|
||||||
|
w = cardWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card width should be MIN_CARD_SIZE at least
|
||||||
|
w = Math.max(w, MIN_CARD_SIZE);
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCardY(d) {
|
||||||
|
let y = yScale(d.y);
|
||||||
|
|
||||||
|
y = y + chartTop - cardHeight - cardPadding + yOffset;
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCardHeight(d) {
|
||||||
|
let y = yScale(d.y) + chartTop - cardHeight - cardPadding;
|
||||||
|
let h = cardHeight;
|
||||||
|
|
||||||
|
// Cut card height to prevent overlay
|
||||||
|
// if (y < chartTop) {
|
||||||
|
// h = yScale(d.y) - cardPadding;
|
||||||
|
// } else if (yScale(d.y) > chartBottom) {
|
||||||
|
// h = chartBottom - y;
|
||||||
|
// } else if (y + cardHeight > chartBottom) {
|
||||||
|
// h = chartBottom - y;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Height can't be more than chart height
|
||||||
|
h = Math.min(h, chartHeight);
|
||||||
|
// Card height should be MIN_CARD_SIZE at least
|
||||||
|
h = Math.max(h, MIN_CARD_SIZE);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCardColor(d) {
|
||||||
|
if (panel.color.mode === 'opacity') {
|
||||||
|
return panel.color.cardColor;
|
||||||
|
} else {
|
||||||
|
return colorScale(d.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCardOpacity(d) {
|
||||||
|
if (panel.nullPointMode === 'null' && d.value == null ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (panel.color.mode === 'opacity') {
|
||||||
|
return opacityScale(d.value);
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Selection and crosshair //
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
// Shared crosshair and tooltip
|
||||||
|
appEvents.on('graph-hover', event => {
|
||||||
|
drawSharedCrosshair(event.pos);
|
||||||
|
}, scope);
|
||||||
|
|
||||||
|
appEvents.on('graph-hover-clear', () => {
|
||||||
|
clearCrosshair();
|
||||||
|
}, scope);
|
||||||
|
|
||||||
|
function onMouseDown(event) {
|
||||||
|
selection.active = true;
|
||||||
|
selection.x1 = event.offsetX;
|
||||||
|
|
||||||
|
mouseUpHandler = function() {
|
||||||
|
onMouseUp();
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).one("mouseup", mouseUpHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseUp() {
|
||||||
|
$(document).unbind("mouseup", mouseUpHandler);
|
||||||
|
mouseUpHandler = null;
|
||||||
|
selection.active = false;
|
||||||
|
|
||||||
|
let selectionRange = Math.abs(selection.x2 - selection.x1);
|
||||||
|
if (selection.x2 >= 0 && selectionRange > MIN_SELECTION_WIDTH) {
|
||||||
|
let timeFrom = xScale.invert(Math.min(selection.x1, selection.x2) - yAxisWidth);
|
||||||
|
let timeTo = xScale.invert(Math.max(selection.x1, selection.x2) - yAxisWidth);
|
||||||
|
|
||||||
|
ctrl.timeSrv.setTime({
|
||||||
|
from: moment.utc(timeFrom),
|
||||||
|
to: moment.utc(timeTo)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseLeave() {
|
||||||
|
appEvents.emit('graph-hover-clear');
|
||||||
|
clearCrosshair();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseMove(event) {
|
||||||
|
if (!heatmap) { return; }
|
||||||
|
|
||||||
|
if (selection.active) {
|
||||||
|
// Clear crosshair and tooltip
|
||||||
|
clearCrosshair();
|
||||||
|
tooltip.destroy();
|
||||||
|
|
||||||
|
selection.x2 = limitSelection(event.offsetX);
|
||||||
|
drawSelection(selection.x1, selection.x2);
|
||||||
|
} else {
|
||||||
|
emitGraphHoverEvet(event);
|
||||||
|
drawCrosshair(event.offsetX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitGraphHoverEvet(event) {
|
||||||
|
let x = xScale.invert(event.offsetX - yAxisWidth).valueOf();
|
||||||
|
let y = yScale(event.offsetY);
|
||||||
|
let pos = {
|
||||||
|
pageX: event.pageX,
|
||||||
|
pageY: event.pageY,
|
||||||
|
x: x, x1: x,
|
||||||
|
y: y, y1: y,
|
||||||
|
panelRelY: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set minimum offset to prevent showing legend from another panel
|
||||||
|
pos.panelRelY = Math.max(event.offsetY / height, 0.001);
|
||||||
|
|
||||||
|
// broadcast to other graph panels that we are hovering
|
||||||
|
appEvents.emit('graph-hover', {pos: pos, panel: panel});
|
||||||
|
}
|
||||||
|
|
||||||
|
function limitSelection(x2) {
|
||||||
|
x2 = Math.max(x2, yAxisWidth);
|
||||||
|
x2 = Math.min(x2, chartWidth + yAxisWidth);
|
||||||
|
return x2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSelection(posX1, posX2) {
|
||||||
|
if (heatmap) {
|
||||||
|
heatmap.selectAll(".status-heatmap-selection").remove();
|
||||||
|
let selectionX = Math.min(posX1, posX2);
|
||||||
|
let selectionWidth = Math.abs(posX1 - posX2);
|
||||||
|
|
||||||
|
if (selectionWidth > MIN_SELECTION_WIDTH) {
|
||||||
|
heatmap.append("rect")
|
||||||
|
.attr("class", "status-heatmap-selection")
|
||||||
|
.attr("x", selectionX)
|
||||||
|
.attr("width", selectionWidth)
|
||||||
|
.attr("y", chartTop)
|
||||||
|
.attr("height", chartHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSelection() {
|
||||||
|
selection.x1 = -1;
|
||||||
|
selection.x2 = -1;
|
||||||
|
|
||||||
|
if (heatmap) {
|
||||||
|
heatmap.selectAll(".status-heatmap-selection").remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCrosshair(position) {
|
||||||
|
if (heatmap) {
|
||||||
|
heatmap.selectAll(".status-heatmap-crosshair").remove();
|
||||||
|
|
||||||
|
let posX = position;
|
||||||
|
posX = Math.max(posX, yAxisWidth);
|
||||||
|
posX = Math.min(posX, chartWidth + yAxisWidth);
|
||||||
|
|
||||||
|
heatmap.append("g")
|
||||||
|
.attr("class", "status-heatmap-crosshair")
|
||||||
|
.attr("transform", "translate(" + posX + ",0)")
|
||||||
|
.append("line")
|
||||||
|
.attr("x1", 1)
|
||||||
|
.attr("y1", chartTop)
|
||||||
|
.attr("x2", 1)
|
||||||
|
.attr("y2", chartBottom)
|
||||||
|
.attr("stroke-width", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSharedCrosshair(pos) {
|
||||||
|
if (heatmap && ctrl.dashboard.graphTooltip !== 0) {
|
||||||
|
let posX = xScale(pos.x) + yAxisWidth;
|
||||||
|
drawCrosshair(posX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCrosshair() {
|
||||||
|
if (heatmap) {
|
||||||
|
heatmap.selectAll(".status-heatmap-crosshair").remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
data = ctrl.data;
|
||||||
|
panel = ctrl.panel;
|
||||||
|
timeRange = ctrl.range;
|
||||||
|
cardsData = ctrl.cardsData;
|
||||||
|
|
||||||
|
if (!setElementHeight() || !data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw default axes and return if no data
|
||||||
|
if (_.isEmpty(cardsData.cards)) {
|
||||||
|
addHeatmapCanvas();
|
||||||
|
addAxes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addHeatmap();
|
||||||
|
scope.yAxisWidth = yAxisWidth;
|
||||||
|
scope.xAxisHeight = xAxisHeight;
|
||||||
|
scope.chartHeight = chartHeight;
|
||||||
|
scope.chartWidth = chartWidth;
|
||||||
|
scope.chartTop = chartTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register selection listeners
|
||||||
|
$heatmap.on("mousedown", onMouseDown);
|
||||||
|
$heatmap.on("mousemove", onMouseMove);
|
||||||
|
$heatmap.on("mouseleave", onMouseLeave);
|
||||||
|
}
|
||||||
|
|
||||||
|
function grafanaTimeFormat(ticks, min, max) {
|
||||||
|
if (min && max && ticks) {
|
||||||
|
let range = max - min;
|
||||||
|
let secPerTick = (range/ticks) / 1000;
|
||||||
|
let oneDay = 86400000;
|
||||||
|
let oneYear = 31536000000;
|
||||||
|
|
||||||
|
if (secPerTick <= 45) {
|
||||||
|
return "%H:%M:%S";
|
||||||
|
}
|
||||||
|
if (secPerTick <= 7200 || range <= oneDay) {
|
||||||
|
return "%H:%M";
|
||||||
|
}
|
||||||
|
if (secPerTick <= 80000) {
|
||||||
|
return "%m/%d %H:%M";
|
||||||
|
}
|
||||||
|
if (secPerTick <= 2419200 || range <= oneYear) {
|
||||||
|
return "%m/%d";
|
||||||
|
}
|
||||||
|
return "%Y-%m";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "%H:%M";
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
import { MetricsPanelCtrl } from 'app/plugins/sdk';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { contextSrv } from 'app/core/core';
|
||||||
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
|
||||||
|
import rendering from './rendering';
|
||||||
|
// import aggregates, { aggregatesMap } from './aggregates';
|
||||||
|
// import fragments, { fragmentsMap } from './fragments';
|
||||||
|
// import { labelFormats } from './xAxisLabelFormats';
|
||||||
|
// import canvasRendering from './canvas/rendering';
|
||||||
|
import {statusHeatmapOptionsEditor} from './options_editor';
|
||||||
|
import './css/status-heatmap.css!';
|
||||||
|
|
||||||
|
const CANVAS = 'CANVAS';
|
||||||
|
const SVG = 'SVG';
|
||||||
|
const VALUE_INDEX = 0,
|
||||||
|
TIME_INDEX = 1;
|
||||||
|
|
||||||
|
const panelDefaults = {
|
||||||
|
// aggregate: aggregates.AVG,
|
||||||
|
// fragment: fragments.HOUR,
|
||||||
|
color: {
|
||||||
|
mode: 'spectrum',
|
||||||
|
cardColor: '#b4ff00',
|
||||||
|
colorScale: 'sqrt',
|
||||||
|
exponent: 0.5,
|
||||||
|
colorScheme: 'interpolateGnYlRd'
|
||||||
|
},
|
||||||
|
cards: {
|
||||||
|
cardPadding: null,
|
||||||
|
cardRound: null
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
show: true,
|
||||||
|
showWeekends: true,
|
||||||
|
minBucketWidthToShowWeekends: 4,
|
||||||
|
showCrosshair: true,
|
||||||
|
labelFormat: '%a %m/%d'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
show: true,
|
||||||
|
showCrosshair: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
unitFormat: 'short',
|
||||||
|
decimals: null
|
||||||
|
},
|
||||||
|
// how null points should be handled
|
||||||
|
nullPointMode: 'null',
|
||||||
|
highlightCards: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderer = CANVAS;
|
||||||
|
|
||||||
|
const colorSchemes = [
|
||||||
|
// Diverging
|
||||||
|
{ name: 'Spectral', value: 'interpolateSpectral', invert: 'always' },
|
||||||
|
{ name: 'RdYlGn', value: 'interpolateRdYlGn', invert: 'always' },
|
||||||
|
{ name: 'GnYlRd', value: 'interpolateGnYlRd', invert: 'always' },
|
||||||
|
|
||||||
|
// Sequential (Single Hue)
|
||||||
|
{ name: 'Blues', value: 'interpolateBlues', invert: 'dark' },
|
||||||
|
{ name: 'Greens', value: 'interpolateGreens', invert: 'dark' },
|
||||||
|
{ name: 'Greys', value: 'interpolateGreys', invert: 'dark' },
|
||||||
|
{ name: 'Oranges', value: 'interpolateOranges', invert: 'dark' },
|
||||||
|
{ name: 'Purples', value: 'interpolatePurples', invert: 'dark' },
|
||||||
|
{ name: 'Reds', value: 'interpolateReds', invert: 'dark' },
|
||||||
|
|
||||||
|
// Sequential (Multi-Hue)
|
||||||
|
{ name: 'BuGn', value: 'interpolateBuGn', invert: 'dark' },
|
||||||
|
{ name: 'BuPu', value: 'interpolateBuPu', invert: 'dark' },
|
||||||
|
{ name: 'GnBu', value: 'interpolateGnBu', invert: 'dark' },
|
||||||
|
{ name: 'OrRd', value: 'interpolateOrRd', invert: 'dark' },
|
||||||
|
{ name: 'PuBuGn', value: 'interpolatePuBuGn', invert: 'dark' },
|
||||||
|
{ name: 'PuBu', value: 'interpolatePuBu', invert: 'dark' },
|
||||||
|
{ name: 'PuRd', value: 'interpolatePuRd', invert: 'dark' },
|
||||||
|
{ name: 'RdPu', value: 'interpolateRdPu', invert: 'dark' },
|
||||||
|
{ name: 'YlGnBu', value: 'interpolateYlGnBu', invert: 'dark' },
|
||||||
|
{ name: 'YlGn', value: 'interpolateYlGn', invert: 'dark' },
|
||||||
|
{ name: 'YlOrBr', value: 'interpolateYlOrBr', invert: 'dark' },
|
||||||
|
{ name: 'YlOrRd', value: 'interpolateYlOrRd', invert: 'darm' }
|
||||||
|
];
|
||||||
|
|
||||||
|
let colorModes = ['opacity', 'spectrum'];
|
||||||
|
let opacityScales = ['linear', 'sqrt'];
|
||||||
|
|
||||||
|
export class StatusHeatmapCtrl extends MetricsPanelCtrl {
|
||||||
|
static templateUrl = 'module.html';
|
||||||
|
|
||||||
|
constructor($scope, $injector, $rootScope, timeSrv) {
|
||||||
|
super($scope, $injector);
|
||||||
|
_.defaultsDeep(this.panel, panelDefaults);
|
||||||
|
|
||||||
|
this.opacityScales = opacityScales;
|
||||||
|
this.colorModes = colorModes;
|
||||||
|
this.colorSchemes = colorSchemes;
|
||||||
|
|
||||||
|
this.events.on('data-received', this.onDataReceived);
|
||||||
|
this.events.on('data-snapshot-load', this.onDataReceived);
|
||||||
|
this.events.on('data-error', this.onDataError);
|
||||||
|
this.events.on('init-edit-mode', this.onInitEditMode);
|
||||||
|
this.events.on('render', this.onRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDataReceived = (dataList) => {
|
||||||
|
this.data = dataList;
|
||||||
|
this.cardsData = this.convertToCards(this.data);
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
|
||||||
|
onInitEditMode = () => {
|
||||||
|
this.addEditorTab('Options', statusHeatmapOptionsEditor, 2);
|
||||||
|
this.unitFormats = kbn.getUnitFormats();
|
||||||
|
};
|
||||||
|
|
||||||
|
onRender = () => {
|
||||||
|
if (!this.data) { return; }
|
||||||
|
};
|
||||||
|
|
||||||
|
onCardColorChange = (newColor) => {
|
||||||
|
this.panel.color.cardColor = newColor;
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
|
||||||
|
onDataError = () => {
|
||||||
|
this.data = [];
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
|
||||||
|
link = (scope, elem, attrs, ctrl) => {
|
||||||
|
console.log('LINK');
|
||||||
|
rendering(scope, elem, attrs, ctrl);
|
||||||
|
// switch (renderer) {
|
||||||
|
// case CANVAS: {
|
||||||
|
// canvasRendering(scope, elem, attrs, ctrl);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case SVG: {
|
||||||
|
// svgRendering(scope, elem, attrs, ctrl);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
convertToCards = (data) => {
|
||||||
|
let cardsData = { cards: [], xBucketSize: 0, yBucketSize: 0, maxValue: 0, minValue: 0 };
|
||||||
|
|
||||||
|
if (!data || data.length == 0) { return cardsData;}
|
||||||
|
|
||||||
|
cardsData.yBucketSize = data.length;
|
||||||
|
cardsData.xBucketSize = _.min(_.map(data, d => d.datapoints.length));
|
||||||
|
|
||||||
|
for(let i = 0; i < cardsData.yBucketSize; i++) {
|
||||||
|
let s = data[i];
|
||||||
|
|
||||||
|
for (let j = 0; j < cardsData.xBucketSize; j++) {
|
||||||
|
let card = {};
|
||||||
|
let v = s.datapoints[j];
|
||||||
|
|
||||||
|
card.x = v[TIME_INDEX];
|
||||||
|
card.y = s.target;
|
||||||
|
card.value = v[VALUE_INDEX];
|
||||||
|
|
||||||
|
if (cardsData.maxValue < card.value)
|
||||||
|
cardsData.maxValue = card.value;
|
||||||
|
|
||||||
|
if (cardsData.minValue > card.value)
|
||||||
|
cardsData.minValue = card.value;
|
||||||
|
|
||||||
|
cardsData.cards.push(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardsData;
|
||||||
|
};
|
||||||
|
}
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
import d3 from 'd3';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
|
||||||
|
let TOOLTIP_PADDING_X = 30;
|
||||||
|
let TOOLTIP_PADDING_Y = 5;
|
||||||
|
let HISTOGRAM_WIDTH = 160;
|
||||||
|
let HISTOGRAM_HEIGHT = 40;
|
||||||
|
|
||||||
|
export class StatusHeatmapTooltip {
|
||||||
|
constructor(elem, scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
this.dashboard = scope.ctrl.dashboard;
|
||||||
|
this.panelCtrl = scope.ctrl;
|
||||||
|
this.panel = scope.ctrl.panel;
|
||||||
|
this.heatmapPanel = elem;
|
||||||
|
this.mouseOverBucket = false;
|
||||||
|
this.originalFillColor = null;
|
||||||
|
|
||||||
|
elem.on("mouseover", this.onMouseOver.bind(this));
|
||||||
|
elem.on("mouseleave", this.onMouseLeave.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseOver(e) {
|
||||||
|
if (!this.panel.tooltip.show || !this.scope.ctrl.data || _.isEmpty(this.scope.ctrl.data)) { return; }
|
||||||
|
|
||||||
|
if (!this.tooltip) {
|
||||||
|
this.add();
|
||||||
|
this.move(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseLeave() {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseMove(e) {
|
||||||
|
if (!this.panel.tooltip.show) { return; }
|
||||||
|
|
||||||
|
this.move(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
add() {
|
||||||
|
this.tooltip = d3.select("body")
|
||||||
|
.append("div")
|
||||||
|
.attr("class", "heatmap-tooltip graph-tooltip grafana-tooltip");
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this.tooltip) {
|
||||||
|
this.tooltip.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tooltip = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
show(pos, x, y, value) {
|
||||||
|
if (!this.panel.tooltip.show) { return; }
|
||||||
|
// shared tooltip mode
|
||||||
|
if (pos.panelRelY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!x || !y || !this.tooltip) {
|
||||||
|
this.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tooltipTimeFormat = 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
let time = this.dashboard.formatDate(+x, tooltipTimeFormat);
|
||||||
|
|
||||||
|
let tooltipHtml = `<div class="graph-tooltip-time">${time}</div>
|
||||||
|
<div class="status-heatmap-histogram"></div>`;
|
||||||
|
|
||||||
|
tooltipHtml += `<div>
|
||||||
|
name: <b>${y}</b> <br>
|
||||||
|
value: <b>${value}</b> <br>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
this.tooltip.html(tooltipHtml);
|
||||||
|
|
||||||
|
this.move(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
move(pos) {
|
||||||
|
if (!this.tooltip) { return; }
|
||||||
|
|
||||||
|
let elem = $(this.tooltip.node())[0];
|
||||||
|
let tooltipWidth = elem.clientWidth;
|
||||||
|
let tooltipHeight = elem.clientHeight;
|
||||||
|
|
||||||
|
let left = pos.pageX + TOOLTIP_PADDING_X;
|
||||||
|
let top = pos.pageY + TOOLTIP_PADDING_Y;
|
||||||
|
|
||||||
|
if (pos.pageX + tooltipWidth + 40 > window.innerWidth) {
|
||||||
|
left = pos.pageX - tooltipWidth - TOOLTIP_PADDING_X;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos.pageY - window.pageYOffset + tooltipHeight + 20 > window.innerHeight) {
|
||||||
|
top = pos.pageY - tooltipHeight - TOOLTIP_PADDING_Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.tooltip
|
||||||
|
.style("left", left + "px")
|
||||||
|
.style("top", top + "px");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user