From df5b4a5d6146034187b446a7cca4ce512f5477e4 Mon Sep 17 00:00:00 2001 From: Ryan Grove Date: Mon, 8 Jan 2018 13:34:59 -0800 Subject: [PATCH] Fix iOS autocorrect issues with Backspace and Return (#1475) * Fix iOS autocorrect issues with Backspace and Return Handling Backspace and Return keystrokes in `onNativeBeforeInput` ensures that iOS recognizes word and paragraph breaks and generates appropriate autocorrect suggestions. Fixes #1471 * Cleaner `onKeyDown` iOS check courtesy of @davidlibland --- .../slate-react/src/components/content.js | 82 +++++++++++++------ packages/slate-react/src/plugins/before.js | 2 +- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/packages/slate-react/src/components/content.js b/packages/slate-react/src/components/content.js index 261d4cc9b..2c1c0d068 100644 --- a/packages/slate-react/src/components/content.js +++ b/packages/slate-react/src/components/content.js @@ -353,7 +353,9 @@ class Content extends React.Component { /** * On a native `beforeinput` event, use the additional range information - * provided by the event to insert text exactly as the browser would. + * provided by the event to manipulate text exactly as the browser would. + * + * This is currently only used on iOS and Android. * * @param {InputEvent} event */ @@ -362,38 +364,66 @@ class Content extends React.Component { if (this.props.readOnly) return if (!this.isInEditor(event.target)) return - const { inputType } = event - if (inputType !== 'insertText' && inputType !== 'insertReplacementText') return - const [ targetRange ] = event.getTargetRanges() if (!targetRange) return - // `data` should have the text for the `insertText` input type and - // `dataTransfer` should have the text for the `insertReplacementText` input - // type, but Safari uses `insertText` for spell check replacements and sets - // `data` to `null`. - const text = event.data == null - ? event.dataTransfer.getData('text/plain') - : event.data - - if (text == null) return - - event.preventDefault() - const { editor } = this.props - const { value } = editor - const { selection } = value - const range = findRange(targetRange, value) - editor.change((change) => { - change.insertTextAtRange(range, text, selection.marks) + switch (event.inputType) { + case 'deleteContentBackward': { + event.preventDefault() - // If the text was successfully inserted, and the selection had marks on it, - // unset the selection's marks. - if (selection.marks && value.document != change.value.document) { - change.select({ marks: null }) + const range = findRange(targetRange, editor.value) + editor.change(change => change.deleteAtRange(range)) + break } - }) + + case 'insertLineBreak': // intentional fallthru + case 'insertParagraph': { + event.preventDefault() + const range = findRange(targetRange, editor.value) + + editor.change((change) => { + if (change.value.isInVoid) { + change.collapseToStartOfNextText() + } else { + change.splitBlockAtRange(range) + } + }) + break + } + + case 'insertReplacementText': // intentional fallthru + case 'insertText': { + // `data` should have the text for the `insertText` input type and + // `dataTransfer` should have the text for the `insertReplacementText` + // input type, but Safari uses `insertText` for spell check replacements + // and sets `data` to `null`. + const text = event.data == null + ? event.dataTransfer.getData('text/plain') + : event.data + + if (text == null) return + + event.preventDefault() + + const { value } = editor + const { selection } = value + const range = findRange(targetRange, value) + + editor.change((change) => { + change.insertTextAtRange(range, text, selection.marks) + + // If the text was successfully inserted, and the selection had marks + // on it, unset the selection's marks. + if (selection.marks && value.document != change.value.document) { + change.select({ marks: null }) + } + }) + + break + } + } } /** diff --git a/packages/slate-react/src/plugins/before.js b/packages/slate-react/src/plugins/before.js index aef4cd804..fb7c782f9 100644 --- a/packages/slate-react/src/plugins/before.js +++ b/packages/slate-react/src/plugins/before.js @@ -381,7 +381,7 @@ function BeforePlugin() { // Certain hotkeys have native behavior in contenteditable elements which // will cause our value to be out of sync, so prevent them. - if (HOTKEYS.CONTENTEDITABLE(event)) { + if (HOTKEYS.CONTENTEDITABLE(event) && !IS_IOS) { event.preventDefault() }