mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
GraphNG: update uPlot v1.5.0 (#29763)
This commit is contained in:
parent
7dd387ce57
commit
77d6100b44
@ -71,7 +71,7 @@
|
||||
"react-transition-group": "4.4.1",
|
||||
"slate": "0.47.8",
|
||||
"tinycolor2": "1.4.1",
|
||||
"uplot": "1.4.7"
|
||||
"uplot": "1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "16.0.0",
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
FieldMatcherID,
|
||||
} from '@grafana/data';
|
||||
import { AlignedFrameWithGapTest } from '../uPlot/types';
|
||||
import uPlot, { AlignedData, AlignedDataWithGapTest } from 'uplot';
|
||||
import uPlot, { AlignedData } from 'uplot';
|
||||
import { XYFieldMatchers } from './GraphNG';
|
||||
|
||||
// the results ofter passing though data
|
||||
@ -103,7 +103,7 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
|
||||
}
|
||||
|
||||
// do the actual alignment (outerJoin on the first arrays)
|
||||
let { data: alignedData, isGap } = outerJoinValues(valuesFromFrames, skipGaps);
|
||||
let { data: alignedData, isGap } = uPlot.join(valuesFromFrames, skipGaps);
|
||||
|
||||
if (alignedData!.length !== sourceFields.length) {
|
||||
throw new Error('outerJoinValues lost a field?');
|
||||
@ -121,76 +121,3 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
|
||||
isGap,
|
||||
};
|
||||
}
|
||||
|
||||
// skipGaps is a tables-matched bool array indicating which series can skip storing indices of original nulls
|
||||
export function outerJoinValues(tables: AlignedData[], skipGaps?: boolean[][]): AlignedDataWithGapTest {
|
||||
if (tables.length === 1) {
|
||||
return {
|
||||
data: tables[0],
|
||||
isGap: skipGaps ? (u: uPlot, seriesIdx: number, dataIdx: number) => !skipGaps[0][seriesIdx] : () => true,
|
||||
};
|
||||
}
|
||||
|
||||
let xVals: Set<number> = new Set();
|
||||
let xNulls: Array<Set<number>> = [new Set()];
|
||||
|
||||
for (let ti = 0; ti < tables.length; ti++) {
|
||||
let t = tables[ti];
|
||||
let xs = t[0];
|
||||
let len = xs.length;
|
||||
let nulls: Set<number> = new Set();
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
xVals.add(xs[i]);
|
||||
}
|
||||
|
||||
for (let j = 1; j < t.length; j++) {
|
||||
if (skipGaps == null || !skipGaps[ti][j]) {
|
||||
let ys = t[j];
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (ys[i] == null) {
|
||||
nulls.add(xs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xNulls.push(nulls);
|
||||
}
|
||||
|
||||
let data: AlignedData = [Array.from(xVals).sort((a, b) => a - b)];
|
||||
|
||||
let alignedLen = data[0].length;
|
||||
|
||||
let xIdxs = new Map();
|
||||
|
||||
for (let i = 0; i < alignedLen; i++) {
|
||||
xIdxs.set(data[0][i], i);
|
||||
}
|
||||
|
||||
for (const t of tables) {
|
||||
let xs = t[0];
|
||||
|
||||
for (let j = 1; j < t.length; j++) {
|
||||
let ys = t[j];
|
||||
|
||||
let yVals = Array(alignedLen).fill(null);
|
||||
|
||||
for (let i = 0; i < ys.length; i++) {
|
||||
yVals[xIdxs.get(xs[i])] = ys[i];
|
||||
}
|
||||
|
||||
data.push(yVals);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data: data,
|
||||
isGap(u: uPlot, seriesIdx: number, dataIdx: number) {
|
||||
// u.data has to be AlignedDate
|
||||
let xVal = u.data[0][dataIdx];
|
||||
return xNulls[seriesIdx].has(xVal!);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -20,12 +20,19 @@ describe('UPlotConfigBuilder', () => {
|
||||
expect(builder.getConfig()).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"axes": Array [],
|
||||
"cursor": Object {
|
||||
"drag": Object {
|
||||
"setScale": false,
|
||||
},
|
||||
},
|
||||
"scales": Object {
|
||||
"scale-x": Object {
|
||||
"auto": false,
|
||||
"range": [Function],
|
||||
"time": true,
|
||||
},
|
||||
"scale-y": Object {
|
||||
"auto": true,
|
||||
"range": [Function],
|
||||
"time": false,
|
||||
},
|
||||
@ -95,6 +102,11 @@ describe('UPlotConfigBuilder', () => {
|
||||
"values": Array [],
|
||||
},
|
||||
],
|
||||
"cursor": Object {
|
||||
"drag": Object {
|
||||
"setScale": false,
|
||||
},
|
||||
},
|
||||
"scales": Object {},
|
||||
"series": Array [
|
||||
Object {},
|
||||
@ -138,6 +150,11 @@ describe('UPlotConfigBuilder', () => {
|
||||
expect(builder.getConfig()).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"axes": Array [],
|
||||
"cursor": Object {
|
||||
"drag": Object {
|
||||
"setScale": false,
|
||||
},
|
||||
},
|
||||
"scales": Object {},
|
||||
"series": Array [
|
||||
Object {},
|
||||
|
@ -4,6 +4,7 @@ import { SeriesProps, UPlotSeriesBuilder } from './UPlotSeriesBuilder';
|
||||
import { AxisProps, UPlotAxisBuilder } from './UPlotAxisBuilder';
|
||||
import { AxisPlacement } from '../config';
|
||||
import { Cursor } from 'uplot';
|
||||
import { defaultsDeep } from 'lodash';
|
||||
|
||||
export class UPlotConfigBuilder {
|
||||
private series: UPlotSeriesBuilder[] = [];
|
||||
@ -68,9 +69,12 @@ export class UPlotConfigBuilder {
|
||||
config.scales = this.scales.reduce((acc, s) => {
|
||||
return { ...acc, ...s.getConfig() };
|
||||
}, {});
|
||||
if (this.cursor) {
|
||||
config.cursor = this.cursor;
|
||||
}
|
||||
|
||||
config.cursor = this.cursor || {};
|
||||
|
||||
// prevent client-side zoom from triggering at the end of a selection
|
||||
defaultsDeep(config.cursor, { drag: { setScale: false } });
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
|
||||
return {
|
||||
[scaleKey]: {
|
||||
time: isTime,
|
||||
auto: !isTime,
|
||||
range: range ?? this.range,
|
||||
},
|
||||
};
|
||||
|
@ -1,9 +1,19 @@
|
||||
import tinycolor from 'tinycolor2';
|
||||
import uPlot, { Series } from 'uplot';
|
||||
import { DrawStyle, LineConfig, AreaConfig, PointsConfig, PointVisibility, LineInterpolation } from '../config';
|
||||
import { barsBuilder, smoothBuilder, stepBeforeBuilder, stepAfterBuilder } from '../paths';
|
||||
import { PlotConfigBuilder } from '../types';
|
||||
|
||||
const pathBuilders = uPlot.paths;
|
||||
|
||||
const barWidthFactor = 0.6;
|
||||
const barMaxWidth = Infinity;
|
||||
|
||||
const barsBuilder = pathBuilders.bars!({ size: [barWidthFactor, barMaxWidth] });
|
||||
const linearBuilder = pathBuilders.linear!();
|
||||
const smoothBuilder = pathBuilders.spline!();
|
||||
const stepBeforeBuilder = pathBuilders.stepped!({ align: -1 });
|
||||
const stepAfterBuilder = pathBuilders.stepped!({ align: 1 });
|
||||
|
||||
export interface SeriesProps extends LineConfig, AreaConfig, PointsConfig {
|
||||
drawStyle: DrawStyle;
|
||||
scaleKey: string;
|
||||
@ -32,15 +42,8 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
|
||||
} else {
|
||||
lineConfig.stroke = lineColor;
|
||||
lineConfig.width = lineWidth;
|
||||
lineConfig.paths = (
|
||||
self: uPlot,
|
||||
seriesIdx: number,
|
||||
idx0: number,
|
||||
idx1: number,
|
||||
extendGap: Series.ExtendGap,
|
||||
buildClip: Series.BuildClip
|
||||
) => {
|
||||
let pathsBuilder = self.paths;
|
||||
lineConfig.paths = (self: uPlot, seriesIdx: number, idx0: number, idx1: number) => {
|
||||
let pathsBuilder = linearBuilder;
|
||||
|
||||
if (drawStyle === DrawStyle.Bars) {
|
||||
pathsBuilder = barsBuilder;
|
||||
@ -54,7 +57,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
|
||||
}
|
||||
}
|
||||
|
||||
return pathsBuilder(self, seriesIdx, idx0, idx1, extendGap, buildClip);
|
||||
return pathsBuilder(self, seriesIdx, idx0, idx1);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -22,10 +22,6 @@ describe('usePlotConfig', () => {
|
||||
// "focus": Object {
|
||||
// "alpha": 1,
|
||||
// },
|
||||
// "gutters": Object {
|
||||
// "x": 8,
|
||||
// "y": 8,
|
||||
// },
|
||||
// "height": 0,
|
||||
// "hooks": Object {},
|
||||
// "legend": Object {
|
||||
@ -66,10 +62,6 @@ describe('usePlotConfig', () => {
|
||||
// "focus": Object {
|
||||
// "alpha": 1,
|
||||
// },
|
||||
// "gutters": Object {
|
||||
// "x": 8,
|
||||
// "y": 8,
|
||||
// },
|
||||
// "height": 0,
|
||||
// "hooks": Object {},
|
||||
// "legend": Object {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { PlotPlugin } from './types';
|
||||
import { pluginLog } from './utils';
|
||||
import uPlot, { Options } from 'uplot';
|
||||
import uPlot, { Options, PaddingSide } from 'uplot';
|
||||
import { getTimeZoneInfo, TimeZone } from '@grafana/data';
|
||||
import { usePlotPluginContext } from './context';
|
||||
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
|
||||
@ -85,7 +85,11 @@ export const usePlotPlugins = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const DEFAULT_PLOT_CONFIG = {
|
||||
const paddingSide: PaddingSide = (u, side, sidesWithAxes, cycleNum) => {
|
||||
return sidesWithAxes[side] ? 0 : 8;
|
||||
};
|
||||
|
||||
export const DEFAULT_PLOT_CONFIG: Partial<Options> = {
|
||||
focus: {
|
||||
alpha: 1,
|
||||
},
|
||||
@ -97,10 +101,7 @@ export const DEFAULT_PLOT_CONFIG = {
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
gutters: {
|
||||
x: 8,
|
||||
y: 8,
|
||||
},
|
||||
padding: [paddingSide, paddingSide, paddingSide, paddingSide],
|
||||
series: [],
|
||||
hooks: {},
|
||||
};
|
||||
|
@ -1,370 +0,0 @@
|
||||
import uPlot, { Series } from 'uplot';
|
||||
|
||||
export const barsBuilder: Series.PathBuilder = (
|
||||
u: uPlot,
|
||||
seriesIdx: number,
|
||||
idx0: number,
|
||||
idx1: number,
|
||||
extendGap: Series.ExtendGap,
|
||||
buildClip: Series.BuildClip
|
||||
) => {
|
||||
const series = u.series[seriesIdx];
|
||||
const xdata = u.data[0];
|
||||
const ydata = u.data[seriesIdx];
|
||||
const scaleX = u.series[0].scale as string;
|
||||
const scaleY = series.scale as string;
|
||||
|
||||
const gapFactor = 0.25;
|
||||
|
||||
let gap = (u.width * gapFactor) / (idx1 - idx0);
|
||||
let maxWidth = Infinity;
|
||||
|
||||
//@ts-ignore
|
||||
let fillTo = series.fillTo(u, seriesIdx, series.min, series.max);
|
||||
|
||||
let y0Pos = u.valToPos(fillTo, scaleY, true);
|
||||
let colWid = u.bbox.width / (idx1 - idx0);
|
||||
|
||||
let strokeWidth = Math.round(series.width! * devicePixelRatio);
|
||||
|
||||
let barWid = Math.round(Math.min(maxWidth, colWid - gap) - strokeWidth);
|
||||
|
||||
let stroke = new Path2D();
|
||||
|
||||
for (let i = idx0; i <= idx1; i++) {
|
||||
let yVal = ydata[i];
|
||||
|
||||
if (yVal == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let xVal = u.scales.x.distr === 2 ? i : xdata[i];
|
||||
|
||||
// TODO: all xPos can be pre-computed once for all series in aligned set
|
||||
let xPos = u.valToPos(xVal, scaleX, true);
|
||||
let yPos = u.valToPos(yVal, scaleY, true);
|
||||
|
||||
let lft = Math.round(xPos - barWid / 2);
|
||||
let btm = Math.round(Math.max(yPos, y0Pos));
|
||||
let top = Math.round(Math.min(yPos, y0Pos));
|
||||
let barHgt = btm - top;
|
||||
|
||||
stroke.rect(lft, top, barWid, barHgt);
|
||||
}
|
||||
|
||||
let fill = series.fill != null ? new Path2D(stroke) : undefined;
|
||||
|
||||
return {
|
||||
stroke,
|
||||
fill,
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
const enum StepSide {
|
||||
Before,
|
||||
After,
|
||||
}
|
||||
*/
|
||||
|
||||
export const stepBeforeBuilder = stepBuilderFactory(false);
|
||||
export const stepAfterBuilder = stepBuilderFactory(true);
|
||||
|
||||
// babel does not support inlined const enums, so this uses a boolean flag for perf
|
||||
// possible workaround: https://github.com/dosentmatter/babel-plugin-const-enum
|
||||
function stepBuilderFactory(after: boolean): Series.PathBuilder {
|
||||
return (
|
||||
u: uPlot,
|
||||
seriesIdx: number,
|
||||
idx0: number,
|
||||
idx1: number,
|
||||
extendGap: Series.ExtendGap,
|
||||
buildClip: Series.BuildClip
|
||||
) => {
|
||||
const series = u.series[seriesIdx];
|
||||
const xdata = u.data[0];
|
||||
const ydata = u.data[seriesIdx];
|
||||
const scaleX = u.series[0].scale as string;
|
||||
const scaleY = series.scale as string;
|
||||
const halfStroke = series.width! / 2;
|
||||
|
||||
const stroke = new Path2D();
|
||||
|
||||
// find first non-null dataPt
|
||||
while (ydata[idx0] == null) {
|
||||
idx0++;
|
||||
}
|
||||
|
||||
// find last-null dataPt
|
||||
while (ydata[idx1] == null) {
|
||||
idx1--;
|
||||
}
|
||||
|
||||
let gaps: Series.Gaps = [];
|
||||
let inGap = false;
|
||||
let prevYPos = Math.round(u.valToPos(ydata[idx0]!, scaleY, true));
|
||||
let firstXPos = Math.round(u.valToPos(xdata[idx0], scaleX, true));
|
||||
let prevXPos = firstXPos;
|
||||
|
||||
stroke.moveTo(firstXPos, prevYPos);
|
||||
|
||||
for (let i = idx0 + 1; i <= idx1; i++) {
|
||||
let yVal1 = ydata[i];
|
||||
|
||||
let x1 = Math.round(u.valToPos(xdata[i], scaleX, true));
|
||||
|
||||
if (yVal1 == null) {
|
||||
//@ts-ignore
|
||||
if (series.isGap(u, seriesIdx, i)) {
|
||||
extendGap(gaps, prevXPos, x1);
|
||||
inGap = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let y1 = Math.round(u.valToPos(yVal1, scaleY, true));
|
||||
|
||||
if (inGap) {
|
||||
extendGap(gaps, prevXPos, x1);
|
||||
|
||||
// don't clip vertical extenders
|
||||
if (prevYPos !== y1) {
|
||||
let lastGap = gaps[gaps.length - 1];
|
||||
lastGap[0] += halfStroke;
|
||||
lastGap[1] -= halfStroke;
|
||||
}
|
||||
|
||||
inGap = false;
|
||||
}
|
||||
|
||||
if (after) {
|
||||
stroke.lineTo(x1, prevYPos);
|
||||
} else {
|
||||
stroke.lineTo(prevXPos, y1);
|
||||
}
|
||||
|
||||
stroke.lineTo(x1, y1);
|
||||
|
||||
prevYPos = y1;
|
||||
prevXPos = x1;
|
||||
}
|
||||
|
||||
const fill = new Path2D(stroke);
|
||||
|
||||
//@ts-ignore
|
||||
let fillTo = series.fillTo(u, seriesIdx, series.min, series.max);
|
||||
|
||||
let minY = Math.round(u.valToPos(fillTo, scaleY, true));
|
||||
|
||||
fill.lineTo(prevXPos, minY);
|
||||
fill.lineTo(firstXPos, minY);
|
||||
|
||||
let clip = !series.spanGaps ? buildClip(gaps) : null;
|
||||
|
||||
return {
|
||||
stroke,
|
||||
fill,
|
||||
clip,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// adapted from https://gist.github.com/nicholaswmin/c2661eb11cad5671d816 (MIT)
|
||||
/**
|
||||
* Interpolates a Catmull-Rom Spline through a series of x/y points
|
||||
* Converts the CR Spline to Cubic Beziers for use with SVG items
|
||||
*
|
||||
* If 'alpha' is 0.5 then the 'Centripetal' variant is used
|
||||
* If 'alpha' is 1 then the 'Chordal' variant is used
|
||||
*
|
||||
*
|
||||
* @param {Array} data - Array of points, each point in object literal holding x/y values
|
||||
* @return {String} d - SVG string with cubic bezier curves representing the Catmull-Rom Spline
|
||||
*/
|
||||
function catmullRomFitting(xCoords: number[], yCoords: number[], alpha: number) {
|
||||
const path = new Path2D();
|
||||
|
||||
const dataLen = xCoords.length;
|
||||
|
||||
let p0x,
|
||||
p0y,
|
||||
p1x,
|
||||
p1y,
|
||||
p2x,
|
||||
p2y,
|
||||
p3x,
|
||||
p3y,
|
||||
bp1x,
|
||||
bp1y,
|
||||
bp2x,
|
||||
bp2y,
|
||||
d1,
|
||||
d2,
|
||||
d3,
|
||||
A,
|
||||
B,
|
||||
N,
|
||||
M,
|
||||
d3powA,
|
||||
d2powA,
|
||||
d3pow2A,
|
||||
d2pow2A,
|
||||
d1pow2A,
|
||||
d1powA;
|
||||
|
||||
path.moveTo(Math.round(xCoords[0]), Math.round(yCoords[0]));
|
||||
|
||||
for (let i = 0; i < dataLen - 1; i++) {
|
||||
let p0i = i === 0 ? 0 : i - 1;
|
||||
|
||||
p0x = xCoords[p0i];
|
||||
p0y = yCoords[p0i];
|
||||
|
||||
p1x = xCoords[i];
|
||||
p1y = yCoords[i];
|
||||
|
||||
p2x = xCoords[i + 1];
|
||||
p2y = yCoords[i + 1];
|
||||
|
||||
if (i + 2 < dataLen) {
|
||||
p3x = xCoords[i + 2];
|
||||
p3y = yCoords[i + 2];
|
||||
} else {
|
||||
p3x = p2x;
|
||||
p3y = p2y;
|
||||
}
|
||||
|
||||
d1 = Math.sqrt(Math.pow(p0x - p1x, 2) + Math.pow(p0y - p1y, 2));
|
||||
d2 = Math.sqrt(Math.pow(p1x - p2x, 2) + Math.pow(p1y - p2y, 2));
|
||||
d3 = Math.sqrt(Math.pow(p2x - p3x, 2) + Math.pow(p2y - p3y, 2));
|
||||
|
||||
// Catmull-Rom to Cubic Bezier conversion matrix
|
||||
|
||||
// A = 2d1^2a + 3d1^a * d2^a + d3^2a
|
||||
// B = 2d3^2a + 3d3^a * d2^a + d2^2a
|
||||
|
||||
// [ 0 1 0 0 ]
|
||||
// [ -d2^2a /N A/N d1^2a /N 0 ]
|
||||
// [ 0 d3^2a /M B/M -d2^2a /M ]
|
||||
// [ 0 0 1 0 ]
|
||||
|
||||
d3powA = Math.pow(d3, alpha);
|
||||
d3pow2A = Math.pow(d3, 2 * alpha);
|
||||
d2powA = Math.pow(d2, alpha);
|
||||
d2pow2A = Math.pow(d2, 2 * alpha);
|
||||
d1powA = Math.pow(d1, alpha);
|
||||
d1pow2A = Math.pow(d1, 2 * alpha);
|
||||
|
||||
A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A;
|
||||
B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A;
|
||||
N = 3 * d1powA * (d1powA + d2powA);
|
||||
|
||||
if (N > 0) {
|
||||
N = 1 / N;
|
||||
}
|
||||
|
||||
M = 3 * d3powA * (d3powA + d2powA);
|
||||
|
||||
if (M > 0) {
|
||||
M = 1 / M;
|
||||
}
|
||||
|
||||
bp1x = (-d2pow2A * p0x + A * p1x + d1pow2A * p2x) * N;
|
||||
bp1y = (-d2pow2A * p0y + A * p1y + d1pow2A * p2y) * N;
|
||||
|
||||
bp2x = (d3pow2A * p1x + B * p2x - d2pow2A * p3x) * M;
|
||||
bp2y = (d3pow2A * p1y + B * p2y - d2pow2A * p3y) * M;
|
||||
|
||||
if (bp1x === 0 && bp1y === 0) {
|
||||
bp1x = p1x;
|
||||
bp1y = p1y;
|
||||
}
|
||||
|
||||
if (bp2x === 0 && bp2y === 0) {
|
||||
bp2x = p2x;
|
||||
bp2y = p2y;
|
||||
}
|
||||
|
||||
path.bezierCurveTo(bp1x, bp1y, bp2x, bp2y, p2x, p2y);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
export const smoothBuilder: Series.PathBuilder = (
|
||||
u: uPlot,
|
||||
seriesIdx: number,
|
||||
idx0: number,
|
||||
idx1: number,
|
||||
extendGap: Series.ExtendGap,
|
||||
buildClip: Series.BuildClip
|
||||
) => {
|
||||
const series = u.series[seriesIdx];
|
||||
const xdata = u.data[0];
|
||||
const ydata = u.data[seriesIdx];
|
||||
const scaleX = u.series[0].scale as string;
|
||||
const scaleY = series.scale as string;
|
||||
|
||||
// find first non-null dataPt
|
||||
while (ydata[idx0] == null) {
|
||||
idx0++;
|
||||
}
|
||||
|
||||
// find last-null dataPt
|
||||
while (ydata[idx1] == null) {
|
||||
idx1--;
|
||||
}
|
||||
|
||||
let gaps: Series.Gaps = [];
|
||||
let inGap = false;
|
||||
let firstXPos = Math.round(u.valToPos(xdata[idx0], scaleX, true));
|
||||
let prevXPos = firstXPos;
|
||||
|
||||
let xCoords = [];
|
||||
let yCoords = [];
|
||||
|
||||
for (let i = idx0; i <= idx1; i++) {
|
||||
let yVal = ydata[i];
|
||||
let xVal = xdata[i];
|
||||
let xPos = u.valToPos(xVal, scaleX, true);
|
||||
|
||||
if (yVal == null) {
|
||||
//@ts-ignore
|
||||
if (series.isGap(u, seriesIdx, i)) {
|
||||
extendGap(gaps, prevXPos + 1, xPos);
|
||||
inGap = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else {
|
||||
if (inGap) {
|
||||
extendGap(gaps, prevXPos + 1, xPos + 1);
|
||||
inGap = false;
|
||||
}
|
||||
|
||||
xCoords.push((prevXPos = xPos));
|
||||
yCoords.push(u.valToPos(ydata[i]!, scaleY, true));
|
||||
}
|
||||
}
|
||||
|
||||
const stroke = catmullRomFitting(xCoords, yCoords, 0.5);
|
||||
|
||||
const fill = new Path2D(stroke);
|
||||
|
||||
//@ts-ignore
|
||||
let fillTo = series.fillTo(u, seriesIdx, series.min, series.max);
|
||||
|
||||
let minY = Math.round(u.valToPos(fillTo, scaleY, true));
|
||||
|
||||
fill.lineTo(prevXPos, minY);
|
||||
fill.lineTo(firstXPos, minY);
|
||||
|
||||
let clip = !series.spanGaps ? buildClip(gaps) : null;
|
||||
|
||||
return {
|
||||
stroke,
|
||||
fill,
|
||||
clip,
|
||||
};
|
||||
};
|
@ -67,6 +67,10 @@ export const SelectionPlugin: React.FC<SelectionPluginProps> = ({ onSelect, onDi
|
||||
width: u.select.width,
|
||||
},
|
||||
});
|
||||
|
||||
// manually hide selected region (since cursor.drag.setScale = false)
|
||||
/* @ts-ignore */
|
||||
u.setSelect({ left: 0, width: 0 }, false);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import throttle from 'lodash/throttle';
|
||||
import { rangeUtil, RawTimeRange } from '@grafana/data';
|
||||
import { Options } from 'uplot';
|
||||
import { PlotPlugin, PlotProps } from './types';
|
||||
|
||||
@ -9,11 +8,6 @@ export const timeFormatToTemplate = (f: string) => {
|
||||
return f.replace(ALLOWED_FORMAT_STRINGS_REGEX, match => `{${match}}`);
|
||||
};
|
||||
|
||||
export function rangeToMinMax(timeRange: RawTimeRange): [number, number] {
|
||||
const v = rangeUtil.convertRawToRange(timeRange);
|
||||
return [v.from.valueOf() / 1000, v.to.valueOf() / 1000];
|
||||
}
|
||||
|
||||
export const buildPlotConfig = (props: PlotProps, plugins: Record<string, PlotPlugin>): Options => {
|
||||
return {
|
||||
width: props.width,
|
||||
|
@ -25703,10 +25703,10 @@ update-notifier@^2.5.0:
|
||||
semver-diff "^2.0.0"
|
||||
xdg-basedir "^3.0.0"
|
||||
|
||||
uplot@1.4.7:
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.4.7.tgz#feab0cf48b569184bfefacf29308deb021bad765"
|
||||
integrity sha512-zyVwJWuZ5/DOULPpJZb8XhVsSFgWXvitPg1acAwIjZEblUfKAwvfHXA6+Gz6JruCWCokNyC4f3ZGgMcqT0Bv0Q==
|
||||
uplot@1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.5.0.tgz#c2814cd7a073ed12b0a1d2fc985f3d9cd33bf0cd"
|
||||
integrity sha512-ghzlTe4rgoYiM98pMUJCplQhSmhyJV8Pv+obV3sHDoC+VBrZDc8fxXnUNGUVcKhUW2eqyeO5D0huardirwOmHw==
|
||||
|
||||
upper-case@^1.1.1:
|
||||
version "1.1.3"
|
||||
|
Loading…
Reference in New Issue
Block a user