mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Histogram: enable client-side zoom (with bucket snapping) (#35220)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import uPlot, { Cursor, Band, Hooks, Select } from 'uplot';
|
import uPlot, { Cursor, Band, Hooks, Select } from 'uplot';
|
||||||
import { defaultsDeep } from 'lodash';
|
import { merge } from 'lodash';
|
||||||
import {
|
import {
|
||||||
DataFrame,
|
DataFrame,
|
||||||
DefaultTimeZone,
|
DefaultTimeZone,
|
||||||
@@ -17,6 +17,24 @@ import { AxisPlacement } from '../config';
|
|||||||
import { pluginLog } from '../utils';
|
import { pluginLog } from '../utils';
|
||||||
import { getThresholdsDrawHook, UPlotThresholdOptions } from './UPlotThresholds';
|
import { getThresholdsDrawHook, UPlotThresholdOptions } from './UPlotThresholds';
|
||||||
|
|
||||||
|
const cursorDefaults: Cursor = {
|
||||||
|
// prevent client-side zoom from triggering at the end of a selection
|
||||||
|
drag: { setScale: false },
|
||||||
|
points: {
|
||||||
|
/*@ts-ignore*/
|
||||||
|
size: (u, seriesIdx) => u.series[seriesIdx].points.size * 2,
|
||||||
|
/*@ts-ignore*/
|
||||||
|
width: (u, seriesIdx, size) => size / 4,
|
||||||
|
/*@ts-ignore*/
|
||||||
|
stroke: (u, seriesIdx) => u.series[seriesIdx].points.stroke(u, seriesIdx) + '80',
|
||||||
|
/*@ts-ignore*/
|
||||||
|
fill: (u, seriesIdx) => u.series[seriesIdx].points.stroke(u, seriesIdx),
|
||||||
|
},
|
||||||
|
focus: {
|
||||||
|
prox: 30,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export class UPlotConfigBuilder {
|
export class UPlotConfigBuilder {
|
||||||
private series: UPlotSeriesBuilder[] = [];
|
private series: UPlotSeriesBuilder[] = [];
|
||||||
private axes: Record<string, UPlotAxisBuilder> = {};
|
private axes: Record<string, UPlotAxisBuilder> = {};
|
||||||
@@ -98,7 +116,7 @@ export class UPlotConfigBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCursor(cursor?: Cursor) {
|
setCursor(cursor?: Cursor) {
|
||||||
this.cursor = { ...this.cursor, ...cursor };
|
this.cursor = merge({}, this.cursor, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelect(select: Select) {
|
setSelect(select: Select) {
|
||||||
@@ -153,11 +171,9 @@ export class UPlotConfigBuilder {
|
|||||||
|
|
||||||
config.hooks = this.hooks;
|
config.hooks = this.hooks;
|
||||||
|
|
||||||
/* @ts-ignore */
|
|
||||||
// uPlot types don't export the Select interface prior to 1.6.4
|
|
||||||
config.select = this.select;
|
config.select = this.select;
|
||||||
|
|
||||||
config.cursor = this.cursor || {};
|
config.cursor = merge({}, cursorDefaults, this.cursor);
|
||||||
|
|
||||||
config.tzDate = this.tzDate;
|
config.tzDate = this.tzDate;
|
||||||
|
|
||||||
@@ -181,26 +197,6 @@ export class UPlotConfigBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cursorDefaults: Cursor = {
|
|
||||||
// prevent client-side zoom from triggering at the end of a selection
|
|
||||||
drag: { setScale: false },
|
|
||||||
points: {
|
|
||||||
/*@ts-ignore*/
|
|
||||||
size: (u, seriesIdx) => u.series[seriesIdx].points.size * 2,
|
|
||||||
/*@ts-ignore*/
|
|
||||||
width: (u, seriesIdx, size) => size / 4,
|
|
||||||
/*@ts-ignore*/
|
|
||||||
stroke: (u, seriesIdx) => u.series[seriesIdx].points.stroke(u, seriesIdx) + '80',
|
|
||||||
/*@ts-ignore*/
|
|
||||||
fill: (u, seriesIdx) => u.series[seriesIdx].points.stroke(u, seriesIdx),
|
|
||||||
},
|
|
||||||
focus: {
|
|
||||||
prox: 30,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultsDeep(config.cursor, cursorDefaults);
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,14 @@ import {
|
|||||||
import { PanelOptions } from './models.gen';
|
import { PanelOptions } from './models.gen';
|
||||||
import { ScaleDistribution } from '@grafana/ui/src/components/uPlot/models.gen';
|
import { ScaleDistribution } from '@grafana/ui/src/components/uPlot/models.gen';
|
||||||
|
|
||||||
|
function incrRoundDn(num: number, incr: number) {
|
||||||
|
return Math.floor(num / incr) * incr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrRoundUp(num: number, incr: number) {
|
||||||
|
return Math.ceil(num / incr) * incr;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HistogramProps extends Themeable2 {
|
export interface HistogramProps extends Themeable2 {
|
||||||
options: PanelOptions; // used for diff
|
options: PanelOptions; // used for diff
|
||||||
alignedFrame: DataFrame; // This could take HistogramFields
|
alignedFrame: DataFrame; // This could take HistogramFields
|
||||||
@@ -70,7 +78,24 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => {
|
|||||||
distribution: ScaleDistribution.Linear,
|
distribution: ScaleDistribution.Linear,
|
||||||
orientation: ScaleOrientation.Horizontal,
|
orientation: ScaleOrientation.Horizontal,
|
||||||
direction: ScaleDirection.Right,
|
direction: ScaleDirection.Right,
|
||||||
range: (u) => [u.data[0][0], u.data[0][u.data[0].length - 1] + bucketSize],
|
range: (u, wantedMin, wantedMax) => {
|
||||||
|
let fullRangeMin = u.data[0][0];
|
||||||
|
let fullRangeMax = u.data[0][u.data[0].length - 1];
|
||||||
|
|
||||||
|
// snap to bucket divisors...
|
||||||
|
|
||||||
|
if (wantedMax === fullRangeMax) {
|
||||||
|
wantedMax += bucketSize;
|
||||||
|
} else {
|
||||||
|
wantedMax = incrRoundUp(wantedMax, bucketSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantedMin > fullRangeMin) {
|
||||||
|
wantedMin = incrRoundDn(wantedMin, bucketSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [wantedMin, wantedMax];
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addScale({
|
builder.addScale({
|
||||||
@@ -114,6 +139,14 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => {
|
|||||||
theme,
|
theme,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.setCursor({
|
||||||
|
drag: {
|
||||||
|
x: true,
|
||||||
|
y: false,
|
||||||
|
setScale: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
let pathBuilder = uPlot.paths.bars!({ align: 1, size: [1, Infinity] });
|
let pathBuilder = uPlot.paths.bars!({ align: 1, size: [1, Infinity] });
|
||||||
|
|
||||||
let seriesIndex = 0;
|
let seriesIndex = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user