Skip to content

fix(dnd): tear down anchored drop overlay when a drag leaves mid-flight - #1536

Merged
mathuo merged 1 commit into
masterfrom
claude/drop-target-overlays-floating-t67o9k
Jul 25, 2026
Merged

fix(dnd): tear down anchored drop overlay when a drag leaves mid-flight#1536
mathuo merged 1 commit into
masterfrom
claude/drop-target-overlays-floating-t67o9k

Conversation

@mathuo

@mathuo mathuo commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Description

While an HTML5 drag was still active, leaving an anchored drop target (e.g. a tab in a floating group) and moving to dead space — or to a target in a different overlay container — left the drop overlay visible until the drag ended.

Reproduction: a floating group with two tabs; drag one tab over the other (a drop overlay appears on the target tab's edge), then move the mouse off the floating group — the overlay stays.

Cause: the HTML5 backend intentionally does not clear the shared anchor container on dragleave — doing so would kill the overlay's slide between adjacent targets and churn on the spurious dragleaves HTML5 fires when crossing a target's own child elements. But never clearing means the overlay lingers whenever the drag leaves to somewhere that doesn't re-render into that container.

Fix: on dragleave the target now schedules a clear on the anchor container (a new optional DropTargetTargetModel.scheduleClear) instead of doing nothing. The next target that renders into the same container cancels it (via getElements), so the overlay still slides between adjacent targets; but if the drag leaves to dead space or another container, the deferred clear fires and the overlay is torn down. The pointer backend already clears on drag-leave, so this is HTML5-only.

Relationship to #1534

This is the root-cause fix and also covers the cross-container end-of-drag case addressed by the earlier dragend safety net in #1534 (releasing elsewhere first involves leaving the target, which now schedules the clear). The #1534 dragend listener is retained as belt-and-suspenders for the narrow case of a drag that ends with no clearing dragleave at all (e.g. an ESC-cancel where the browser skips dragleave).

Type of change

  • Bug fix

Affected packages

  • dockview-core

How to test

Steps above (floating group, two tabs, drag one over the other, then leave the group — overlay no longer lingers).

Automated (droptargetDeferredClear.spec.ts):

  • leaving a target to dead space tears the overlay down;
  • sliding to another target in the same container preserves the overlay (the scheduled clear is cancelled by the re-render);
  • leaving a floating-container target for an in-place (other-container) target clears it purely from the leave — no dragend needed.

Checklist

  • yarn test passes (1683 dockview-core tests)
  • tsc --noEmit typecheck clean
  • biome format / biome lint clean
  • I have added or updated tests where applicable
  • Breaking changes are documented (none — scheduleClear is an optional addition to an internal model interface)

🤖 Generated with Claude Code

https://claude.ai/code/session_01PjU3r4fnmpLUz7Bx1SjNvP


Generated by Claude Code

While an HTML5 drag was still active, leaving an anchored drop target (e.g. a
tab in a floating group) and moving to dead space — or to a target in a
different overlay container — left the drop overlay visible until the drag
ended.

The HTML5 backend intentionally does not clear the shared anchor container on
`dragleave`: doing so would kill the overlay's slide between adjacent targets
and churn on the spurious `dragleave`s HTML5 fires when crossing a target's own
child elements. But never clearing means the overlay lingers whenever the drag
leaves to somewhere that doesn't re-render into that container.

Fix: on `dragleave` the target now *schedules* a clear on the anchor container
(`DropTargetTargetModel.scheduleClear`) instead of doing nothing. The next
target that renders into the same container cancels it (via `getElements`), so
the overlay still slides between adjacent targets; but if the drag leaves to
dead space or another container, the clear fires and the overlay is torn down.
The pointer backend already clears on drag-leave, so this is HTML5-only.

This is the root-cause fix for the lingering-overlay class of bug and also
covers the cross-container end-of-drag case addressed by the earlier `dragend`
safety net (#1534), which now acts as belt-and-suspenders for a drag that ends
with no clearing `dragleave` at all.

Tests: leaving to dead space tears the overlay down; sliding to another target
in the same container preserves it (scheduled clear cancelled); and the #1534
cross-container leave is cleared purely by the leave, with no `dragend`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjU3r4fnmpLUz7Bx1SjNvP
@sonarqubecloud

Copy link
Copy Markdown

@mathuo
mathuo merged commit 4af4ee5 into master Jul 25, 2026
9 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.

2 participants