diff --git a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx index 4ca3c131c0e..9d890b02bb3 100644 --- a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx +++ b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx @@ -323,6 +323,7 @@ export const CandlestickPanel = ({ /> )} )} void; canvasRegionRendering?: boolean; + annotationsConfig?: VizAnnotations; } // TODO: batch by color, use Path2D objects @@ -58,6 +59,7 @@ function getVals(frame: DataFrame) { } export const AnnotationsPlugin2 = ({ + annotationsConfig, annotations, timeZone, config, @@ -131,77 +133,73 @@ export const AnnotationsPlugin2 = ({ ctx.rect(u.bbox.left, u.bbox.top, u.bbox.width, u.bbox.height); ctx.clip(); - annos.forEach((frame) => { - let vals = getVals(frame); + // Multi-lane annotations do not support vertical lines or shaded regions + if (!annotationsConfig?.multiLane) { + annos.forEach((frame) => { + let vals = getVals(frame); - if (frame.name === 'xymark') { - // xMin, xMax, yMin, yMax, color, lineWidth, lineStyle, fillOpacity, text + if (frame.name === 'xymark') { + // xMin, xMax, yMin, yMax, color, lineWidth, lineStyle, fillOpacity, text - let xKey = config.scales[0].props.scaleKey; - let yKey = config.scales[1].props.scaleKey; + let xKey = config.scales[0].props.scaleKey; + let yKey = config.scales[1].props.scaleKey; - for (let i = 0; i < frame.length; i++) { - let color = getColorByName(vals.color?.[i] || DEFAULT_ANNOTATION_COLOR_HEX8); + for (let i = 0; i < frame.length; i++) { + let color = getColorByName(vals.color?.[i] || DEFAULT_ANNOTATION_COLOR_HEX8); - let x0 = u.valToPos(vals.xMin[i], xKey, true); - let x1 = u.valToPos(vals.xMax[i], xKey, true); - let y0 = u.valToPos(vals.yMax[i], yKey, true); - let y1 = u.valToPos(vals.yMin[i], yKey, true); + let x0 = u.valToPos(vals.xMin[i], xKey, true); + let x1 = u.valToPos(vals.xMax[i], xKey, true); + let y0 = u.valToPos(vals.yMax[i], yKey, true); + let y1 = u.valToPos(vals.yMin[i], yKey, true); - ctx.fillStyle = colorManipulator.alpha(color, vals.fillOpacity[i]); - ctx.fillRect(x0, y0, x1 - x0, y1 - y0); + ctx.fillStyle = colorManipulator.alpha(color, vals.fillOpacity[i]); + ctx.fillRect(x0, y0, x1 - x0, y1 - y0); - ctx.lineWidth = Math.round(vals.lineWidth[i] * uPlot.pxRatio); + ctx.lineWidth = Math.round(vals.lineWidth[i] * uPlot.pxRatio); - if (vals.lineStyle[i] === 'dash') { - // maybe extract this to vals.lineDash[i] in future? - ctx.setLineDash([5, 5]); - } else { - // solid - ctx.setLineDash([]); + if (vals.lineStyle[i] === 'dash') { + // maybe extract this to vals.lineDash[i] in future? + ctx.setLineDash([5, 5]); + } else { + // solid + ctx.setLineDash([]); + } + + ctx.strokeStyle = color; + ctx.strokeRect(x0, y0, x1 - x0, y1 - y0); } + } else { + let y0 = u.bbox.top; + let y1 = y0 + u.bbox.height; - ctx.strokeStyle = color; - ctx.strokeRect(x0, y0, x1 - x0, y1 - y0); - } - } else { - // if multiple regions, don't shade - // @todo toggle functionality, new annotation config option? + ctx.lineWidth = 2; + ctx.setLineDash([5, 5]); - let y0 = u.bbox.top; - let y1 = y0 + u.bbox.height; + for (let i = 0; i < vals.time.length; i++) { + let color = getColorByName(vals.color?.[i] || DEFAULT_ANNOTATION_COLOR_HEX8); - ctx.lineWidth = 2; - ctx.setLineDash([5, 5]); + let x0 = u.valToPos(vals.time[i], 'x', true); + renderLine(ctx, y0, y1, x0, color); - // @todo Don't render the vertical lines if multi lane is enabled - //@todo Don't render shaded region if multi-lane is enabled - // skipping this loop should do it + // If dataframe does not have end times, let's omit rendering the region for now to prevent runtime error in valToPos + // @todo do we want to fix isRegion to render a point (or use "to" as timeEnd) when we're missing timeEnd? + if (vals.isRegion?.[i] && vals.timeEnd?.[i]) { + let x1 = u.valToPos(vals.timeEnd[i], 'x', true); + renderLine(ctx, y0, y1, x1, color); - for (let i = 0; i < vals.time.length; i++) { - let color = getColorByName(vals.color?.[i] || DEFAULT_ANNOTATION_COLOR_HEX8); - - let x0 = u.valToPos(vals.time[i], 'x', true); - renderLine(ctx, y0, y1, x0, color); - - // If dataframe does not have end times, let's omit rendering the region for now - // @todo do we want to fix isRegion to render a point when we're missing timeEnd? - if (vals.isRegion?.[i] && vals.timeEnd?.[i]) { - let x1 = u.valToPos(vals.timeEnd[i], 'x', true); - renderLine(ctx, y0, y1, x1, color); - - if (canvasRegionRendering) { - ctx.fillStyle = colorManipulator.alpha(color, 0.1); - ctx.fillRect(x0, y0, x1 - x0, u.bbox.height); + if (canvasRegionRendering) { + ctx.fillStyle = colorManipulator.alpha(color, 0.1); + ctx.fillRect(x0, y0, x1 - x0, u.bbox.height); + } } } } - } - }); + }); + } ctx.restore(); }); - }, [config, canvasRegionRendering, getColorByName]); + }, [config, canvasRegionRendering, getColorByName, annotationsConfig]); // ensure annos are re-drawn whenever they change useEffect(() => { @@ -223,7 +221,8 @@ export const AnnotationsPlugin2 = ({ let markers: React.ReactNode[] = []; - const top = frameIdx * ANNOTATION_LANE_SIZE; + // Top offset for multi-lane annotations + const top = annotationsConfig?.multiLane ? frameIdx * ANNOTATION_LANE_SIZE : undefined; for (let i = 0; i < vals.time.length; i++) { let color = getColorByName(vals.color?.[i] || DEFAULT_ANNOTATION_COLOR); let left = Math.round(plot.valToPos(vals.time[i], 'x')) || 0; // handles -0