feat: allow support for drag-and-drop between multiple editors (#5893)

* feat: drag and drop across multi editors

* feat: drag and drop across multi editors optimize

* feat: drag and drop across multi editors optimize

* added changeset

---------

Co-authored-by: songhandong <songhandong@baidu.com>
Co-authored-by: songispm <38745323+songispm@users.noreply.github.com>
Co-authored-by: songispm <songispm@gmail.com>
This commit is contained in:
bdbch 2024-11-30 13:16:45 +01:00 committed by GitHub
parent 01547d5b2f
commit ca6269e928
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 2 deletions

View File

@ -0,0 +1,5 @@
---
"@tiptap/core": patch
---
Added support for drag-and-drop between multiple editors

View File

@ -44,7 +44,8 @@ export default Extension.create({
}
function dragStart(e, view) {
view.composing = true
// Must delete this line, Otherwise: Uncaught TypeError: Cannot set property composing of #<EditorView> which has only a getter
// view.composing = true
if (!e.dataTransfer) {
return
@ -75,6 +76,11 @@ export default Extension.create({
}
}
function dragEnd(e, view) {
// reset the dragging, otherwise wrong content after dragging across multi editors repeatedly
view.dragging = null
}
let dropElement
const WIDTH = 28
@ -86,8 +92,10 @@ export default Extension.create({
element.draggable = 'true'
element.classList.add('global-drag-handle')
element.addEventListener('dragstart', e => dragStart(e, editorView))
element.addEventListener('dragend', e => dragEnd(e, editorView))
dropElement = element
document.body.appendChild(dropElement)
// append to editor's parentNode (not document.body), to match the logic of dragging across multi editors in pasteRule.ts
editorView.dom.parentNode.appendChild(dropElement)
return {
// update(view, prevState) {

View File

@ -32,6 +32,7 @@
import Bold from '@tiptap/extension-bold'
import Collaboration from '@tiptap/extension-collaboration'
import Document from '@tiptap/extension-document'
import DropCursor from '@tiptap/extension-dropcursor'
import Heading from '@tiptap/extension-heading'
import Paragraph from '@tiptap/extension-paragraph'
import TaskItem from '@tiptap/extension-task-item'
@ -75,6 +76,8 @@ export default {
levels: [2],
}),
Text,
Bold,
DropCursor,
Collaboration.configure({
document: this.ydoc,
field: 'title',
@ -88,6 +91,8 @@ export default {
TaskListDocument,
Paragraph,
Text,
Bold,
DropCursor,
TaskList,
CustomTaskItem,
Collaboration.configure({
@ -110,6 +115,7 @@ export default {
Paragraph,
Text,
Bold,
DropCursor,
Collaboration.configure({
document: this.ydoc,
field: 'description',

View File

@ -162,6 +162,9 @@ function run(config: {
return success
}
// When dragging across editors, must get another editor instance to delete selection content.
let tiptapDragFromOtherEditor: Editor | null = null
const createClipboardPasteEvent = (text: string) => {
const event = new ClipboardEvent('paste', {
clipboardData: new DataTransfer(),
@ -242,13 +245,25 @@ export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }):
dragSourceElement = view.dom.parentElement?.contains(event.target as Element)
? view.dom.parentElement
: null
if (dragSourceElement) {
tiptapDragFromOtherEditor = editor
}
}
const handleDragend = () => {
if (tiptapDragFromOtherEditor) {
tiptapDragFromOtherEditor = null
}
}
window.addEventListener('dragstart', handleDragstart)
window.addEventListener('dragend', handleDragend)
return {
destroy() {
window.removeEventListener('dragstart', handleDragstart)
window.removeEventListener('dragend', handleDragend)
},
}
},
@ -259,6 +274,20 @@ export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }):
isDroppedFromProseMirror = dragSourceElement === view.dom.parentElement
dropEvent = event as DragEvent
if (!isDroppedFromProseMirror) {
const dragFromOtherEditor = tiptapDragFromOtherEditor
if (dragFromOtherEditor) {
// setTimeout to avoid the wrong content after drop, timeout arg can't be empty or 0
setTimeout(() => {
const selection = dragFromOtherEditor.state.selection
if (selection) {
dragFromOtherEditor.commands.deleteRange({ from: selection.from, to: selection.to })
}
}, 10)
}
}
return false
},