mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'develop-graph-legend' into develop
This commit is contained in:
commit
9f87d8d344
@ -52,6 +52,8 @@ import {gfPageDirective} from './components/gf_page';
|
|||||||
import {orgSwitcher} from './components/org_switcher';
|
import {orgSwitcher} from './components/org_switcher';
|
||||||
import {profiler} from './profiler';
|
import {profiler} from './profiler';
|
||||||
import {registerAngularDirectives} from './angular_wrappers';
|
import {registerAngularDirectives} from './angular_wrappers';
|
||||||
|
import {updateLegendValues} from './time_series2';
|
||||||
|
import TimeSeries from './time_series2';
|
||||||
import {searchResultsDirective} from './components/search/search_results';
|
import {searchResultsDirective} from './components/search/search_results';
|
||||||
import {manageDashboardsDirective} from './components/manage_dashboards/manage_dashboards';
|
import {manageDashboardsDirective} from './components/manage_dashboards/manage_dashboards';
|
||||||
|
|
||||||
@ -86,6 +88,8 @@ export {
|
|||||||
geminiScrollbar,
|
geminiScrollbar,
|
||||||
gfPageDirective,
|
gfPageDirective,
|
||||||
orgSwitcher,
|
orgSwitcher,
|
||||||
searchResultsDirective,
|
manageDashboardsDirective,
|
||||||
manageDashboardsDirective
|
TimeSeries,
|
||||||
|
updateLegendValues,
|
||||||
|
searchResultsDirective
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
import {getFlotTickDecimals} from 'app/core/utils/ticks';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
function matchSeriesOverride(aliasOrRegex, seriesAlias) {
|
function matchSeriesOverride(aliasOrRegex, seriesAlias) {
|
||||||
@ -16,6 +17,48 @@ function translateFillOption(fill) {
|
|||||||
return fill === 0 ? 0.001 : fill/10;
|
return fill === 0 ? 0.001 : fill/10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate decimals for legend and update values for each series.
|
||||||
|
* @param data series data
|
||||||
|
* @param panel
|
||||||
|
*/
|
||||||
|
export function updateLegendValues(data: TimeSeries[], panel) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let series = data[i];
|
||||||
|
let yaxes = panel.yaxes;
|
||||||
|
let axis = yaxes[series.yaxis - 1];
|
||||||
|
let {tickDecimals, scaledDecimals} = getFlotTickDecimals(data, axis);
|
||||||
|
let formater = kbn.valueFormats[panel.yaxes[series.yaxis - 1].format];
|
||||||
|
|
||||||
|
// decimal override
|
||||||
|
if (_.isNumber(panel.decimals)) {
|
||||||
|
series.updateLegendValues(formater, panel.decimals, null);
|
||||||
|
} else {
|
||||||
|
// auto decimals
|
||||||
|
// legend and tooltip gets one more decimal precision
|
||||||
|
// than graph legend ticks
|
||||||
|
tickDecimals = (tickDecimals || -1) + 1;
|
||||||
|
series.updateLegendValues(formater, tickDecimals, scaledDecimals + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataMinMax(data: TimeSeries[]) {
|
||||||
|
let datamin = null;
|
||||||
|
let datamax = null;
|
||||||
|
|
||||||
|
for (let series of data) {
|
||||||
|
if (datamax === null || datamax < series.stats.max) {
|
||||||
|
datamax = series.stats.max;
|
||||||
|
}
|
||||||
|
if (datamin === null || datamin > series.stats.min) {
|
||||||
|
datamin = series.stats.min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {datamin, datamax};
|
||||||
|
}
|
||||||
|
|
||||||
export default class TimeSeries {
|
export default class TimeSeries {
|
||||||
datapoints: any;
|
datapoints: any;
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import {getDataMinMax} from 'app/core/time_series2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate tick step.
|
* Calculate tick step.
|
||||||
* Implementation from d3-array (ticks.js)
|
* Implementation from d3-array (ticks.js)
|
||||||
@ -32,6 +34,7 @@ export function getScaledDecimals(decimals, tick_size) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate tick size based on min and max values, number of ticks and precision.
|
* Calculate tick size based on min and max values, number of ticks and precision.
|
||||||
|
* Implementation from Flot.
|
||||||
* @param min Axis minimum
|
* @param min Axis minimum
|
||||||
* @param max Axis maximum
|
* @param max Axis maximum
|
||||||
* @param noTicks Number of ticks
|
* @param noTicks Number of ticks
|
||||||
@ -65,3 +68,91 @@ export function getFlotTickSize(min: number, max: number, noTicks: number, tickD
|
|||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate axis range (min and max).
|
||||||
|
* Implementation from Flot.
|
||||||
|
*/
|
||||||
|
export function getFlotRange(panelMin, panelMax, datamin, datamax) {
|
||||||
|
const autoscaleMargin = 0.02;
|
||||||
|
|
||||||
|
let min = +(panelMin != null ? panelMin : datamin);
|
||||||
|
let max = +(panelMax != null ? panelMax : datamax);
|
||||||
|
let delta = max - min;
|
||||||
|
|
||||||
|
if (delta === 0.0) {
|
||||||
|
// Grafana fix: wide Y min and max using increased wideFactor
|
||||||
|
// when all series values are the same
|
||||||
|
var wideFactor = 0.25;
|
||||||
|
var widen = Math.abs(max === 0 ? 1 : max * wideFactor);
|
||||||
|
|
||||||
|
if (panelMin === null) {
|
||||||
|
min -= widen;
|
||||||
|
}
|
||||||
|
// always widen max if we couldn't widen min to ensure we
|
||||||
|
// don't fall into min == max which doesn't work
|
||||||
|
if (panelMax == null || panelMin != null) {
|
||||||
|
max += widen;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// consider autoscaling
|
||||||
|
var margin = autoscaleMargin;
|
||||||
|
if (margin != null) {
|
||||||
|
if (panelMin == null) {
|
||||||
|
min -= delta * margin;
|
||||||
|
// make sure we don't go below zero if all values
|
||||||
|
// are positive
|
||||||
|
if (min < 0 && datamin != null && datamin >= 0) {
|
||||||
|
min = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (panelMax == null) {
|
||||||
|
max += delta * margin;
|
||||||
|
if (max > 0 && datamax != null && datamax <= 0) {
|
||||||
|
max = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {min, max};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate tick decimals.
|
||||||
|
* Implementation from Flot.
|
||||||
|
*/
|
||||||
|
export function getFlotTickDecimals(data, axis) {
|
||||||
|
let {datamin, datamax} = getDataMinMax(data);
|
||||||
|
let {min, max} = getFlotRange(axis.min, axis.max, datamin, datamax);
|
||||||
|
let noTicks = 3;
|
||||||
|
let tickDecimals, maxDec;
|
||||||
|
let delta = (max - min) / noTicks;
|
||||||
|
let dec = -Math.floor(Math.log(delta) / Math.LN10);
|
||||||
|
|
||||||
|
let magn = Math.pow(10, -dec);
|
||||||
|
// norm is between 1.0 and 10.0
|
||||||
|
let norm = delta / magn;
|
||||||
|
let size;
|
||||||
|
|
||||||
|
if (norm < 1.5) {
|
||||||
|
size = 1;
|
||||||
|
} else if (norm < 3) {
|
||||||
|
size = 2;
|
||||||
|
// special case for 2.5, requires an extra decimal
|
||||||
|
if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
|
||||||
|
size = 2.5;
|
||||||
|
++dec;
|
||||||
|
}
|
||||||
|
} else if (norm < 7.5) {
|
||||||
|
size = 5;
|
||||||
|
} else {
|
||||||
|
size = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
size *= magn;
|
||||||
|
|
||||||
|
tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
|
||||||
|
// grafana addition
|
||||||
|
const scaledDecimals = tickDecimals - Math.floor(Math.log(size) / Math.LN10);
|
||||||
|
return {tickDecimals, scaledDecimals};
|
||||||
|
}
|
||||||
|
@ -20,7 +20,7 @@ var panelTemplate = `
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-content">
|
<div class="panel-content">
|
||||||
<ng-transclude></ng-transclude>
|
<ng-transclude class="panel-height-helper"></ng-transclude>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import {EventManager} from 'app/features/annotations/all';
|
|||||||
import {convertValuesToHistogram, getSeriesValues} from './histogram';
|
import {convertValuesToHistogram, getSeriesValues} from './histogram';
|
||||||
|
|
||||||
/** @ngInject **/
|
/** @ngInject **/
|
||||||
function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
function graphDirective(timeSrv, popoverSrv, contextSrv) {
|
||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
template: '',
|
template: '',
|
||||||
@ -34,8 +34,6 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
var data;
|
var data;
|
||||||
var plot;
|
var plot;
|
||||||
var sortedSeries;
|
var sortedSeries;
|
||||||
var legendSideLastValue = null;
|
|
||||||
var rootScope = scope.$root;
|
|
||||||
var panelWidth = 0;
|
var panelWidth = 0;
|
||||||
var eventManager = new EventManager(ctrl);
|
var eventManager = new EventManager(ctrl);
|
||||||
var thresholdManager = new ThresholdManager(ctrl);
|
var thresholdManager = new ThresholdManager(ctrl);
|
||||||
@ -53,17 +51,28 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ctrl.events.on('render', function(renderData) {
|
/**
|
||||||
|
* Split graph rendering into two parts.
|
||||||
|
* First, calculate series stats in buildFlotPairs() function. Then legend rendering started
|
||||||
|
* (see ctrl.events.on('render') in legend.ts).
|
||||||
|
* When legend is rendered it emits 'legend-rendering-complete' and graph rendered.
|
||||||
|
*/
|
||||||
|
ctrl.events.on('render', (renderData) => {
|
||||||
data = renderData || data;
|
data = renderData || data;
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
annotations = ctrl.annotations || [];
|
annotations = ctrl.annotations || [];
|
||||||
|
buildFlotPairs(data);
|
||||||
|
ctrl.events.emit('render-legend');
|
||||||
|
});
|
||||||
|
|
||||||
|
ctrl.events.on('legend-rendering-complete', () => {
|
||||||
render_panel();
|
render_panel();
|
||||||
});
|
});
|
||||||
|
|
||||||
// global events
|
// global events
|
||||||
appEvents.on('graph-hover', function(evt) {
|
appEvents.on('graph-hover', (evt) => {
|
||||||
// ignore other graph hover events if shared tooltip is disabled
|
// ignore other graph hover events if shared tooltip is disabled
|
||||||
if (!dashboard.sharedTooltipModeEnabled()) {
|
if (!dashboard.sharedTooltipModeEnabled()) {
|
||||||
return;
|
return;
|
||||||
@ -77,47 +86,17 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
tooltip.show(evt.pos);
|
tooltip.show(evt.pos);
|
||||||
}, scope);
|
}, scope);
|
||||||
|
|
||||||
appEvents.on('graph-hover-clear', function(event, info) {
|
appEvents.on('graph-hover-clear', (event, info) => {
|
||||||
if (plot) {
|
if (plot) {
|
||||||
tooltip.clear(plot);
|
tooltip.clear(plot);
|
||||||
}
|
}
|
||||||
}, scope);
|
}, scope);
|
||||||
|
|
||||||
function getLegendHeight(panelHeight) {
|
|
||||||
if (!panel.legend.show || panel.legend.rightSide) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.legend.alignAsTable) {
|
|
||||||
var legendSeries = _.filter(data, function(series) {
|
|
||||||
return series.hideFromLegend(panel.legend) === false;
|
|
||||||
});
|
|
||||||
var total = 23 + (21 * legendSeries.length);
|
|
||||||
return Math.min(total, Math.floor(panelHeight/2));
|
|
||||||
} else {
|
|
||||||
return 26;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setElementHeight() {
|
|
||||||
try {
|
|
||||||
var height = ctrl.height - getLegendHeight(ctrl.height);
|
|
||||||
elem.css('height', height + 'px');
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (e) { // IE throws errors sometimes
|
|
||||||
console.log(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldAbortRender() {
|
function shouldAbortRender() {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!setElementHeight()) { return true; }
|
|
||||||
|
|
||||||
if (panelWidth === 0) {
|
if (panelWidth === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -126,27 +105,6 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawHook(plot) {
|
function drawHook(plot) {
|
||||||
// Update legend values
|
|
||||||
var yaxis = plot.getYAxes();
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
var series = data[i];
|
|
||||||
var axis = yaxis[series.yaxis - 1];
|
|
||||||
var formater = kbn.valueFormats[panel.yaxes[series.yaxis - 1].format];
|
|
||||||
|
|
||||||
// decimal override
|
|
||||||
if (_.isNumber(panel.decimals)) {
|
|
||||||
series.updateLegendValues(formater, panel.decimals, null);
|
|
||||||
} else {
|
|
||||||
// auto decimals
|
|
||||||
// legend and tooltip gets one more decimal precision
|
|
||||||
// than graph legend ticks
|
|
||||||
var tickDecimals = (axis.tickDecimals || -1) + 1;
|
|
||||||
series.updateLegendValues(formater, tickDecimals, axis.scaledDecimals + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rootScope.$$phase) { scope.$digest(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// add left axis labels
|
// add left axis labels
|
||||||
if (panel.yaxes[0].label && panel.yaxes[0].show) {
|
if (panel.yaxes[0].label && panel.yaxes[0].show) {
|
||||||
$("<div class='axisLabel left-yaxis-label flot-temp-elem'></div>").text(panel.yaxes[0].label).appendTo(elem);
|
$("<div class='axisLabel left-yaxis-label flot-temp-elem'></div>").text(panel.yaxes[0].label).appendTo(elem);
|
||||||
@ -157,6 +115,10 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
$("<div class='axisLabel right-yaxis-label flot-temp-elem'></div>").text(panel.yaxes[1].label).appendTo(elem);
|
$("<div class='axisLabel right-yaxis-label flot-temp-elem'></div>").text(panel.yaxes[1].label).appendTo(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctrl.dataWarning) {
|
||||||
|
$(`<div class="datapoints-warning flot-temp-elem">${ctrl.dataWarning.title}</div>`).appendTo(elem);
|
||||||
|
}
|
||||||
|
|
||||||
thresholdManager.draw(plot);
|
thresholdManager.draw(plot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +169,6 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
// Function for rendering panel
|
// Function for rendering panel
|
||||||
function render_panel() {
|
function render_panel() {
|
||||||
panelWidth = elem.width();
|
panelWidth = elem.width();
|
||||||
|
|
||||||
if (shouldAbortRender()) {
|
if (shouldAbortRender()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -218,10 +179,99 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
// un-check dashes if lines are unchecked
|
// un-check dashes if lines are unchecked
|
||||||
panel.dashes = panel.lines ? panel.dashes : false;
|
panel.dashes = panel.lines ? panel.dashes : false;
|
||||||
|
|
||||||
var stack = panel.stack ? true : null;
|
|
||||||
|
|
||||||
// Populate element
|
// Populate element
|
||||||
var options: any = {
|
let options: any = buildFlotOptions(panel);
|
||||||
|
prepareXAxis(options, panel);
|
||||||
|
configureYAxisOptions(data, options);
|
||||||
|
thresholdManager.addFlotOptions(options, panel);
|
||||||
|
eventManager.addFlotEvents(annotations, options);
|
||||||
|
|
||||||
|
sortedSeries = sortSeries(data, panel);
|
||||||
|
callPlot(options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFlotPairs(data) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let series = data[i];
|
||||||
|
series.data = series.getFlotPairs(series.nullPointMode || panel.nullPointMode);
|
||||||
|
|
||||||
|
// if hidden remove points and disable stack
|
||||||
|
if (ctrl.hiddenSeries[series.alias]) {
|
||||||
|
series.data = [];
|
||||||
|
series.stack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareXAxis(options, panel) {
|
||||||
|
switch (panel.xaxis.mode) {
|
||||||
|
case 'series': {
|
||||||
|
options.series.bars.barWidth = 0.7;
|
||||||
|
options.series.bars.align = 'center';
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let series = data[i];
|
||||||
|
series.data = [[i + 1, series.stats[panel.xaxis.values[0]]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
addXSeriesAxis(options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'histogram': {
|
||||||
|
let bucketSize: number;
|
||||||
|
let values = getSeriesValues(data);
|
||||||
|
|
||||||
|
if (data.length && values.length) {
|
||||||
|
let histMin = _.min(_.map(data, s => s.stats.min));
|
||||||
|
let histMax = _.max(_.map(data, s => s.stats.max));
|
||||||
|
let ticks = panel.xaxis.buckets || panelWidth / 50;
|
||||||
|
bucketSize = tickStep(histMin, histMax, ticks);
|
||||||
|
let histogram = convertValuesToHistogram(values, bucketSize);
|
||||||
|
data[0].data = histogram;
|
||||||
|
options.series.bars.barWidth = bucketSize * 0.8;
|
||||||
|
} else {
|
||||||
|
bucketSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
addXHistogramAxis(options, bucketSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'table': {
|
||||||
|
options.series.bars.barWidth = 0.7;
|
||||||
|
options.series.bars.align = 'center';
|
||||||
|
addXTableAxis(options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
options.series.bars.barWidth = getMinTimeStepOfSeries(data) / 1.5;
|
||||||
|
addTimeAxis(options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callPlot(options, incrementRenderCounter) {
|
||||||
|
try {
|
||||||
|
plot = $.plot(elem, sortedSeries, options);
|
||||||
|
if (ctrl.renderError) {
|
||||||
|
delete ctrl.error;
|
||||||
|
delete ctrl.inspector;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('flotcharts error', e);
|
||||||
|
ctrl.error = e.message || "Render Error";
|
||||||
|
ctrl.renderError = true;
|
||||||
|
ctrl.inspector = {error: e};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incrementRenderCounter) {
|
||||||
|
ctrl.renderingCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFlotOptions(panel) {
|
||||||
|
const stack = panel.stack ? true : null;
|
||||||
|
let options = {
|
||||||
hooks: {
|
hooks: {
|
||||||
draw: [drawHook],
|
draw: [drawHook],
|
||||||
processOffset: [processOffsetHook],
|
processOffset: [processOffsetHook],
|
||||||
@ -278,96 +328,7 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
mode: 'x'
|
mode: 'x'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
return options;
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
let series = data[i];
|
|
||||||
series.data = series.getFlotPairs(series.nullPointMode || panel.nullPointMode);
|
|
||||||
|
|
||||||
// if hidden remove points and disable stack
|
|
||||||
if (ctrl.hiddenSeries[series.alias]) {
|
|
||||||
series.data = [];
|
|
||||||
series.stack = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (panel.xaxis.mode) {
|
|
||||||
case 'series': {
|
|
||||||
options.series.bars.barWidth = 0.7;
|
|
||||||
options.series.bars.align = 'center';
|
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
let series = data[i];
|
|
||||||
series.data = [[i + 1, series.stats[panel.xaxis.values[0]]]];
|
|
||||||
}
|
|
||||||
|
|
||||||
addXSeriesAxis(options);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'histogram': {
|
|
||||||
let bucketSize: number;
|
|
||||||
let values = getSeriesValues(data);
|
|
||||||
|
|
||||||
if (data.length && values.length) {
|
|
||||||
let histMin = _.min(_.map(data, s => s.stats.min));
|
|
||||||
let histMax = _.max(_.map(data, s => s.stats.max));
|
|
||||||
let ticks = panel.xaxis.buckets || panelWidth / 50;
|
|
||||||
bucketSize = tickStep(histMin, histMax, ticks);
|
|
||||||
let histogram = convertValuesToHistogram(values, bucketSize);
|
|
||||||
data[0].data = histogram;
|
|
||||||
options.series.bars.barWidth = bucketSize * 0.8;
|
|
||||||
} else {
|
|
||||||
bucketSize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
addXHistogramAxis(options, bucketSize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'table': {
|
|
||||||
options.series.bars.barWidth = 0.7;
|
|
||||||
options.series.bars.align = 'center';
|
|
||||||
addXTableAxis(options);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
options.series.bars.barWidth = getMinTimeStepOfSeries(data) / 1.5;
|
|
||||||
addTimeAxis(options);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thresholdManager.addFlotOptions(options, panel);
|
|
||||||
eventManager.addFlotEvents(annotations, options);
|
|
||||||
configureAxisOptions(data, options);
|
|
||||||
|
|
||||||
sortedSeries = sortSeries(data, ctrl.panel);
|
|
||||||
|
|
||||||
function callPlot(incrementRenderCounter) {
|
|
||||||
try {
|
|
||||||
plot = $.plot(elem, sortedSeries, options);
|
|
||||||
if (ctrl.renderError) {
|
|
||||||
delete ctrl.error;
|
|
||||||
delete ctrl.inspector;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('flotcharts error', e);
|
|
||||||
ctrl.error = e.message || "Render Error";
|
|
||||||
ctrl.renderError = true;
|
|
||||||
ctrl.inspector = {error: e};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (incrementRenderCounter) {
|
|
||||||
ctrl.renderingCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldDelayDraw(panel)) {
|
|
||||||
// temp fix for legends on the side, need to render twice to get dimensions right
|
|
||||||
callPlot(false);
|
|
||||||
setTimeout(function() { callPlot(true); }, 50);
|
|
||||||
legendSideLastValue = panel.legend.rightSide;
|
|
||||||
} else {
|
|
||||||
callPlot(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortSeries(series, panel) {
|
function sortSeries(series, panel) {
|
||||||
@ -410,16 +371,6 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldDelayDraw(panel) {
|
|
||||||
if (panel.legend.rightSide) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (legendSideLastValue !== null && panel.legend.rightSide !== legendSideLastValue) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTimeAxis(options) {
|
function addTimeAxis(options) {
|
||||||
var ticks = panelWidth / 100;
|
var ticks = panelWidth / 100;
|
||||||
var min = _.isUndefined(ctrl.range.from) ? null : ctrl.range.from.valueOf();
|
var min = _.isUndefined(ctrl.range.from) ? null : ctrl.range.from.valueOf();
|
||||||
@ -519,7 +470,7 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function configureAxisOptions(data, options) {
|
function configureYAxisOptions(data, options) {
|
||||||
var defaults = {
|
var defaults = {
|
||||||
position: 'left',
|
position: 'left',
|
||||||
show: panel.yaxes[0].show,
|
show: panel.yaxes[0].show,
|
||||||
|
@ -1,215 +0,0 @@
|
|||||||
define([
|
|
||||||
'angular',
|
|
||||||
'lodash',
|
|
||||||
'jquery',
|
|
||||||
],
|
|
||||||
function (angular, _, $) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var module = angular.module('grafana.directives');
|
|
||||||
|
|
||||||
module.directive('graphLegend', function(popoverSrv, $timeout) {
|
|
||||||
|
|
||||||
return {
|
|
||||||
link: function(scope, elem) {
|
|
||||||
var $container = $('<section class="graph-legend"></section>');
|
|
||||||
var firstRender = true;
|
|
||||||
var ctrl = scope.ctrl;
|
|
||||||
var panel = ctrl.panel;
|
|
||||||
var data;
|
|
||||||
var seriesList;
|
|
||||||
var i;
|
|
||||||
|
|
||||||
ctrl.events.on('render', function() {
|
|
||||||
data = ctrl.seriesList;
|
|
||||||
if (data) {
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function getSeriesIndexForElement(el) {
|
|
||||||
return el.parents('[data-series-index]').data('series-index');
|
|
||||||
}
|
|
||||||
|
|
||||||
function openColorSelector(e) {
|
|
||||||
// if we clicked inside poup container ignore click
|
|
||||||
if ($(e.target).parents('.popover').length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var el = $(e.currentTarget).find('.fa-minus');
|
|
||||||
var index = getSeriesIndexForElement(el);
|
|
||||||
var series = seriesList[index];
|
|
||||||
|
|
||||||
$timeout(function() {
|
|
||||||
popoverSrv.show({
|
|
||||||
element: el[0],
|
|
||||||
position: 'bottom center',
|
|
||||||
template: '<series-color-picker series="series" onToggleAxis="toggleAxis" onColorChange="colorSelected">' +
|
|
||||||
'</series-color-picker>',
|
|
||||||
openOn: 'hover',
|
|
||||||
model: {
|
|
||||||
series: series,
|
|
||||||
toggleAxis: function() {
|
|
||||||
ctrl.toggleAxis(series);
|
|
||||||
},
|
|
||||||
colorSelected: function(color) {
|
|
||||||
ctrl.changeSeriesColor(series, color);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleSeries(e) {
|
|
||||||
var el = $(e.currentTarget);
|
|
||||||
var index = getSeriesIndexForElement(el);
|
|
||||||
var seriesInfo = seriesList[index];
|
|
||||||
var scrollPosition = $($container.children('tbody')).scrollTop();
|
|
||||||
ctrl.toggleSeries(seriesInfo, e);
|
|
||||||
$($container.children('tbody')).scrollTop(scrollPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortLegend(e) {
|
|
||||||
var el = $(e.currentTarget);
|
|
||||||
var stat = el.data('stat');
|
|
||||||
|
|
||||||
if (stat !== panel.legend.sort) { panel.legend.sortDesc = null; }
|
|
||||||
|
|
||||||
// if already sort ascending, disable sorting
|
|
||||||
if (panel.legend.sortDesc === false) {
|
|
||||||
panel.legend.sort = null;
|
|
||||||
panel.legend.sortDesc = null;
|
|
||||||
ctrl.render();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
panel.legend.sortDesc = !panel.legend.sortDesc;
|
|
||||||
panel.legend.sort = stat;
|
|
||||||
ctrl.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTableHeaderHtml(statName) {
|
|
||||||
if (!panel.legend[statName]) { return ""; }
|
|
||||||
var html = '<th class="pointer" data-stat="' + statName + '">' + statName;
|
|
||||||
|
|
||||||
if (panel.legend.sort === statName) {
|
|
||||||
var cssClass = panel.legend.sortDesc ? 'fa fa-caret-down' : 'fa fa-caret-up' ;
|
|
||||||
html += ' <span class="' + cssClass + '"></span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return html + '</th>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function render() {
|
|
||||||
if (!ctrl.panel.legend.show) {
|
|
||||||
elem.empty();
|
|
||||||
firstRender = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstRender) {
|
|
||||||
elem.append($container);
|
|
||||||
$container.on('click', '.graph-legend-icon', openColorSelector);
|
|
||||||
$container.on('click', '.graph-legend-alias', toggleSeries);
|
|
||||||
$container.on('click', 'th', sortLegend);
|
|
||||||
firstRender = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
seriesList = data;
|
|
||||||
|
|
||||||
$container.empty();
|
|
||||||
|
|
||||||
// Set min-width if side style and there is a value, otherwise remove the CSS propery
|
|
||||||
var width = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + "px" : "";
|
|
||||||
$container.css("min-width", width);
|
|
||||||
|
|
||||||
$container.toggleClass('graph-legend-table', panel.legend.alignAsTable === true);
|
|
||||||
|
|
||||||
var tableHeaderElem;
|
|
||||||
if (panel.legend.alignAsTable) {
|
|
||||||
var header = '<tr>';
|
|
||||||
header += '<th colspan="2" style="text-align:left"></th>';
|
|
||||||
if (panel.legend.values) {
|
|
||||||
header += getTableHeaderHtml('min');
|
|
||||||
header += getTableHeaderHtml('max');
|
|
||||||
header += getTableHeaderHtml('avg');
|
|
||||||
header += getTableHeaderHtml('current');
|
|
||||||
header += getTableHeaderHtml('total');
|
|
||||||
}
|
|
||||||
header += '</tr>';
|
|
||||||
tableHeaderElem = $(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.legend.sort) {
|
|
||||||
seriesList = _.sortBy(seriesList, function(series) {
|
|
||||||
return series.stats[panel.legend.sort];
|
|
||||||
});
|
|
||||||
if (panel.legend.sortDesc) {
|
|
||||||
seriesList = seriesList.reverse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var seriesShown = 0;
|
|
||||||
var seriesElements = [];
|
|
||||||
|
|
||||||
for (i = 0; i < seriesList.length; i++) {
|
|
||||||
var series = seriesList[i];
|
|
||||||
|
|
||||||
if (series.hideFromLegend(panel.legend)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var html = '<div class="graph-legend-series';
|
|
||||||
|
|
||||||
if (series.yaxis === 2) { html += ' graph-legend-series--right-y'; }
|
|
||||||
if (ctrl.hiddenSeries[series.alias]) { html += ' graph-legend-series-hidden'; }
|
|
||||||
html += '" data-series-index="' + i + '">';
|
|
||||||
html += '<div class="graph-legend-icon">';
|
|
||||||
html += '<i class="fa fa-minus pointer" style="color:' + series.color + '"></i>';
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
html += '<a class="graph-legend-alias pointer" title="' + series.aliasEscaped + '">' + series.aliasEscaped + '</a>';
|
|
||||||
|
|
||||||
if (panel.legend.values) {
|
|
||||||
var avg = series.formatValue(series.stats.avg);
|
|
||||||
var current = series.formatValue(series.stats.current);
|
|
||||||
var min = series.formatValue(series.stats.min);
|
|
||||||
var max = series.formatValue(series.stats.max);
|
|
||||||
var total = series.formatValue(series.stats.total);
|
|
||||||
|
|
||||||
if (panel.legend.min) { html += '<div class="graph-legend-value min">' + min + '</div>'; }
|
|
||||||
if (panel.legend.max) { html += '<div class="graph-legend-value max">' + max + '</div>'; }
|
|
||||||
if (panel.legend.avg) { html += '<div class="graph-legend-value avg">' + avg + '</div>'; }
|
|
||||||
if (panel.legend.current) { html += '<div class="graph-legend-value current">' + current + '</div>'; }
|
|
||||||
if (panel.legend.total) { html += '<div class="graph-legend-value total">' + total + '</div>'; }
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '</div>';
|
|
||||||
seriesElements.push($(html));
|
|
||||||
|
|
||||||
seriesShown++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.legend.alignAsTable) {
|
|
||||||
var maxHeight = ctrl.height;
|
|
||||||
|
|
||||||
if (!panel.legend.rightSide) {
|
|
||||||
maxHeight = maxHeight/2;
|
|
||||||
}
|
|
||||||
|
|
||||||
var topPadding = 6;
|
|
||||||
var tbodyElem = $('<tbody></tbody>');
|
|
||||||
tbodyElem.css("max-height", maxHeight - topPadding);
|
|
||||||
tbodyElem.append(tableHeaderElem);
|
|
||||||
tbodyElem.append(seriesElements);
|
|
||||||
$container.append(tbodyElem);
|
|
||||||
} else {
|
|
||||||
$container.append(seriesElements);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
233
public/app/plugins/panel/graph/legend.ts
Normal file
233
public/app/plugins/panel/graph/legend.ts
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
import angular from 'angular';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import PerfectScrollbar from 'perfect-scrollbar';
|
||||||
|
import {updateLegendValues} from 'app/core/core';
|
||||||
|
|
||||||
|
var module = angular.module('grafana.directives');
|
||||||
|
|
||||||
|
module.directive('graphLegend', function(popoverSrv, $timeout) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
link: function(scope, elem) {
|
||||||
|
var firstRender = true;
|
||||||
|
var ctrl = scope.ctrl;
|
||||||
|
var panel = ctrl.panel;
|
||||||
|
var data;
|
||||||
|
var seriesList;
|
||||||
|
var i;
|
||||||
|
var legendScrollbar;
|
||||||
|
|
||||||
|
scope.$on("$destroy", function() {
|
||||||
|
if (!legendScrollbar) {
|
||||||
|
legendScrollbar.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ctrl.events.on('render-legend', () => {
|
||||||
|
data = ctrl.seriesList;
|
||||||
|
if (data) {
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
ctrl.events.emit('legend-rendering-complete');
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateLegendDecimals() {
|
||||||
|
updateLegendValues(data, panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSeriesIndexForElement(el) {
|
||||||
|
return el.parents('[data-series-index]').data('series-index');
|
||||||
|
}
|
||||||
|
|
||||||
|
function openColorSelector(e) {
|
||||||
|
// if we clicked inside poup container ignore click
|
||||||
|
if ($(e.target).parents('.popover').length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var el = $(e.currentTarget).find('.fa-minus');
|
||||||
|
var index = getSeriesIndexForElement(el);
|
||||||
|
var series = seriesList[index];
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
popoverSrv.show({
|
||||||
|
element: el[0],
|
||||||
|
position: 'bottom center',
|
||||||
|
template: '<series-color-picker series="series" onToggleAxis="toggleAxis" onColorChange="colorSelected">' +
|
||||||
|
'</series-color-picker>',
|
||||||
|
openOn: 'hover',
|
||||||
|
model: {
|
||||||
|
series: series,
|
||||||
|
toggleAxis: function() {
|
||||||
|
ctrl.toggleAxis(series);
|
||||||
|
},
|
||||||
|
colorSelected: function(color) {
|
||||||
|
ctrl.changeSeriesColor(series, color);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSeries(e) {
|
||||||
|
var el = $(e.currentTarget);
|
||||||
|
var index = getSeriesIndexForElement(el);
|
||||||
|
var seriesInfo = seriesList[index];
|
||||||
|
var scrollPosition = $(elem.children('tbody')).scrollTop();
|
||||||
|
ctrl.toggleSeries(seriesInfo, e);
|
||||||
|
$(elem.children('tbody')).scrollTop(scrollPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortLegend(e) {
|
||||||
|
var el = $(e.currentTarget);
|
||||||
|
var stat = el.data('stat');
|
||||||
|
|
||||||
|
if (stat !== panel.legend.sort) { panel.legend.sortDesc = null; }
|
||||||
|
|
||||||
|
// if already sort ascending, disable sorting
|
||||||
|
if (panel.legend.sortDesc === false) {
|
||||||
|
panel.legend.sort = null;
|
||||||
|
panel.legend.sortDesc = null;
|
||||||
|
ctrl.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.legend.sortDesc = !panel.legend.sortDesc;
|
||||||
|
panel.legend.sort = stat;
|
||||||
|
ctrl.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTableHeaderHtml(statName) {
|
||||||
|
if (!panel.legend[statName]) { return ""; }
|
||||||
|
var html = '<th class="pointer" data-stat="' + statName + '">' + statName;
|
||||||
|
|
||||||
|
if (panel.legend.sort === statName) {
|
||||||
|
var cssClass = panel.legend.sortDesc ? 'fa fa-caret-down' : 'fa fa-caret-up' ;
|
||||||
|
html += ' <span class="' + cssClass + '"></span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return html + '</th>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
if (!ctrl.panel.legend.show) {
|
||||||
|
elem.empty();
|
||||||
|
firstRender = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstRender) {
|
||||||
|
elem.on('click', '.graph-legend-icon', openColorSelector);
|
||||||
|
elem.on('click', '.graph-legend-alias', toggleSeries);
|
||||||
|
elem.on('click', 'th', sortLegend);
|
||||||
|
firstRender = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
seriesList = data;
|
||||||
|
|
||||||
|
elem.empty();
|
||||||
|
|
||||||
|
// Set min-width if side style and there is a value, otherwise remove the CSS propery
|
||||||
|
var width = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + "px" : "";
|
||||||
|
elem.css("min-width", width);
|
||||||
|
|
||||||
|
elem.toggleClass('graph-legend-table', panel.legend.alignAsTable === true);
|
||||||
|
|
||||||
|
var tableHeaderElem;
|
||||||
|
if (panel.legend.alignAsTable) {
|
||||||
|
var header = '<tr>';
|
||||||
|
header += '<th colspan="2" style="text-align:left"></th>';
|
||||||
|
if (panel.legend.values) {
|
||||||
|
header += getTableHeaderHtml('min');
|
||||||
|
header += getTableHeaderHtml('max');
|
||||||
|
header += getTableHeaderHtml('avg');
|
||||||
|
header += getTableHeaderHtml('current');
|
||||||
|
header += getTableHeaderHtml('total');
|
||||||
|
}
|
||||||
|
header += '</tr>';
|
||||||
|
tableHeaderElem = $(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel.legend.sort) {
|
||||||
|
seriesList = _.sortBy(seriesList, function(series) {
|
||||||
|
return series.stats[panel.legend.sort];
|
||||||
|
});
|
||||||
|
if (panel.legend.sortDesc) {
|
||||||
|
seriesList = seriesList.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// render first time for getting proper legend height
|
||||||
|
if (!panel.legend.rightSide) {
|
||||||
|
renderLegendElement(tableHeaderElem);
|
||||||
|
updateLegendDecimals();
|
||||||
|
elem.empty();
|
||||||
|
} else {
|
||||||
|
updateLegendDecimals();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLegendElement(tableHeaderElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSeriesLegendElements() {
|
||||||
|
let seriesElements = [];
|
||||||
|
for (i = 0; i < seriesList.length; i++) {
|
||||||
|
var series = seriesList[i];
|
||||||
|
|
||||||
|
if (series.hideFromLegend(panel.legend)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<div class="graph-legend-series';
|
||||||
|
|
||||||
|
if (series.yaxis === 2) { html += ' graph-legend-series--right-y'; }
|
||||||
|
if (ctrl.hiddenSeries[series.alias]) { html += ' graph-legend-series-hidden'; }
|
||||||
|
html += '" data-series-index="' + i + '">';
|
||||||
|
html += '<div class="graph-legend-icon">';
|
||||||
|
html += '<i class="fa fa-minus pointer" style="color:' + series.color + '"></i>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<a class="graph-legend-alias pointer" title="' + series.aliasEscaped + '">' + series.aliasEscaped + '</a>';
|
||||||
|
|
||||||
|
if (panel.legend.values) {
|
||||||
|
var avg = series.formatValue(series.stats.avg);
|
||||||
|
var current = series.formatValue(series.stats.current);
|
||||||
|
var min = series.formatValue(series.stats.min);
|
||||||
|
var max = series.formatValue(series.stats.max);
|
||||||
|
var total = series.formatValue(series.stats.total);
|
||||||
|
|
||||||
|
if (panel.legend.min) { html += '<div class="graph-legend-value min">' + min + '</div>'; }
|
||||||
|
if (panel.legend.max) { html += '<div class="graph-legend-value max">' + max + '</div>'; }
|
||||||
|
if (panel.legend.avg) { html += '<div class="graph-legend-value avg">' + avg + '</div>'; }
|
||||||
|
if (panel.legend.current) { html += '<div class="graph-legend-value current">' + current + '</div>'; }
|
||||||
|
if (panel.legend.total) { html += '<div class="graph-legend-value total">' + total + '</div>'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
seriesElements.push($(html));
|
||||||
|
}
|
||||||
|
return seriesElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLegendElement(tableHeaderElem) {
|
||||||
|
var seriesElements = renderSeriesLegendElements();
|
||||||
|
|
||||||
|
if (panel.legend.alignAsTable) {
|
||||||
|
var tbodyElem = $('<tbody></tbody>');
|
||||||
|
tbodyElem.append(tableHeaderElem);
|
||||||
|
tbodyElem.append(seriesElements);
|
||||||
|
elem.append(tbodyElem);
|
||||||
|
} else {
|
||||||
|
elem.append(seriesElements);
|
||||||
|
|
||||||
|
if (!legendScrollbar) {
|
||||||
|
legendScrollbar = new PerfectScrollbar(elem[0]);
|
||||||
|
} else {
|
||||||
|
legendScrollbar.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
@ -87,6 +87,8 @@ describe('grafanaGraph', function() {
|
|||||||
|
|
||||||
$.plot = ctx.plotSpy = sinon.spy();
|
$.plot = ctx.plotSpy = sinon.spy();
|
||||||
ctrl.events.emit('render', ctx.data);
|
ctrl.events.emit('render', ctx.data);
|
||||||
|
ctrl.events.emit('render-legend');
|
||||||
|
ctrl.events.emit('legend-rendering-complete');
|
||||||
ctx.plotData = ctx.plotSpy.getCall(0).args[1];
|
ctx.plotData = ctx.plotSpy.getCall(0).args[1];
|
||||||
ctx.plotOptions = ctx.plotSpy.getCall(0).args[2];
|
ctx.plotOptions = ctx.plotSpy.getCall(0).args[2];
|
||||||
}));
|
}));
|
||||||
|
@ -1,22 +1,10 @@
|
|||||||
var template = `
|
var template = `
|
||||||
<div class="graph-wrapper" ng-class="{'graph-legend-rightside': ctrl.panel.legend.rightSide}">
|
<div class="graph-panel" ng-class="{'graph-panel--legend-right': ctrl.panel.legend.rightSide}">
|
||||||
<div class="graph-canvas-wrapper">
|
<div class="graph-panel__chart" grafana-graph ng-dblclick="ctrl.zoomOut()">
|
||||||
|
|
||||||
<div class="datapoints-warning" ng-if="ctrl.dataWarning">
|
|
||||||
<span class="small" bs-tooltip="ctrl.dataWarning.tip">{{ctrl.dataWarning.title}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div grafana-graph class="histogram-chart" ng-dblclick="ctrl.zoomOut()">
|
<div class="graph-legend" graph-legend></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="graph-legend-wrapper" graph-legend></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default template;
|
export default template;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,31 @@
|
|||||||
.graph-canvas-wrapper {
|
.graph-panel {
|
||||||
position: relative;
|
display: flex;
|
||||||
cursor: crosshair;
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&--legend-right {
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.graph-legend {
|
||||||
|
flex: 0 1 10px;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph-legend-series {
|
||||||
|
display: block;
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph-legend-table .graph-legend-series {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.histogram-chart {
|
.graph-panel__chart {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: crosshair;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.datapoints-warning {
|
.datapoints-warning {
|
||||||
@ -22,11 +43,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.graph-legend {
|
.graph-legend {
|
||||||
@include clearfix();
|
flex: 0 1 auto;
|
||||||
|
max-height: 30%;
|
||||||
margin: 0 $spacer;
|
margin: 0 $spacer;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: calc(100% - $spacer);
|
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.popover-content {
|
.popover-content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -89,7 +111,9 @@
|
|||||||
display: block;
|
display: block;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
height: 100%;
|
||||||
padding-bottom: 1px;
|
padding-bottom: 1px;
|
||||||
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph-legend-series {
|
.graph-legend-series {
|
||||||
@ -160,39 +184,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph-legend-rightside {
|
|
||||||
|
|
||||||
&.graph-wrapper {
|
|
||||||
display: table;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-canvas-wrapper {
|
|
||||||
display: table-cell;
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-legend-wrapper {
|
|
||||||
display: table-cell;
|
|
||||||
vertical-align: top;
|
|
||||||
position: relative;
|
|
||||||
left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-legend {
|
|
||||||
margin: 0 0 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-legend-series {
|
|
||||||
display: block;
|
|
||||||
padding-left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-legend-table .graph-legend-series {
|
|
||||||
display: table-row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-legend-series-hidden {
|
.graph-legend-series-hidden {
|
||||||
.graph-legend-value,
|
.graph-legend-value,
|
||||||
|
Loading…
Reference in New Issue
Block a user