Skip to content

Conversation

@msokh
Copy link

@msokh msokh commented Aug 18, 2025

Changes Overview

This PR fixes support for drag and drop between multiple Tiptap editor instances (#6690).

  • Add support for dragging content between multiple editor instances
  • Use serializeForClipboard for better content preservation
  • Add smart deletion control based on source editor's editable state
  • Update React, Vue 2, and Vue 3 drag handle components
  • Add comprehensive demos and tests for cross-editor functionality

Implementation Approach

Moved cross-editor logic from PasteRule.ts to dragHandler.ts:

  • Problem: PasteRule.ts caused deletion to execute multiple times (once per rule plugin)
  • Solution: Moved logic to dragHandler.ts for single execution per drag operation
  • Encapsulation: Keeps drag-and-drop logic contained within the drag handle extension
  • Added proper editor instance detection using instanceId comparison
  • Added getEditor() helper to find target editors from DOM elements
  • Improved content serialization with view.serializeForClipboard()

Testing Done

  • Added Cypress tests for React and Vue demos
  • Verified cross-editor drag and drop functionality
  • Confirmed content formatting preservation
  • ✅ Lint, build, and test execution verified

Verification Steps

  1. Open demos/src/Experiments/GlobalDragHandle/React/
  2. Drag content between the two editors
  3. Verify content moves (not copies) between editors
  4. Test with complex content (lists, blockquotes, etc.)

Additional Notes

2025-08-18_16.30.06_compressed.mp4

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

changeset-bot bot commented Aug 18, 2025

🦋 Changeset detected

Latest commit: 5730a4a

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

This PR includes changesets to release 70 packages
Name Type
@tiptap/extension-drag-handle Major
tiptap-demos Patch
@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-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-typography Major
@tiptap/extension-underline Major
@tiptap/extension-unique-id Major
@tiptap/extension-youtube Major
@tiptap/extensions Major
@tiptap/html 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

netlify bot commented Aug 18, 2025

Deploy Preview for tiptap-embed ready!

Name Link
🔨 Latest commit 5730a4a
🔍 Latest deploy log https://app.netlify.com/projects/tiptap-embed/deploys/68b932c26e5b790008d95d31
😎 Deploy Preview https://deploy-preview-6835--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.


if (dragFromOtherEditor?.isEditable) {
// setTimeout to avoid the wrong content after drop, timeout arg can't be empty or 0
setTimeout(() => {
Copy link
Author

Choose a reason for hiding this comment

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

this deletion may occur for multiple times (once per rule plugin)
that is the cause of #6690 issue i think

// in @tiptap/extension-drag-handle-react, @tiptap/extension-drag-handle-vue-2,3, the element is hidden by default
// Add a default style to avoid the element being visible when only @tiptap/extension-drag-handle is added
element.style.visibility = 'hidden'
element.style.position = 'absolute'
Copy link
Author

Choose a reason for hiding this comment

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

If user use @tiptap/extension-drag-handle without "-react" or "-vue",
handle marker is always shown, so i add default style

@aten0724
Copy link

Hello @msokh , I'm thrilled that someone is finally fixing this issue. I'm working on another third-party plugin, "tiptap-extension-global-drag-handle," and after solving many other problems, I'm ultimately stuck on dragging tables. It's been a real headache. I was wondering, is your solution applicable to tables?

@msokh
Copy link
Author

msokh commented Aug 18, 2025

Hi @aten0724 ,
Thank you for your interest in this fix!
I'm glad to hear that you're working on a third-party drag handle plugin.

I recorded a video and confirmed that the solution works properly with tables as well.
The cross-editor drag and drop functionality should handle table content without issues,
as it uses view.serializeForClipboard() which preserves the table structure during the transfer.

Could you please confirm if this behavior matches what you were expecting for table dragging?

table_behavior_compressed.mov

@aten0724
Copy link

Hi @aten0724 , Thank you for your interest in this fix! I'm glad to hear that you're working on a third-party drag handle plugin.

I recorded a video and confirmed that the solution works properly with tables as well. The cross-editor drag and drop functionality should handle table content without issues, as it uses view.serializeForClipboard() which preserves the table structure during the transfer.

Could you please confirm if this behavior matches what you were expecting for table dragging?

table_behavior_compressed.mov

Yes, that's excellent! This is exactly the effect I was looking for. I'm really looking forward to your pull request going live.

@msokh msokh force-pushed the fix/cross-editor-dnd branch 2 times, most recently from 2242000 to 37261f6 Compare August 23, 2025 13:15
@bdbch bdbch requested a review from Copilot August 23, 2025 14:49
Copy link
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 enables cross-editor drag and drop functionality for Tiptap editors, allowing content to be seamlessly moved between multiple editor instances. The implementation moves cross-editor logic from PasteRule.ts to dragHandler.ts to prevent duplicate deletion execution and improve encapsulation.

Key changes:

  • Moved cross-editor drag logic from PasteRule.ts to dragHandler.ts for better organization and single execution per operation
  • Enhanced content serialization using view.serializeForClipboard() for better preservation during transfers
  • Added getEditor() helper function to find target editors from DOM elements using editor instance detection

Reviewed Changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/extension-drag-handle/src/helpers/getEditor.ts New helper function to find editor instances from DOM elements
packages/extension-drag-handle/src/helpers/dragHandler.ts Enhanced drag handling with cross-editor support and improved content serialization
packages/extension-drag-handle/src/drag-handle.ts Added default styling to prevent visibility issues
packages/core/src/PasteRule.ts Removed cross-editor logic that was moved to dragHandler.ts
demos/src/Experiments/GlobalDragHandle/Vue/index.vue Complete rewrite of Vue demo with improved styling and dual editor setup
demos/src/Experiments/GlobalDragHandle/Vue/index.spec.js New Cypress tests for Vue demo functionality
demos/src/Experiments/GlobalDragHandle/Vue/DragHandle.js Removed custom drag handle implementation
demos/src/Experiments/GlobalDragHandle/React/styles.scss New comprehensive styling for React demo
demos/src/Experiments/GlobalDragHandle/React/index.tsx New React demo implementation with dual editors
demos/src/Experiments/GlobalDragHandle/React/index.spec.js New Cypress tests for React demo
.changeset/friendly-crabs-stare.md Changeset documenting the new cross-editor functionality

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 64 to 65
if (!selection.empty) {
dragEditor.commands.deleteRange(selection)
Copy link

Copilot AI Aug 23, 2025

Choose a reason for hiding this comment

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

The condition checks !selection.empty but then calls deleteRange(selection) which expects a range object with from and to properties. This should be dragEditor.commands.deleteRange({ from: selection.from, to: selection.to }) to match the expected API signature.

Suggested change
if (!selection.empty) {
dragEditor.commands.deleteRange(selection)
dragEditor.commands.deleteRange({ from: selection.from, to: selection.to })

Copilot uses AI. Check for mistakes.
@msokh msokh force-pushed the fix/cross-editor-dnd branch from 37261f6 to d9deeb0 Compare August 26, 2025 12:44
- Add support for dragging content between multiple editor instances
- Improve content preservation using serializeForClipboard
- Add smart deletion control based on source editor's editable state
- Update React, Vue 2, and Vue 3 drag handle components
- Add comprehensive demos and tests for cross-editor functionality

This enhancement allows users to seamlessly move content between different Tiptap editor instances, making it easier to build applications with multiple interconnected editors.
@msokh msokh force-pushed the fix/cross-editor-dnd branch from 61325af to 5730a4a Compare September 4, 2025 06:33
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