diff --git a/lib/components/content.js b/lib/components/content.js index bebdb0fd1..52f2524e0 100644 --- a/lib/components/content.js +++ b/lib/components/content.js @@ -1,5 +1,6 @@ import Key from '../utils/key' +import Selection from '../models/selection' import OffsetKey from '../utils/offset-key' import Raw from '../serializers/raw' import React from 'react' @@ -120,7 +121,6 @@ class Content extends React.Component { onBlur = (e) => { if (this.props.readOnly) return if (this.tmp.isCopying) return - if (this.tmp.isComposing) return let { state } = this.props state = state @@ -180,7 +180,6 @@ class Content extends React.Component { */ onCopy = (e) => { - if (this.tmp.isComposing) return this.onCutCopy(e) } @@ -192,7 +191,6 @@ class Content extends React.Component { onCut = (e) => { if (this.props.readOnly) return - if (this.tmp.isComposing) return this.onCutCopy(e) // Once the cut has successfully executed, delete the current selection. @@ -260,6 +258,121 @@ class Content extends React.Component { }) } + /** + * On drag end, unset the `isDragging` flag. + * + * @param {Event} e + */ + + onDragEnd = (e) => { + this.tmp.isDragging = false + } + + /** + * On drag over, set the `isDragging` flag and the `isInternalDrag` flag. + * + * @param {Event} e + */ + + onDragOver = (e) => { + if (this.tmp.isDragging) return + this.tmp.isDragging = true + this.tmp.isInternalDrag = false + } + + /** + * On drag start, set the `isDragging` flag and the `isInternalDrag` flag. + * + * @param {Event} e + */ + + onDragStart = (e) => { + this.tmp.isDragging = true + this.tmp.isInternalDrag = true + } + + /** + * On drop. + * + * @param {Event} e + */ + + onDrop = (e) => { + if (this.props.readOnly) return + e.preventDefault() + + const { state } = this.props + const { selection } = state + const data = e.nativeEvent.dataTransfer + const drop = {} + + // Resolve the point where the drop occured. + const { x, y } = e.nativeEvent + const range = window.document.caretRangeFromPoint(x, y) + const startNode = range.startContainer + const startOffset = range.startOffset + const point = OffsetKey.findPoint(startNode, startOffset, state) + let target = Selection.create({ + anchorKey: point.key, + anchorOffset: point.offset, + focusKey: point.key, + focusOffset: point.offset, + isFocused: true + }) + + // If the drag is internal, handle it now. And it the target is after the + // selection, it needs to account for the selection's content being deleted. + if (this.tmp.isInternalDrag) { + if ( + selection.endKey == target.endKey && + selection.endOffset < target.endOffset + ) { + const width = selection.startKey == selection.endKey + ? selection.endOffset - selection.startOffset + : selection.endOffset + + target = target.moveBackward(width) + } + + const fragment = state.fragment + const next = state + .transform() + .delete() + .moveTo(target) + .insertFragment(fragment) + .apply() + + this.onChange(next) + return + } + + // COMPAT: In Firefox, `types` is array-like. (2016/06/21) + const types = Array.from(data.types) + + // Handle files. + if (data.files.length) { + drop.type = 'files' + drop.files = data.files + } + + // Handle HTML. + else if (includes(types, 'text/html')) { + drop.type = 'html' + drop.text = data.getData('text/plain') + drop.html = data.getData('text/html') + } + + // Handle plain text. + else { + drop.type = 'text' + drop.text = data.getData('text/plain') + } + + drop.data = data + drop.target = target + this.props.onDrop(e, drop) + } + /** * On key down, prevent the default behavior of certain commands that will * leave the editor in an out-of-sync state, then bubble up. @@ -302,8 +415,8 @@ class Content extends React.Component { onPaste = (e) => { if (this.props.readOnly) return - if (this.tmp.isComposing) return e.preventDefault() + const data = e.clipboardData const paste = {} @@ -311,7 +424,7 @@ class Content extends React.Component { const types = Array.from(data.types) // Handle files. - if (data.files.length != 0) { + if (data.files.length) { paste.type = 'files' paste.files = data.files } @@ -434,20 +547,24 @@ class Content extends React.Component { return (