diff --git a/.changeset/node-live.md b/.changeset/node-live.md new file mode 100644 index 00000000..f522709d --- /dev/null +++ b/.changeset/node-live.md @@ -0,0 +1,5 @@ +--- +"json-schema-studio": minor +--- + +Fix : Handle Node edge misallignment during Live edits \ No newline at end of file diff --git a/src/components/CustomReactFlowNode.tsx b/src/components/CustomReactFlowNode.tsx index f612998c..81f1bfb5 100644 --- a/src/components/CustomReactFlowNode.tsx +++ b/src/components/CustomReactFlowNode.tsx @@ -1,6 +1,6 @@ -import { Handle } from "@xyflow/react"; +import { Handle, useUpdateNodeInternals } from "@xyflow/react"; import type { RFNodeData } from "../utils/processAST"; -import { useContext, useLayoutEffect, useRef, useState } from "react"; +import { useContext, useEffect, useLayoutEffect, useRef, useState } from "react"; import { AppContext } from "../contexts/AppContext"; const CustomNode = ({ data, id, selected }: { data: RFNodeData; id: string; selected: boolean }) => { @@ -13,18 +13,17 @@ const CustomNode = ({ data, id, selected }: { data: RFNodeData; id: string; sele {} ); - useLayoutEffect(() => { - const container = Object.values(rowRefs.current)[0]?.offsetParent; - if (!(container instanceof HTMLElement)) return; + const updateNodeInternals = useUpdateNodeInternals(); + useEffect(() => { + updateNodeInternals(id); + }, [handleOffsets, id, updateNodeInternals]); - const containerRect = container.getBoundingClientRect(); + useLayoutEffect(() => { const offsets: Record = {}; for (const [key, row] of Object.entries(rowRefs.current)) { if (!row) continue; - - const rect = row.getBoundingClientRect(); - offsets[key] = rect.top + rect.height / 2 - containerRect.top; + offsets[key] = row.offsetTop + row.offsetHeight / 2; } setHandleOffsets(offsets); diff --git a/src/components/GraphView.tsx b/src/components/GraphView.tsx index 727b4bd9..bc986304 100644 --- a/src/components/GraphView.tsx +++ b/src/components/GraphView.tsx @@ -58,6 +58,7 @@ const GraphView = ({ const [nodes, setNodes, onNodeChange] = useNodesState([]); const [edges, setEdges, onEdgeChange] = useEdgesState([]); const [collisionResolved, setCollisionResolved] = useState(false); + const [isGraphReady, setIsGraphReady] = useState(false); const [hoveredEdgeId, setHoveredEdgeId] = useState(null); const [matchedNodes, setMatchedNodes] = useState([]); const [currentMatchIndex, setCurrentMatchIndex] = useState(0); @@ -266,7 +267,8 @@ const GraphView = ({ setCollisionResolved(true); setTimeout(() => { - fitView({ duration: 800, padding: 0.05 }); + fitView({ duration: isGraphReady ? 800 : 0, padding: 0.05 }); + setIsGraphReady(true); }, 300); }, [nodes, collisionResolved, allNodesMeasured, setNodes, fitView]); @@ -286,8 +288,13 @@ const GraphView = ({ if (!containerRef.current) return; let timeoutId: ReturnType; + let initialized = false; const observer = new ResizeObserver(() => { + if (!initialized) { + initialized = true; + return; + } clearTimeout(timeoutId); timeoutId = setTimeout(() => { const currentZoom = getZoom(); @@ -410,8 +417,11 @@ const GraphView = ({