Skip to content

fix(core): Dragging between editors revisions#7674

Open
mweidner037 wants to merge 11 commits intoueberdosis:mainfrom
commoncurriculum:drag-between-editors
Open

fix(core): Dragging between editors revisions#7674
mweidner037 wants to merge 11 commits intoueberdosis:mainfrom
commoncurriculum:drag-between-editors

Conversation

@mweidner037
Copy link
Copy Markdown
Contributor

@mweidner037 mweidner037 commented Mar 30, 2026

Changes Overview

#5893 added support for drag-and-drop between multiple editors - namely, ensuring that the source content is removed on drop. This PR revises that behavior:

  1. Move the removal logic from PasteRule.ts to the core Drop extension. This ensures that it runs exactly once, instead of once per paste rule that you happen to have installed.
    1. Usually, this issue is not noticeable because (a) most installs have at least one paste rule and (b) the first paste rule deletes the dragged content, typically setting the selection to a cursor, and so later paste rules do nothing. However, it's possible for the first deleteRange command to end with a nontrivial selection, causing the second paste rule to delete extra content.
  2. Change the removal logic to only run conditionally, depending on whether the user intended to move or copy. ProseMirror, mimicking browser behavior, allows the user to specify a drag-as-copy operation by holding down an appropriate modifier key (Alt on Mac, Ctrl otherwise). This PR adds the same logic for dragging between editors.
  3. Update the global DragHandle plugin's dragStart handler to set dataTransfer items for text/html and text/plain, like ProseMirror does for dragstart events within the editor DOM. Otherwise, the browser thinks you are dragging nothing, so dragging outside of the editor does not work.

Implementation Approach

  • Undo the change to PasteRule.ts from feat: allow support for drag-and-drop between multiple editors #5893.
  • Add the same logic to the core Drop extension instead.
  • Copy prosemirror-view's dragMoves function (which is not exported from there) and only remove the dragged content from the source editor if it says to.
  • Modify extension-drag-handle's dragHandler to set dataTransfer data, in addition to the dragImage.

Testing Done

Verification Steps

Try out the new demos:

  • GlobalDragHandleMultiEditor: dragging between editors using a global drag handle (mounted parallel to the editor DOM, so ProseMirror doesn't see the dragstart event).
  • NodeViewDragHandleMultiEditor: dragging between editors using NodeView drag handles (mounted within the editor DOM).

Hold Alt (Mac) or Control (Windows/Linux) while dragging to test out the drag-as-copy feature.

You can also git checkout main packages to try these demos without the PR's fixes. GlobalDragHandleMultiEditor in particular is exciting - every drag deletes the entire source editor content, due to issue 1.

Additional Notes

Checklist

  • I have created a changeset for this PR if necessary.
  • My changes do not break the library.
  • I have added tests where applicable.
  • I have followed the project guidelines.
  • I have fixed any lint issues.

Related Issues

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 30, 2026

🦋 Changeset detected

Latest commit: 5c620ef

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

This PR includes changesets to release 72 packages
Name Type
@tiptap/extension-drag-handle Major
@tiptap/core Major
@tiptap/extension-drag-handle-react Major
@tiptap/extension-drag-handle-vue-2 Major
@tiptap/extension-drag-handle-vue-3 Major
@tiptap/extension-audio Major
@tiptap/extension-blockquote Major
@tiptap/extension-bold Major
@tiptap/extension-bubble-menu Major
@tiptap/extension-code-block-lowlight Major
@tiptap/extension-code-block Major
@tiptap/extension-code Major
@tiptap/extension-collaboration-caret Major
@tiptap/extension-collaboration Major
@tiptap/extension-details Major
@tiptap/extension-document Major
@tiptap/extension-emoji Major
@tiptap/extension-file-handler Major
@tiptap/extension-floating-menu Major
@tiptap/extension-hard-break Major
@tiptap/extension-heading Major
@tiptap/extension-highlight Major
@tiptap/extension-horizontal-rule Major
@tiptap/extension-image Major
@tiptap/extension-invisible-characters Major
@tiptap/extension-italic Major
@tiptap/extension-link Major
@tiptap/extension-list Major
@tiptap/extension-mathematics Major
@tiptap/extension-mention Major
@tiptap/extension-node-range Major
@tiptap/extension-paragraph Major
@tiptap/extension-strike Major
@tiptap/extension-subscript Major
@tiptap/extension-superscript Major
@tiptap/extension-table-of-contents Major
@tiptap/extension-table Major
@tiptap/extension-text-align Major
@tiptap/extension-text-style Major
@tiptap/extension-text Major
@tiptap/extension-twitch Major
@tiptap/extension-typography Major
@tiptap/extension-underline Major
@tiptap/extension-unique-id Major
@tiptap/extension-youtube Major
@tiptap/extensions Major
@tiptap/html Major
@tiptap/markdown Major
@tiptap/react Major
@tiptap/starter-kit Major
@tiptap/static-renderer Major
@tiptap/suggestion Major
@tiptap/vue-2 Major
@tiptap/vue-3 Major
@tiptap/extension-bullet-list Major
@tiptap/extension-ordered-list Major
@tiptap/extension-list-item Major
@tiptap/extension-list-keymap Major
@tiptap/extension-task-item Major
@tiptap/extension-task-list Major
@tiptap/extension-table-cell Major
@tiptap/extension-table-header Major
@tiptap/extension-table-row Major
@tiptap/extension-color Major
@tiptap/extension-font-family Major
@tiptap/extension-character-count Major
@tiptap/extension-dropcursor Major
@tiptap/extension-focus Major
@tiptap/extension-gapcursor Major
@tiptap/extension-history Major
@tiptap/extension-placeholder Major
@tiptap/pm Major

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

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 30, 2026

Deploy Preview for tiptap-embed ready!

Name Link
🔨 Latest commit 5c620ef
🔍 Latest deploy log https://app.netlify.com/projects/tiptap-embed/deploys/69cc00fe1f1c4900082a1894
😎 Deploy Preview https://deploy-preview-7674--tiptap-embed.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@mweidner037 mweidner037 marked this pull request as ready for review March 31, 2026 16:50
Copilot AI review requested due to automatic review settings March 31, 2026 16:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR revises Tiptap’s cross-editor drag-and-drop behavior by moving “remove source content on drop” logic into the core Drop extension (so it runs exactly once), making removal conditional on whether the drag should be treated as move vs copy, and improving global drag-handle dragstart dataTransfer population so drags out of the editor work reliably.

Changes:

  • Move cross-editor “delete source selection after drop” logic from PasteRule into the core Drop extension, and gate deletion by copy-modifier detection.
  • Update @tiptap/extension-drag-handle to populate dataTransfer with text/html and text/plain on dragstart.
  • Add two new demos (GlobalDragHandleMultiEditor, NodeViewDragHandleMultiEditor) to validate the behavior.

Reviewed changes

Copilot reviewed 16 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/extension-drag-handle/src/helpers/dragHandler.ts Sets dataTransfer HTML/plain payloads on dragstart to match ProseMirror behavior.
packages/core/src/PasteRule.ts Removes prior cross-editor deletion logic from paste rules plugin.
packages/core/src/extensions/drop.ts Adds global drag source tracking and conditional source deletion on cross-editor drop.
demos/src/Experiments/NodeViewDragHandleMultiEditor/React/styles.scss Styles for the new NodeView drag-handle multi-editor demo.
demos/src/Experiments/NodeViewDragHandleMultiEditor/React/index.jsx New demo mounting two editors with draggable nodeviews.
demos/src/Experiments/NodeViewDragHandleMultiEditor/React/index.html HTML entry for the new NodeView drag-handle demo.
demos/src/Experiments/NodeViewDragHandleMultiEditor/React/DraggableItem.js Demo extension defining a draggable block node with a React nodeview.
demos/src/Experiments/NodeViewDragHandleMultiEditor/React/Component.jsx Demo nodeview component rendering a drag handle + content.
demos/src/Experiments/GlobalDragHandleMultiEditor/React/styles.scss Styles for the new global drag-handle multi-editor demo.
demos/src/Experiments/GlobalDragHandleMultiEditor/React/index.jsx New demo mounting two editors with the global drag handle component.
demos/src/Experiments/GlobalDragHandleMultiEditor/React/index.html HTML entry for the new GlobalDragHandle demo.
demos/src/Experiments/GlobalDragHandleMultiEditor/React/extensions/recommendation/views/RecommendationView.jsx Demo nodeview for a draggable “recommendation” node.
demos/src/Experiments/GlobalDragHandleMultiEditor/React/extensions/recommendation/views/index.jsx Barrel export for recommendation views.
demos/src/Experiments/GlobalDragHandleMultiEditor/React/extensions/recommendation/Recommendation.jsx Demo extension for a draggable recommendation node.
demos/src/Experiments/GlobalDragHandleMultiEditor/React/extensions/recommendation/index.jsx Barrel export for the recommendation extension.
.changeset/sixty-falcons-boil.md Changeset for core bug fix (extra deletion on cross-editor drag).
.changeset/short-steaks-cover.md Changeset for core minor feature (modifier-key copy vs move).
.changeset/cyan-carpets-wonder.md Changeset for drag-handle patch (set dataTransfer so dragging outside works).

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