feat(dia.Paper): add startLinkDrag() to drag a link end programmatically - #3505
Draft
kumilingus wants to merge 7 commits into
Draft
feat(dia.Paper): add startLinkDrag() to drag a link end programmatically#3505kumilingus wants to merge 7 commits into
kumilingus wants to merge 7 commits into
Conversation
Adds `paper.startLinkDrag(link, { end, whenNotAllowed })` returning a
`dia.LinkDrag` handle. The handle is driven either manually (`move()`,
`finish()`, `cancel()`) or by the pointer (`followPointer({ finishOn })`),
which covers press-drag-release as well as click-move-click flows and
cancels on Escape / context menu.
The link may not be in the graph yet (it is added in an `add-link` batch)
and its view may not be rendered yet (async paper), so the entry point
lives on the paper rather than on a view.
`LinkView.cancelArrowheadMove()` is added as the counterpart of
`startArrowheadMove()`. `CellView.addLinkFromMagnet()` and `getLinkEnd()`
become public in the type definitions so a link can be created from a
magnet of an element or a link before starting the drag.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…through LinkDrag `LinkDrag` takes `(paper, link, opt)` and owns the whole lifecycle (batch, adding the link, `requireView`, `startArrowheadMove`). `move()` and `finish()` accept `(evt, x, y)` like every other JointJS handler, and extra `startLinkDrag()` options flow into the batch data. `CellView.dragLinkStart/dragLink/dragLinkEnd`, `cellTools.Connect` (both `elementTools.Connect` and `linkTools.Connect`) and `linkTools.Arrowhead` now use it. `CellView.createLinkFromMagnet()` builds the link without adding it so the add still lands in the `add-link` batch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds `updateArrowheadMove(data, evt, x, y)` and `finishArrowheadMove(data, evt, x, y)` next to `startArrowheadMove()` / `cancelArrowheadMove()`, so the move state is passed explicitly instead of being looked up in `evt.data`. `dragArrowhead()` / `dragArrowheadEnd()` are thin wrappers over them, and `LinkDrag` no longer needs to know the `View.eventData()` key format. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
kumilingus
force-pushed
the
feat/paper-start-link-drag
branch
from
September 12, 2026 15:12
89c140a to
493cca6
Compare
…ate and getConnectionCandidate()
`followPointer({ finishOn })` now accepts `'connection'` (a primary click
over a valid magnet finishes, other clicks are ignored) and a function
`(evt, linkDrag) => boolean` called on every pointerdown / pointerup. The
finishing event first moves the end to its position so the candidate is
evaluated where the user clicked.
`getConnectionCandidate()` exposes the validated magnet the end would
connect to (`magnetUnderPointer`, or the closest one with `snapLinks`).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an official API to start dragging an end of any link, replacing the private-state workarounds apps currently need (e.g.
ai-agent-builder/ts/src/system/actions/connect-nodes.tsin joint-demos, which injectstargetMagnetintoeventData, reads the privatelinkViewfrom it and "cancels" by removing the link).dia.Paper.startLinkDrag(link, { end = 'target', whenNotAllowed, ...batchData })→dia.LinkDragSugar for
new dia.LinkDrag(paper, link, opt). Takes a link model only (aLinkViewcould belong to another paper).add-linkbatch andwhenNotAlloweddefaults to'remove'. A link already in the graph is wrapped in anarrowhead-movebatch (default'revert'), likelinkTools.Arrowhead.{ ui: true, tool: cid }from the tools.requireView(), so it works on an async paper before the view is rendered. This is why the entry point lives on the paper and not on a view.dia.LinkDragmove(evt)/move(evt, x, y)/move(x, y), same forfinish();cancel(),isActive(),link,linkView,end.followPointer({ finishOn })→Promise<{ cancelled, linkView }>. Binds documentpointermove,pointerdown/pointerup,keydownEscape andcontextmenu(prevented + cancel).finishOn:'pointerup'(default) – primary release, press-drag-release'pointerdown'– primary click, click-move-click'connection'– primary click over a valid magnet (validateConnection), other clicks are ignored(evt, linkDrag) => boolean– called on everypointerdown/pointerup, full control (e.g. add a vertex on a blank click, finish on a magnet)The finishing event first moves the end to its position, so the connection is evaluated where the user clicked even without a preceding
pointermove.getConnectionCandidate()→{ cellView, magnet } | null– the validated magnet the end would connect to right now (under the pointer, or the closest one withsnapLinks). Suspends paper events while active and restores them afterwards, exactly as the tools do. Calling it twice returns the same promise; on a finished handle it resolves immediately.External
link.remove()mid-drag cleans up (highlighters,pointer-events,z, available-magnet marks) and resolves withcancelled: true.Internal consumers ported onto
LinkDragCellView.dragLinkStart / dragLink / dragLinkEnd(paper magnet dragging),elementTools.Connect/linkTools.Connect(+HoverConnect) andlinkTools.Arrowhead. Paper event sequences (link:pointerdown/move/up,link:connect/disconnect,mouseleave) are unchanged and covered by the existing tests.add-link/arrowhead-movebatch now stops right after the end is connected, i.e. beforelink:pointerupis triggered (it used to stop after it). Flagged in the changeset.Other changes
dia.LinkView.cancelArrowheadMove(data)– counterpart ofstartArrowheadMove(); reverts/removes only if the link is still in the graph.dia.CellView.createLinkFromMagnet(magnet, x, y)– builds the default link with its source set throughconnectionStrategywithout adding it to the graph;addLinkFromMagnet()uses it. Both, andgetLinkEnd(), are public in the type definitions.Not in this PR (follow-ups)
snapLinks/snapLinksSelf/markAvailable.Test plan
test/jointjs/dia/linkDrag.js– 35 QUnit tests (manual control incl. the(evt, x, y)form, batch data,getConnectionCandidate()plain and withsnapLinks, link resolution incl. async paper, magnet on element and on link, external removal,followPointerwith realPointerEvent/KeyboardEventdispatch incl. the'connection'mode and the predicate form, listener cleanup).karma:joint: 2148 pass.linkTools.js/elementTools.js/paper.jstests for Arrowhead, Connect and magnet dragging pass unchanged.yarn test-tswith a new usage snippet intest/ts/index.test.ts,yarn test-server,yarn lint.🤖 Generated with Claude Code