Skip to content

Harden touch reorder: one drag grip for layers and frames - #77

Merged
freethinkel merged 1 commit into
mainfrom
fix-touch-reorder-review
Oct 18, 2026
Merged

freethinkel merged 1 commit into
mainfrom
fix-touch-reorder-review

Conversation

@freethinkel

Copy link
Copy Markdown
Owner

Follow-up to #73 (issue #65), whose review findings landed after the merge.

What changed

A new editor/components/DragGrip.svelte holds the touch drag in one place. The layers tree and the inspector's frame list both use it.

  • Pointer capture + pointerId: the grip captures the pointer that went down on it and ignores every other one. A second finger lifting no longer drops early. pointercancel and lostpointercapture end the drag without a drop.
  • No hijacking: a long press on the grip calls preventDefault on contextmenu and stops it from bubbling, so onRowMenu doesn't open. The grip also sets -webkit-touch-callout: none and user-select: none. The row's draggable is false while its grip is held, so a long press can't start a native HTML5 drag on top of the pointer drag.
  • (any-pointer: coarse) instead of (hover: none): touch laptops and iPads with a trackpad now get the grip.
  • Frames: the frame order (which is data, since frames are indexed by value) could only be changed by native HTML5 drag, and the drag_indicator icon did nothing. It is now a working grip. It stays visible with a mouse too, as the icon was before. This also removes the :global(.grip) rule in frames.svelte, which had been leaking onto every .grip in the app.

Tests

tests/touch.ts turns on CDP touch emulation, which makes any-pointer: coarse match, and sends real fingers through Input.dispatchTouchEvent. The grip is located by hit-testing its centre, so the tests fail if the grip isn't visible on a coarse-pointer device. Each test opens its own document.

  • tree: touch reorder; a second finger on the same grip lifting doesn't drop, then touchCancel moves nothing; a touch drop onto a group's middle puts the layer into the group
  • frames: a touch reorder of the weekday widget's frames

I checked that these tests catch real breakage: hiding the grip, removing the pointerId check, and treating a cancel as a drop each make them fail.

Still needed after merge

This was tested only in emulated Chromium. It still needs a check on a real phone in Android Chrome and iOS Safari: long-press behaviour, the context menu or callout, and whether the list scrolls while dragging.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MDZAQ82FMBa8WgPzfddQSU

Follow-up to #73's review. The grip is now a shared DragGrip component:

- captures its pointer and only follows the finger that went down on it, so
  a second finger lifting no longer drops; pointercancel and lost capture end
  the drag without a drop
- a long press on it doesn't open the row's context menu, and the row stops
  being natively draggable while the grip is held, so HTML5 drag can't take
  the gesture over
- shows under (any-pointer: coarse), not (hover: none), so touch laptops and
  iPads with a trackpad get it too
- the frame list in the inspector uses it as well: frame order was only
  reorderable by HTML5 drag, and its drag_indicator icon was decorative

Tests drive real touches through CDP touch emulation, so they fail when the
grip isn't visible under a finger; each test opens its own document, and
pointercancel, second-finger and drop-into-group cases are covered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDZAQ82FMBa8WgPzfddQSU
@freethinkel
freethinkel merged commit 31282f0 into main Oct 18, 2026
2 checks passed
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.

1 participant