GraphNG: Fix annotations and exemplars plugins (#29613)

This commit is contained in:
Dominik Prokop 2020-12-07 09:16:13 +01:00 committed by GitHub
parent a5e43535b3
commit 56e7ce6f16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 17 additions and 11 deletions

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import uPlot, { AlignedData, AlignedDataWithGapTest, Options } from 'uplot'; import uPlot, { AlignedData, AlignedDataWithGapTest, Options } from 'uplot';
import { buildPlotContext, PlotContext } from './context'; import { buildPlotContext, PlotContext } from './context';
import { pluginLog } from './utils'; import { pluginLog } from './utils';
@ -15,6 +15,7 @@ import usePrevious from 'react-use/lib/usePrevious';
export const UPlotChart: React.FC<PlotProps> = props => { export const UPlotChart: React.FC<PlotProps> = props => {
const canvasRef = useRef<HTMLDivElement>(null); const canvasRef = useRef<HTMLDivElement>(null);
const plotInstance = useRef<uPlot>(); const plotInstance = useRef<uPlot>();
const [isPlotReady, setIsPlotReady] = useState(false);
const prevProps = usePrevious(props); const prevProps = usePrevious(props);
const { isConfigReady, currentConfig, registerPlugin } = usePlotConfig( const { isConfigReady, currentConfig, registerPlugin } = usePlotConfig(
props.width, props.width,
@ -36,6 +37,7 @@ export const UPlotChart: React.FC<PlotProps> = props => {
// 1. When config is ready and there is no uPlot instance, create new uPlot and return // 1. When config is ready and there is no uPlot instance, create new uPlot and return
if (isConfigReady && !plotInstance.current) { if (isConfigReady && !plotInstance.current) {
plotInstance.current = initializePlot(prepareData(props.data), currentConfig.current, canvasRef.current); plotInstance.current = initializePlot(prepareData(props.data), currentConfig.current, canvasRef.current);
setIsPlotReady(true);
return; return;
} }
@ -68,8 +70,8 @@ export const UPlotChart: React.FC<PlotProps> = props => {
// Memoize plot context // Memoize plot context
const plotCtx = useMemo(() => { const plotCtx = useMemo(() => {
return buildPlotContext(canvasRef, props.data, registerPlugin, getPlotInstance); return buildPlotContext(isPlotReady, canvasRef, props.data, registerPlugin, getPlotInstance);
}, [plotInstance, canvasRef, props.data, registerPlugin, getPlotInstance]); }, [plotInstance, canvasRef, props.data, registerPlugin, getPlotInstance, isPlotReady]);
return ( return (
<PlotContext.Provider value={plotCtx}> <PlotContext.Provider value={plotCtx}>

View File

@ -21,6 +21,7 @@ interface PlotPluginsContextType {
} }
interface PlotContextType extends PlotPluginsContextType { interface PlotContextType extends PlotPluginsContextType {
isPlotReady: boolean;
getPlotInstance: () => uPlot | undefined; getPlotInstance: () => uPlot | undefined;
getSeries: () => Series[]; getSeries: () => Series[];
getCanvas: () => PlotCanvasContextType; getCanvas: () => PlotCanvasContextType;
@ -126,12 +127,14 @@ export const usePlotData = (): PlotDataAPI => {
}; };
export const buildPlotContext = ( export const buildPlotContext = (
isPlotReady: boolean,
canvasRef: any, canvasRef: any,
data: AlignedFrameWithGapTest, data: AlignedFrameWithGapTest,
registerPlugin: any, registerPlugin: any,
getPlotInstance: () => uPlot | undefined getPlotInstance: () => uPlot | undefined
): PlotContextType => { ): PlotContextType => {
return { return {
isPlotReady,
canvasRef, canvasRef,
data, data,
registerPlugin, registerPlugin,

View File

@ -32,7 +32,7 @@ export const AnnotationsPlugin: React.FC<AnnotationsPluginProps> = ({ annotation
); );
useEffect(() => { useEffect(() => {
if (plotCtx.getPlotInstance()) { if (plotCtx.isPlotReady) {
const views: Array<DataFrameView<AnnotationsDataFrameViewDTO>> = []; const views: Array<DataFrameView<AnnotationsDataFrameViewDTO>> = [];
for (const frame of annotations) { for (const frame of annotations) {
@ -41,7 +41,7 @@ export const AnnotationsPlugin: React.FC<AnnotationsPluginProps> = ({ annotation
annotationsRef.current = views; annotationsRef.current = views;
} }
}, [plotCtx, annotations]); }, [plotCtx.isPlotReady, annotations]);
useEffect(() => { useEffect(() => {
const unregister = plotCtx.registerPlugin({ const unregister = plotCtx.registerPlugin({
@ -70,7 +70,7 @@ export const AnnotationsPlugin: React.FC<AnnotationsPluginProps> = ({ annotation
continue; continue;
} }
const xpos = u.valToPos(annotation.time / 1000, 'x', true); const xpos = u.valToPos(annotation.time, 'x', true);
ctx.beginPath(); ctx.beginPath();
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.strokeStyle = theme.palette.red; ctx.strokeStyle = theme.palette.red;

View File

@ -42,7 +42,7 @@ export const ExemplarsPlugin: React.FC<ExemplarsPluginProps> = ({ exemplars, tim
// THIS EVENT ONLY MOCKS EXEMPLAR Y VALUE!!!! TO BE REMOVED WHEN WE GET CORRECT EXEMPLARS SHAPE VIA PROPS // THIS EVENT ONLY MOCKS EXEMPLAR Y VALUE!!!! TO BE REMOVED WHEN WE GET CORRECT EXEMPLARS SHAPE VIA PROPS
useEffect(() => { useEffect(() => {
if (plotCtx.getPlotInstance()) { if (plotCtx.isPlotReady) {
const mocks: DataFrame[] = []; const mocks: DataFrame[] = [];
for (const frame of exemplars) { for (const frame of exemplars) {
@ -61,22 +61,23 @@ export const ExemplarsPlugin: React.FC<ExemplarsPluginProps> = ({ exemplars, tim
setExemplarsMock(mocks); setExemplarsMock(mocks);
} }
}, [plotCtx, exemplars]); }, [plotCtx.isPlotReady, exemplars]);
const mapExemplarToXYCoords = useCallback( const mapExemplarToXYCoords = useCallback(
(exemplar: ExemplarsDataFrameViewDTO) => { (exemplar: ExemplarsDataFrameViewDTO) => {
const plotInstance = plotCtx.getPlotInstance(); const plotInstance = plotCtx.getPlotInstance();
if (!exemplar.time || !plotInstance) {
if (!exemplar.time || !plotCtx.isPlotReady || !plotInstance) {
return undefined; return undefined;
} }
return { return {
x: plotInstance.valToPos(exemplar.time / 1000, 'x'), x: plotInstance.valToPos(exemplar.time, 'x'),
// exemplar.y is a temporary mock for an examplar. This Needs to be calculated according to examplar scale! // exemplar.y is a temporary mock for an examplar. This Needs to be calculated according to examplar scale!
y: Math.floor((exemplar.y * plotInstance.bbox.height) / window.devicePixelRatio), y: Math.floor((exemplar.y * plotInstance.bbox.height) / window.devicePixelRatio),
}; };
}, },
[plotCtx.getPlotInstance] [plotCtx.isPlotReady, plotCtx.getPlotInstance]
); );
const renderMarker = useCallback( const renderMarker = useCallback(