From 0fb3347b16aba36a98f242ef4ba0e4f473f5f268 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Wed, 25 Feb 2026 22:57:44 +1300 Subject: [PATCH] feat(44-03): apply CSS-based visual dimming to nodes - Subscribe to dimmedNodeIds in WorkflowCanvas - Apply switch-dimmed className in allNodes useMemo - Exclude Switch nodes from dimming (they stay fully visible) - Added CSS: 40% opacity with 250ms transition - Transition applies in both directions (dimming and un-dimming) - Dimming updates reactively when toggles or edges change --- src/app/globals.css | 10 ++++++++++ src/components/WorkflowCanvas.tsx | 22 ++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index ad52a5df..a3fd7a32 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -49,6 +49,16 @@ body { box-shadow: none !important; } +/* Switch dimming - nodes downstream of disabled Switch outputs */ +.react-flow__node.switch-dimmed { + opacity: 0.4; + transition: opacity 250ms ease-in-out; +} + +.react-flow__node:not(.switch-dimmed) { + transition: opacity 250ms ease-in-out; +} + /* Base handle styles */ .react-flow__handle { width: 10px; diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx index 48e039de..3a91739a 100644 --- a/src/components/WorkflowCanvas.tsx +++ b/src/components/WorkflowCanvas.tsx @@ -251,7 +251,7 @@ const findScrollableAncestor = (target: HTMLElement, deltaX: number, deltaY: num }; export function WorkflowCanvas() { - const { nodes, edges, groups, onNodesChange, onEdgesChange, onConnect, addNode, updateNodeData, loadWorkflow, getNodeById, addToGlobalHistory, setNodeGroupId, executeWorkflow, isModalOpen, showQuickstart, setShowQuickstart, navigationTarget, setNavigationTarget, captureSnapshot, applyEditOperations, setWorkflowMetadata, canvasNavigationSettings, setShortcutsDialogOpen } = + const { nodes, edges, groups, onNodesChange, onEdgesChange, onConnect, addNode, updateNodeData, loadWorkflow, getNodeById, addToGlobalHistory, setNodeGroupId, executeWorkflow, isModalOpen, showQuickstart, setShowQuickstart, navigationTarget, setNavigationTarget, captureSnapshot, applyEditOperations, setWorkflowMetadata, canvasNavigationSettings, setShortcutsDialogOpen, dimmedNodeIds } = useWorkflowStore(); const { screenToFlowPosition, getViewport, zoomIn, zoomOut, setViewport, setCenter } = useReactFlow(); const { show: showToast } = useToast(); @@ -286,10 +286,24 @@ export function WorkflowCanvas() { } }, [navigationTarget, nodes, setCenter, setNavigationTarget]); - // Just pass regular nodes to React Flow - groups are rendered separately + // Apply dimming className to nodes downstream of disabled Switch outputs const allNodes = useMemo(() => { - return nodes; - }, [nodes]); + return nodes.map((node) => { + // Never dim Switch nodes themselves + if (node.type === "switch") return node; + + const isDimmed = dimmedNodeIds.has(node.id); + const dimClass = isDimmed ? "switch-dimmed" : ""; + + // Preserve existing className if any, add/remove dimmed class + const baseClass = (node.className || "").replace(/\bswitch-dimmed\b/g, "").trim(); + const newClass = dimClass ? `${baseClass} ${dimClass}`.trim() : baseClass; + + // Only create new node object if className changed + if (node.className === newClass) return node; + return { ...node, className: newClass }; + }); + }, [nodes, dimmedNodeIds]); // Check if a node was dropped into a group and add it to that group