Keep an endpoint's measurement so its drag stops relighting the edges
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7LwYgJfpkbLCTeiAf8U4A
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
BackgroundVariant,
|
||||
type Connection,
|
||||
type Node as FlowCanvasNode,
|
||||
type NodeChange,
|
||||
ReactFlow,
|
||||
ReactFlowProvider,
|
||||
useNodesState,
|
||||
@@ -412,6 +413,25 @@ function FlowEditorInner({
|
||||
() => placementsFor(flowName),
|
||||
)
|
||||
|
||||
// React Flow measures a node once and keeps the size on it. Endpoints are
|
||||
// rebuilt on every drag frame, so unless the measurement is carried over
|
||||
// they arrive unmeasured and React Flow drops the edges attached to them
|
||||
// until it has measured again — remounting those edges, which makes them
|
||||
// pulse as if a value had just landed. Their own drag lit up the canvas.
|
||||
const measured = useRef(new Map<string, { width: number; height: number }>())
|
||||
const trackMeasured = useCallback(
|
||||
(changes: NodeChange<FlowCanvasNode>[]) => {
|
||||
for (const change of changes) {
|
||||
if (change.type !== "dimensions" || !change.dimensions) continue
|
||||
if (isEndpointNode({ id: change.id })) {
|
||||
measured.current.set(change.id, change.dimensions)
|
||||
}
|
||||
}
|
||||
onNodesChange(changes)
|
||||
},
|
||||
[onNodesChange],
|
||||
)
|
||||
|
||||
/** Where clicking an endpoint takes you: the thing it stands for. */
|
||||
const openEndpoint = useCallback(
|
||||
(id: string) => {
|
||||
@@ -435,17 +455,23 @@ function FlowEditorInner({
|
||||
// stored: they join at render, after everything that reads or writes
|
||||
// canvasNodes, so an autosave, an undo or a delete cannot reach them.
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: positions change on every drag frame; the key covers the wiring.
|
||||
const external = useMemo(
|
||||
() =>
|
||||
deriveEndpoints(
|
||||
detail.endpoints ?? [],
|
||||
definitions,
|
||||
flowName,
|
||||
new Map(canvasNodes.map((node) => [node.id, node.position])),
|
||||
moved,
|
||||
),
|
||||
[detail.endpoints, key, flowName, moved],
|
||||
)
|
||||
const external = useMemo(() => {
|
||||
const built = deriveEndpoints(
|
||||
detail.endpoints ?? [],
|
||||
definitions,
|
||||
flowName,
|
||||
new Map(canvasNodes.map((node) => [node.id, node.position])),
|
||||
moved,
|
||||
)
|
||||
return {
|
||||
...built,
|
||||
nodes: built.nodes.map((node) => {
|
||||
const size = measured.current.get(node.id)
|
||||
return size ? { ...node, measured: size, ...size } : node
|
||||
}),
|
||||
}
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: positions change on every drag frame; the key covers the wiring.
|
||||
}, [detail.endpoints, key, flowName, moved])
|
||||
|
||||
// Edges follow from the name bindings, so they are derived, never stored.
|
||||
// Kept off `external` deliberately: an endpoint's edges depend on which
|
||||
@@ -856,7 +882,7 @@ function FlowEditorInner({
|
||||
<ReactFlow
|
||||
nodes={shownNodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onNodesChange={trackMeasured}
|
||||
onNodeDrag={(_event, _node, dragged) => {
|
||||
// An endpoint's position is ours, not React Flow's, so it only
|
||||
// follows the pointer if we move it every frame.
|
||||
|
||||
Reference in New Issue
Block a user