mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-11 17:53:59 +02:00
Fix duplicated content and other bugs related to drag and drop handling (#4238)
* Fix drag and drop logic * Add changeset
This commit is contained in:
5
.changeset/drag-and-drop.md
Normal file
5
.changeset/drag-and-drop.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'slate-react': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix duplicated content and other bugs related to drag and drop handling
|
@@ -130,6 +130,7 @@ export const Editable = (props: EditableProps) => {
|
|||||||
const state = useMemo(
|
const state = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
isComposing: false,
|
isComposing: false,
|
||||||
|
isDraggingInternally: false,
|
||||||
isUpdatingSelection: false,
|
isUpdatingSelection: false,
|
||||||
latestElement: null as DOMElement | null,
|
latestElement: null as DOMElement | null,
|
||||||
}),
|
}),
|
||||||
@@ -423,7 +424,12 @@ export const Editable = (props: EditableProps) => {
|
|||||||
// while a selection is being dragged.
|
// while a selection is being dragged.
|
||||||
const onDOMSelectionChange = useCallback(
|
const onDOMSelectionChange = useCallback(
|
||||||
throttle(() => {
|
throttle(() => {
|
||||||
if (!readOnly && !state.isComposing && !state.isUpdatingSelection) {
|
if (
|
||||||
|
!readOnly &&
|
||||||
|
!state.isComposing &&
|
||||||
|
!state.isUpdatingSelection &&
|
||||||
|
!state.isDraggingInternally
|
||||||
|
) {
|
||||||
const root = ReactEditor.findDocumentOrShadowRoot(editor)
|
const root = ReactEditor.findDocumentOrShadowRoot(editor)
|
||||||
const { activeElement } = root
|
const { activeElement } = root
|
||||||
const el = ReactEditor.toDOMNode(editor, editor)
|
const el = ReactEditor.toDOMNode(editor, editor)
|
||||||
@@ -748,7 +754,9 @@ export const Editable = (props: EditableProps) => {
|
|||||||
) {
|
) {
|
||||||
const node = ReactEditor.toSlateNode(editor, event.target)
|
const node = ReactEditor.toSlateNode(editor, event.target)
|
||||||
const path = ReactEditor.findPath(editor, node)
|
const path = ReactEditor.findPath(editor, node)
|
||||||
const voidMatch = Editor.void(editor, { at: path })
|
const voidMatch =
|
||||||
|
Editor.isVoid(editor, node) ||
|
||||||
|
Editor.void(editor, { at: path, voids: true })
|
||||||
|
|
||||||
// If starting a drag on a void node, make sure it is selected
|
// If starting a drag on a void node, make sure it is selected
|
||||||
// so that it shows up in the selection's fragment.
|
// so that it shows up in the selection's fragment.
|
||||||
@@ -757,6 +765,8 @@ export const Editable = (props: EditableProps) => {
|
|||||||
Transforms.select(editor, range)
|
Transforms.select(editor, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.isDraggingInternally = true
|
||||||
|
|
||||||
ReactEditor.setFragmentData(editor, event.dataTransfer)
|
ReactEditor.setFragmentData(editor, event.dataTransfer)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -765,28 +775,58 @@ export const Editable = (props: EditableProps) => {
|
|||||||
onDrop={useCallback(
|
onDrop={useCallback(
|
||||||
(event: React.DragEvent<HTMLDivElement>) => {
|
(event: React.DragEvent<HTMLDivElement>) => {
|
||||||
if (
|
if (
|
||||||
hasTarget(editor, event.target) &&
|
|
||||||
!readOnly &&
|
!readOnly &&
|
||||||
|
hasTarget(editor, event.target) &&
|
||||||
!isEventHandled(event, attributes.onDrop)
|
!isEventHandled(event, attributes.onDrop)
|
||||||
) {
|
|
||||||
// COMPAT: Certain browsers don't fire `beforeinput` events at all, and
|
|
||||||
// Chromium browsers don't properly fire them for files being
|
|
||||||
// dropped into a `contenteditable`. (2019/11/26)
|
|
||||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1028668
|
|
||||||
if (
|
|
||||||
!HAS_BEFORE_INPUT_SUPPORT ||
|
|
||||||
(!IS_SAFARI && event.dataTransfer.files.length > 0)
|
|
||||||
) {
|
) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
|
// Keep a reference to the dragged range before updating selection
|
||||||
|
const draggedRange = editor.selection
|
||||||
|
|
||||||
|
// Find the range where the drop happened
|
||||||
const range = ReactEditor.findEventRange(editor, event)
|
const range = ReactEditor.findEventRange(editor, event)
|
||||||
const data = event.dataTransfer
|
const data = event.dataTransfer
|
||||||
|
|
||||||
Transforms.select(editor, range)
|
Transforms.select(editor, range)
|
||||||
|
|
||||||
|
if (state.isDraggingInternally) {
|
||||||
|
if (draggedRange) {
|
||||||
|
Transforms.delete(editor, {
|
||||||
|
at: draggedRange,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
state.isDraggingInternally = false
|
||||||
|
}
|
||||||
|
|
||||||
ReactEditor.insertData(editor, data)
|
ReactEditor.insertData(editor, data)
|
||||||
|
|
||||||
|
// When dragging from another source into the editor, it's possible
|
||||||
|
// that the current editor does not have focus.
|
||||||
|
if (!ReactEditor.isFocused(editor)) {
|
||||||
|
ReactEditor.focus(editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[readOnly, attributes.onDrop]
|
[readOnly, attributes.onDrop]
|
||||||
)}
|
)}
|
||||||
|
onDragEnd={useCallback(
|
||||||
|
(event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
// When dropping on a different droppable element than the current editor,
|
||||||
|
// `onDrop` is not called. So we need to clean up in `onDragEnd` instead.
|
||||||
|
// Note: `onDragEnd` is only called when `onDrop` is not called
|
||||||
|
if (
|
||||||
|
!readOnly &&
|
||||||
|
state.isDraggingInternally &&
|
||||||
|
hasTarget(editor, event.target) &&
|
||||||
|
!isEventHandled(event, attributes.onDragEnd)
|
||||||
|
) {
|
||||||
|
state.isDraggingInternally = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[readOnly, attributes.onDragEnd]
|
||||||
|
)}
|
||||||
onFocus={useCallback(
|
onFocus={useCallback(
|
||||||
(event: React.FocusEvent<HTMLDivElement>) => {
|
(event: React.FocusEvent<HTMLDivElement>) => {
|
||||||
if (
|
if (
|
||||||
|
Reference in New Issue
Block a user