Skip to content

Move whole lines as blocks when dragging or cutting - #6171

Merged
dylans merged 2 commits into
mainfrom
drop-keep-hanging
Oct 8, 2026
Merged

dylans merged 2 commits into
mainfrom
drop-keep-hanging

Conversation

@dylans

@dylans dylans commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #5376

Dragging or cutting a selection that runs from the start of one line to the start of the next copies the line break, but the delete unhung the range and left the emptied block behind. Each move grew the document by one empty line.

The first version of this PR deleted with hanging: true. That merged containers: a cell drag removed a table cell, the paragraph after a list was pulled into the list, and the block after a dragged heading became a heading. This version decides per range:

Range Copied fragment Delete
Start of a top-level block to the start of a later one hanging, with the line break remove those blocks outright, no merge
Anything else (list items, table cells, partial lines) unhung editor.deleteFragment, as main does for cut

Also:

  • Drag and cut share one path: the drop deletes through editor.deleteFragment (as cut already did), so plugins that guard deletion cover both.
  • Tables example: uses that hook to clear a cross-cell selection cell by cell instead of merging cells. Core can't know about cells, so this lives in the example.
  • Drop onto own selection: dropping text back onto its own selection is a no-op.

Tests:

  • packages/slate-react/test/move-range.spec.ts (13 cases): plain lines, backward selections, headings, quotes, several lines at once, empty lines, a non-hanging range, an inline start, list items, a list followed by a paragraph, table cells, table rows, and the deleteFragment hook.
  • Playwright drag tests run in Firefox only (the one engine that performs native text drags under Playwright); cut tests run in all browsers. They cover plaintext (drag a line, drop onto itself, cut a line), richtext (drag the quote line) and tables (drag between cells, cut across cells).
  • Every new Playwright test fails on main except the drop-onto-itself guard, which main already handles. The full integration suite passes locally (179 passed), as do the strict checks on Node 24.

Known limit: nested moves leave the emptied list item or cell in place (main does the same), because removing it can't be done generically without breaking tables.

The drag copies the selection as-is, so a selection running from the
start of one line to the start of the next carries the line break. On
drop, Transforms.delete unhung that range first, deleting only the text
and leaving the now-empty block behind, while the dropped fragment
re-added the break, so every such drag grew the document by an empty
line. Delete with hanging: true so the removed range matches the
dragged fragment.

The Playwright test runs in Firefox only, the one engine that performs
a native drag of a text selection under Playwright.

Fixes #5376
@changeset-bot

changeset-bot Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f141316

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
slate-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dylans
dylans marked this pull request as draft October 8, 2026 06:57
@dylans

dylans commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

Moving this back to draft. hanging: true fixes plain blocks and sibling list items, but in nested structures it merges containers. In a table, dragging from the start of one cell to the start of the next removes the second cell, leaving the row one cell short. Dragging the last list item up to the paragraph after the list pulls that paragraph into the list. Main only empties the first cell or item in those cases. I'll rework this so the deleted range matches the dragged fragment without merging across containers.

Deleting the dragged range with hanging: true fixed plain lines but
merged containers elsewhere: a drag from the start of one table cell to
the start of the next removed a cell, the paragraph after a list was
pulled into the list, and the block after a dragged heading became a
heading.

Decide per range instead. When a range runs from the start of one
top-level block to the start of a later one, keep the hanging fragment
and remove those blocks outright, without merging anything. Otherwise,
unhang the range before copying it, so the fragment matches what the
unhung delete removes. Cut gets the same rule, since it had the same
extra-line bug.

The drop now deletes through editor.deleteFragment, as cut does, so
plugins that guard deletion cover both. The tables example uses that
hook to clear a cross-cell selection cell by cell instead of merging
cells. Dropping onto the dragged selection itself now does nothing.

Tests: unit tests for the range helper (plain lines, headings, quotes,
lists, tables, empty lines, inline starts), plus Playwright drag tests
(Firefox only) and cut tests (all browsers) in the plaintext, richtext
and tables examples. All but the drop-onto-itself test fail on main.
@dylans dylans changed the title Delete the exact dragged range when dropping inside the editor Move whole lines as blocks when dragging or cutting Oct 8, 2026
@dylans
dylans marked this pull request as ready for review October 8, 2026 07:13
@dylans
dylans merged commit 7843bd5 into main Oct 8, 2026
16 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 8, 2026
janpaepke added a commit to janpaepke/slate that referenced this pull request Oct 8, 2026
Resolves the conflict with ianstormtaylor#6171 in `<Editable>`'s `onDrop`: its handling of
internal drags (skip drops onto the dragged range, move via `deleteMovedRange`)
now runs inside the branch that has a drop range, while a drop without one keeps
falling back to the selection or leaving internal drags in place.
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.

Bug occurs when you copy and paste multiple lines

1 participant