I have customized drag and drop paragraphs through NodeView, but there is a bug. If there are two paragraphs, the mouse selects from the end of the last paragraph of text to any position in the previous paragraph of text, presses Ctrl+X, or deletes it, it will fail. But selecting from the previous paragraph to the next paragraph will not be a problem
I haven’t managed to reproduce this. Could you try to show the minimal code that sets up an editor that has this problem? And does it happen on all browsers or just some?
Okay, I need some time to organize the minimum code
I found that this issue only occurs when using the addNodeView of tiptab, and it does not occur when using the prosemirror native interface. But if I use prosemirror for implementation, I also need to implement additional functions for cursor movement and selection. For example, selecting multiple paragraphs, moving the cursor between paragraphs through the keyboard, and selecting multiple paragraphs through shift+arrow keys. I suspect that Tibtab implements the cursor function mentioned above, so I tried to read the code of Tibtab to find out what the problem was, but due to my limited skills, I couldn’t find its handling of the cursor and selection. Here is my drag and drop paragraph based on tiptab:
index.vue
<template>
<div class="height-full" style="padding: 20px 200px;">
<editor-content class="height-full" :editor="editor"></editor-content>
</div>
</template>
<script setup lang="ts">
import {useEditor, EditorContent} from '@tiptap/vue-3';
import {Document} from '@tiptap/extension-document';
import {Text} from '@tiptap/extension-text';
import {Dropcursor} from '@tiptap/extension-dropcursor';
import {DraggableParagraph} from './draggableParagraph';
const editor = window.CONFIG.editor = useEditor({
extensions: [
Document,
DraggableParagraph,
Text,
Dropcursor,
],
content: '<p>This is a first paragraph.</p>' +
'<p>New paragraph.</p>'
});
</script>
draggableParagraph.ts
import {VueNodeViewRenderer} from '@tiptap/vue-3';
import {Paragraph} from '@tiptap/extension-paragraph';
import Component from './index.vue';
export const DraggableParagraph = Paragraph.extend({
draggable: true,
addNodeView() {
// I went to see the source code from here, but I didn't get any results
return VueNodeViewRenderer(Component);
},
});
Component.vue
<template>
<node-view-wrapper style="display: flex;">
<div contenteditable="false" data-drag-handle style="width: 20px;height: 20px;border: 1px solid #000;">
</div>
<node-view-content class="drag-content"></node-view-content>
</node-view-wrapper>
</template>
<script setup lang="ts">
import {NodeViewContent, nodeViewProps, NodeViewWrapper} from '@tiptap/vue-3';
const props = defineProps(nodeViewProps);
</script>
I found out where the problem lies. Tiptab has a NodeView.ts file that implements the stopEvent method.
stopEvent(event: Event) {
if (!this.dom) {
return false
}
if (typeof this.options.stopEvent === 'function') {
return this.options.stopEvent({ event })
}
const target = event.target as HTMLElement
const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target)
// any event from child nodes should be handled by ProseMirror
if (!isInElement) {
return false
}
const isDragEvent = event.type.startsWith('drag')
const isDropEvent = event.type === 'drop'
const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable
// any input event within node views should be ignored by ProseMirror
if (isInput && !isDropEvent && !isDragEvent) {
return true
}
const { isEditable } = this.editor
const { isDragging } = this
const isDraggable = !!this.node.type.spec.draggable
const isSelectable = NodeSelection.isSelectable(this.node)
const isCopyEvent = event.type === 'copy'
const isPasteEvent = event.type === 'paste'
const isCutEvent = event.type === 'cut'
const isClickEvent = event.type === 'mousedown'
// ProseMirror tries to drag selectable nodes
// even if `draggable` is set to `false`
// this fix prevents that
if (!isDraggable && isSelectable && isDragEvent) {
event.preventDefault()
}
if (isDraggable && isDragEvent && !isDragging) {
event.preventDefault()
return false
}
//Omitted below...
}
The problem lies in
if (isDraggable && isDragEvent && !isDragging) {
event.preventDefault()
return false
}
The event is blocked here. This avoids triggering drag and drop behavior when the cursor is at the end of the text. But it seems to have also led to the problem of selecting multiple paragraphs and deleting them from the end.
May I ask if there is any way to solve this problem? Can I not use event. preventDefault. But rather to determine whether the current cursor position is at the end, and if it is at the end, make the paragraph non draggable
If Tiptap is doing something problematic, you’d want to report that on their bug tracker. This forum isn’t really Tiptap-related.
Oh, thank you. I understand.