mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TimeSeries: Fix showing datalinks when clicking on a point (#76492)
This commit is contained in:
parent
1573f253bf
commit
2857870bfb
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useClickAway } from 'react-use';
|
import { useClickAway } from 'react-use';
|
||||||
|
|
||||||
import { CartesianCoords2D, DataFrame, getFieldDisplayName, InterpolateFunction, TimeZone } from '@grafana/data';
|
import { CartesianCoords2D, DataFrame, getFieldDisplayName, InterpolateFunction, TimeZone } from '@grafana/data';
|
||||||
@ -11,7 +11,6 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
UPlotConfigBuilder,
|
UPlotConfigBuilder,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import { pluginLog } from '@grafana/ui/src/components/uPlot/utils';
|
|
||||||
|
|
||||||
type ContextMenuSelectionCoords = { viewport: CartesianCoords2D; plotCanvas: CartesianCoords2D };
|
type ContextMenuSelectionCoords = { viewport: CartesianCoords2D; plotCanvas: CartesianCoords2D };
|
||||||
type ContextMenuSelectionPoint = { seriesIdx: number | null; dataIdx: number | null };
|
type ContextMenuSelectionPoint = { seriesIdx: number | null; dataIdx: number | null };
|
||||||
@ -39,108 +38,37 @@ export const ContextMenuPlugin = ({
|
|||||||
replaceVariables,
|
replaceVariables,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: ContextMenuPluginProps) => {
|
}: ContextMenuPluginProps) => {
|
||||||
const plotCanvas = useRef<HTMLDivElement>();
|
|
||||||
const [coords, setCoords] = useState<ContextMenuSelectionCoords | null>(null);
|
const [coords, setCoords] = useState<ContextMenuSelectionCoords | null>(null);
|
||||||
const [point, setPoint] = useState<ContextMenuSelectionPoint | null>(null);
|
const [point, setPoint] = useState<ContextMenuSelectionPoint | null>(null);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const openMenu = useCallback(() => {
|
|
||||||
setIsOpen(true);
|
|
||||||
}, [setIsOpen]);
|
|
||||||
|
|
||||||
const closeMenu = useCallback(() => {
|
|
||||||
setIsOpen(false);
|
|
||||||
}, [setIsOpen]);
|
|
||||||
|
|
||||||
const clearSelection = useCallback(() => {
|
|
||||||
pluginLog('ContextMenuPlugin', false, 'clearing click selection');
|
|
||||||
setPoint(null);
|
|
||||||
}, [setPoint]);
|
|
||||||
|
|
||||||
// Add uPlot hooks to the config, or re-add when the config changed
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
let bbox: DOMRect | undefined = undefined;
|
let seriesIdx: number | null = null;
|
||||||
|
|
||||||
const onMouseCapture = (e: MouseEvent) => {
|
|
||||||
let update = {
|
|
||||||
viewport: {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
},
|
|
||||||
plotCanvas: {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (bbox) {
|
|
||||||
update = {
|
|
||||||
...update,
|
|
||||||
plotCanvas: {
|
|
||||||
x: e.clientX - bbox.left,
|
|
||||||
y: e.clientY - bbox.top,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
setCoords(update);
|
|
||||||
};
|
|
||||||
|
|
||||||
// cache uPlot plotting area bounding box
|
|
||||||
config.addHook('syncRect', (u, rect) => {
|
|
||||||
bbox = rect;
|
|
||||||
});
|
|
||||||
|
|
||||||
config.addHook('init', (u) => {
|
config.addHook('init', (u) => {
|
||||||
const canvas = u.over;
|
u.over.addEventListener('click', (e) => {
|
||||||
plotCanvas.current = canvas || undefined;
|
// only open when have a focused point, and not for explicit annotations, zooms, etc.
|
||||||
plotCanvas.current?.addEventListener('mousedown', onMouseCapture);
|
if (seriesIdx != null && !e.metaKey && !e.ctrlKey && !e.shiftKey) {
|
||||||
|
setCoords({
|
||||||
pluginLog('ContextMenuPlugin', false, 'init');
|
viewport: {
|
||||||
// for naive click&drag check
|
x: e.clientX,
|
||||||
let isClick = false;
|
y: e.clientY,
|
||||||
|
},
|
||||||
// REF: https://github.com/leeoniya/uPlot/issues/239
|
plotCanvas: {
|
||||||
let pts = Array.from(u.root.querySelectorAll<HTMLDivElement>('.u-cursor-pt'));
|
x: e.clientX - u.rect.left,
|
||||||
|
y: e.clientY - u.rect.top,
|
||||||
plotCanvas.current?.addEventListener('mousedown', () => {
|
},
|
||||||
isClick = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
plotCanvas.current?.addEventListener('mousemove', () => {
|
|
||||||
isClick = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: remove listeners on unmount
|
|
||||||
plotCanvas.current?.addEventListener('mouseup', (e: MouseEvent) => {
|
|
||||||
// ignore cmd+click, this is handled by annotation editor
|
|
||||||
if (!isClick || e.metaKey || e.ctrlKey) {
|
|
||||||
setPoint(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isClick = true;
|
|
||||||
|
|
||||||
if (e.target instanceof HTMLElement) {
|
|
||||||
if (!e.target.classList.contains('u-cursor-pt')) {
|
|
||||||
pluginLog('ContextMenuPlugin', false, 'canvas click');
|
|
||||||
setPoint({ seriesIdx: null, dataIdx: null });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openMenu();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (pts.length > 0) {
|
|
||||||
pts.forEach((pt, i) => {
|
|
||||||
// TODO: remove listeners on unmount
|
|
||||||
pt.addEventListener('click', () => {
|
|
||||||
const seriesIdx = i + 1;
|
|
||||||
const dataIdx = u.cursor.idx;
|
|
||||||
pluginLog('ContextMenuPlugin', false, seriesIdx, dataIdx);
|
|
||||||
setPoint({ seriesIdx, dataIdx: dataIdx ?? null });
|
|
||||||
});
|
});
|
||||||
});
|
setPoint({ seriesIdx, dataIdx: u.cursor.idxs![seriesIdx] });
|
||||||
}
|
setIsOpen(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}, [config, openMenu, setCoords, setPoint]);
|
|
||||||
|
config.addHook('setSeries', (u, _seriesIdx) => {
|
||||||
|
seriesIdx = _seriesIdx;
|
||||||
|
});
|
||||||
|
}, [config]);
|
||||||
|
|
||||||
const defaultItems = useMemo(() => {
|
const defaultItems = useMemo(() => {
|
||||||
return otherProps.defaultItems
|
return otherProps.defaultItems
|
||||||
@ -175,8 +103,8 @@ export const ContextMenuPlugin = ({
|
|||||||
selection={{ point, coords }}
|
selection={{ point, coords }}
|
||||||
replaceVariables={replaceVariables}
|
replaceVariables={replaceVariables}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
clearSelection();
|
setPoint(null);
|
||||||
closeMenu();
|
setIsOpen(false);
|
||||||
if (onClose) {
|
if (onClose) {
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import {
|
|||||||
import { XYFieldMatchers } from '@grafana/ui/src/components/GraphNG/types';
|
import { XYFieldMatchers } from '@grafana/ui/src/components/GraphNG/types';
|
||||||
import { findFieldIndex } from 'app/features/dimensions';
|
import { findFieldIndex } from 'app/features/dimensions';
|
||||||
|
|
||||||
import { ContextMenuPlugin } from '../timeseries/plugins/ContextMenuPlugin';
|
|
||||||
import { prepareGraphableFields, regenerateLinksSupplier } from '../timeseries/utils';
|
import { prepareGraphableFields, regenerateLinksSupplier } from '../timeseries/utils';
|
||||||
|
|
||||||
import { Options } from './panelcfg.gen';
|
import { Options } from './panelcfg.gen';
|
||||||
@ -137,15 +136,6 @@ export const TrendPanel = ({
|
|||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ContextMenuPlugin
|
|
||||||
data={alignedDataFrame}
|
|
||||||
frames={info.frames!}
|
|
||||||
config={config}
|
|
||||||
timeZone={timeZone}
|
|
||||||
replaceVariables={replaceVariables}
|
|
||||||
defaultItems={[]}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
Loading…
Reference in New Issue
Block a user