From 6269fcaa7168d6a7b79a62b286fd0d0316819c8f Mon Sep 17 00:00:00 2001 From: Justin Weiss Date: Sun, 8 Sep 2019 10:06:20 -0700 Subject: [PATCH] Drop dragged nodes into the correct place (#3001) When drag-and-dropping nodes within the same editor, the removal of the dragged nodes can cause paths and offsets to change. This is really hard to compensate for. Instead of compensating for it, rearranging the order things happen mean we are always working with a document in a reasonable state. Now, we: 1. Fire a MouseUp event on `event.target` (because we haven't done anything, it's guaranteed to exist) 2. Save the range we're dragging (for later) 3. Select the target range (because we haven't deleted anything, this is still valid) 4. Delete the range we're dragging (this will automatically adjust the editor's selection) 5. Insert the dragged fragment at the current range No matter where we're dragging from or to, these should all be pointing at places that both exist, and haven't changed. --- packages/slate-react/src/plugins/dom/after.js | 50 +++++++------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/packages/slate-react/src/plugins/dom/after.js b/packages/slate-react/src/plugins/dom/after.js index 479fd879a..3a9e6f467 100644 --- a/packages/slate-react/src/plugins/dom/after.js +++ b/packages/slate-react/src/plugins/dom/after.js @@ -299,7 +299,7 @@ function AfterPlugin(options = {}) { const { value } = editor const { document, selection } = value const window = getWindow(event.target) - let target = editor.findEventRange(event) + const target = editor.findEventRange(event) if (!target) { return next() @@ -312,26 +312,30 @@ function AfterPlugin(options = {}) { editor.focus() - // If the drag is internal and the target is after the selection, it - // needs to account for the selection's content being deleted. - if ( - isDraggingInternally && - selection.end.offset < target.end.offset && - selection.end.path.equals(target.end.path) - ) { - target = target.moveForward( - selection.start.path.equals(selection.end.path) - ? 0 - selection.end.offset + selection.start.offset - : 0 - selection.end.offset + // COMPAT: React's onSelect event breaks after an onDrop event + // has fired in a node: https://github.com/facebook/react/issues/11379. + // Until this is fixed in React, we dispatch a mouseup event on that + // DOM node, since that will make it go back to normal. + const el = editor.findDOMNode(target.focus.path) + + if (el) { + el.dispatchEvent( + new MouseEvent('mouseup', { + view: window, + bubbles: true, + cancelable: true, + }) ) } - if (isDraggingInternally) { - editor.delete() - } + const draggedRange = selection editor.select(target) + if (isDraggingInternally) { + editor.deleteAtRange(draggedRange) + } + if (type === 'text' || type === 'html') { const { anchor } = target let hasVoidParent = document.hasVoidParent(anchor.path, editor) @@ -366,22 +370,6 @@ function AfterPlugin(options = {}) { editor.insertFragment(fragment) } - // COMPAT: React's onSelect event breaks after an onDrop event - // has fired in a node: https://github.com/facebook/react/issues/11379. - // Until this is fixed in React, we dispatch a mouseup event on that - // DOM node, since that will make it go back to normal. - const el = editor.findDOMNode(target.focus.path) - - if (el) { - el.dispatchEvent( - new MouseEvent('mouseup', { - view: window, - bubbles: true, - cancelable: true, - }) - ) - } - next() }