mirror of
https://github.com/grafana/grafana.git
synced 2025-08-03 00:42:03 +08:00
GraphNG: refactor by-value color schemes (#37670)
* GraphNG: account for top canvas padding in gradient gen for color scheme/thresholds-by-value * Updated test dashboard * Added fix for issue when scaleMin was same as threshold * fixed firefox issue * revert docs changes * update gdev dash for easier comparisons & regression spotting * refactor * optimize gradient re-gen/re-use and color more tinycolor.setAlpha() -> alpha(). update uPlot to dev build. * fix percentage steps * implement % threshold region rendering * crisp threshold line rendering * simplify * WIP: hoverpoint dynamic color interpolation * fix hover point color interp * re-use gradient gen to draw threshold areas * re-implement by-value color scales * tweak comment * mimic tinycolor behavior in colorManipulator.alpha() for empty colors * explicitly disable hover points for BarChart and Histogram * reduce test failures and required changes to tests * fix barchart tests * uPlot 1.6.15 Co-authored-by: Torkel Ödegaard <torkel@grafana.com> Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -255,6 +255,10 @@ export function emphasize(color: string, coefficient = 0.15) {
|
||||
* @beta
|
||||
*/
|
||||
export function alpha(color: string, value: number) {
|
||||
if (color === '') {
|
||||
return '#000000';
|
||||
}
|
||||
|
||||
value = clamp(value);
|
||||
|
||||
// hex 6, hex 8 (w/alpha)
|
||||
|
@ -74,7 +74,7 @@
|
||||
"react-transition-group": "4.4.1",
|
||||
"slate": "0.47.8",
|
||||
"tinycolor2": "1.4.1",
|
||||
"uplot": "1.6.14"
|
||||
"uplot": "1.6.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "16.0.0",
|
||||
|
@ -436,7 +436,7 @@ describe('UPlotConfigBuilder', () => {
|
||||
theme: darkTheme,
|
||||
});
|
||||
|
||||
expect(builder.getConfig().series[1].fill).toBe('rgba(255, 170, 187, 0.5)');
|
||||
expect(builder.getConfig().series[1].fill).toBe('#FFAABB80');
|
||||
});
|
||||
|
||||
it('when fillColor is set ignore fillOpacity', () => {
|
||||
|
@ -25,10 +25,6 @@ const cursorDefaults: Cursor = {
|
||||
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,
|
||||
@ -50,6 +46,7 @@ export class UPlotConfigBuilder {
|
||||
private hooks: Hooks.Arrays = {};
|
||||
private tz: string | undefined = undefined;
|
||||
private sync = false;
|
||||
private frame: DataFrame | undefined = undefined;
|
||||
// to prevent more than one threshold per scale
|
||||
private thresholds: Record<string, UPlotThresholdOptions> = {};
|
||||
/**
|
||||
@ -158,7 +155,10 @@ export class UPlotConfigBuilder {
|
||||
}
|
||||
|
||||
setPrepData(prepData: PrepData) {
|
||||
this.prepData = prepData;
|
||||
this.prepData = (frame) => {
|
||||
this.frame = frame;
|
||||
return prepData(frame);
|
||||
};
|
||||
}
|
||||
|
||||
setSync() {
|
||||
@ -187,7 +187,25 @@ export class UPlotConfigBuilder {
|
||||
|
||||
config.select = this.select;
|
||||
|
||||
config.cursor = merge({}, cursorDefaults, this.cursor);
|
||||
const pointColorFn = (alphaHex = '') => (u: uPlot, seriesIdx: number) => {
|
||||
/*@ts-ignore*/
|
||||
let s = u.series[seriesIdx].points._stroke;
|
||||
|
||||
// interpolate for gradients/thresholds
|
||||
if (typeof s !== 'string') {
|
||||
let field = this.frame!.fields[seriesIdx];
|
||||
s = field.display!(field.values.get(u.cursor.idxs![seriesIdx]!)).color!;
|
||||
}
|
||||
|
||||
return s + alphaHex;
|
||||
};
|
||||
|
||||
config.cursor = merge({}, cursorDefaults, this.cursor, {
|
||||
points: {
|
||||
stroke: pointColorFn('80'),
|
||||
fill: pointColorFn(),
|
||||
},
|
||||
});
|
||||
|
||||
config.tzDate = this.tzDate;
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { DataFrameFieldIndex, FALLBACK_COLOR, FieldColorMode, GrafanaTheme2, ThresholdsConfig } from '@grafana/data';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import {
|
||||
colorManipulator,
|
||||
DataFrameFieldIndex,
|
||||
FALLBACK_COLOR,
|
||||
FieldColorMode,
|
||||
GrafanaTheme2,
|
||||
ThresholdsConfig,
|
||||
} from '@grafana/data';
|
||||
import uPlot, { Series } from 'uplot';
|
||||
import {
|
||||
BarAlignment,
|
||||
@ -55,16 +61,18 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
|
||||
} = this.props;
|
||||
|
||||
let lineConfig: Partial<Series> = {};
|
||||
const lineColor = this.getLineColor();
|
||||
|
||||
let lineColor = this.getLineColor();
|
||||
|
||||
// DrawStyle.Points mode also needs this for fill/stroke sharing & re-use in series.points. see getColor() below.
|
||||
lineConfig.stroke = lineColor;
|
||||
|
||||
if (pathBuilder != null) {
|
||||
lineConfig.paths = pathBuilder;
|
||||
lineConfig.stroke = lineColor;
|
||||
lineConfig.width = lineWidth;
|
||||
} else if (drawStyle === DrawStyle.Points) {
|
||||
lineConfig.paths = () => null;
|
||||
} else if (drawStyle != null) {
|
||||
lineConfig.stroke = lineColor;
|
||||
lineConfig.width = lineWidth;
|
||||
if (lineStyle && lineStyle.fill !== 'solid') {
|
||||
if (lineStyle.fill === 'dot') {
|
||||
@ -84,10 +92,14 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
|
||||
};
|
||||
}
|
||||
|
||||
const useColor: uPlot.Series.Stroke =
|
||||
// @ts-ignore
|
||||
typeof lineColor === 'string' ? lineColor : (u, seriesIdx) => u.series[seriesIdx]._stroke;
|
||||
|
||||
const pointsConfig: Partial<Series> = {
|
||||
points: {
|
||||
stroke: lineColor,
|
||||
fill: lineColor,
|
||||
stroke: useColor,
|
||||
fill: useColor,
|
||||
size: pointSize,
|
||||
filter: pointsFilter,
|
||||
},
|
||||
@ -153,7 +165,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
|
||||
return getScaleGradientFn(opacityPercent, theme, colorMode, thresholds);
|
||||
default:
|
||||
if (opacityPercent > 0) {
|
||||
return tinycolor(lineColor).setAlpha(opacityPercent).toString();
|
||||
return colorManipulator.alpha(lineColor ?? '', opacityPercent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { getColorForTheme, GrafanaTheme2, ThresholdsConfig } from '@grafana/data';
|
||||
import { GrafanaTheme2, ThresholdsConfig, ThresholdsMode } from '@grafana/data';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { GraphThresholdsStyleConfig, GraphTresholdsStyleMode } from '../config';
|
||||
import { getDataRange, GradientDirection, scaleGradient } from './gradientFills';
|
||||
|
||||
export interface UPlotThresholdOptions {
|
||||
scaleKey: string;
|
||||
@ -13,7 +14,6 @@ export function getThresholdsDrawHook(options: UPlotThresholdOptions) {
|
||||
return (u: uPlot) => {
|
||||
const ctx = u.ctx;
|
||||
const { scaleKey, thresholds, theme, config } = options;
|
||||
const { steps } = thresholds;
|
||||
const { min: xMin, max: xMax } = u.scales.x;
|
||||
const { min: yMin, max: yMax } = u.scales[scaleKey];
|
||||
|
||||
@ -21,6 +21,18 @@ export function getThresholdsDrawHook(options: UPlotThresholdOptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { steps, mode } = thresholds;
|
||||
|
||||
if (mode === ThresholdsMode.Percentage) {
|
||||
let [min, max] = getDataRange(u, scaleKey);
|
||||
let range = max - min;
|
||||
|
||||
steps = steps.map((step) => ({
|
||||
...step,
|
||||
value: min + range * (step.value / 100),
|
||||
}));
|
||||
}
|
||||
|
||||
function addLines() {
|
||||
// Thresholds below a transparent threshold is treated like "less than", and line drawn previous threshold
|
||||
let transparentIndex = 0;
|
||||
@ -40,9 +52,9 @@ export function getThresholdsDrawHook(options: UPlotThresholdOptions) {
|
||||
|
||||
// if we are below a transparent index treat this a less then threshold, use previous thresholds color
|
||||
if (transparentIndex >= idx && idx > 0) {
|
||||
color = tinycolor(getColorForTheme(steps[idx - 1].color, theme.v1));
|
||||
color = tinycolor(theme.visualization.getColorByName(steps[idx - 1].color));
|
||||
} else {
|
||||
color = tinycolor(getColorForTheme(step.color, theme.v1));
|
||||
color = tinycolor(theme.visualization.getColorByName(step.color));
|
||||
}
|
||||
|
||||
// Unless alpha specififed set to default value
|
||||
@ -50,10 +62,10 @@ export function getThresholdsDrawHook(options: UPlotThresholdOptions) {
|
||||
color.setAlpha(0.7);
|
||||
}
|
||||
|
||||
let x0 = u.valToPos(xMin!, 'x', true);
|
||||
let y0 = u.valToPos(step.value, scaleKey, true);
|
||||
let x1 = u.valToPos(xMax!, 'x', true);
|
||||
let y1 = u.valToPos(step.value, scaleKey, true);
|
||||
let x0 = Math.round(u.valToPos(xMin!, 'x', true));
|
||||
let y0 = Math.round(u.valToPos(step.value, scaleKey, true));
|
||||
let x1 = Math.round(u.valToPos(xMax!, 'x', true));
|
||||
let y1 = Math.round(u.valToPos(step.value, scaleKey, true));
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 2;
|
||||
@ -66,49 +78,26 @@ export function getThresholdsDrawHook(options: UPlotThresholdOptions) {
|
||||
}
|
||||
|
||||
function addAreas() {
|
||||
for (let idx = 0; idx < steps.length; idx++) {
|
||||
const step = steps[idx];
|
||||
let grd = scaleGradient(
|
||||
u,
|
||||
u.series[1].scale!,
|
||||
GradientDirection.Up,
|
||||
steps.map((step) => {
|
||||
let color = tinycolor(theme.visualization.getColorByName(step.color));
|
||||
|
||||
// skip thresholds that cannot be seen
|
||||
if (step.value > yMax!) {
|
||||
continue;
|
||||
}
|
||||
if (color.getAlpha() === 1) {
|
||||
color.setAlpha(0.15);
|
||||
}
|
||||
|
||||
// if this is the last step make the next step the same color but +Infinity
|
||||
const nextStep =
|
||||
idx + 1 < steps.length
|
||||
? steps[idx + 1]
|
||||
: {
|
||||
...step,
|
||||
value: Infinity,
|
||||
};
|
||||
return [step.value, color.toString()];
|
||||
}),
|
||||
true
|
||||
);
|
||||
|
||||
let color = tinycolor(getColorForTheme(step.color, theme.v1));
|
||||
|
||||
// Ignore fully transparent colors
|
||||
const alpha = color.getAlpha();
|
||||
if (alpha === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/// if no alpha set automatic alpha
|
||||
if (alpha === 1) {
|
||||
color = color.setAlpha(0.15);
|
||||
}
|
||||
|
||||
let value = step.value === -Infinity ? yMin : step.value;
|
||||
let nextValue = nextStep.value === Infinity || nextStep.value > yMax! ? yMax : nextStep.value;
|
||||
|
||||
let x0 = u.valToPos(xMin ?? 0, 'x', true);
|
||||
let y0 = u.valToPos(value ?? 0, scaleKey, true);
|
||||
let x1 = u.valToPos(xMax ?? 1, 'x', true);
|
||||
let y1 = u.valToPos(nextValue ?? 1, scaleKey, true);
|
||||
|
||||
ctx.save();
|
||||
ctx.fillStyle = color.toString();
|
||||
ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
|
||||
ctx.restore();
|
||||
}
|
||||
ctx.save();
|
||||
ctx.fillStyle = grd;
|
||||
ctx.fillRect(u.bbox.left, u.bbox.top, u.bbox.width, u.bbox.height);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
switch (config.mode) {
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { FieldColorMode, FieldColorModeId, GrafanaTheme2, ThresholdsConfig, ThresholdsMode } from '@grafana/data';
|
||||
import {
|
||||
colorManipulator,
|
||||
FieldColorMode,
|
||||
FieldColorModeId,
|
||||
GrafanaTheme2,
|
||||
ThresholdsConfig,
|
||||
ThresholdsMode,
|
||||
} from '@grafana/data';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import uPlot from 'uplot';
|
||||
import { getCanvasContext } from '../../../utils/measureText';
|
||||
@ -11,8 +18,8 @@ export function getOpacityGradientFn(
|
||||
const ctx = getCanvasContext();
|
||||
const gradient = ctx.createLinearGradient(0, plot.bbox.top, 0, plot.bbox.top + plot.bbox.height);
|
||||
|
||||
gradient.addColorStop(0, tinycolor(color).setAlpha(opacity).toRgbString());
|
||||
gradient.addColorStop(1, tinycolor(color).setAlpha(0).toRgbString());
|
||||
gradient.addColorStop(0, colorManipulator.alpha(color, opacity));
|
||||
gradient.addColorStop(1, colorManipulator.alpha(color, 0));
|
||||
|
||||
return gradient;
|
||||
};
|
||||
@ -39,10 +46,132 @@ export function getHueGradientFn(
|
||||
return gradient;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Experimental & quick and dirty test
|
||||
* Not being used
|
||||
*/
|
||||
|
||||
export enum GradientDirection {
|
||||
'Right' = 0,
|
||||
'Up' = 1,
|
||||
}
|
||||
|
||||
type ValueStop = [value: number, color: string];
|
||||
|
||||
type ScaleValueStops = ValueStop[];
|
||||
|
||||
export function scaleGradient(
|
||||
u: uPlot,
|
||||
scaleKey: string,
|
||||
dir: GradientDirection,
|
||||
scaleStops: ScaleValueStops,
|
||||
discrete = false
|
||||
) {
|
||||
let scale = u.scales[scaleKey];
|
||||
|
||||
// we want the stop below or at the scaleMax
|
||||
// and the stop below or at the scaleMin, else the stop above scaleMin
|
||||
let minStopIdx: number | null = null;
|
||||
let maxStopIdx: number | null = null;
|
||||
|
||||
for (let i = 0; i < scaleStops.length; i++) {
|
||||
let stopVal = scaleStops[i][0];
|
||||
|
||||
if (stopVal <= scale.min! || minStopIdx == null) {
|
||||
minStopIdx = i;
|
||||
}
|
||||
|
||||
maxStopIdx = i;
|
||||
|
||||
if (stopVal >= scale.max!) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (minStopIdx === maxStopIdx) {
|
||||
return scaleStops[minStopIdx!][1];
|
||||
}
|
||||
|
||||
let minStopVal = scaleStops[minStopIdx!][0];
|
||||
let maxStopVal = scaleStops[maxStopIdx!][0];
|
||||
|
||||
if (minStopVal === -Infinity) {
|
||||
minStopVal = scale.min!;
|
||||
}
|
||||
|
||||
if (maxStopVal === Infinity) {
|
||||
maxStopVal = scale.max!;
|
||||
}
|
||||
|
||||
let minStopPos = Math.round(u.valToPos(minStopVal, scaleKey, true));
|
||||
let maxStopPos = Math.round(u.valToPos(maxStopVal, scaleKey, true));
|
||||
|
||||
let range = minStopPos - maxStopPos;
|
||||
|
||||
let x0, y0, x1, y1;
|
||||
|
||||
if (dir === GradientDirection.Up) {
|
||||
x0 = x1 = 0;
|
||||
y0 = minStopPos;
|
||||
y1 = maxStopPos;
|
||||
} else {
|
||||
y0 = y1 = 0;
|
||||
x0 = minStopPos;
|
||||
x1 = maxStopPos;
|
||||
}
|
||||
|
||||
let ctx = getCanvasContext();
|
||||
|
||||
let grd = ctx.createLinearGradient(x0, y0, x1, y1);
|
||||
|
||||
let prevColor: string;
|
||||
|
||||
for (let i = minStopIdx!; i <= maxStopIdx!; i++) {
|
||||
let s = scaleStops[i];
|
||||
|
||||
let stopPos =
|
||||
i === minStopIdx ? minStopPos : i === maxStopIdx ? maxStopPos : Math.round(u.valToPos(s[0], scaleKey, true));
|
||||
|
||||
let pct = (minStopPos - stopPos) / range;
|
||||
|
||||
if (discrete && i > minStopIdx!) {
|
||||
grd.addColorStop(pct, prevColor!);
|
||||
}
|
||||
|
||||
grd.addColorStop(pct, (prevColor = s[1]));
|
||||
}
|
||||
|
||||
return grd;
|
||||
}
|
||||
|
||||
export function getDataRange(plot: uPlot, scaleKey: string) {
|
||||
let sc = plot.scales[scaleKey];
|
||||
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
|
||||
plot.series.forEach((ser, seriesIdx) => {
|
||||
if (ser.show && ser.scale === scaleKey) {
|
||||
// uPlot skips finding data min/max when a scale has a pre-defined range
|
||||
if (ser.min == null) {
|
||||
let data = plot.data[seriesIdx];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i] != null) {
|
||||
min = Math.min(min, data[i]!);
|
||||
max = Math.max(max, data[i]!);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
min = Math.min(min, ser.min!);
|
||||
max = Math.max(max, ser.max!);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (max === min) {
|
||||
min = sc.min!;
|
||||
max = sc.max!;
|
||||
}
|
||||
|
||||
return [min, max];
|
||||
}
|
||||
|
||||
export function getScaleGradientFn(
|
||||
opacity: number,
|
||||
theme: GrafanaTheme2,
|
||||
@ -58,68 +187,41 @@ export function getScaleGradientFn(
|
||||
}
|
||||
|
||||
return (plot: uPlot, seriesIdx: number) => {
|
||||
// A uplot bug (I think) where this is called before there is bbox
|
||||
// Color used for cursor highlight, not sure what to do here as this is called before we have bbox
|
||||
// and only once so same color is used for all points
|
||||
if (plot.bbox.top == null) {
|
||||
return theme.colors.text.primary;
|
||||
}
|
||||
let scaleKey = plot.series[seriesIdx].scale!;
|
||||
|
||||
const ctx = getCanvasContext();
|
||||
const gradient = ctx.createLinearGradient(0, plot.bbox.top, 0, plot.bbox.top + plot.bbox.height);
|
||||
const canvasHeight = plot.bbox.height;
|
||||
const series = plot.series[seriesIdx];
|
||||
const scale = plot.scales[series.scale!];
|
||||
const scaleMin = scale.min ?? 0;
|
||||
const scaleMax = scale.max ?? 100;
|
||||
const scaleRange = scaleMax - scaleMin;
|
||||
|
||||
const addColorStop = (value: number, color: string) => {
|
||||
const pos = plot.valToPos(value, series.scale!, true);
|
||||
// when above range we get negative values here
|
||||
if (pos < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const percent = Math.max(pos / canvasHeight, 0);
|
||||
const realColor = tinycolor(theme.visualization.getColorByName(color)).setAlpha(opacity).toString();
|
||||
const colorStopPos = Math.min(percent, 1);
|
||||
|
||||
gradient.addColorStop(colorStopPos, realColor);
|
||||
};
|
||||
let gradient: CanvasGradient | string = '';
|
||||
|
||||
if (colorMode.id === FieldColorModeId.Thresholds) {
|
||||
for (let idx = 0; idx < thresholds.steps.length; idx++) {
|
||||
const step = thresholds.steps[idx];
|
||||
|
||||
if (thresholds.mode === ThresholdsMode.Absolute) {
|
||||
const value = step.value === -Infinity ? scaleMin : step.value;
|
||||
addColorStop(value, step.color);
|
||||
|
||||
if (thresholds.steps.length > idx + 1) {
|
||||
// to make the gradient discrete
|
||||
addColorStop(thresholds.steps[idx + 1].value - 0.00000001, step.color);
|
||||
}
|
||||
} else {
|
||||
const percent = step.value === -Infinity ? 0 : step.value;
|
||||
const realValue = (percent / 100) * scaleRange;
|
||||
addColorStop(realValue, step.color);
|
||||
|
||||
// to make the gradient discrete
|
||||
if (thresholds.steps.length > idx + 1) {
|
||||
// to make the gradient discrete
|
||||
const nextValue = (thresholds.steps[idx + 1].value / 100) * scaleRange - 0.0000001;
|
||||
addColorStop(nextValue, step.color);
|
||||
}
|
||||
}
|
||||
if (thresholds.mode === ThresholdsMode.Absolute) {
|
||||
const valueStops = thresholds.steps.map(
|
||||
(step) =>
|
||||
[step.value, colorManipulator.alpha(theme.visualization.getColorByName(step.color), opacity)] as ValueStop
|
||||
);
|
||||
gradient = scaleGradient(plot, scaleKey, GradientDirection.Up, valueStops, true);
|
||||
} else {
|
||||
const [min, max] = getDataRange(plot, scaleKey);
|
||||
const range = max - min;
|
||||
const valueStops = thresholds.steps.map(
|
||||
(step) =>
|
||||
[
|
||||
min + range * (step.value / 100),
|
||||
colorManipulator.alpha(theme.visualization.getColorByName(step.color), opacity),
|
||||
] as ValueStop
|
||||
);
|
||||
gradient = scaleGradient(plot, scaleKey, GradientDirection.Up, valueStops, true);
|
||||
}
|
||||
} else if (colorMode.getColors) {
|
||||
const colors = colorMode.getColors(theme);
|
||||
const stepValue = (scaleMax - scaleMin) / colors.length;
|
||||
|
||||
for (let idx = 0; idx < colors.length; idx++) {
|
||||
addColorStop(scaleMin + stepValue * idx, colors[idx]);
|
||||
}
|
||||
const [min, max] = getDataRange(plot, scaleKey);
|
||||
const range = max - min;
|
||||
const valueStops = colors.map(
|
||||
(color, i) =>
|
||||
[
|
||||
min + range * (i / (colors.length - 1)),
|
||||
colorManipulator.alpha(theme.visualization.getColorByName(color), opacity),
|
||||
] as ValueStop
|
||||
);
|
||||
gradient = scaleGradient(plot, scaleKey, GradientDirection.Up, valueStops, false);
|
||||
}
|
||||
|
||||
return gradient;
|
||||
|
@ -63,6 +63,7 @@ Object {
|
||||
},
|
||||
"points": Object {
|
||||
"fill": [Function],
|
||||
"show": false,
|
||||
"size": [Function],
|
||||
"stroke": [Function],
|
||||
"width": [Function],
|
||||
@ -191,6 +192,7 @@ Object {
|
||||
},
|
||||
"points": Object {
|
||||
"fill": [Function],
|
||||
"show": false,
|
||||
"size": [Function],
|
||||
"stroke": [Function],
|
||||
"width": [Function],
|
||||
@ -319,6 +321,7 @@ Object {
|
||||
},
|
||||
"points": Object {
|
||||
"fill": [Function],
|
||||
"show": false,
|
||||
"size": [Function],
|
||||
"stroke": [Function],
|
||||
"width": [Function],
|
||||
@ -447,6 +450,7 @@ Object {
|
||||
},
|
||||
"points": Object {
|
||||
"fill": [Function],
|
||||
"show": false,
|
||||
"size": [Function],
|
||||
"stroke": [Function],
|
||||
"width": [Function],
|
||||
@ -575,6 +579,7 @@ Object {
|
||||
},
|
||||
"points": Object {
|
||||
"fill": [Function],
|
||||
"show": false,
|
||||
"size": [Function],
|
||||
"stroke": [Function],
|
||||
"width": [Function],
|
||||
@ -703,6 +708,7 @@ Object {
|
||||
},
|
||||
"points": Object {
|
||||
"fill": [Function],
|
||||
"show": false,
|
||||
"size": [Function],
|
||||
"stroke": [Function],
|
||||
"width": [Function],
|
||||
@ -831,6 +837,7 @@ Object {
|
||||
},
|
||||
"points": Object {
|
||||
"fill": [Function],
|
||||
"show": false,
|
||||
"size": [Function],
|
||||
"stroke": [Function],
|
||||
"width": [Function],
|
||||
@ -959,6 +966,7 @@ Object {
|
||||
},
|
||||
"points": Object {
|
||||
"fill": [Function],
|
||||
"show": false,
|
||||
"size": [Function],
|
||||
"stroke": [Function],
|
||||
"width": [Function],
|
||||
|
@ -324,6 +324,7 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) {
|
||||
cursor: {
|
||||
x: false,
|
||||
y: false,
|
||||
points: { show: false },
|
||||
},
|
||||
// scale & axis opts
|
||||
xValues,
|
||||
|
@ -146,6 +146,7 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => {
|
||||
});
|
||||
|
||||
builder.setCursor({
|
||||
points: { show: false },
|
||||
drag: {
|
||||
x: true,
|
||||
y: false,
|
||||
|
@ -23909,10 +23909,10 @@ update-notifier@^2.5.0:
|
||||
semver-diff "^2.0.0"
|
||||
xdg-basedir "^3.0.0"
|
||||
|
||||
uplot@1.6.14:
|
||||
version "1.6.14"
|
||||
resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.14.tgz#49edfaea3090a9c71d8ae389780b90635aeda3e0"
|
||||
integrity sha512-I/fO/pujHe6uurtCEVy6L0Vy6/p7AclbrUGu3Mw+oW0PTGPo0khnAWLyyDqSRyMyOwIin8y5HbBEiN3g4qOLuw==
|
||||
uplot@1.6.15:
|
||||
version "1.6.15"
|
||||
resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.15.tgz#785ca9f66d9c28ec2fdfb7e623d627ea0dcb0dd5"
|
||||
integrity sha512-6Fgq9tMaEM9Yu9oLkKd0w7VLJtV8LHG6dBrg1TmYi0LmSLkrj2Hqr11IrHk68cMaExnWAqay6YToQCrMZt1fcQ==
|
||||
|
||||
upper-case@^1.1.1:
|
||||
version "1.1.3"
|
||||
|
Reference in New Issue
Block a user