Skip to content

BUG: Layer Manager drag keeps a stale drop target on slow drags (regression from #6542, 0.22.10+) #6789

Description

@msimundza

GrapesJS version

  • I confirm to use the latest version of GrapesJS (0.23.2)

What browser are you using?

Chrome

Reproducible demo link

Broken — grapesjs 0.23.2 (latest): https://jsfiddle.net/msimundza/cehak4b0/3/

Working — grapesjs 0.22.2 (pre-regression), identical setup, only the version differs: https://jsfiddle.net/msimundza/cehak4b0/5/

Note: the official demo at https://grapesjs.com/demo.html does NOT reproduce this, because it also loads grapesjs 0.22.2, which predates the regression.

Describe the bug

How to reproduce:

  1. Create content where the last top-level element is a container with children (e.g. a section with two blocks), plus a few sibling blocks above it.
  2. Open the Layer Manager and expand everything.
  3. Start dragging one of the middle blocks and move the pointer slowly downward over the last container, toward and past its bottom edge.

Expected behavior:

As the pointer crosses the bottom edge zone, the drop target should escalate from "inside the container / inside its last child" to the parent (the wrapper gets the selected-parent highlight) so the block can be dropped at the end of the list — this is how 0.22.2 behaves.

Current behavior:

The "nest inside" target (highlight on the last container / its child) sticks and never updates while moving slowly — the block cannot be dropped at the last position and often ends up nested in the wrong element. Moving the pointer quickly across the same area does update the target correctly, which makes the behavior feel random. The insertion-line placeholder flickers accordingly.

Screen recordings (0.23.2 stuck target on slow drag vs 0.22.2 escalating
correctly): attached in the first comment below.

Root cause (we traced it):

The regression was introduced by #6542 ("Sorter improvements", first released in 0.22.10). DropLocationDeterminer#getValidParent added a stale-target cache whose guard mixes two coordinate spaces:

https://github.com/GrapesJS/grapesjs/blob/v0.23.2/packages/core/src/utils/sorter/DropLocationDeterminer.ts#L378-L388

isWithinDropArea is computed from container-relative mouseX/mouseY, but it is compared against targetNode.isWithinDropBounds(lastMouseEvent?.clientX ?? 0, lastMouseEvent?.clientY ?? 0) — raw viewport coordinates passed into the same node-relative bounds check. The two spaces differ by the container offset/scroll, so the second call returns an essentially constant value. Whenever it happens to equal isWithinDropArea (typically both false), sameHoverPosition becomes true for slow drags (same hovered node, same hovered index between move samples) and the cached lastTargetNode is returned — even though the pointer has entered an edge band that should escalate the target to the parent. Fast drags change the hovered node/index between samples, bypass the cache, and work correctly — matching the erratic behavior users report.

Suggested fix: compare within a single coordinate space — e.g. store the previous isWithinDropArea result in lastMoveData instead of recomputing it from clientX/clientY, or convert lastMouseEvent through getMousePositionRelativeToContainer before the check. Alternatively, drop the early-return entirely: pre-0.22.10 behavior recomputed every move and worked well, and layer trees are small. We're happy to submit a PR with the fix and can share more of our analysis if useful.

Possibly related: #6669 (Layer Manager cross-level reordering issues).

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions