From 037acc6fdb8402ce3c1a462f55dbb9d4a6c8f909 Mon Sep 17 00:00:00 2001 From: Paul Marbach Date: Wed, 3 Dec 2025 17:53:36 -0500 Subject: [PATCH] add filter for connections --- .../PanelDataPane/ConnectionLines.tsx | 24 +++-- .../PanelDataPane/QueryTransformList.tsx | 98 ++++++++++++------- 2 files changed, 75 insertions(+), 47 deletions(-) diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/ConnectionLines.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/ConnectionLines.tsx index 3cb3758cc39..fd40592e987 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/ConnectionLines.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/ConnectionLines.tsx @@ -12,10 +12,12 @@ interface Connection { interface ConnectionLinesProps { connections: Connection[]; isDragging?: boolean; + onClick?: () => void; + selected?: boolean; } -export function ConnectionLines({ connections, isDragging = false }: ConnectionLinesProps) { - const styles = useStyles2(getStyles); +export function ConnectionLines({ connections, isDragging = false, selected, onClick }: ConnectionLinesProps) { + const styles = useStyles2(getStyles, selected); const [positions, setPositions] = useState>(new Map()); const containerRef = useRef(null); @@ -61,7 +63,7 @@ export function ConnectionLines({ connections, isDragging = false }: ConnectionL if (mutationTimeout) { clearTimeout(mutationTimeout); } - mutationTimeout = window.setTimeout(updatePositions, 100); + mutationTimeout = window.setTimeout(updatePositions, 50); }); observer.observe(container, { childList: true, subtree: true }); @@ -125,7 +127,7 @@ export function ConnectionLines({ connections, isDragging = false }: ConnectionL return ( - + {Array.from(refIds).map((refId) => { const cardRect = findCardRect(refId); @@ -140,19 +142,21 @@ export function ConnectionLines({ connections, isDragging = false }: ConnectionL ); } -const getStyles = (theme: GrafanaTheme2) => ({ +const getStyles = (theme: GrafanaTheme2, selected?: boolean) => ({ svg: css({ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, - pointerEvents: 'none', zIndex: 10, overflow: 'visible', + pointerEvents: 'none', width: '100%', }), connectionGroup: css({ + pointerEvents: 'auto', + cursor: 'pointer', [theme.transitions.handleMotion('no-preference', 'reduce')]: { animation: 'fadeIn 0.2s ease-in-out', '@keyframes fadeIn': { @@ -166,12 +170,12 @@ const getStyles = (theme: GrafanaTheme2) => ({ }, }), swimlane: css({ - stroke: theme.colors.text.maxContrast, - strokeWidth: 2, - opacity: 0.3, + stroke: selected ? theme.colors.primary.border : theme.colors.text.maxContrast, + strokeWidth: 4, + opacity: 0.6, }), point: css({ - fill: theme.colors.text.maxContrast, + fill: selected ? theme.colors.primary.border : theme.colors.text.maxContrast, opacity: 0.8, }), }); diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformList.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformList.tsx index 1d5abaf0acb..09a183821e1 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformList.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformList.tsx @@ -1,6 +1,6 @@ import { css, cx } from '@emotion/css'; import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd'; -import { HTMLAttributes, memo, useMemo, useState } from 'react'; +import { HTMLAttributes, memo, useCallback, useMemo, useState } from 'react'; import { DataTransformerConfig, GrafanaTheme2 } from '@grafana/data'; import { t } from '@grafana/i18n'; @@ -68,6 +68,7 @@ export const QueryTransformList = memo( const [isClosing, setIsClosing] = useState(false); const [selectedContextIds, setSelectedContextIds] = useState([]); const [hovered, setHovered] = useState(null); + const [viewingConnections, setViewingConnections] = useState(false); const onDragStart = () => { setIsDragging(true); @@ -126,14 +127,13 @@ export const QueryTransformList = memo( return conns; }, [allItems]); + const activeItem = useMemo( + () => (selectedId ? allItems.find((item) => item.id === selectedId) : undefined), + [allItems, selectedId] + ); + // Filter connections to only show for selected card const visibleConnections = useMemo(() => { - if (!selectedId) { - return []; - } - - // Find the item to get its refId - const activeItem = allItems.find((item) => item.id === selectedId); if (!activeItem || !('refId' in activeItem.data)) { return []; } @@ -142,7 +142,7 @@ export const QueryTransformList = memo( // Show connections where this card is involved (either as source or destination) return allConnections.filter((conn) => conn.from === activeRefId || conn.to === activeRefId); - }, [allConnections, selectedId, allItems]); + }, [activeItem, allConnections]); const getHandlers = (item: QueryTransformItem) => { switch (item.type) { @@ -239,24 +239,39 @@ export const QueryTransformList = memo( } }; - const cardListHoverHandlerFactory = ( - itemList: QueryTransformItem[], - lastItemId: string - ): HTMLAttributes['onMouseMove'] => { - return (ev) => { - const rect = ev.currentTarget.getBoundingClientRect(); - const y = ev.clientY - rect.top; - let hoveredIdx = Math.floor((y - 16 + CARD_HEIGHT / 2) / CARD_HEIGHT); - if (hoveredIdx < 0) { - hoveredIdx = 0; - } - if (hoveredIdx > itemList.length) { - hoveredIdx = itemList.length; - } - const hoveredId = hoveredIdx === itemList.length ? lastItemId : itemList[hoveredIdx].id; - setHovered(hoveredId); - }; - }; + const filteredDataSourceItems = useMemo(() => { + if (!viewingConnections) { + return dataSourceItems; + } + const visibleConnectionsRefIds = new Set(); + for (const conn of visibleConnections) { + visibleConnectionsRefIds.add(conn.from); + visibleConnectionsRefIds.add(conn.to); + } + return dataSourceItems.filter((item) => { + return 'refId' in item.data && visibleConnectionsRefIds.has(item.data.refId); + }); + }, [dataSourceItems, viewingConnections, visibleConnections]); + + const canAdd = !isDragging && !viewingConnections; + + const cardListHoverHandlerFactory = useCallback( + (itemList: QueryTransformItem[], lastItemId: string): HTMLAttributes['onMouseMove'] => + (ev) => { + const rect = ev.currentTarget.getBoundingClientRect(); + const y = ev.clientY - rect.top; + let hoveredIdx = Math.floor((y - 16 + CARD_HEIGHT / 2) / CARD_HEIGHT); + if (hoveredIdx < 0) { + hoveredIdx = 0; + } + if (hoveredIdx > itemList.length) { + hoveredIdx = itemList.length; + } + const hoveredId = hoveredIdx === itemList.length ? lastItemId : itemList[hoveredIdx].id; + setHovered(hoveredId); + }, + [] + ); return (
setHovered(null)}> @@ -306,7 +321,12 @@ export const QueryTransformList = memo(
- + setViewingConnections((current) => !current)} + />
- {dataSourceItems.map((item, index) => ( + {filteredDataSourceItems.map((item) => (
- + {(provided, snapshot) => (
@@ -391,7 +415,7 @@ export const QueryTransformList = memo( onAddExpression={onAddExpression} allowedTypes={['query', 'expression']} index={dataSourceItems.length} - show={!isDragging && hovered === 'queries-last'} + show={canAdd && hovered === 'queries-last'} />
@@ -434,9 +458,9 @@ export const QueryTransformList = memo( onMouseMove={cardListHoverHandlerFactory(transformItems, 'transformations-last')} > - {transformItems.map((item, index) => ( + {transformItems.map((item) => (
- + {(provided, snapshot) => (
@@ -484,7 +508,7 @@ export const QueryTransformList = memo( onAddExpression={onAddExpression} allowedTypes={['transform']} index={transformItems.length} - show={!isDragging && hovered === 'transformations-last'} + show={canAdd && hovered === 'transformations-last'} />