diff --git a/src/components/content.js b/src/components/content.js index 388f77c66..91526b246 100644 --- a/src/components/content.js +++ b/src/components/content.js @@ -623,6 +623,31 @@ class Content extends React.Component { isBackward: null } + // If the selection is at the end of a non-void inline node, and there is + // a node after it, put it in the node after instead. + const anchorText = document.getNode(anchor.key) + const focusText = document.getNode(focus.key) + const anchorInline = document.getClosestInline(anchor.key) + const focusInline = document.getClosestInline(focus.key) + + if (anchorInline && anchor.offset == anchorText.length) { + const block = document.getClosestBlock(anchor.key) + const next = block.getNextText(anchor.key) + if (next) { + properties.anchorKey = next.key + properties.anchorOffset = 0 + } + } + + if (focusInline && focus.offset == focusText.length) { + const block = document.getClosestBlock(focus.key) + const next = block.getNextText(focus.key) + if (next) { + properties.focusKey = next.key + properties.focusOffset = 0 + } + } + data.selection = selection .merge(properties) .normalize(document) diff --git a/src/plugins/core.js b/src/plugins/core.js index 0c34959c1..b2287d736 100644 --- a/src/plugins/core.js +++ b/src/plugins/core.js @@ -71,7 +71,11 @@ function Plugin(options = {}) { */ function onBeforeInput(e, data, state, editor) { - const { document, startKey, startOffset, startInline, startText } = state + const { document, startKey, startBlock, startOffset, startInline, startText } = state + const pText = startBlock.getPreviousText(startKey) + const pInline = pText && startBlock.getClosestInline(pText.key) + const nText = startBlock.getNextText(startKey) + const nInline = nText && startBlock.getClosestInline(nText.key) // Determine what the characters would be if natively inserted. const schema = editor.getSchema() @@ -103,15 +107,30 @@ function Plugin(options = {}) { // We do not have to re-render if the current selection is collapsed, the // current node is not empty, there are no marks on the cursor, the cursor - // is not at the edge of an inline node, and the natively inserted + // is not at the edge of an inline node, the cursor isn't at the starting + // edge of a text node after an inline node, and the natively inserted // characters would be the same as the non-native. const isNative = ( + // If the selection is expanded, we don't know what the edit will look + // like so we can't let it happen natively. (state.isCollapsed) && - (state.startText.text != '') && + // If the selection has marks, then we need to render it non-natively + // because we need to create the new marks as well. (state.selection.marks == null) && - // Must not be, for example, at edge of an inline link + // If the text node in question has no content, browsers might do weird + // things so we need to insert it normally instead. + (state.startText.text != '') && + // COMPAT: Browsers do weird things when typing at the edges of inline + // nodes, so we can't let them render natively. (?) (!startInline || !state.selection.isAtStartOf(startInline)) && (!startInline || !state.selection.isAtEndOf(startInline)) && + // COMPAT: In Chrome & Safari, it isn't possible to have a selection at + // the starting edge of a text node after another inline node. It will + // have been automatically changed. So we can't render natively because + // the cursor isn't technique in the right spot. (2016/12/01) + (!(pInline && !pInline.isVoid && startOffset == 0)) && + (!(nInline && !nInline.isVoid && startOffset == startText.length)) && + // If the (chars.equals(nextChars)) )