Histogram: enable client-side zoom (with bucket snapping) (#35220)

This commit is contained in:
Leon Sorokin 2021-06-03 20:05:47 -05:00 committed by GitHub
parent e1af571efd
commit 43d3d97562
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 26 deletions

View File

@ -1,5 +1,5 @@
import uPlot, { Cursor, Band, Hooks, Select } from 'uplot';
import { defaultsDeep } from 'lodash';
import { merge } from 'lodash';
import {
DataFrame,
DefaultTimeZone,
@ -17,6 +17,24 @@ import { AxisPlacement } from '../config';
import { pluginLog } from '../utils';
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 {
private series: UPlotSeriesBuilder[] = [];
private axes: Record<string, UPlotAxisBuilder> = {};
@ -98,7 +116,7 @@ export class UPlotConfigBuilder {
}
setCursor(cursor?: Cursor) {
this.cursor = { ...this.cursor, ...cursor };
this.cursor = merge({}, this.cursor, cursor);
}
setSelect(select: Select) {
@ -153,11 +171,9 @@ export class UPlotConfigBuilder {
config.hooks = this.hooks;
/* @ts-ignore */
// uPlot types don't export the Select interface prior to 1.6.4
config.select = this.select;
config.cursor = this.cursor || {};
config.cursor = merge({}, cursorDefaults, this.cursor);
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;
}

View File

@ -27,6 +27,14 @@ import {
import { PanelOptions } from './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 {
options: PanelOptions; // used for diff
alignedFrame: DataFrame; // This could take HistogramFields
@ -70,7 +78,24 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => {
distribution: ScaleDistribution.Linear,
orientation: ScaleOrientation.Horizontal,
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({
@ -114,6 +139,14 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => {
theme,
});
builder.setCursor({
drag: {
x: true,
y: false,
setScale: true,
},
});
let pathBuilder = uPlot.paths.bars!({ align: 1, size: [1, Infinity] });
let seriesIndex = 0;