diff --git a/src/component/base/DraftEditor.react.js b/src/component/base/DraftEditor.react.js index 15801d8494..5217523327 100644 --- a/src/component/base/DraftEditor.react.js +++ b/src/component/base/DraftEditor.react.js @@ -57,8 +57,7 @@ const handlerMap = { }; type State = { - containerKey: number, - contentsKey: number, + contentsKey: number }; /** @@ -118,7 +117,7 @@ class DraftEditor extends React.Component { blur: () => void; setMode: (mode: DraftEditorModes) => void; exitCurrentMode: () => void; - restoreEditorDOM: (scrollPosition: DraftScrollPosition, keyToUse: string) => void; + restoreEditorDOM: (scrollPosition: DraftScrollPosition) => void; setRenderGuard: () => void; removeRenderGuard: () => void; setClipboard: (clipboard?: BlockMap) => void; @@ -177,7 +176,7 @@ class DraftEditor extends React.Component { this.onDragLeave = this._onDragLeave.bind(this); // See `_restoreEditorDOM()`. - this.state = {containerKey: 0, contentsKey: 0}; + this.state = {contentsKey: 0}; } /** @@ -235,7 +234,6 @@ class DraftEditor extends React.Component {
{this._renderPlaceholder()}
{ + _restoreEditorDOM(scrollPosition?: DraftScrollPosition): void { + this.setState({contentsKey: this.state.contentsKey + 1}, () => { this._focus(scrollPosition); }); } diff --git a/src/component/handlers/composition/DraftEditorCompositionHandler.js b/src/component/handlers/composition/DraftEditorCompositionHandler.js index 8bc566d1b3..4505d0d52b 100644 --- a/src/component/handlers/composition/DraftEditorCompositionHandler.js +++ b/src/component/handlers/composition/DraftEditorCompositionHandler.js @@ -44,7 +44,6 @@ const RESOLVE_DELAY = 20; * and it simplifies state management with respect to the DraftEditor component. */ let resolved = false; -let initial = true; let stillComposing = false; let textInputData = ''; let formerTextInputData = ''; @@ -193,9 +192,16 @@ var DraftEditorCompositionHandler = { } // In Android 7+ there is (unlike Android 6-) no textInput event just before ending composition. - // Rely on the data in the composition end event instead. - if (isAndroid7 && e.data && !textInputData) { - textInputData = e.data; + // Rely on native handling instead. + if (isAndroid7) { + if (isSelectionAtLeafStart(this.props.editorState)) { + this.restoreEditorDOM(undefined); + } + this.exitCurrentMode(); + this.removeRenderGuard(); + this._onInput(e); + textInputData = ''; // Reset the composition handler. + return; } charInCompStart = ''; @@ -268,7 +274,6 @@ var DraftEditorCompositionHandler = { } resolved = true; - initial = true; const wasKoreanOnIE = isKoreanOnIE; isKoreanOnIE = false; @@ -301,7 +306,7 @@ var DraftEditorCompositionHandler = { ); if (mustReset) { - this.restoreEditorDOM(undefined, ((composedChars && composedChars.length > 0) ? "contentsKey" : "containerKey")); + this.restoreEditorDOM(undefined); } this.exitCurrentMode(); diff --git a/src/component/handlers/edit/editOnInput.js b/src/component/handlers/edit/editOnInput.js index c1235144b9..ec99de429b 100644 --- a/src/component/handlers/edit/editOnInput.js +++ b/src/component/handlers/edit/editOnInput.js @@ -18,6 +18,8 @@ var EditorState = require('EditorState'); var Entity = require('DraftEntity'); var UserAgent = require('UserAgent'); +var keyCommandPlainBackspace = require('keyCommandPlainBackspace'); + var findAncestorOffsetKey = require('findAncestorOffsetKey'); var nullthrows = require('nullthrows'); @@ -37,9 +39,14 @@ var DOUBLE_NEWLINE = '\n\n'; * when an `input` change leads to a DOM/model mismatch, the change should be * due to a spellcheck change, and we can incorporate it into our model. */ -function editOnInput(): void { +function editOnInput(e): void { + var {editorState} = this.props; var domSelection = global.getSelection(); + if (editorState.isInCompositionMode()) { + editorState = EditorState.set(editorState, {inCompositionMode: false}); + } + var {anchorNode, isCollapsed} = domSelection; if (anchorNode.nodeType !== Node.TEXT_NODE && anchorNode.nodeType !== Node.ELEMENT_NODE) { return; @@ -67,7 +74,7 @@ function editOnInput(): void { } var domText = anchorNode.textContent; - var {editorState} = this.props; + var offsetKey = nullthrows(findAncestorOffsetKey(anchorNode)); var {blockKey, decoratorKey, leafKey} = DraftOffsetKey.decode(offsetKey); @@ -77,6 +84,32 @@ function editOnInput(): void { var content = editorState.getCurrentContent(); var block = content.getBlockForKey(blockKey); + var selection = editorState.getSelection(); + + // Special case for backwards deletion on Android. + // Pressing the backspace key on the soft keyboard does not always trigger a 'backspace' keydown event, + // so we try to detect it by comparing the editor selection (as it was before the input event) + // with the actual cursor position. + // If the cursor is in a smaller block than before, or in the same block but in a smaller leaf than before, + // we perform a backspace command to sync the editorstate with the DOM + if (isAndroid && e && e.nativeEvent.type === 'input') { + var blocksArray = content.getBlocksAsArray(); + var isDeletion = blocksArray.indexOf(block) < blocksArray.indexOf(content.getBlockForKey(selection.getEndKey())) + || end < selection.getEndOffset(); + if (isDeletion) { + this.restoreEditorDOM(undefined); + editorState = EditorState.set(editorState, { + selection: selection.merge({ + anchorKey: blockKey, anchorOffset: start + domSelection.anchorOffset, + focusKey: selection.getEndKey(), focusOffset: selection.getEndOffset(), + isBackward: false + }) + }); + this.update(keyCommandPlainBackspace(editorState)); + return; + } + } + var modelText = block.getText().slice(start, end); // Special-case soft newlines here. If the DOM text ends in a soft newline, @@ -92,7 +125,6 @@ function editOnInput(): void { return; } - var selection = editorState.getSelection(); // We'll replace the entire leaf with the text content of the target. var targetRange = selection.merge({