From d9a60f46c75ef27591a6b2b59ffc24cbe3b5befb Mon Sep 17 00:00:00 2001 From: shrimbly Date: Sat, 7 Mar 2026 21:30:45 +1300 Subject: [PATCH] fix: replace imperative DOM listener with React synthetic event for resize-handle double-click The aspect-fit double-click feature broke after manual resize because imperative DOM listeners (useEffect + addEventListener) fall out of sync when React Flow re-renders resize controls. Switches to a React onDoubleClick handler on a `display: contents` wrapper, which stays in sync across all re-renders. Co-Authored-By: Claude Opus 4.6 --- src/components/nodes/BaseNode.tsx | 34 ++++++++++--------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/components/nodes/BaseNode.tsx b/src/components/nodes/BaseNode.tsx index 41af9874..04236610 100644 --- a/src/components/nodes/BaseNode.tsx +++ b/src/components/nodes/BaseNode.tsx @@ -1,6 +1,6 @@ "use client"; -import { ReactNode, useCallback, useEffect, useRef } from "react"; +import { ReactNode, useCallback } from "react"; import { NodeResizer, OnResize, useReactFlow } from "@xyflow/react"; import { useWorkflowStore } from "@/store/workflowStore"; import { getMediaDimensions, calculateAspectFitSize } from "@/utils/nodeDimensions"; @@ -68,22 +68,13 @@ export function BaseNode({ ); // Double-click resize handle → aspect-fit to media - const containerRef = useRef(null); - - useEffect(() => { - if (!aspectFitMedia || !selected) return; - - const el = containerRef.current; - if (!el) return; - - // Attach to the stable .react-flow__node wrapper (not resize controls directly, - // which get recreated by React Flow on re-render after manual resize) - const nodeWrapper = el.closest(".react-flow__node"); - if (!nodeWrapper) return; - - const handler = async (e: Event) => { + // Uses React synthetic event on a `display: contents` wrapper so it stays + // in sync across React Flow re-renders (imperative DOM listeners break after manual resize). + const handleResizeHandleDblClick = useCallback( + async (e: React.MouseEvent) => { const target = e.target as HTMLElement; if (!target.closest(".react-flow__resize-control")) return; + if (!aspectFitMedia) return; e.stopPropagation(); const dims = await getMediaDimensions(aspectFitMedia); @@ -123,14 +114,12 @@ export function BaseNode({ return n; }) ); - }; - - nodeWrapper.addEventListener("dblclick", handler); - return () => nodeWrapper.removeEventListener("dblclick", handler); - }, [aspectFitMedia, selected, id, fullBleed, getNodes, setNodes]); + }, + [aspectFitMedia, id, fullBleed, getNodes, setNodes] + ); return ( - <> +
{children}
- +
); }