diff --git a/src/app/components/settings.js b/src/app/components/settings.js index 01f40c2e761..38313aeb198 100644 --- a/src/app/components/settings.js +++ b/src/app/components/settings.js @@ -17,6 +17,7 @@ function (_, crypto) { window_title_prefix : 'Grafana - ', panels : { 'graph': { path: 'panels/graph' }, + 'stats': { path: 'panels/stats' }, 'text': { path: 'panels/text' } }, plugins : {}, diff --git a/src/app/directives/panelMenu.js b/src/app/directives/panelMenu.js index f8ea42c6873..2a68dd30865 100644 --- a/src/app/directives/panelMenu.js +++ b/src/app/directives/panelMenu.js @@ -9,7 +9,7 @@ function (angular, $, _) { angular .module('grafana.directives') .directive('panelMenu', function($compile) { - var linkTemplate = '{{panel.title | interpolateTemplateVars}}'; + var linkTemplate = '{{panel.title | interpolateTemplateVars}}'; function createMenuTemplate($scope) { var template = '
'; @@ -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'); } diff --git a/src/app/panels/stats/module.html b/src/app/panels/stats/module.html index 64e61cacc69..353cdd29c5a 100644 --- a/src/app/panels/stats/module.html +++ b/src/app/panels/stats/module.html @@ -1,40 +1,14 @@
-
- - - - - - - - - - - - - - - - - - - - - - - - - - +
- - Panel settings + + Singlestat
diff --git a/src/app/panels/stats/module.js b/src/app/panels/stats/module.js index 5cb84ecffb3..d5fdc9e38ac 100644 --- a/src/app/panels/stats/module.js +++ b/src/app/panels/stats/module.js @@ -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 ''+ valueString + ''; - } - - 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 '' + text + ''; - } - - function render() { - var panel = scope.panel; - var body = ''; - var i, series; - - if (panel.stats.show) { - body += '
'; - body += ''; - var valueHtml = panel.stats.template.replace(valueRegex, valueTemplateReplaceFunc); - body += valueHtml.replace(smallValueTextRegex, smallValueTextReplaceFunc); - body += '
'; - body += '
'; - } - - 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 += ''; - body += ''; - body += ''; - body += ''; - for (i = 0; i < data.series.length; i++) { - series = data.series[i]; - body += ''; - body += ''; - body += ''; - body += ''; - body += ''; - body += ''; - body += ''; - } - body += '
avgminmaxcurrenttotal
'; - body += series.info.alias + ' ' + series.info.avg + '' + series.info.min + '' + series.info.max + '' + series.info.total + '' + series.info.current + '
'; - } - - elem.html(body); - } - } - }; - }); - }); diff --git a/src/app/panels/stats/statsDirective.js b/src/app/panels/stats/statsDirective.js new file mode 100644 index 00000000000..f486445b73c --- /dev/null +++ b/src/app/panels/stats/statsDirective.js @@ -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 ''+ valueString + ''; + } + + 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 '' + text + ''; + } + + function render() { + setElementHeight(); + + var panel = scope.panel; + var body = ''; + + body += '
'; + body += ''; + var valueHtml = panel.template.replace(valueRegex, valueTemplateReplaceFunc); + body += valueHtml.replace(smallValueTextRegex, smallValueTextReplaceFunc); + body += '
'; + body += '
'; + + 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 = $('
'); + 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); + } + } + }; + }); + +}); diff --git a/src/app/panels/stats/statsEditor.html b/src/app/panels/stats/statsEditor.html index 70798d65adf..3646470c84a 100644 --- a/src/app/panels/stats/statsEditor.html +++ b/src/app/panels/stats/statsEditor.html @@ -1,14 +1,9 @@
-
-
Main options
- - -
-
+
Big values
- +
@@ -20,22 +15,35 @@
Coloring
- - + +
- +
- - - + + + invert order
+
+
+
Spark lines
+
+ + +
+ + + +
+
+
Series options
diff --git a/src/css/less/stats-panel.less b/src/css/less/stats-panel.less index 34bbefd806d..29e162c77ea 100644 --- a/src/css/less/stats-panel.less +++ b/src/css/less/stats-panel.less @@ -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 {