diff --git a/src/models/selection.js b/src/models/selection.js index 538824e8f..1462a125c 100644 --- a/src/models/selection.js +++ b/src/models/selection.js @@ -3,19 +3,6 @@ import getLeafText from '../utils/get-leaf-text' import warn from '../utils/warn' import { Record } from 'immutable' -/** - * Start-end-and-edge convenience methods to auto-generate. - * - * @type {Array} - */ - -const EDGE_METHODS = [ - 'has%AtStartOf', - 'has%AtEndOf', - 'has%Between', - 'has%In', -] - /** * Default properties. * @@ -422,38 +409,6 @@ class Selection extends new Record(DEFAULTS) { }) } - /** - * Move the end point to the start point. - * - * @return {Selection} - */ - - collapseToStart() { - return this.merge({ - anchorKey: this.startKey, - anchorOffset: this.startOffset, - focusKey: this.startKey, - focusOffset: this.startOffset, - isBackward: false - }) - } - - /** - * Move the end point to the start point. - * - * @return {Selection} - */ - - collapseToEnd() { - return this.merge({ - anchorKey: this.endKey, - anchorOffset: this.endOffset, - focusKey: this.endKey, - focusOffset: this.endOffset, - isBackward: false - }) - } - /** * Move to the start of a `node`. * @@ -590,45 +545,35 @@ class Selection extends new Record(DEFAULTS) { } /** - * Extend the start point forward `n` characters. + * Move the anchor offset `n` characters. * * @param {Number} n (optional) * @return {Selection} */ - moveStartOffset(n = 1) { - if (this.isBackward) { - return this.merge({ - focusOffset: this.focusOffset + n, - isBackward: null - }) - } else { - return this.merge({ - anchorOffset: this.anchorOffset + n, - isBackward: null - }) - } + moveAnchorOffset(n = 1) { + const { anchorKey, focusKey, focusOffset } = this + const anchorOffset = this.anchorOffset + n + return this.merge({ + anchorOffset, + isBackward: anchorKey == focusKey ? anchorOffset > focusOffset : this.isBackward + }) } /** - * Extend the end point forward `n` characters. + * Move the anchor offset `n` characters. * * @param {Number} n (optional) * @return {Selection} */ - moveEndOffset(n = 1) { - if (this.isBackward) { - return this.merge({ - anchorOffset: this.anchorOffset + n, - isBackward: null - }) - } else { - return this.merge({ - focusOffset: this.focusOffset + n, - isBackward: null - }) - } + moveFocusOffset(n = 1) { + const { focusKey, anchorKey, anchorOffset } = this + const focusOffset = this.focusOffset + n + return this.merge({ + focusOffset, + isBackward: focusKey == anchorKey ? anchorOffset > focusOffset : this.isBackward + }) } /** @@ -724,14 +669,37 @@ class Selection extends new Record(DEFAULTS) { }) } + /** + * Flip the selection. + * + * @return {Selection} + */ + + flip() { + return this.merge({ + anchorKey: this.focusKey, + anchorOffset: this.focusOffset, + focusKey: this.anchorKey, + focusOffset: this.anchorOffset, + isBackward: this.isBackward == null ? null : !this.isBackward, + }) + } + } /** * Add start, end and edge convenience methods. */ -EDGE_METHODS.forEach((pattern) => { - const [ p, s ] = pattern.split('%') +[ + ['has', 'AtStartOf', true], + ['has', 'AtEndOf', true], + ['has', 'Between', true], + ['has', 'In', true], + ['collapseTo', ''], + ['move', 'Offset'], +].forEach((opts) => { + const [ p, s, hasEdge ] = opts const anchor = `${p}Anchor${s}` const edge = `${p}Edge${s}` const end = `${p}End${s}` @@ -750,8 +718,10 @@ EDGE_METHODS.forEach((pattern) => { : this[focus](...args) } - Selection.prototype[edge] = function (...args) { - return this[anchor](...args) || this[focus](...args) + if (hasEdge) { + Selection.prototype[edge] = function (...args) { + return this[anchor](...args) || this[focus](...args) + } } }) diff --git a/src/transforms/apply-operation.js b/src/transforms/apply-operation.js index b1c47227d..790862165 100644 --- a/src/transforms/apply-operation.js +++ b/src/transforms/apply-operation.js @@ -104,7 +104,7 @@ function insertNode(state, operation) { function insertText(state, operation) { const { path, offset, text, marks } = operation let { document, selection } = state - const { startKey, endKey, startOffset, endOffset } = selection + const { anchorKey, focusKey, anchorOffset, focusOffset } = selection let node = document.assertPath(path) // Update the document @@ -112,11 +112,11 @@ function insertText(state, operation) { document = document.updateDescendant(node) // Update the selection - if (startKey == node.key && startOffset >= offset) { - selection = selection.moveStartOffset(text.length) + if (anchorKey == node.key && anchorOffset >= offset) { + selection = selection.moveAnchorOffset(text.length) } - if (endKey == node.key && endOffset >= offset) { - selection = selection.moveEndOffset(text.length) + if (focusKey == node.key && focusOffset >= offset) { + selection = selection.moveFocusOffset(text.length) } state = state.merge({ document, selection }) @@ -287,15 +287,15 @@ function removeText(state, operation) { const { path, offset, length } = operation const rangeOffset = offset + length let { document, selection } = state - const { startKey, endKey, startOffset, endOffset } = selection + const { anchorKey, focusKey, anchorOffset, focusOffset } = selection let node = document.assertPath(path) // Update the selection - if (startKey == node.key && startOffset >= rangeOffset) { - selection = selection.moveStartOffset(-length) + if (anchorKey == node.key && anchorOffset >= rangeOffset) { + selection = selection.moveAnchorOffset(-length) } - if (endKey == node.key && endOffset >= rangeOffset) { - selection = selection.moveEndOffset(-length) + if (focusKey == node.key && focusOffset >= rangeOffset) { + selection = selection.moveFocusOffset(-length) } node = node.removeText(offset, length) diff --git a/src/transforms/at-current-range.js b/src/transforms/at-current-range.js index de5623779..615f380bc 100644 --- a/src/transforms/at-current-range.js +++ b/src/transforms/at-current-range.js @@ -466,12 +466,18 @@ export function wrapText(transform, prefix, suffix = prefix) { const { selection } = state transform.wrapTextAtRange(selection, prefix, suffix) - // Adding the suffix will have pushed the end of the selection further on, so - // we need to move it back to account for this. - transform.moveEndOffset(0 - suffix.length) - // If the selection was collapsed, it will have moved the start offset too. if (selection.isCollapsed) { transform.moveStartOffset(0 - prefix.length) } + + // Adding the suffix will have pushed the end of the selection further on, so + // we need to move it back to account for this. + transform.moveEndOffset(0 - suffix.length) + + // There's a chance that the selection points moved "through" each other, + // resulting in a now-incorrect selection direction. + if (selection.isForward != transform.state.selection.isForward) { + transform.flipSelection() + } } diff --git a/src/transforms/index.js b/src/transforms/index.js index 280901a63..7967bc703 100644 --- a/src/transforms/index.js +++ b/src/transforms/index.js @@ -144,6 +144,7 @@ import { extendForward, extendToEndOf, extendToStartOf, + flipSelection, focus, moveBackward, moveForward, @@ -321,6 +322,7 @@ export default { extendToEndOf, extendToStartOf, focus, + flipSelection, moveBackward, moveForward, moveEndOffset, diff --git a/src/transforms/on-selection.js b/src/transforms/on-selection.js index 9b6e72786..59730e7a3 100644 --- a/src/transforms/on-selection.js +++ b/src/transforms/on-selection.js @@ -22,6 +22,8 @@ export const moveToRangeOf = generate('moveToRangeOf') export const moveStartOffset = generate('moveStartOffset') export const moveEndOffset = generate('moveEndOffset') +export const flipSelection = generate('flip') + /** * Move the selection to the end of the next block. *