mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-04-21 13:51: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:
parent
f2d110100b
commit
c14e1fbc77
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(
|
||||
() => ({
|
||||
isComposing: false,
|
||||
isDraggingInternally: false,
|
||||
isUpdatingSelection: false,
|
||||
latestElement: null as DOMElement | null,
|
||||
}),
|
||||
@ -423,7 +424,12 @@ export const Editable = (props: EditableProps) => {
|
||||
// while a selection is being dragged.
|
||||
const onDOMSelectionChange = useCallback(
|
||||
throttle(() => {
|
||||
if (!readOnly && !state.isComposing && !state.isUpdatingSelection) {
|
||||
if (
|
||||
!readOnly &&
|
||||
!state.isComposing &&
|
||||
!state.isUpdatingSelection &&
|
||||
!state.isDraggingInternally
|
||||
) {
|
||||
const root = ReactEditor.findDocumentOrShadowRoot(editor)
|
||||
const { activeElement } = root
|
||||
const el = ReactEditor.toDOMNode(editor, editor)
|
||||
@ -748,7 +754,9 @@ export const Editable = (props: EditableProps) => {
|
||||
) {
|
||||
const node = ReactEditor.toSlateNode(editor, event.target)
|
||||
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
|
||||
// so that it shows up in the selection's fragment.
|
||||
@ -757,6 +765,8 @@ export const Editable = (props: EditableProps) => {
|
||||
Transforms.select(editor, range)
|
||||
}
|
||||
|
||||
state.isDraggingInternally = true
|
||||
|
||||
ReactEditor.setFragmentData(editor, event.dataTransfer)
|
||||
}
|
||||
},
|
||||
@ -765,28 +775,58 @@ export const Editable = (props: EditableProps) => {
|
||||
onDrop={useCallback(
|
||||
(event: React.DragEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
hasTarget(editor, event.target) &&
|
||||
!readOnly &&
|
||||
hasTarget(editor, event.target) &&
|
||||
!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()
|
||||
const range = ReactEditor.findEventRange(editor, event)
|
||||
const data = event.dataTransfer
|
||||
Transforms.select(editor, range)
|
||||
ReactEditor.insertData(editor, data)
|
||||
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 data = event.dataTransfer
|
||||
|
||||
Transforms.select(editor, range)
|
||||
|
||||
if (state.isDraggingInternally) {
|
||||
if (draggedRange) {
|
||||
Transforms.delete(editor, {
|
||||
at: draggedRange,
|
||||
})
|
||||
}
|
||||
|
||||
state.isDraggingInternally = false
|
||||
}
|
||||
|
||||
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]
|
||||
)}
|
||||
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(
|
||||
(event: React.FocusEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
|
Loading…
x
Reference in New Issue
Block a user