Files
excalidraw/packages
David Luzar c0ad61c674 feat(editor): rightclick to pan canvas (#12110)
* refactor(editor): move the drag-pan out of App.tsx into App.pan.ts

`handleCanvasPanUsingWheelOrSpaceDrag` — gating, the Linux middle-click
paste guard, the throttled move and the teardown — moves as-is into an
`AppPan` helper (`app.pan`), in the shape of `App.cursor` and
`App.viewport`. The session state it ran on was module-level in App.tsx
(`isHoldingSpace`, `isPanning`, `flushPanMove`), shared by every `App`
instance in the page: with two editors mounted, a wheel-button pan in one
made the other ignore wheel input. It is instance state now — `spaceHeld`,
`active`, the pending move flush and the teardown — behind `start`,
`isActive`, `flushMove`, `end` and the space accessors. Only the pointer
count is injected; `gesture` stays where it is.

`AppWheel` asks `app.pan` directly instead of taking those two callbacks
as dependencies, the wysiwyg's wheel-button pan calls `app.pan.start`,
and `maybeCleanupAfterMissingPointerUp` ends the pan explicitly, which
leaves `lastPointerUp` to the scrollbar drag alone. `savePointer` becomes
public for the teardown. No behavior change.

Claude Fable 5.1, high effort

* feat(editor): pan by dragging with the secondary mouse button

A secondary-button press starts a pan session like the wheel button
does, except that it stays a right-click until the pointer travels past
the drag threshold: nothing moves and the cursor stays put before that;
past it the canvas follows the pointer (from that point, the threshold
distance is not caught up) and no menu opens. Released before the
threshold, the session opens the context menu itself at the release
point, through the same `openContextMenu` the `contextmenu` handler now
delegates to.

That handler ignores the platform's `contextmenu` event while a
secondary-button session is active — macOS and Linux fire it on
mousedown, before a drag can be told from a click — and once after the
session ends if it has not fired yet, which is Windows firing it on
mouseup. Touch long-press, pen barrel taps and keyboard-triggered menus
never go through a session, so they open as before. On macOS and Linux
the menu now opens on release rather than on press.

View mode already let any button pan; a right-click there opens the
view-mode menu on release the same way. With interaction disabled the
drag still pans (when navigation is enabled) and no menu opens.

Tests: the threshold, the menu on release, both platform orders of the
native event, no menu after a drag, element selection under the pointer,
and the non-interactive editor.

Claude Fable 5.1, high effort

* fix(editor): let the contextmenu after mouseup open the menu for a right-click, as before

The secondary-button session opened the menu on release everywhere. On
Windows the platform's `contextmenu` fires after mouseup, by which time
our menu was rendered under the pointer; the event then targeted the
popover instead of the canvas, the canvas handler that prevented the
native menu never ran, and the native menu opened on top of ours.

The session now opens the menu on release only where the platform's
`contextmenu` already came with the press (macOS, Linux) and was
swallowed because a drag could not be told from a click yet. Where it
follows the release, it opens the menu itself, exactly as before this
branch, and lands on the canvas as there is nothing under the pointer.
After a drag that event is swallowed once, as it was.

Tests: after mouseup, nothing is open at release and the platform's event
opens the menu; the mousedown-order and drag cases are unchanged.

Claude Fable 5.1, high effort

* reintroduce: ctrl+wheel should always zoom for both input devices

* fix(editor): lower right-button pan threshold to 5px

* fix(editor): broadcast the final pointer position after panning

* fix(editor): suppress cursor broadcasts while panning
2026-09-16 19:50:39 +02:00
..