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:
@@ -1259,7 +1259,7 @@ const Node = {
|
|||||||
/**
|
/**
|
||||||
* Split a node by `path` at `offset`.
|
* Split a node by `path` at `offset`.
|
||||||
*
|
*
|
||||||
* @param {String} path
|
* @param {Array} path
|
||||||
* @param {Number} offset
|
* @param {Number} offset
|
||||||
* @return {Node}
|
* @return {Node}
|
||||||
*/
|
*/
|
||||||
@@ -1312,6 +1312,40 @@ const Node = {
|
|||||||
return base
|
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`.
|
* Split the block nodes at a `range`, to optional `height`.
|
||||||
*
|
*
|
||||||
|
@@ -392,63 +392,76 @@ function setSelection(state, operation) {
|
|||||||
*
|
*
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @param {Object} operation
|
* @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}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function splitNode(state, operation) {
|
function splitNode(state, operation) {
|
||||||
const { path, offset } = operation
|
const { path, offset, count } = operation
|
||||||
const { document } = state
|
const { document } = state
|
||||||
|
|
||||||
// Update document
|
if (offset === undefined) {
|
||||||
const newDocument = document.splitNode(path, offset)
|
return state.merge({
|
||||||
|
document: document.splitNodeAfter(path, count)
|
||||||
// Update selection
|
// No need to 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({
|
else {
|
||||||
document: newDocument,
|
// Update document
|
||||||
selection
|
let newDocument = document.splitNode(path, offset)
|
||||||
})
|
|
||||||
return state
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -106,13 +106,26 @@ export function joinNodeOperation(transform, path, withPath) {
|
|||||||
const { state } = transform
|
const { state } = transform
|
||||||
const { document } = state
|
const { document } = state
|
||||||
const node = document.assertPath(withPath)
|
const node = document.assertPath(withPath)
|
||||||
const offset = node.length
|
|
||||||
|
|
||||||
const inverse = [{
|
let inverse
|
||||||
type: 'split_node',
|
if (node.kind === 'text') {
|
||||||
path: withPath,
|
const offset = node.length
|
||||||
offset,
|
|
||||||
}]
|
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 = {
|
const operation = {
|
||||||
type: 'join_node',
|
type: 'join_node',
|
||||||
|
Reference in New Issue
Block a user