GrapesJS version
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:
- 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.
- Open the Layer Manager and expand everything.
- 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
GrapesJS version
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:
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-parenthighlight) 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#getValidParentadded 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
isWithinDropAreais computed from container-relativemouseX/mouseY, but it is compared againsttargetNode.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 equalisWithinDropArea(typically bothfalse),sameHoverPositionbecomes true for slow drags (same hovered node, same hovered index between move samples) and the cachedlastTargetNodeis 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
isWithinDropArearesult inlastMoveDatainstead of recomputing it fromclientX/clientY, or convertlastMouseEventthroughgetMousePositionRelativeToContainerbefore 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