diff --git a/packages/grafana-ui/src/components/uPlot/Plot.tsx b/packages/grafana-ui/src/components/uPlot/Plot.tsx index eef918f8b8c..386a5e3d744 100755 --- a/packages/grafana-ui/src/components/uPlot/Plot.tsx +++ b/packages/grafana-ui/src/components/uPlot/Plot.tsx @@ -1,6 +1,6 @@ -import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'; +import React, { useEffect, useLayoutEffect, useMemo, useRef } from 'react'; import uPlot, { AlignedData, Options } from 'uplot'; -import { buildPlotContext, PlotContext } from './context'; +import { PlotContext } from './context'; import { DEFAULT_PLOT_CONFIG, pluginLog } from './utils'; import { PlotProps } from './types'; import usePrevious from 'react-use/lib/usePrevious'; @@ -12,7 +12,7 @@ import usePrevious from 'react-use/lib/usePrevious'; * Exposes contexts for plugins registration and uPlot instance access */ export const UPlotChart: React.FC = (props) => { - const canvasRef = useRef(null); + const plotContainer = useRef(null); const plotInstance = useRef(); const prevProps = usePrevious(props); @@ -26,17 +26,13 @@ export const UPlotChart: React.FC = (props) => { } as uPlot.Options; }, [props.config]); - const getPlotInstance = useCallback(() => { - return plotInstance.current; - }, []); - useLayoutEffect(() => { if (!plotInstance.current || props.width === 0 || props.height === 0) { return; } pluginLog('uPlot core', false, 'updating size'); - plotInstance.current!.setSize({ + plotInstance.current.setSize({ width: props.width, height: props.height, }); @@ -45,13 +41,13 @@ export const UPlotChart: React.FC = (props) => { // Effect responsible for uPlot updates/initialization logic. It's performed whenever component's props have changed useLayoutEffect(() => { // 0. Exit early if the component is not ready to initialize uPlot - if (!canvasRef.current || props.width === 0 || props.height === 0) { + if (!plotContainer.current || props.width === 0 || props.height === 0) { return; } // 1. When config is ready and there is no uPlot instance, create new uPlot and return if (!plotInstance.current || !prevProps) { - plotInstance.current = initializePlot(props.data, config, canvasRef.current); + plotInstance.current = initializePlot(props.data, config, plotContainer.current); return; } @@ -61,7 +57,7 @@ export const UPlotChart: React.FC = (props) => { pluginLog('uPlot core', false, 'destroying instance'); plotInstance.current.destroy(); } - plotInstance.current = initializePlot(props.data, config, canvasRef.current); + plotInstance.current = initializePlot(props.data, config, plotContainer.current); return; } @@ -77,13 +73,15 @@ export const UPlotChart: React.FC = (props) => { // Memoize plot context const plotCtx = useMemo(() => { - return buildPlotContext(canvasRef, props.data, getPlotInstance); - }, [plotInstance, canvasRef, props.data, getPlotInstance]); + return { + plot: plotInstance.current, + }; + }, [plotInstance.current, props.data]); return (
-
+
{props.children}
diff --git a/packages/grafana-ui/src/components/uPlot/context.ts b/packages/grafana-ui/src/components/uPlot/context.ts index c14ed4ddeab..338b90f8c60 100644 --- a/packages/grafana-ui/src/components/uPlot/context.ts +++ b/packages/grafana-ui/src/components/uPlot/context.ts @@ -1,28 +1,8 @@ import React, { useContext } from 'react'; -import uPlot, { AlignedData, Series } from 'uplot'; - -/** - * @alpha - */ -interface PlotCanvasContextType { - // canvas size css pxs - width: number; - height: number; - // plotting area bbox, css pxs - plot: { - width: number; - height: number; - top: number; - left: number; - }; -} +import uPlot from 'uplot'; interface PlotContextType { - getPlotInstance: () => uPlot | undefined; - getSeries: () => Series[]; - getCanvas: () => PlotCanvasContextType; - canvasRef: any; - data: AlignedData; + plot: uPlot | undefined; } /** @@ -34,34 +14,3 @@ export const PlotContext = React.createContext({} as PlotContex export const usePlotContext = (): PlotContextType => { return useContext(PlotContext); }; - -/** - * @alpha - */ -export const buildPlotContext = ( - canvasRef: any, - data: AlignedData, - getPlotInstance: () => uPlot | undefined -): PlotContextType => { - return { - canvasRef, - data, - getPlotInstance, - getSeries: () => getPlotInstance()!.series, - getCanvas: () => { - const plotInstance = getPlotInstance()!; - const bbox = plotInstance.bbox; - const pxRatio = window.devicePixelRatio; - return { - width: plotInstance.width, - height: plotInstance.height, - plot: { - width: bbox.width / pxRatio, - height: bbox.height / pxRatio, - top: bbox.top / pxRatio, - left: bbox.left / pxRatio, - }, - }; - }, - }; -}; diff --git a/packages/grafana-ui/src/components/uPlot/geometries/EventsCanvas.tsx b/packages/grafana-ui/src/components/uPlot/geometries/EventsCanvas.tsx index 1672e73f57b..ceb410581a7 100644 --- a/packages/grafana-ui/src/components/uPlot/geometries/EventsCanvas.tsx +++ b/packages/grafana-ui/src/components/uPlot/geometries/EventsCanvas.tsx @@ -27,7 +27,7 @@ export function EventsCanvas({ id, events, renderEventMarker, mapEventToXYCoords const eventMarkers = useMemo(() => { const markers: React.ReactNode[] = []; - if (!plotCtx.getPlotInstance() || events.length === 0) { + if (!plotCtx.plot || events.length === 0) { return markers; } @@ -49,7 +49,7 @@ export function EventsCanvas({ id, events, renderEventMarker, mapEventToXYCoords return <>{markers}; }, [events, renderEventMarker, renderToken, plotCtx]); - if (!plotCtx.getPlotInstance()) { + if (!plotCtx.plot) { return null; } diff --git a/packages/grafana-ui/src/components/uPlot/geometries/XYCanvas.tsx b/packages/grafana-ui/src/components/uPlot/geometries/XYCanvas.tsx index 20e37c71019..0b2ad895bfb 100644 --- a/packages/grafana-ui/src/components/uPlot/geometries/XYCanvas.tsx +++ b/packages/grafana-ui/src/components/uPlot/geometries/XYCanvas.tsx @@ -9,8 +9,8 @@ interface XYCanvasProps {} * Useful when you want to render some overlay with canvas-independent elements on top of the plot. */ export const XYCanvas: React.FC = ({ children }) => { - const plotContext = usePlotContext(); - const plotInstance = plotContext.getPlotInstance(); + const plotCtx = usePlotContext(); + const plotInstance = plotCtx.plot; if (!plotInstance) { return null; diff --git a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx index e5cd1039b28..5f4d14b326a 100644 --- a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx +++ b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx @@ -30,7 +30,7 @@ export const TooltipPlugin: React.FC = ({ config, ...otherProps }) => { - const plotContext = usePlotContext(); + const plotCtx = usePlotContext(); const plotCanvas = useRef(); const plotCanvasBBox = useRef({ left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0 }); const [focusedSeriesIdx, setFocusedSeriesIdx] = useState(null); @@ -72,7 +72,7 @@ export const TooltipPlugin: React.FC = ({ }); }, [config]); - if (!plotContext.getPlotInstance() || focusedPointIdx === null) { + if (!plotCtx.plot || focusedPointIdx === null) { return null; } @@ -89,10 +89,10 @@ export const TooltipPlugin: React.FC = ({ // when interacting with a point in single mode if (mode === TooltipDisplayMode.Single && focusedSeriesIdx !== null) { const field = otherProps.data.fields[focusedSeriesIdx]; - const plotSeries = plotContext.getSeries(); + const plotSeries = plotCtx.plot.series; const fieldFmt = field.display || getDisplayProcessor({ field, timeZone }); - const value = fieldFmt(plotContext.data[focusedSeriesIdx!][focusedPointIdx]); + const value = fieldFmt(plotCtx.plot.data[focusedSeriesIdx!][focusedPointIdx]); tooltip = ( = ({ if (mode === TooltipDisplayMode.Multi) { let series: SeriesTableRowProps[] = []; - const plotSeries = plotContext.getSeries(); + const plotSeries = plotCtx.plot.series; for (let i = 0; i < plotSeries.length; i++) { const frame = otherProps.data; @@ -125,7 +125,7 @@ export const TooltipPlugin: React.FC = ({ continue; } - const value = field.display!(plotContext.data[i][focusedPointIdx]); + const value = field.display!(plotCtx.plot.data[i][focusedPointIdx]); series.push({ // TODO: align with uPlot typings diff --git a/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin.tsx b/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin.tsx index d98a15cfcda..f2f54f39162 100644 --- a/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin.tsx +++ b/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin.tsx @@ -17,7 +17,7 @@ interface AnnotationsDataFrameViewDTO { export const AnnotationsPlugin: React.FC = ({ annotations, timeZone, config }) => { const theme = useTheme(); - const { getPlotInstance } = usePlotContext(); + const plotCtx = usePlotContext(); const annotationsRef = useRef>>(); @@ -84,7 +84,7 @@ export const AnnotationsPlugin: React.FC = ({ annotation (frame: DataFrame, index: number) => { const view = new DataFrameView(frame); const annotation = view.get(index); - const plotInstance = getPlotInstance(); + const plotInstance = plotCtx.plot; if (!annotation.time || !plotInstance) { return undefined; } @@ -94,7 +94,7 @@ export const AnnotationsPlugin: React.FC = ({ annotation y: plotInstance.bbox.height / window.devicePixelRatio + 4, }; }, - [getPlotInstance] + [plotCtx.plot] ); const renderMarker = useCallback( diff --git a/public/app/plugins/panel/timeseries/plugins/ExemplarsPlugin.tsx b/public/app/plugins/panel/timeseries/plugins/ExemplarsPlugin.tsx index 15be9af8283..ad1be4156ee 100644 --- a/public/app/plugins/panel/timeseries/plugins/ExemplarsPlugin.tsx +++ b/public/app/plugins/panel/timeseries/plugins/ExemplarsPlugin.tsx @@ -19,9 +19,10 @@ interface ExemplarsPluginProps { export const ExemplarsPlugin: React.FC = ({ exemplars, timeZone, getFieldLinks, config }) => { const plotCtx = usePlotContext(); + const mapExemplarToXYCoords = useCallback( (dataFrame: DataFrame, index: number) => { - const plotInstance = plotCtx.getPlotInstance(); + const plotInstance = plotCtx.plot; const time = dataFrame.fields.find((f) => f.name === TIME_SERIES_TIME_FIELD_NAME); const value = dataFrame.fields.find((f) => f.name === TIME_SERIES_VALUE_FIELD_NAME);