mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
SingleStatPanel: Added graph to single stat panel, #951
This commit is contained in:
parent
69fdfd5cb3
commit
31a4d9204c
@ -17,6 +17,7 @@ function (_, crypto) {
|
||||
window_title_prefix : 'Grafana - ',
|
||||
panels : {
|
||||
'graph': { path: 'panels/graph' },
|
||||
'stats': { path: 'panels/stats' },
|
||||
'text': { path: 'panels/text' }
|
||||
},
|
||||
plugins : {},
|
||||
|
@ -9,7 +9,7 @@ function (angular, $, _) {
|
||||
angular
|
||||
.module('grafana.directives')
|
||||
.directive('panelMenu', function($compile) {
|
||||
var linkTemplate = '<a class="panel-title drag-handle">{{panel.title | interpolateTemplateVars}}</a>';
|
||||
var linkTemplate = '<span class="panel-title drag-handle pointer">{{panel.title | interpolateTemplateVars}}</span>';
|
||||
|
||||
function createMenuTemplate($scope) {
|
||||
var template = '<div class="panel-menu small">';
|
||||
@ -113,7 +113,7 @@ function (angular, $, _) {
|
||||
dismiss(2500);
|
||||
};
|
||||
|
||||
if ($scope.panelMeta.titlePos) {
|
||||
if ($scope.panelMeta.titlePos && $scope.panel.title) {
|
||||
elem.css('text-align', 'left');
|
||||
$link.css('padding-left', '10px');
|
||||
}
|
||||
|
@ -1,40 +1,14 @@
|
||||
<div ng-controller='StatsCtrl'>
|
||||
<div stats-panel></div>
|
||||
|
||||
<!-- <div class="stats-panel-value-container"> -->
|
||||
<!-- <span class="stats-panel-value">{{mainstat.value}}</span> -->
|
||||
<!-- <span class="stats-panel-func">({{mainstat.func}})</span> -->
|
||||
<!-- </div> -->
|
||||
<!-- -->
|
||||
<!-- <table class="stats-panel-table"> -->
|
||||
<!-- <tr> -->
|
||||
<!-- <th></th> -->
|
||||
<!-- <th>avg</th> -->
|
||||
<!-- <th>min</th> -->
|
||||
<!-- <th>max</th> -->
|
||||
<!-- <th>current</th> -->
|
||||
<!-- <th>total</th> -->
|
||||
<!-- </tr> -->
|
||||
<!-- <tr class="stats-series-item" ng-repeat="series in series"> -->
|
||||
<!-- <td> -->
|
||||
<!-- <i class='icon-minus pointer' ng-style="{color: series.color}"></i> -->
|
||||
<!-- {{series.info.alias}} -->
|
||||
<!-- </td> -->
|
||||
<!-- <td>{{series.info.avg}}</td> -->
|
||||
<!-- <td>{{series.info.min}}</td> -->
|
||||
<!-- <td>{{series.info.max}}</td> -->
|
||||
<!-- <td>{{series.info.current}}</td> -->
|
||||
<!-- <td>{{series.info.total}}</td> -->
|
||||
<!-- </tr> -->
|
||||
<!-- </table> -->
|
||||
<div class="stats-panel" stats-panel></div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div style="margin-top: 30px" ng-if="editMode">
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<i class="icon icon-bar-chart"></i>
|
||||
Panel settings
|
||||
<i class="icon icon-dashboard"></i>
|
||||
Singlestat
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs>
|
||||
|
@ -5,14 +5,15 @@ define([
|
||||
'components/timeSeries',
|
||||
'kbn',
|
||||
'services/panelSrv',
|
||||
'./statsDirective',
|
||||
],
|
||||
function (angular, app, _, TimeSeries, kbn) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.panels.stats', []);
|
||||
var module = angular.module('grafana.panels.stats');
|
||||
app.useModule(module);
|
||||
|
||||
module.controller('StatsCtrl', function($scope, panelSrv, timeSrv, $rootScope) {
|
||||
module.controller('StatsCtrl', function($scope, panelSrv, timeSrv) {
|
||||
|
||||
$scope.panelMeta = {
|
||||
titlePos: 'left',
|
||||
@ -27,7 +28,7 @@ function (angular, app, _, TimeSeries, kbn) {
|
||||
src:'app/partials/metrics.html'
|
||||
},
|
||||
{
|
||||
title: 'Display Styles',
|
||||
title: 'Options',
|
||||
src:'app/panels/stats/statsEditor.html'
|
||||
}
|
||||
],
|
||||
@ -39,25 +40,20 @@ function (angular, app, _, TimeSeries, kbn) {
|
||||
targets: [{}],
|
||||
cacheTimeout: null,
|
||||
format: 'none',
|
||||
stats: {
|
||||
show: true,
|
||||
avg: true,
|
||||
template: '{{value}} {{func}}'
|
||||
},
|
||||
coloring: {
|
||||
thresholds: '',
|
||||
background: false,
|
||||
value: false,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"]
|
||||
},
|
||||
table: {
|
||||
show: true,
|
||||
template: '{{avg}} !(avg)',
|
||||
thresholds: '',
|
||||
colorBackground: false,
|
||||
colorValue: false,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
sparkline: {
|
||||
show: false,
|
||||
full: false,
|
||||
lineColor: 'rgb(31, 120, 193)',
|
||||
fillColor: 'rgb(31, 120, 193)',
|
||||
}
|
||||
};
|
||||
|
||||
_.defaults($scope.panel, _d);
|
||||
_.defaults($scope.panel.stats, _d.stats);
|
||||
_.defaults($scope.panel.coloring, _d.coloring);
|
||||
|
||||
$scope.init = function() {
|
||||
panelSrv.init($scope);
|
||||
@ -101,43 +97,33 @@ function (angular, app, _, TimeSeries, kbn) {
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.seriesHandler = function(seriesData, index) {
|
||||
var datapoints = seriesData.datapoints;
|
||||
var alias = seriesData.target;
|
||||
var color = $rootScope.colors[index];
|
||||
|
||||
var seriesInfo = {
|
||||
alias: alias,
|
||||
enable: true,
|
||||
color: color
|
||||
};
|
||||
|
||||
$scope.seriesHandler = function(seriesData) {
|
||||
var series = new TimeSeries({
|
||||
datapoints: datapoints,
|
||||
info: seriesInfo,
|
||||
datapoints: seriesData.datapoints,
|
||||
info: { alias: seriesData.target },
|
||||
});
|
||||
|
||||
series.points = series.getFlotPairs('connected');
|
||||
series.data = series.getFlotPairs('connected');
|
||||
|
||||
return series;
|
||||
};
|
||||
|
||||
$scope.setColoring = function(options) {
|
||||
if (options.background) {
|
||||
$scope.panel.coloring.value = false;
|
||||
$scope.panel.coloring.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
|
||||
$scope.panel.colorValue = false;
|
||||
$scope.panel.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
|
||||
}
|
||||
else {
|
||||
$scope.panel.coloring.background = false;
|
||||
$scope.panel.coloring.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
|
||||
$scope.panel.colorBackground = false;
|
||||
$scope.panel.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
|
||||
}
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.invertColorOrder = function() {
|
||||
var tmp = $scope.panel.coloring.colors[0];
|
||||
$scope.panel.coloring.colors[0] = $scope.panel.coloring.colors[2];
|
||||
$scope.panel.coloring.colors[2] = tmp;
|
||||
var tmp = $scope.panel.colors[0];
|
||||
$scope.panel.colors[0] = $scope.panel.colors[2];
|
||||
$scope.panel.colors[2] = tmp;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
@ -153,11 +139,11 @@ function (angular, app, _, TimeSeries, kbn) {
|
||||
series.updateLegendValues(kbn.valueFormats[$scope.panel.format], 2, -7);
|
||||
}
|
||||
|
||||
data.thresholds = $scope.panel.coloring.thresholds.split(',').map(function(strVale) {
|
||||
data.thresholds = $scope.panel.thresholds.split(',').map(function(strVale) {
|
||||
return Number(strVale.trim());
|
||||
});
|
||||
|
||||
data.colorMap = $scope.panel.coloring.colors;
|
||||
data.colorMap = $scope.panel.colors;
|
||||
|
||||
$scope.data = data;
|
||||
$scope.$emit('render');
|
||||
@ -165,112 +151,4 @@ function (angular, app, _, TimeSeries, kbn) {
|
||||
|
||||
$scope.init();
|
||||
});
|
||||
|
||||
module.directive('statsPanel', function() {
|
||||
|
||||
return {
|
||||
link: function(scope, elem) {
|
||||
var data;
|
||||
var valueRegex = /\{\{([a-zA-Z]+)\}\}/g;
|
||||
var smallValueTextRegex = /!(\S+)/g;
|
||||
|
||||
scope.$on('render', function() {
|
||||
data = scope.data;
|
||||
data.mainValue = null;
|
||||
|
||||
if (!data || data.series.length === 0) {
|
||||
elem.html('no data');
|
||||
return;
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
function applyColoringThresholds(value, valueString) {
|
||||
if (!scope.panel.coloring.value) {
|
||||
return valueString;
|
||||
}
|
||||
|
||||
var color = getColorForValue(value);
|
||||
if (color) {
|
||||
return '<span style="color:' + color + '">'+ valueString + '</span>';
|
||||
}
|
||||
|
||||
return valueString;
|
||||
}
|
||||
|
||||
function getColorForValue(value) {
|
||||
for (var i = data.thresholds.length - 1; i >= 0 ; i--) {
|
||||
if (value > data.thresholds[i]) {
|
||||
return data.colorMap[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function valueTemplateReplaceFunc(match, statType) {
|
||||
var stats = data.series[0].stats;
|
||||
data.mainValue = stats[statType];
|
||||
var valueFormated = scope.formatValue(data.mainValue);
|
||||
return applyColoringThresholds(data.mainValue, valueFormated);
|
||||
}
|
||||
|
||||
function smallValueTextReplaceFunc(match, text) {
|
||||
return '<span class="stats-panel-value-small">' + text + '</span>';
|
||||
}
|
||||
|
||||
function render() {
|
||||
var panel = scope.panel;
|
||||
var body = '';
|
||||
var i, series;
|
||||
|
||||
if (panel.stats.show) {
|
||||
body += '<div class="stats-panel-value-container">';
|
||||
body += '<span class="stats-panel-value">';
|
||||
var valueHtml = panel.stats.template.replace(valueRegex, valueTemplateReplaceFunc);
|
||||
body += valueHtml.replace(smallValueTextRegex, smallValueTextReplaceFunc);
|
||||
body += '</div>';
|
||||
body += '</div>';
|
||||
}
|
||||
|
||||
if (panel.coloring.background && data.mainValue) {
|
||||
var color = getColorForValue(data.mainValue);
|
||||
if (color) {
|
||||
elem.parents('.panel-container').css('background-color', color);
|
||||
if (scope.fullscreen) {
|
||||
elem.css('background-color', color);
|
||||
} else {
|
||||
elem.css('background-color', '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
elem.parents('.panel-container').css('background-color', '');
|
||||
elem.css('background-color', '');
|
||||
}
|
||||
|
||||
if (panel.table.show) {
|
||||
body += '<table class="stats-panel-table">';
|
||||
body += '<tr>';
|
||||
body += '<th></th><th>avg</th><th>min</th><th>max</th><th>current</th><th>total</th>';
|
||||
body += '</tr>';
|
||||
for (i = 0; i < data.series.length; i++) {
|
||||
series = data.series[i];
|
||||
body += '<tr>';
|
||||
body += '<td><i class="icon-minus pointer" style="color:' + series.color + '"></i> ';
|
||||
body += series.info.alias + ' </td>';
|
||||
body += '<td>' + series.info.avg + '</td>';
|
||||
body += '<td>' + series.info.min + '</td>';
|
||||
body += '<td>' + series.info.max + '</td>';
|
||||
body += '<td>' + series.info.total + '</td>';
|
||||
body += '<td>' + series.info.current + '</td>';
|
||||
}
|
||||
body += '</table>';
|
||||
}
|
||||
|
||||
elem.html(body);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
|
170
src/app/panels/stats/statsDirective.js
Normal file
170
src/app/panels/stats/statsDirective.js
Normal file
@ -0,0 +1,170 @@
|
||||
define([
|
||||
'angular',
|
||||
'app',
|
||||
'lodash',
|
||||
'kbn',
|
||||
'jquery',
|
||||
'jquery.flot',
|
||||
'jquery.flot.time',
|
||||
],
|
||||
function (angular, app, _, kbn, $) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.panels.stats', []);
|
||||
app.useModule(module);
|
||||
|
||||
module.directive('statsPanel', function() {
|
||||
|
||||
return {
|
||||
link: function(scope, elem) {
|
||||
var data;
|
||||
var valueRegex = /\{\{([a-zA-Z]+)\}\}/g;
|
||||
var smallValueTextRegex = /!(\S+)/g;
|
||||
var $panelContainer = elem.parents('.panel-container');
|
||||
|
||||
scope.$on('render', function() {
|
||||
data = scope.data;
|
||||
data.mainValue = null;
|
||||
|
||||
if (!data || data.series.length === 0) {
|
||||
elem.html('no data');
|
||||
return;
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
function setElementHeight() {
|
||||
try {
|
||||
var height = scope.height || scope.panel.height || scope.row.height;
|
||||
if (_.isString(height)) {
|
||||
height = parseInt(height.replace('px', ''), 10);
|
||||
}
|
||||
|
||||
height -= scope.panel.title ? 24 : 9; // subtract panel title bar
|
||||
|
||||
elem.css('height', height + 'px');
|
||||
|
||||
return true;
|
||||
} catch(e) { // IE throws errors sometimes
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function applyColoringThresholds(value, valueString) {
|
||||
if (!scope.panel.colorValue) {
|
||||
return valueString;
|
||||
}
|
||||
|
||||
var color = getColorForValue(value);
|
||||
if (color) {
|
||||
return '<span style="color:' + color + '">'+ valueString + '</span>';
|
||||
}
|
||||
|
||||
return valueString;
|
||||
}
|
||||
|
||||
function getColorForValue(value) {
|
||||
for (var i = data.thresholds.length - 1; i >= 0 ; i--) {
|
||||
if (value > data.thresholds[i]) {
|
||||
return data.colorMap[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function valueTemplateReplaceFunc(match, statType) {
|
||||
var stats = data.series[0].stats;
|
||||
data.mainValue = stats[statType];
|
||||
var valueFormated = scope.formatValue(data.mainValue);
|
||||
return applyColoringThresholds(data.mainValue, valueFormated);
|
||||
}
|
||||
|
||||
function smallValueTextReplaceFunc(match, text) {
|
||||
return '<span class="stats-panel-value-small">' + text + '</span>';
|
||||
}
|
||||
|
||||
function render() {
|
||||
setElementHeight();
|
||||
|
||||
var panel = scope.panel;
|
||||
var body = '';
|
||||
|
||||
body += '<div class="stats-panel-value-container">';
|
||||
body += '<span class="stats-panel-value">';
|
||||
var valueHtml = panel.template.replace(valueRegex, valueTemplateReplaceFunc);
|
||||
body += valueHtml.replace(smallValueTextRegex, smallValueTextReplaceFunc);
|
||||
body += '</div>';
|
||||
body += '</div>';
|
||||
|
||||
if (panel.colorBackground && data.mainValue) {
|
||||
var color = getColorForValue(data.mainValue);
|
||||
if (color) {
|
||||
$panelContainer.css('background-color', color);
|
||||
if (scope.fullscreen) {
|
||||
elem.css('background-color', color);
|
||||
} else {
|
||||
elem.css('background-color', '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$panelContainer.css('background-color', '');
|
||||
elem.css('background-color', '');
|
||||
}
|
||||
|
||||
var width = elem.width() + 20;
|
||||
var height = elem.height() || 100;
|
||||
|
||||
var plotCanvas = $('<div></div>');
|
||||
var plotCss = {};
|
||||
plotCss.position = 'absolute';
|
||||
|
||||
if (panel.sparkline.full) {
|
||||
plotCss.bottom = '5px';
|
||||
plotCss.left = '-5px';
|
||||
plotCss.width = (width - 10) + 'px';
|
||||
plotCss.height = (height - 45) + 'px';
|
||||
}
|
||||
else {
|
||||
plotCss.bottom = "0px";
|
||||
plotCss.left = "-5px";
|
||||
plotCss.width = (width - 10) + 'px';
|
||||
plotCss.height = Math.floor(height * 0.3) + "px";
|
||||
}
|
||||
|
||||
plotCanvas.css(plotCss);
|
||||
|
||||
var options = {
|
||||
legend: { show: false },
|
||||
series: {
|
||||
lines: {
|
||||
show: true,
|
||||
fill: 1,
|
||||
lineWidth: 1,
|
||||
fillColor: panel.sparkline.fillColor,
|
||||
},
|
||||
},
|
||||
yaxes: { show: false },
|
||||
xaxis: {
|
||||
show: false,
|
||||
mode: "time",
|
||||
min: scope.range.from.getTime(),
|
||||
max: scope.range.to.getTime(),
|
||||
},
|
||||
grid: { hoverable: false, show: false },
|
||||
};
|
||||
|
||||
elem.html(body);
|
||||
elem.append(plotCanvas);
|
||||
|
||||
data.series[0].color = panel.sparkline.lineColor;
|
||||
|
||||
setTimeout(function() {
|
||||
$.plot(plotCanvas, [data.series[0]], options);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
@ -1,14 +1,9 @@
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Main options</h5>
|
||||
<editor-opt-bool text="Show table" model="panel.table.show" change="render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Show big values" model="panel.stats.show" change="render()"></editor-opt-bool>
|
||||
</div>
|
||||
<div class="section" ng-if="panel.stats">
|
||||
<div class="section">
|
||||
<h5>Big values</h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">Template</label>
|
||||
<input type="text" class="input-large" ng-model="panel.stats.template" ng-blur="render()"></input>
|
||||
<input type="text" class="input-xlarge" ng-model="panel.template" ng-blur="render()"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
@ -20,22 +15,35 @@
|
||||
</div>
|
||||
<div class="section">
|
||||
<h5>Coloring</h5>
|
||||
<editor-opt-bool text="Background" model="panel.coloring.background" change="setColoring({background: true})"></editor-opt-bool>
|
||||
<editor-opt-bool text="Value" model="panel.coloring.value" change="setColoring({value: true})"></editor-opt-bool>
|
||||
<editor-opt-bool text="Background" model="panel.colorBackground" change="setColoring({background: true})"></editor-opt-bool>
|
||||
<editor-opt-bool text="Value" model="panel.colorValue" change="setColoring({value: true})"></editor-opt-bool>
|
||||
<div class="editor-option">
|
||||
<label class="small">Thresholds</label>
|
||||
<input type="text" class="input-large" ng-model="panel.coloring.thresholds" ng-blur="render()"></input>
|
||||
<input type="text" class="input-large" ng-model="panel.thresholds" ng-blur="render()"></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Color</label>
|
||||
<spectrum-picker ng-model="panel.coloring.colors[0]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="panel.coloring.colors[1]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="panel.coloring.colors[2]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="panel.colors[0]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="panel.colors[1]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="panel.colors[2]" ng-change="render()" ></spectrum-picker>
|
||||
<a class="pointer" ng-click="invertColorOrder()">invert order</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Spark lines</h5>
|
||||
</div>
|
||||
<editor-opt-bool text="Spark line" model="panel.sparkline.show" change="render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Background mode" model="panel.sparkline.full" change="render()"></editor-opt-bool>
|
||||
<div class="editor-option">
|
||||
<label class="small">Line color</label>
|
||||
<spectrum-picker ng-model="panel.sparkline.lineColor" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="panel.sparkline.fillColor" ng-change="render()" ></spectrum-picker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Series options</h5>
|
||||
|
@ -1,6 +1,16 @@
|
||||
.stats-panel {
|
||||
position: relative;
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stats-panel-value-container {
|
||||
padding: 20px;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.stats-panel-value {
|
||||
|
Loading…
Reference in New Issue
Block a user