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,
|
BackgroundVariant,
|
||||||
type Connection,
|
type Connection,
|
||||||
type Node as FlowCanvasNode,
|
type Node as FlowCanvasNode,
|
||||||
|
type NodeChange,
|
||||||
ReactFlow,
|
ReactFlow,
|
||||||
ReactFlowProvider,
|
ReactFlowProvider,
|
||||||
useNodesState,
|
useNodesState,
|
||||||
@@ -412,6 +413,25 @@ function FlowEditorInner({
|
|||||||
() => placementsFor(flowName),
|
() => 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. */
|
/** Where clicking an endpoint takes you: the thing it stands for. */
|
||||||
const openEndpoint = useCallback(
|
const openEndpoint = useCallback(
|
||||||
(id: string) => {
|
(id: string) => {
|
||||||
@@ -435,17 +455,23 @@ function FlowEditorInner({
|
|||||||
// stored: they join at render, after everything that reads or writes
|
// stored: they join at render, after everything that reads or writes
|
||||||
// canvasNodes, so an autosave, an undo or a delete cannot reach them.
|
// 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.
|
// biome-ignore lint/correctness/useExhaustiveDependencies: positions change on every drag frame; the key covers the wiring.
|
||||||
const external = useMemo(
|
const external = useMemo(() => {
|
||||||
() =>
|
const built = deriveEndpoints(
|
||||||
deriveEndpoints(
|
|
||||||
detail.endpoints ?? [],
|
detail.endpoints ?? [],
|
||||||
definitions,
|
definitions,
|
||||||
flowName,
|
flowName,
|
||||||
new Map(canvasNodes.map((node) => [node.id, node.position])),
|
new Map(canvasNodes.map((node) => [node.id, node.position])),
|
||||||
moved,
|
moved,
|
||||||
),
|
|
||||||
[detail.endpoints, key, flowName, 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.
|
// Edges follow from the name bindings, so they are derived, never stored.
|
||||||
// Kept off `external` deliberately: an endpoint's edges depend on which
|
// Kept off `external` deliberately: an endpoint's edges depend on which
|
||||||
@@ -856,7 +882,7 @@ function FlowEditorInner({
|
|||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={shownNodes}
|
nodes={shownNodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={trackMeasured}
|
||||||
onNodeDrag={(_event, _node, dragged) => {
|
onNodeDrag={(_event, _node, dragged) => {
|
||||||
// An endpoint's position is ours, not React Flow's, so it only
|
// An endpoint's position is ours, not React Flow's, so it only
|
||||||
// follows the pointer if we move it every frame.
|
// follows the pointer if we move it every frame.
|
||||||
|
|||||||
Reference in New Issue
Block a user