1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-09-01 19:22:35 +02:00

Add Node.splitNodeAfter, to allow for exact undo of joinNode

This commit is contained in:
Soreine
2016-11-09 12:16:13 +01:00
parent 6b38caf72d
commit 583b560ec3
3 changed files with 118 additions and 58 deletions

View File

@@ -1259,7 +1259,7 @@ const Node = {
/**
* Split a node by `path` at `offset`.
*
* @param {String} path
* @param {Array} path
* @param {Number} offset
* @return {Node}
*/
@@ -1312,6 +1312,40 @@ const Node = {
return base
},
/**
* Split a node by `path` after 'count' children.
* Does not work on Text nodes. Use `Node.splitNode` to split text nodes as well.
*
* @param {Array} path
* @param {Number} count
* @return {Node}
*/
splitNodeAfter(path, count) {
let base = this
let node = base.assertPath(path)
if (node.kind === 'text') throw new Error('Cannot split text node at index. Use Node.splitNode at offset instead')
const { nodes } = node
let parent = base.getParent(node.key)
const isParent = base == parent
const oneNodes = nodes.take(count)
const twoNodes = nodes.skip(count)
const one = node.merge({ nodes: oneNodes })
const two = node.merge({ nodes: twoNodes, key: uid() })
const nodeIndex = parent.nodes.indexOf(node)
parent = parent.removeNode(nodeIndex)
parent = parent.insertNode(nodeIndex, two)
parent = parent.insertNode(nodeIndex, one)
base = isParent ? parent : base.updateDescendant(parent)
return base
},
/**
* Split the block nodes at a `range`, to optional `height`.
*

View File

@@ -392,63 +392,76 @@ function setSelection(state, operation) {
*
* @param {State} state
* @param {Object} operation
* @param {Array} operation.path The path of the node to split
* @param {Number} operation.offset (optional) Split using a relative offset
* @param {Number} operation.count (optional) Split after `count`
* children. Cannot be used in combination with offset.
* @return {State}
*/
function splitNode(state, operation) {
const { path, offset } = operation
const { path, offset, count } = operation
const { document } = state
// Update document
const newDocument = document.splitNode(path, offset)
// Update selection
let { selection } = state
const { anchorKey, anchorOffset, focusKey, focusOffset } = selection
const node = document.assertPath(path)
// The text node that was split
const splittedText = node.kind == 'text'
? node
: node.getTextAtOffset(offset)
const textOffset = node.kind == 'text'
? offset
: offset - node.getOffset(splittedText.key)
// Should we update the selection ?
const shouldUpdateAnchor = splittedText.key == anchorKey && textOffset <= anchorOffset
const shouldUpdateFocus = splittedText.key == focusKey && textOffset <= focusOffset
if (shouldUpdateFocus || shouldUpdateAnchor) {
// The node next to `node`, resulting from the split
const secondNode = newDocument.getNextSibling(node.key)
let secondText, newOffset
if (shouldUpdateAnchor) {
newOffset = anchorOffset - textOffset
secondText = secondNode.kind == 'text'
? secondNode
: secondNode.getTextAtOffset(newOffset)
selection = selection.merge({
anchorKey: secondText.key,
anchorOffset: newOffset
})
}
if (shouldUpdateFocus) {
newOffset = focusOffset - textOffset
secondText = secondNode.kind == 'text'
? secondNode
: secondNode.getTextAtOffset(newOffset)
selection = selection.merge({
focusKey: secondText.key,
focusOffset: newOffset
})
}
if (offset === undefined) {
return state.merge({
document: document.splitNodeAfter(path, count)
// No need to update selection
})
}
state = state.merge({
document: newDocument,
selection
})
return state
else {
// Update document
let newDocument = document.splitNode(path, offset)
// Update selection
let { selection } = state
const { anchorKey, anchorOffset, focusKey, focusOffset } = selection
const node = document.assertPath(path)
// The text node that was split
const splittedText = node.kind == 'text'
? node
: node.getTextAtOffset(offset)
const textOffset = node.kind == 'text'
? offset
: offset - node.getOffset(splittedText.key)
// Should we update the selection ?
const shouldUpdateAnchor = splittedText.key == anchorKey && textOffset <= anchorOffset
const shouldUpdateFocus = splittedText.key == focusKey && textOffset <= focusOffset
if (shouldUpdateFocus || shouldUpdateAnchor) {
// The node next to `node`, resulting from the split
const secondNode = newDocument.getNextSibling(node.key)
let secondText, newOffset
if (shouldUpdateAnchor) {
newOffset = anchorOffset - textOffset
secondText = secondNode.kind == 'text'
? secondNode
: secondNode.getTextAtOffset(newOffset)
selection = selection.merge({
anchorKey: secondText.key,
anchorOffset: newOffset
})
}
if (shouldUpdateFocus) {
newOffset = focusOffset - textOffset
secondText = secondNode.kind == 'text'
? secondNode
: secondNode.getTextAtOffset(newOffset)
selection = selection.merge({
focusKey: secondText.key,
focusOffset: newOffset
})
}
}
state = state.merge({
document: newDocument,
selection
})
return state
}
}

View File

@@ -106,13 +106,26 @@ export function joinNodeOperation(transform, path, withPath) {
const { state } = transform
const { document } = state
const node = document.assertPath(withPath)
const offset = node.length
const inverse = [{
type: 'split_node',
path: withPath,
offset,
}]
let inverse
if (node.kind === 'text') {
const offset = node.length
inverse = [{
type: 'split_node',
path: withPath,
offset,
}]
} else {
// The number of children after which we split
const count = node.nodes.count()
inverse = [{
type: 'split_node',
path: withPath,
count,
}]
}
const operation = {
type: 'join_node',