diff --git a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx index f5378db98cf..ce2f072ce6a 100644 --- a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx +++ b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx @@ -13,6 +13,7 @@ import { CloseButton } from './CloseButton'; export const DEFAULT_TOOLTIP_WIDTH = 300; export const DEFAULT_TOOLTIP_HEIGHT = 600; +export const TOOLTIP_OFFSET = 10; // todo: barchart? histogram? export const enum TooltipHoverMode { @@ -40,7 +41,8 @@ interface TooltipPlugin2Props { isPinned: boolean, dismiss: () => void, // selected time range (for annotation triggering) - timeRange: TimeRange2 | null + timeRange: TimeRange2 | null, + viaSync: boolean ) => React.ReactNode; maxWidth?: number; @@ -155,13 +157,14 @@ export const TooltipPlugin2 = ({ let winHeight = htmlEl.clientHeight - 16; window.addEventListener('resize', (e) => { - winWidth = htmlEl.clientWidth - 5; - winHeight = htmlEl.clientHeight - 5; + winWidth = htmlEl.clientWidth - 16; + winHeight = htmlEl.clientHeight - 16; }); let selectedRange: TimeRange2 | null = null; let seriesIdxs: Array = plot?.cursor.idxs!.slice()!; let closestSeriesIdx: number | null = null; + let viaSync = false; let pendingRender = false; let pendingPinned = false; @@ -217,7 +220,7 @@ export const TooltipPlugin2 = ({ isHovering: _isHovering, contents: _isHovering || selectedRange != null - ? renderRef.current(_plot!, seriesIdxs, closestSeriesIdx, _isPinned, dismiss, selectedRange) + ? renderRef.current(_plot!, seriesIdxs, closestSeriesIdx, _isPinned, dismiss, selectedRange, viaSync) : null, dismiss, }; @@ -225,6 +228,7 @@ export const TooltipPlugin2 = ({ setState(state); selectedRange = null; + viaSync = false; }; const dismiss = () => { @@ -410,46 +414,62 @@ export const TooltipPlugin2 = ({ // fires on mousemoves config.addHook('setCursor', (u) => { - let { left = -10, top = -10 } = u.cursor; + let { left = -10, top = -10, event } = u.cursor; if (left >= 0 || top >= 0) { - let { width, height } = sizeRef.current!; + viaSync = event == null; - let clientX = u.rect.left + left; - let clientY = u.rect.top + top; + let transform = ''; - if (offsetY) { - if (clientY + height < winHeight || clientY - height < 0) { - offsetY = 0; - } else if (offsetY !== -height) { - offsetY = -height; - } + // this means it's a synthetic event from uPlot's sync + if (viaSync) { + // TODO: smarter positioning here to avoid viewport clipping? + transform = `translateX(${left}px) translateY(${u.rect.height / 2}px) translateY(-50%)`; } else { - if (clientY + height > winHeight && clientY - height >= 0) { - offsetY = -height; + let { width, height } = sizeRef.current!; + + width += TOOLTIP_OFFSET; + height += TOOLTIP_OFFSET; + + let clientX = u.rect.left + left; + let clientY = u.rect.top + top; + + if (offsetY !== 0) { + if (clientY + height < winHeight || clientY - height < 0) { + offsetY = 0; + } else if (offsetY !== -height) { + offsetY = -height; + } + } else { + if (clientY + height > winHeight && clientY - height >= 0) { + offsetY = -height; + } } + + if (offsetX !== 0) { + if (clientX + width < winWidth || clientX - width < 0) { + offsetX = 0; + } else if (offsetX !== -width) { + offsetX = -width; + } + } else { + if (clientX + width > winWidth && clientX - width >= 0) { + offsetX = -width; + } + } + + const shiftX = left + (offsetX === 0 ? TOOLTIP_OFFSET : -TOOLTIP_OFFSET); + const shiftY = top + (offsetY === 0 ? TOOLTIP_OFFSET : -TOOLTIP_OFFSET); + + const reflectX = offsetX === 0 ? '' : 'translateX(-100%)'; + const reflectY = offsetY === 0 ? '' : 'translateY(-100%)'; + + // TODO: to a transition only when switching sides + // transition: transform 100ms; + + transform = `translateX(${shiftX}px) ${reflectX} translateY(${shiftY}px) ${reflectY}`; } - if (offsetX) { - if (clientX + width < winWidth || clientX - width < 0) { - offsetX = 0; - } else if (offsetX !== -width) { - offsetX = -width; - } - } else { - if (clientX + width > winWidth && clientX - width >= 0) { - offsetX = -width; - } - } - - const shiftX = offsetX !== 0 ? 'translateX(-100%)' : ''; - const shiftY = offsetY !== 0 ? 'translateY(-100%)' : ''; - - // TODO: to a transition only when switching sides - // transition: transform 100ms; - - const transform = `${shiftX} translateX(${left}px) ${shiftY} translateY(${top}px)`; - if (_isHovering) { if (domRef.current != null) { domRef.current.style.transform = transform; diff --git a/public/app/core/components/GraphNG/__snapshots__/utils.test.ts.snap b/public/app/core/components/GraphNG/__snapshots__/utils.test.ts.snap index 3b9265254b5..5e836cca3ac 100644 --- a/public/app/core/components/GraphNG/__snapshots__/utils.test.ts.snap +++ b/public/app/core/components/GraphNG/__snapshots__/utils.test.ts.snap @@ -81,7 +81,7 @@ exports[`GraphNG utils preparePlotConfigBuilder 1`] = ` "key": "__global_", "scales": [ "x", - "__fixed/na-na/na-na/auto/linear/na/number", + null, ], }, }, diff --git a/public/app/core/components/TimeSeries/utils.ts b/public/app/core/components/TimeSeries/utils.ts index ea063cd2125..6aa5654292a 100644 --- a/public/app/core/components/TimeSeries/utils.ts +++ b/public/app/core/components/TimeSeries/utils.ts @@ -643,8 +643,8 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ return true; }, }, - scales: [xScaleKey, yScaleKey], - // match: [() => true, (a, b) => a === b], + scales: [xScaleKey, null], + // match: [() => true, () => false], }; } diff --git a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx index bae0f5bfb1d..9f8e6c3c249 100644 --- a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx +++ b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx @@ -234,7 +234,7 @@ export const CandlestickPanel = ({ const enableAnnotationCreation = Boolean(canAddAnnotations && canAddAnnotations()); const showNewVizTooltips = - config.featureToggles.newVizTooltips && (sync == null || sync() === DashboardCursorSync.Off); + config.featureToggles.newVizTooltips && (sync == null || sync() !== DashboardCursorSync.Tooltip); return ( { + render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2, viaSync) => { + if (viaSync) { + return null; + } + if (timeRange2 != null) { setNewAnnotationRange(timeRange2); dismiss(); diff --git a/public/app/plugins/panel/heatmap/HeatmapPanel.tsx b/public/app/plugins/panel/heatmap/HeatmapPanel.tsx index 1c6d9432062..0751aaba3de 100644 --- a/public/app/plugins/panel/heatmap/HeatmapPanel.tsx +++ b/public/app/plugins/panel/heatmap/HeatmapPanel.tsx @@ -160,7 +160,7 @@ export const HeatmapPanel = ({ const dataRef = useRef(info); dataRef.current = info; const showNewVizTooltips = - config.featureToggles.newVizTooltips && (sync == null || sync() === DashboardCursorSync.Off); + config.featureToggles.newVizTooltips && (sync == null || sync() !== DashboardCursorSync.Tooltip); const builder = useMemo(() => { const scaleConfig: ScaleDistributionConfig = dataRef.current?.heatmap?.fields[1].config?.custom?.scaleDistribution; @@ -243,7 +243,11 @@ export const HeatmapPanel = ({ config={builder} hoverMode={TooltipHoverMode.xyOne} queryZoom={onChangeTimeRange} - render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2) => { + render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync) => { + if (viaSync) { + return null; + } + if (timeRange2 != null) { setNewAnnotationRange(timeRange2); dismiss(); diff --git a/public/app/plugins/panel/heatmap/utils.ts b/public/app/plugins/panel/heatmap/utils.ts index d2997b46dc0..fc7856b06dc 100644 --- a/public/app/plugins/panel/heatmap/utils.ts +++ b/public/app/plugins/panel/heatmap/utils.ts @@ -603,7 +603,7 @@ export function prepConfig(opts: PrepConfigOpts) { if (sync && sync() !== DashboardCursorSync.Off) { cursor.sync = { key: eventsScope, - scales: [xScaleKey, yScaleKey], + scales: [xScaleKey, null], filters: { pub: (type: string, src: uPlot, x: number, y: number, w: number, h: number, dataIdx: number) => { if (x < 0) { diff --git a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx index f950152b0c5..6c0c3ef7f0b 100644 --- a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx +++ b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx @@ -167,7 +167,7 @@ export const StateTimelinePanel = ({ } const enableAnnotationCreation = Boolean(canAddAnnotations && canAddAnnotations()); const showNewVizTooltips = - config.featureToggles.newVizTooltips && (sync == null || sync() === DashboardCursorSync.Off); + config.featureToggles.newVizTooltips && (sync == null || sync() !== DashboardCursorSync.Tooltip); return ( { + render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync) => { + if (viaSync) { + return null; + } + if (timeRange2 != null) { setNewAnnotationRange(timeRange2); dismiss(); diff --git a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx index 7cd4dc0a6e1..2cacd71287b 100644 --- a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx +++ b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx @@ -196,7 +196,7 @@ export const StatusHistoryPanel = ({ } const showNewVizTooltips = - config.featureToggles.newVizTooltips && (sync == null || sync() === DashboardCursorSync.Off); + config.featureToggles.newVizTooltips && (sync == null || sync() !== DashboardCursorSync.Tooltip); return ( { + render={(u, dataIdxs, seriesIdx, isPinned, dismiss, timeRange2, viaSync) => { + if (viaSync) { + return null; + } + if (timeRange2 != null) { setNewAnnotationRange(timeRange2); dismiss(); diff --git a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx index f38587cf9f5..6834fa6c3f0 100644 --- a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx +++ b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx @@ -52,7 +52,7 @@ export const TimeSeriesPanel = ({ const enableAnnotationCreation = Boolean(canAddAnnotations && canAddAnnotations()); const showNewVizTooltips = - config.featureToggles.newVizTooltips && (sync == null || sync() === DashboardCursorSync.Off); + config.featureToggles.newVizTooltips && (sync == null || sync() !== DashboardCursorSync.Tooltip); // temp range set for adding new annotation set by TooltipPlugin2, consumed by AnnotationPlugin2 const [newAnnotationRange, setNewAnnotationRange] = useState(null); @@ -113,7 +113,11 @@ export const TimeSeriesPanel = ({ } queryZoom={onChangeTimeRange} clientZoom={true} - render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2) => { + render={(u, dataIdxs, seriesIdx, isPinned = false, dismiss, timeRange2, viaSync) => { + if (viaSync) { + return null; + } + if (timeRange2 != null) { setNewAnnotationRange(timeRange2); dismiss();