mirror of https://github.com/fantasticit/think.git
tiptap: drag extension
This commit is contained in:
parent
dcb0a0cf22
commit
6788efa7ab
|
@ -0,0 +1,130 @@
|
|||
import { Extension } from '@tiptap/core';
|
||||
import { Plugin } from 'prosemirror-state';
|
||||
import { NodeSelection } from 'prosemirror-state';
|
||||
import { __serializeForClipboard } from 'prosemirror-view';
|
||||
|
||||
function createRect(rect) {
|
||||
if (rect == null) {
|
||||
return null;
|
||||
}
|
||||
const newRect = {
|
||||
left: rect.left + document.body.scrollLeft,
|
||||
top: rect.top + document.body.scrollTop,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
};
|
||||
newRect.bottom = newRect.top + newRect.height;
|
||||
newRect.right = newRect.left + newRect.width;
|
||||
return newRect;
|
||||
}
|
||||
|
||||
function absoluteRect(element) {
|
||||
return createRect(element.getBoundingClientRect());
|
||||
}
|
||||
|
||||
export const Dragable = Extension.create({
|
||||
name: 'dragable',
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
let dropElement;
|
||||
let currentNode;
|
||||
let editorView;
|
||||
const WIDTH = 24;
|
||||
|
||||
function drag(e) {
|
||||
if (!currentNode || currentNode.nodeType !== 1) return;
|
||||
let pos = null;
|
||||
const desc = editorView.docView.nearestDesc(currentNode, true);
|
||||
|
||||
if (!(!desc || desc === editorView.docView)) {
|
||||
pos = desc.posBefore;
|
||||
}
|
||||
|
||||
if (!pos) return;
|
||||
|
||||
editorView.dispatch(editorView.state.tr.setSelection(NodeSelection.create(editorView.state.doc, pos)));
|
||||
const slice = editorView.state.selection.content();
|
||||
const { dom, text } = __serializeForClipboard(editorView, slice);
|
||||
e.dataTransfer.clearData();
|
||||
e.dataTransfer.setData('text/html', dom.innerHTML);
|
||||
e.dataTransfer.setData('text/plain', text);
|
||||
editorView.dragging = { slice, move: true };
|
||||
}
|
||||
|
||||
return [
|
||||
new Plugin({
|
||||
view(view) {
|
||||
editorView = view;
|
||||
dropElement = document.createElement('div');
|
||||
dropElement.setAttribute('draggable', 'true');
|
||||
dropElement.className = 'drag-handler';
|
||||
dropElement.addEventListener('dragstart', drag);
|
||||
view.dom.parentElement.appendChild(dropElement);
|
||||
|
||||
return {
|
||||
update(view) {
|
||||
editorView = view;
|
||||
},
|
||||
destroy() {
|
||||
if (dropElement && dropElement.parentNode) {
|
||||
dropElement.removeEventListener('dragstart', drag);
|
||||
dropElement.parentNode.removeChild(dropElement);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
handleDOMEvents: {
|
||||
drop() {
|
||||
dropElement.style.opacity = 0;
|
||||
setTimeout(() => {
|
||||
const node = document.querySelector('.ProseMirror-hideselection');
|
||||
if (node) {
|
||||
node.classList.remove('ProseMirror-hideselection');
|
||||
}
|
||||
}, 50);
|
||||
},
|
||||
mousemove(view, event) {
|
||||
const coords = { left: event.clientX, top: event.clientY };
|
||||
const pos = view.posAtCoords(coords);
|
||||
|
||||
if (!pos) return;
|
||||
|
||||
let node = view.domAtPos(pos.pos);
|
||||
|
||||
node = node.node;
|
||||
|
||||
while (node && node.parentNode) {
|
||||
if (node.parentNode?.classList?.contains?.('ProseMirror')) {
|
||||
break;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
if (!node || !node.getBoundingClientRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node?.classList?.contains('node-title') || node?.classList?.contains('node-table')) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentNode = node;
|
||||
|
||||
const rect = absoluteRect(node);
|
||||
const win = node.ownerDocument.defaultView;
|
||||
rect.top += win.pageYOffset;
|
||||
rect.left += win.pageXOffset;
|
||||
rect.width = WIDTH + 'px';
|
||||
dropElement.style.left = rect.left - WIDTH + 'px';
|
||||
dropElement.style.top = rect.top + 6 + 'px';
|
||||
dropElement.style.opacity = 1;
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
});
|
|
@ -1,12 +1,3 @@
|
|||
import TitapParagraph from '@tiptap/extension-paragraph';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
|
||||
import { ParagraphWrapper } from '../wrappers/paragraph';
|
||||
|
||||
export const Paragraph = TitapParagraph.extend({
|
||||
draggable: true,
|
||||
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(ParagraphWrapper);
|
||||
},
|
||||
});
|
||||
export const Paragraph = TitapParagraph.extend({});
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
}
|
||||
|
||||
p {
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 1em;
|
||||
font-weight: normal;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
.drag-handler {
|
||||
position: fixed;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: grab;
|
||||
opacity: 1;
|
||||
background-image: url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 16"><path fill-opacity="0.2" d="M4 14c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zM2 6C.9 6 0 6.9 0 8s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6C.9 0 0 .9 0 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" /></svg>');
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
}
|
|
@ -17,3 +17,4 @@
|
|||
@import './table.scss';
|
||||
@import './title.scss';
|
||||
@import './kityminder.scss';
|
||||
@import './drag.scss';
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
.dragHandle {
|
||||
position: absolute;
|
||||
top: 0.3rem;
|
||||
left: -1.5rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
left: -24px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: grab;
|
||||
opacity: 0;
|
||||
background-image: url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 16"><path fill-opacity="0.2" d="M4 14c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zM2 6C.9 6 0 6.9 0 8s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6C.9 0 0 .9 0 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" /></svg>');
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.paragraph {
|
||||
margin-top: 0.75em;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import { NodeViewContent } from '@tiptap/react';
|
||||
import { useCallback } from 'react';
|
||||
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ParagraphWrapper = ({ editor }) => {
|
||||
const prevent = useCallback((e) => {
|
||||
e.prevntDefault();
|
||||
return false;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DragableWrapper editor={editor} className={styles.paragraph}>
|
||||
<NodeViewContent draggable="false" onDragStart={prevent} />
|
||||
</DragableWrapper>
|
||||
);
|
||||
};
|
|
@ -16,6 +16,7 @@ import { Countdown } from 'tiptap/core/extensions/countdown';
|
|||
import { Document } from 'tiptap/core/extensions/document';
|
||||
import { DocumentChildren } from 'tiptap/core/extensions/document-children';
|
||||
import { DocumentReference } from 'tiptap/core/extensions/document-reference';
|
||||
import { Dragable } from 'tiptap/core/extensions/dragable';
|
||||
import { Dropcursor } from 'tiptap/core/extensions/dropcursor';
|
||||
import { Emoji } from 'tiptap/core/extensions/emoji';
|
||||
import { EventEmitter } from 'tiptap/core/extensions/event-emitter';
|
||||
|
@ -98,6 +99,7 @@ export const CollaborationKit = [
|
|||
CodeBlock,
|
||||
Color,
|
||||
ColorHighlighter,
|
||||
Dragable,
|
||||
Dropcursor,
|
||||
EventEmitter,
|
||||
Focus,
|
||||
|
|
Loading…
Reference in New Issue