Skip to content

feat(joint-core, joint-react): PointerEvents drag survival + capture in React preset#3320

Merged
Geliogabalus merged 4 commits into
clientIO:devfrom
kumilingus:feat/paper-pointer-events
May 19, 2026
Merged

feat(joint-core, joint-react): PointerEvents drag survival + capture in React preset#3320
Geliogabalus merged 4 commits into
clientIO:devfrom
kumilingus:feat/paper-pointer-events

Conversation

@kumilingus

Copy link
Copy Markdown
Contributor

Summary

  • joint-core: new public Paper#setDragging(evt, value=true) / Paper#isDragging(evt) API. Joint-core itself sets the flag from every action-confirmed drag-start branch (element / link / label / arrowhead / magnet→link). External consumers can ask "is a drag actually happening?" without poking view eventData internals.
  • joint-react: paper drag-survival migrates to real PointerEvent (pointermove / pointerup / pointercancel) and acquires setPointerCapture on the original pointerdown target on the first pointermove of a confirmed drag. Drags now survive the pointer leaving the host.

Joint-core's events hash is unchanged — mousedown / touchstart still drive pointerdown. Only documentEvents (the drag-survival layer) is swapped to pointer events in the React preset.

joint-core changes

  • dia/Paper.mjs — add setDragging / isDragging near delegateDragEvents. No callers outside the new API methods.
  • dia/ElementView.mjsdragStart calls paper.setDragging(evt) after can('elementMove') passes.
  • dia/LinkView.mjsdragStart, dragLabelStart, dragArrowheadStart all call paper.setDragging(evt) inside their respective can(\u2026) gates.
  • dia/CellView.mjsdragLinkStart calls paper.setDragging(evt) once the magnet\u2192link transition actually creates the link (covers the magnet path that defers via magnetThreshold='onleave').
  • types/dia.d.ts — declare both methods.

joint-react changes

packages/joint-react/src/presets/paper.ts:

  • documentEvents overridden to { pointermove, pointerup, pointercancel }.
  • startListening extended to record pointerTarget on cell:pointerdown + element:magnet:pointerdown (magnet event fires first on passive magnets; guard prevents the later cell event from overwriting).
  • pointermove override: when paper.isDragging(event) passes and captureTarget not yet set, calls setPointerCapture(pointerId) on data.pointerTarget (falls back to paper.el) and stashes captureTarget. Adds jj-is-dragging class to paper.el.
  • pointerup override: releases capture on captureTarget, clears jj-is-dragging class.

packages/joint-react/src/css/styles.css:

  • New CSS vars --jj-drag-cursor, --jj-dragging-cursor, --jj-port-drag-cursor, --jj-port-dragging-cursor.
  • .jj-is-dragging .jj-box, .jj-is-dragging .jj-port-body[magnet=\"active\"] cursor rules gated on the dragging class.

Test plan

  • yarn workspace @joint/react jest --testPathPatterns=\"paper\" — 85/85 pass.
  • yarn workspace @joint/react typecheck clean (modulo the pre-existing code-controlled-mode-reset-bug.tsx height regression already on dev).
  • Live smoke (storybook):
    • Drag an element — captured, drag follows the pointer when it leaves paper bounds, drops at release point.
    • Drag a link / link label — same behavior.
    • Drag a magnet → creates a link — capture acquired after the magnet threshold; arrowhead follows the pointer beyond paper bounds.
    • Right-click on cells/blank — no drag, contextmenu fires normally.

\ud83e\udd16 Generated with Claude Code

kumilingus and others added 4 commits May 18, 2026 17:42
…in React preset

joint-core
- Adds `Paper#setDragging(evt, value=true)` / `Paper#isDragging(evt)` — public API for marking and reading the active drag state on an event. Called from every action-confirmed drag-start branch: `ElementView.dragStart`, `LinkView.dragStart`, `LinkView.dragLabelStart`, `LinkView.dragArrowheadStart`, `CellView.dragLinkStart`. External consumers (e.g. joint-react) can detect "drag is actually happening" without inspecting view-level eventData.

joint-react preset (`presets/paper.ts`)
- Switches paper drag-survival to real PointerEvents: `documentEvents` binds `pointermove` / `pointerup` / `pointercancel` instead of mouse + touch pairs. Joint-core's events hash stays unchanged (mousedown / touchstart still drive pointerdown).
- On first pointermove of a confirmed drag (per `paper.isDragging(evt)`), calls `setPointerCapture` on the original pointerdown target — so drags survive the pointer leaving the host. `pointerup` / `pointercancel` releases capture.
- Tracks the original pointerdown target via `startListening` listeners on `cell:pointerdown` + `element:magnet:pointerdown` — captures the magnet's deeper target when present (passive-magnet ordering: magnet event fires before cell event, guard prevents overwrite).
- Adds `jj-is-dragging` class on paper.el during drag; styles.css gates new `--jj-drag-cursor` / `--jj-dragging-cursor` / `--jj-port-drag-cursor` / `--jj-port-dragging-cursor` CSS vars on `.jj-box` and `.jj-port-body[magnet="active"]`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Always sets to true. YAGNI — easy to add later if needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a QUnit module that verifies:
- direct API round-trip (set then read);
- `isDragging` defaults to false on fresh events;
- `ElementView.dragStart` flips the flag after `can('elementMove')` passes;
- `isDragging` stays false when `elementMove` is denied;
- `LinkView.dragStart` flips the flag after `can('linkMove')`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ging signatures

`dia.Event` was unresolved inside dia.d.ts itself; the file declares
`export type Event = mvc.TriggeredEvent` and other methods reference
`Event` directly. Match the convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kumilingus kumilingus requested a review from Geliogabalus May 18, 2026 16:09
@Geliogabalus Geliogabalus merged commit e8dd520 into clientIO:dev May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants