mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
StateTimeline: Share cursor with rest of the panels (#41038)
* First working version of shared cursor for state timeline * Only publish x value for time series * Don't send legacy graph event * Don't add y scale to cursor sync * Snap cursor to the bottom of the canvas when sync is out of bounds * Fix snapshot
This commit is contained in:
parent
d69ffe0e44
commit
af61839a26
@ -95,7 +95,7 @@ Object {
|
|||||||
],
|
],
|
||||||
"scales": Array [
|
"scales": Array [
|
||||||
"x",
|
"x",
|
||||||
"__fixed",
|
null,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -363,7 +363,8 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
// ??? setSeries: syncMode === DashboardCursorSync.Tooltip,
|
// ??? setSeries: syncMode === DashboardCursorSync.Tooltip,
|
||||||
scales: builder.scaleKeys,
|
//TODO: remove any once https://github.com/leeoniya/uPlot/pull/611 got merged or the typing is fixed
|
||||||
|
scales: [xScaleKey, null as any],
|
||||||
match: [() => true, () => true],
|
match: [() => true, () => true],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -181,6 +181,11 @@ export function findMidPointYPosition(u: uPlot, idx: number) {
|
|||||||
y = u.valToPos((min || max)!, u.series[(sMaxIdx || sMinIdx)!].scale!);
|
y = u.valToPos((min || max)!, u.series[(sMaxIdx || sMinIdx)!].scale!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if y is out of canvas bounds, snap it to the bottom
|
||||||
|
if (y !== undefined && y < 0) {
|
||||||
|
y = u.bbox.height / devicePixelRatio;
|
||||||
|
}
|
||||||
|
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { DataFrame, PanelProps } from '@grafana/data';
|
import { DataFrame, PanelProps } from '@grafana/data';
|
||||||
import { TooltipPlugin, useTheme2, ZoomPlugin } from '@grafana/ui';
|
import { TooltipPlugin, useTheme2, ZoomPlugin, usePanelContext } from '@grafana/ui';
|
||||||
import { TimelineMode, TimelineOptions } from './types';
|
import { TimelineMode, TimelineOptions } from './types';
|
||||||
import { TimelineChart } from './TimelineChart';
|
import { TimelineChart } from './TimelineChart';
|
||||||
import { prepareTimelineFields, prepareTimelineLegendItems } from './utils';
|
import { prepareTimelineFields, prepareTimelineLegendItems } from './utils';
|
||||||
@ -22,6 +22,7 @@ export const StateTimelinePanel: React.FC<TimelinePanelProps> = ({
|
|||||||
onChangeTimeRange,
|
onChangeTimeRange,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
|
const { sync } = usePanelContext();
|
||||||
|
|
||||||
const { frames, warn } = useMemo(() => prepareTimelineFields(data?.series, options.mergeValues ?? true, theme), [
|
const { frames, warn } = useMemo(() => prepareTimelineFields(data?.series, options.mergeValues ?? true, theme), [
|
||||||
data,
|
data,
|
||||||
@ -103,6 +104,7 @@ export const StateTimelinePanel: React.FC<TimelinePanelProps> = ({
|
|||||||
<ZoomPlugin config={config} onZoom={onChangeTimeRange} />
|
<ZoomPlugin config={config} onZoom={onChangeTimeRange} />
|
||||||
<TooltipPlugin
|
<TooltipPlugin
|
||||||
data={alignedFrame}
|
data={alignedFrame}
|
||||||
|
sync={sync}
|
||||||
config={config}
|
config={config}
|
||||||
mode={options.tooltip.mode}
|
mode={options.tooltip.mode}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
|
@ -36,12 +36,13 @@ export class TimelineChart extends React.Component<TimelineProps> {
|
|||||||
|
|
||||||
prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => {
|
prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => {
|
||||||
this.panelContext = this.context as PanelContext;
|
this.panelContext = this.context as PanelContext;
|
||||||
const { eventBus } = this.panelContext;
|
const { eventBus, sync } = this.panelContext;
|
||||||
|
|
||||||
return preparePlotConfigBuilder({
|
return preparePlotConfigBuilder({
|
||||||
frame: alignedFrame,
|
frame: alignedFrame,
|
||||||
getTimeRange,
|
getTimeRange,
|
||||||
eventBus,
|
eventBus,
|
||||||
|
sync,
|
||||||
allFrames: this.props.frames,
|
allFrames: this.props.frames,
|
||||||
...this.props,
|
...this.props,
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { OptionsWithTooltip, OptionsWithLegend, HideableFieldConfig, VisibilityMode } from '@grafana/schema';
|
import { DashboardCursorSync } from '@grafana/data';
|
||||||
|
import { HideableFieldConfig, OptionsWithLegend, OptionsWithTooltip, VisibilityMode } from '@grafana/schema';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @alpha
|
* @alpha
|
||||||
@ -15,6 +16,8 @@ export interface TimelineOptions extends OptionsWithLegend, OptionsWithTooltip {
|
|||||||
mergeValues?: boolean;
|
mergeValues?: boolean;
|
||||||
// only used in "changes" mode (state-timeline)
|
// only used in "changes" mode (state-timeline)
|
||||||
alignValue?: TimelineValueAlignment;
|
alignValue?: TimelineValueAlignment;
|
||||||
|
|
||||||
|
sync?: DashboardCursorSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TimelineValueAlignment = 'center' | 'left' | 'right';
|
export type TimelineValueAlignment = 'center' | 'left' | 'right';
|
||||||
|
@ -3,6 +3,10 @@ import { XYFieldMatchers } from '@grafana/ui/src/components/GraphNG/types';
|
|||||||
import {
|
import {
|
||||||
ArrayVector,
|
ArrayVector,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
|
DashboardCursorSync,
|
||||||
|
DataHoverPayload,
|
||||||
|
DataHoverEvent,
|
||||||
|
DataHoverClearEvent,
|
||||||
FALLBACK_COLOR,
|
FALLBACK_COLOR,
|
||||||
Field,
|
Field,
|
||||||
FieldColorModeId,
|
FieldColorModeId,
|
||||||
@ -58,6 +62,8 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
|
|||||||
timeZone,
|
timeZone,
|
||||||
getTimeRange,
|
getTimeRange,
|
||||||
mode,
|
mode,
|
||||||
|
eventBus,
|
||||||
|
sync,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
colWidth,
|
colWidth,
|
||||||
showValue,
|
showValue,
|
||||||
@ -65,6 +71,9 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const builder = new UPlotConfigBuilder(timeZone);
|
const builder = new UPlotConfigBuilder(timeZone);
|
||||||
|
|
||||||
|
const xScaleUnit = 'time';
|
||||||
|
const xScaleKey = 'x';
|
||||||
|
|
||||||
const isDiscrete = (field: Field) => {
|
const isDiscrete = (field: Field) => {
|
||||||
const mode = field.config?.color?.mode;
|
const mode = field.config?.color?.mode;
|
||||||
return !(mode && field.display && mode.startsWith('continuous-'));
|
return !(mode && field.display && mode.startsWith('continuous-'));
|
||||||
@ -116,6 +125,13 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
|
|||||||
let hoveredDataIdx: number | null = null;
|
let hoveredDataIdx: number | null = null;
|
||||||
|
|
||||||
const coreConfig = getConfig(opts);
|
const coreConfig = getConfig(opts);
|
||||||
|
const payload: DataHoverPayload = {
|
||||||
|
point: {
|
||||||
|
[xScaleUnit]: null,
|
||||||
|
[FIXED_UNIT]: null,
|
||||||
|
},
|
||||||
|
data: frame,
|
||||||
|
};
|
||||||
|
|
||||||
builder.addHook('init', coreConfig.init);
|
builder.addHook('init', coreConfig.init);
|
||||||
builder.addHook('drawClear', coreConfig.drawClear);
|
builder.addHook('drawClear', coreConfig.drawClear);
|
||||||
@ -148,7 +164,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
|
|||||||
builder.setCursor(coreConfig.cursor);
|
builder.setCursor(coreConfig.cursor);
|
||||||
|
|
||||||
builder.addScale({
|
builder.addScale({
|
||||||
scaleKey: 'x',
|
scaleKey: xScaleKey,
|
||||||
isTime: true,
|
isTime: true,
|
||||||
orientation: ScaleOrientation.Horizontal,
|
orientation: ScaleOrientation.Horizontal,
|
||||||
direction: ScaleDirection.Right,
|
direction: ScaleDirection.Right,
|
||||||
@ -164,7 +180,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.addAxis({
|
builder.addAxis({
|
||||||
scaleKey: 'x',
|
scaleKey: xScaleKey,
|
||||||
isTime: true,
|
isTime: true,
|
||||||
splits: coreConfig.xSplits!,
|
splits: coreConfig.xSplits!,
|
||||||
placement: AxisPlacement.Bottom,
|
placement: AxisPlacement.Bottom,
|
||||||
@ -219,6 +235,34 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sync !== DashboardCursorSync.Off) {
|
||||||
|
let cursor: Partial<uPlot.Cursor> = {};
|
||||||
|
|
||||||
|
cursor.sync = {
|
||||||
|
key: '__global_',
|
||||||
|
filters: {
|
||||||
|
pub: (type: string, src: uPlot, x: number, y: number, w: number, h: number, dataIdx: number) => {
|
||||||
|
payload.rowIndex = dataIdx;
|
||||||
|
if (x < 0 && y < 0) {
|
||||||
|
payload.point[xScaleUnit] = null;
|
||||||
|
payload.point[FIXED_UNIT] = null;
|
||||||
|
eventBus.publish(new DataHoverClearEvent());
|
||||||
|
} else {
|
||||||
|
payload.point[xScaleUnit] = src.posToVal(x, xScaleKey);
|
||||||
|
payload.point.panelRelY = y > 0 ? y / h : 1; // used for old graph panel to position tooltip
|
||||||
|
payload.down = undefined;
|
||||||
|
eventBus.publish(new DataHoverEvent(payload));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//TODO: remove any once https://github.com/leeoniya/uPlot/pull/611 got merged or the typing is fixed
|
||||||
|
scales: [xScaleKey, null as any],
|
||||||
|
};
|
||||||
|
builder.setSync();
|
||||||
|
builder.setCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user