diff --git a/lib/components/void.js b/lib/components/void.js index 2dc05bfbd..bf684d39f 100644 --- a/lib/components/void.js +++ b/lib/components/void.js @@ -121,7 +121,6 @@ class Void extends React.Component { visibility: 'hidden' } : { - pointerEvents: 'none', position: 'absolute', top: '0px', left: '-9999px', @@ -129,7 +128,7 @@ class Void extends React.Component { } return ( - {this.renderLeaf()} + {this.renderLeaf()} ) } diff --git a/lib/models/state.js b/lib/models/state.js index 632490280..fc5661470 100644 --- a/lib/models/state.js +++ b/lib/models/state.js @@ -558,13 +558,36 @@ class State extends new Record(DEFAULTS) { delete() { let state = this let { document, selection } = state + let after // When collapsed, there's nothing to do. if (selection.isCollapsed) return state - // Otherwise, delete and update the selection. + // Determine what the selection will be after deleting. + const { startText } = this + const { startKey, startOffset, endKey, endOffset } = selection + const previous = document.getPreviousSibling(startText) + + if ( + previous && + startOffset == 0 && + (endKey != startKey || endOffset == startText.length) + ) { + if (previous.kind == 'text') { + after = selection.collapseToEndOf(previous) + } else { + const last = previous.getTexts().last() + after = selection.collapseToEndOf(last) + } + } + + else { + after = selection.collapseToStart() + } + + // Delete and update the selection. document = document.deleteAtRange(selection) - selection = selection.collapseToStart() + selection = after state = state.merge({ document, selection }) return state } @@ -607,6 +630,19 @@ class State extends new Record(DEFAULTS) { } } + else if (selection.isAtEndOf(startNode) && startNode.length == 1) { + const previous = document.getPreviousSibling(startNode) + + if (previous && previous.kind == 'text') { + after = selection.collapseToEndOf(previous) + } else if (previous) { + const last = previous.getTexts().last() + after = selection.collapseToEndOf(last) + } else { + after = selection.moveBackward(n) + } + } + else { after = selection.moveBackward(n) } @@ -628,12 +664,14 @@ class State extends new Record(DEFAULTS) { deleteForward(n = 1) { let state = this let { document, selection } = state - let { startKey } = selection + let { startKey, startOffset } = selection let after = selection // Determine what the selection should be after deleting. const block = document.getClosestBlock(startKey) const inline = document.getClosestInline(startKey) + const previous = document.getPreviousSibling(startKey) + const startText = document.getDescendant(startKey) if (selection.isExpanded) { after = selection.collapseToStart() @@ -641,10 +679,19 @@ class State extends new Record(DEFAULTS) { else if ((block && block.isVoid) || (inline && inline.isVoid)) { const next = document.getNextText(startKey) - const previous = document.getPreviousText(startKey) + const prev = document.getPreviousText(startKey) after = next ? selection.collapseToStartOf(next) - : selection.collapseToEndOf(previous) + : selection.collapseToEndOf(prev) + } + + else if (previous && startOffset == 0 && startText.length == 1) { + if (previous.kind == 'text') { + after = selection.collapseToEndOf(previous) + } else { + const last = previous.getTexts().last() + after = selection.collapseToEndOf(last) + } } // Delete forward and then update the selection. diff --git a/lib/models/transforms.js b/lib/models/transforms.js index e95737719..8daa3458a 100644 --- a/lib/models/transforms.js +++ b/lib/models/transforms.js @@ -79,7 +79,7 @@ const Transforms = { let text = node.getDescendant(startKey) text = text.removeCharacters(startOffset, endOffset) node = node.updateDescendant(text) - return node + return node.normalize() } // Split the blocks and determine the edge boundaries. diff --git a/test/transforms/fixtures/delete-at-range/before-inline-sibling/index.js b/test/transforms/fixtures/delete-at-range/before-inline-sibling/index.js new file mode 100644 index 000000000..e7311ea44 --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/before-inline-sibling/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const last = texts.last() + const range = selection.merge({ + anchorKey: last.key, + anchorOffset: last.length - 1, + focusKey: last.key, + focusOffset: last.length + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/transforms/fixtures/delete-at-range/before-inline-sibling/input.yaml b/test/transforms/fixtures/delete-at-range/before-inline-sibling/input.yaml new file mode 100644 index 000000000..6286c3b87 --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/before-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two + - kind: text + text: a diff --git a/test/transforms/fixtures/delete-at-range/before-inline-sibling/output.yaml b/test/transforms/fixtures/delete-at-range/before-inline-sibling/output.yaml new file mode 100644 index 000000000..798f00a1b --- /dev/null +++ b/test/transforms/fixtures/delete-at-range/before-inline-sibling/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/index.js b/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/index.js new file mode 100644 index 000000000..cce7cc965 --- /dev/null +++ b/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const last = texts.last() + const range = selection.merge({ + anchorKey: last.key, + anchorOffset: last.length, + focusKey: last.key, + focusOffset: last.length + }) + + return state + .transform() + .deleteBackwardAtRange(range) + .apply() +} diff --git a/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/input.yaml b/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/input.yaml new file mode 100644 index 000000000..6286c3b87 --- /dev/null +++ b/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two + - kind: text + text: a diff --git a/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/output.yaml b/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/output.yaml new file mode 100644 index 000000000..798f00a1b --- /dev/null +++ b/test/transforms/fixtures/delete-backward-at-range/before-inline-sibling/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-backward/before-inline-sibling/index.js b/test/transforms/fixtures/delete-backward/before-inline-sibling/index.js new file mode 100644 index 000000000..238bf01cf --- /dev/null +++ b/test/transforms/fixtures/delete-backward/before-inline-sibling/index.js @@ -0,0 +1,29 @@ + +import assert from 'assert' + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const last = texts.last() + const range = selection.merge({ + anchorKey: last.key, + anchorOffset: last.length, + focusKey: last.key, + focusOffset: last.length + }) + + const next = state + .transform() + .moveTo(range) + .deleteBackward() + .apply() + + const updated = next.document.getTexts().last() + + assert.deepEqual( + next.selection.toJS(), + range.collapseToEndOf(updated).toJS() + ) + + return next +} diff --git a/test/transforms/fixtures/delete-backward/before-inline-sibling/input.yaml b/test/transforms/fixtures/delete-backward/before-inline-sibling/input.yaml new file mode 100644 index 000000000..6286c3b87 --- /dev/null +++ b/test/transforms/fixtures/delete-backward/before-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two + - kind: text + text: a diff --git a/test/transforms/fixtures/delete-backward/before-inline-sibling/output.yaml b/test/transforms/fixtures/delete-backward/before-inline-sibling/output.yaml new file mode 100644 index 000000000..798f00a1b --- /dev/null +++ b/test/transforms/fixtures/delete-backward/before-inline-sibling/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/index.js b/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/index.js new file mode 100644 index 000000000..876bb95df --- /dev/null +++ b/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const last = texts.last() + const range = selection.merge({ + anchorKey: last.key, + anchorOffset: 0, + focusKey: last.key, + focusOffset: 0 + }) + + return state + .transform() + .deleteForwardAtRange(range) + .apply() +} diff --git a/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/input.yaml b/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/input.yaml new file mode 100644 index 000000000..6286c3b87 --- /dev/null +++ b/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two + - kind: text + text: a diff --git a/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/output.yaml b/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/output.yaml new file mode 100644 index 000000000..798f00a1b --- /dev/null +++ b/test/transforms/fixtures/delete-forward-at-range/before-inline-sibling/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/delete-forward/before-inline-sibling/index.js b/test/transforms/fixtures/delete-forward/before-inline-sibling/index.js new file mode 100644 index 000000000..1bd341afc --- /dev/null +++ b/test/transforms/fixtures/delete-forward/before-inline-sibling/index.js @@ -0,0 +1,29 @@ + +import assert from 'assert' + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const last = texts.last() + const range = selection.merge({ + anchorKey: last.key, + anchorOffset: 0, + focusKey: last.key, + focusOffset: 0 + }) + + const next = state + .transform() + .moveTo(range) + .deleteForward() + .apply() + + const updated = next.document.getTexts().last() + + assert.deepEqual( + next.selection.toJS(), + range.collapseToEndOf(updated).toJS() + ) + + return next +} diff --git a/test/transforms/fixtures/delete-forward/before-inline-sibling/input.yaml b/test/transforms/fixtures/delete-forward/before-inline-sibling/input.yaml new file mode 100644 index 000000000..6286c3b87 --- /dev/null +++ b/test/transforms/fixtures/delete-forward/before-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two + - kind: text + text: a diff --git a/test/transforms/fixtures/delete-forward/before-inline-sibling/output.yaml b/test/transforms/fixtures/delete-forward/before-inline-sibling/output.yaml new file mode 100644 index 000000000..798f00a1b --- /dev/null +++ b/test/transforms/fixtures/delete-forward/before-inline-sibling/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two diff --git a/test/transforms/fixtures/delete/before-inline-sibling/index.js b/test/transforms/fixtures/delete/before-inline-sibling/index.js new file mode 100644 index 000000000..0bdd2d9a3 --- /dev/null +++ b/test/transforms/fixtures/delete/before-inline-sibling/index.js @@ -0,0 +1,34 @@ + +import assert from 'assert' + +export default function (state) { + const { document, selection } = state + const texts = document.getTexts() + const last = texts.last() + const range = selection.merge({ + anchorKey: last.key, + anchorOffset: last.length - 1, + focusKey: last.key, + focusOffset: last.length + }) + + const next = state + .transform() + .moveTo(range) + .delete() + .apply() + + const updated = next.document.getTexts().last() + + assert.deepEqual( + next.selection.toJS(), + range.merge({ + anchorKey: updated.key, + anchorOffset: updated.length, + focusKey: updated.key, + focusOffset: updated.length + }).toJS() + ) + + return next +} diff --git a/test/transforms/fixtures/delete/before-inline-sibling/input.yaml b/test/transforms/fixtures/delete/before-inline-sibling/input.yaml new file mode 100644 index 000000000..6286c3b87 --- /dev/null +++ b/test/transforms/fixtures/delete/before-inline-sibling/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two + - kind: text + text: a diff --git a/test/transforms/fixtures/delete/before-inline-sibling/output.yaml b/test/transforms/fixtures/delete/before-inline-sibling/output.yaml new file mode 100644 index 000000000..798f00a1b --- /dev/null +++ b/test/transforms/fixtures/delete/before-inline-sibling/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + text: one + - kind: inline + type: link + nodes: + - kind: text + text: two