mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-02-24 09:13:24 +01:00
refactor operations
This commit is contained in:
parent
52820af7ae
commit
b9e083d55f
@ -1,4 +1,5 @@
|
||||
|
||||
import Operations from '../transforms/operations'
|
||||
import Transforms from '../transforms'
|
||||
import includes from 'lodash/includes'
|
||||
import xor from 'lodash/xor'
|
||||
@ -70,13 +71,19 @@ class Transform {
|
||||
/**
|
||||
* Add an `operation` to the transform that resulted in `state`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {Transform}
|
||||
*/
|
||||
|
||||
add(state, operation) {
|
||||
this.state = state
|
||||
operate(operation) {
|
||||
const { type } = operation
|
||||
const fn = Operations[type]
|
||||
|
||||
if (!fn) {
|
||||
throw new Error(`Unknown operation type: "${type}".`)
|
||||
}
|
||||
|
||||
this.state = fn(this.state, operation)
|
||||
this.operations.push(operation)
|
||||
return this
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ export function addMark(transform, mark) {
|
||||
if (selection.isCollapsed) {
|
||||
const marks = document.getMarksAtRange(selection).add(mark)
|
||||
const sel = selection.merge({ marks })
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
return transform.addMarkAtRange(selection, mark)
|
||||
@ -73,7 +73,7 @@ export function _delete(transform) {
|
||||
|
||||
return transform
|
||||
.deleteAtRange(selection)
|
||||
.updateSelection(after)
|
||||
.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,7 +147,7 @@ export function deleteBackward(transform, n = 1) {
|
||||
|
||||
return transform
|
||||
.deleteBackwardAtRange(selection, n)
|
||||
.updateSelection(after)
|
||||
.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,7 +206,7 @@ export function deleteForward(transform, n = 1) {
|
||||
|
||||
return transform
|
||||
.deleteForwardAtRange(selection, n)
|
||||
.updateSelection(after)
|
||||
.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -229,7 +229,7 @@ export function insertBlock(transform, block) {
|
||||
const text = document.getTexts().find(n => !keys.includes(n.key))
|
||||
const after = selection.collapseToEndOf(text)
|
||||
|
||||
return transform.updateSelection(after)
|
||||
return transform.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -279,7 +279,7 @@ export function insertFragment(transform, fragment) {
|
||||
.moveForward(lastText.length)
|
||||
}
|
||||
|
||||
return transform.updateSelection(after)
|
||||
return transform.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -311,7 +311,7 @@ export function insertInline(transform, inline) {
|
||||
after = selection.collapseToEndOf(text)
|
||||
}
|
||||
|
||||
return transform.updateSelection(after)
|
||||
return transform.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -346,7 +346,7 @@ export function insertText(transform, text, marks) {
|
||||
|
||||
return transform
|
||||
.insertTextAtRange(selection, text, marks)
|
||||
.updateSelection(after)
|
||||
.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -399,7 +399,7 @@ export function splitBlock(transform, depth = 1) {
|
||||
const nextNode = document.getNextText(startNode)
|
||||
const after = selection.collapseToStartOf(nextNode)
|
||||
|
||||
return transform.updateSelection(after)
|
||||
return transform.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -428,7 +428,7 @@ export function splitInline(transform, depth = Infinity) {
|
||||
after = selection.collapseToStartOf(nextNode)
|
||||
}
|
||||
|
||||
return transform.updateSelection(after)
|
||||
return transform.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -448,7 +448,7 @@ export function removeMark(transform, mark) {
|
||||
if (selection.isCollapsed) {
|
||||
const marks = document.getMarksAtRange(selection).remove(mark)
|
||||
const sel = selection.merge({ marks })
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
return transform.removeMarkAtRange(selection, mark)
|
||||
@ -566,7 +566,7 @@ export function wrapInline(transform, properties) {
|
||||
}
|
||||
|
||||
after = after.normalize(document)
|
||||
return transform.updateSelection(after)
|
||||
return transform.setSelection(after)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -597,5 +597,5 @@ export function wrapText(transform, prefix, suffix = prefix) {
|
||||
|
||||
return transform
|
||||
.wrapTextAtRange(selection, prefix, suffix)
|
||||
.updateSelection(after)
|
||||
.setSelection(after)
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ export function insertFragmentAtRange(transform, range, fragment) {
|
||||
const lastIndex = lastBlock.nodes.size
|
||||
|
||||
nextNodes.forEach((node, i) => {
|
||||
const newIndex = lastIndex + i + 1
|
||||
const newIndex = lastIndex + i
|
||||
transform.moveNodeByKey(node.key, lastBlock.key, newIndex)
|
||||
})
|
||||
}
|
||||
@ -737,7 +737,7 @@ export function wrapBlockAtRange(transform, range, block) {
|
||||
const parent = document.getParent(first)
|
||||
const index = parent.nodes.indexOf(first)
|
||||
|
||||
transform.insertNodeByKey(parent.key, index + 1, block)
|
||||
transform.insertNodeByKey(parent.key, index, block)
|
||||
|
||||
siblings.forEach((node, i) => {
|
||||
transform.moveNodeByKey(node.key, block.key, i)
|
||||
@ -818,8 +818,8 @@ export function wrapInlineAtRange(transform, range, inline) {
|
||||
const startNode = inline.merge({ key: uid() })
|
||||
const endNode = inline.merge({ key: uid() })
|
||||
|
||||
transform.insertNodeByKey(startBlock.key, startIndex + 1, startNode)
|
||||
transform.insertNodeByKey(endBlock.key, endIndex + 1, endNode)
|
||||
transform.insertNodeByKey(startBlock.key, startIndex - 1, startNode)
|
||||
transform.insertNodeByKey(endBlock.key, endIndex, endNode)
|
||||
|
||||
startInlines.forEach((child, i) => {
|
||||
transform.moveNodeByKey(child.key, startNode.key, i)
|
||||
|
@ -16,22 +16,28 @@ import uid from '../utils/uid'
|
||||
export function addMarkByKey(transform, key, offset, length, mark) {
|
||||
mark = Normalize.mark(mark)
|
||||
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const path = document.getPath(key)
|
||||
|
||||
node = node.addMark(offset, length, mark)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'add-mark',
|
||||
const inverse = {
|
||||
type: 'remove_mark',
|
||||
path,
|
||||
offset,
|
||||
length,
|
||||
mark,
|
||||
offset,
|
||||
}
|
||||
|
||||
const operation = {
|
||||
type: 'add_mark',
|
||||
path,
|
||||
})
|
||||
offset,
|
||||
length,
|
||||
mark,
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,23 +51,25 @@ export function addMarkByKey(transform, key, offset, length, mark) {
|
||||
*/
|
||||
|
||||
export function insertNodeByKey(transform, key, index, node) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let parent = document.key == key ? document : document.assertDescendant(key)
|
||||
const isParent = document == parent
|
||||
const path = document.getPath(parent)
|
||||
const nodes = parent.nodes.splice(index, 0, node)
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const path = document.getPath(key)
|
||||
const newPath = path.slice().push(index)
|
||||
|
||||
parent = parent.merge({ nodes })
|
||||
document = isParent ? parent : document.updateDescendant(parent)
|
||||
state = state.merge({ document })
|
||||
const inverse = {
|
||||
type: 'remove_node',
|
||||
path: newPath,
|
||||
}
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'insert-node',
|
||||
const operation = {
|
||||
type: 'insert_node',
|
||||
path,
|
||||
index,
|
||||
node,
|
||||
path,
|
||||
})
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,22 +84,26 @@ export function insertNodeByKey(transform, key, index, node) {
|
||||
*/
|
||||
|
||||
export function insertTextByKey(transform, key, offset, text, marks) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const path = document.getPath(key)
|
||||
|
||||
node = node.insertText(offset, text, marks)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'insert-text',
|
||||
offset,
|
||||
marks,
|
||||
const inverse = {
|
||||
type: 'remove_text',
|
||||
path,
|
||||
offset,
|
||||
length: text.length,
|
||||
}
|
||||
|
||||
const operation = {
|
||||
type: 'insert_text',
|
||||
path,
|
||||
offset,
|
||||
text,
|
||||
})
|
||||
marks,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,32 +117,32 @@ export function insertTextByKey(transform, key, offset, text, marks) {
|
||||
*/
|
||||
|
||||
export function moveNodeByKey(transform, key, newKey, newIndex) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
const path = document.getPath(key)
|
||||
const parent = document.getParent(key)
|
||||
const parentPath = path.slice(0, -1)
|
||||
const parentIndex = path[path.length - 1]
|
||||
const newPath = document.getPath(newKey)
|
||||
let parent = document.getParent(node)
|
||||
const isParent = document == parent
|
||||
const index = parent.nodes.indexOf(node)
|
||||
const nodePath = newPath.slice().concat([newIndex])
|
||||
|
||||
parent = parent.removeNode(index)
|
||||
document = isParent ? parent : document.updateDescendant(parent)
|
||||
const inverse = {
|
||||
type: 'move_node',
|
||||
path: nodePath,
|
||||
newPath: parentPath,
|
||||
newIndex: parentIndex,
|
||||
}
|
||||
|
||||
let target = document.key == newKey ? document : document.assertDescendant(newKey)
|
||||
const isTarget = document == target
|
||||
|
||||
target = target.insertNode(newIndex, node)
|
||||
document = isTarget ? target : document.updateDescendant(target)
|
||||
// document = document.normalize()
|
||||
state = state.merge({ document })
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'move-node',
|
||||
const operation = {
|
||||
type: 'move_node',
|
||||
path,
|
||||
newPath,
|
||||
newIndex,
|
||||
})
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,22 +159,28 @@ export function moveNodeByKey(transform, key, newKey, newIndex) {
|
||||
export function removeMarkByKey(transform, key, offset, length, mark) {
|
||||
mark = Normalize.mark(mark)
|
||||
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const path = document.getPath(key)
|
||||
|
||||
node = node.removeMark(offset, length, mark)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'remove-mark',
|
||||
const inverse = {
|
||||
type: 'add_mark',
|
||||
path,
|
||||
offset,
|
||||
length,
|
||||
mark,
|
||||
}
|
||||
|
||||
const operation = {
|
||||
type: 'remove_mark',
|
||||
path,
|
||||
})
|
||||
offset,
|
||||
length,
|
||||
mark,
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -174,23 +192,27 @@ export function removeMarkByKey(transform, key, offset, length, mark) {
|
||||
*/
|
||||
|
||||
export function removeNodeByKey(transform, key) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
let parent = document.getParent(node)
|
||||
const index = parent.nodes.indexOf(node)
|
||||
const isParent = document == parent
|
||||
const path = document.getPath(key)
|
||||
const parentPath = path.slice(0, -1)
|
||||
const parentIndex = path.slice(-1)
|
||||
|
||||
parent = parent.removeNode(index)
|
||||
document = isParent ? parent : document.updateDescendant(parent)
|
||||
document = document.normalize()
|
||||
state = state.merge({ document })
|
||||
const inverse = {
|
||||
type: 'insert_node',
|
||||
path: parentPath,
|
||||
index: parentIndex,
|
||||
node,
|
||||
}
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'remove-node',
|
||||
const operation = {
|
||||
type: 'remove_node',
|
||||
path,
|
||||
})
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,22 +226,22 @@ export function removeNodeByKey(transform, key) {
|
||||
*/
|
||||
|
||||
export function removeTextByKey(transform, key, offset, length) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const path = document.getPath(key)
|
||||
|
||||
node = node.removeText(offset, length)
|
||||
document = document.updateDescendant(node)
|
||||
document = document.normalize()
|
||||
state = state.merge({ document })
|
||||
// TODO!
|
||||
const inverse = {}
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'remove-text',
|
||||
const operation = {
|
||||
type: 'remove_text',
|
||||
path,
|
||||
offset,
|
||||
length,
|
||||
path,
|
||||
})
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,23 +259,23 @@ export function setMarkByKey(transform, key, offset, length, mark, properties) {
|
||||
mark = Normalize.mark(mark)
|
||||
properties = Normalize.markProperties(properties)
|
||||
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const path = document.getPath(key)
|
||||
|
||||
node = node.updateMark(offset, length, mark, properties)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
// TODO!
|
||||
const inverse = {}
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'set-mark',
|
||||
const operation = {
|
||||
type: 'set_mark',
|
||||
path,
|
||||
offset,
|
||||
length,
|
||||
mark,
|
||||
path,
|
||||
properties,
|
||||
})
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,21 +290,30 @@ export function setMarkByKey(transform, key, offset, length, mark, properties) {
|
||||
export function setNodeByKey(transform, key, properties) {
|
||||
properties = Normalize.nodeProperties(properties)
|
||||
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const node = document.assertDescendant(key)
|
||||
const path = document.getPath(key)
|
||||
const prevProps = {}
|
||||
|
||||
node = node.merge(properties)
|
||||
document = document.updateDescendant(node)
|
||||
document = document.normalize()
|
||||
state = state.merge({ document })
|
||||
for (const k in properties) {
|
||||
prevProps[k] = node[k]
|
||||
}
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'set-node',
|
||||
const inverse = {
|
||||
type: 'set_node',
|
||||
path,
|
||||
properties: prevProps
|
||||
}
|
||||
|
||||
const operation = {
|
||||
type: 'set_node',
|
||||
path,
|
||||
properties,
|
||||
})
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -295,53 +326,20 @@ export function setNodeByKey(transform, key, properties) {
|
||||
*/
|
||||
|
||||
export function splitNodeByKey(transform, key, offset) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
let parent = document.getParent(node)
|
||||
const isParent = document == parent
|
||||
const index = parent.nodes.indexOf(node)
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const path = document.getPath(key)
|
||||
|
||||
let child = node
|
||||
let one
|
||||
let two
|
||||
// TODO!
|
||||
const inverse = {}
|
||||
|
||||
if (node.kind != 'text') {
|
||||
child = node.getTextAtOffset(offset)
|
||||
}
|
||||
|
||||
while (child && child != parent) {
|
||||
if (child.kind == 'text') {
|
||||
const i = node.kind == 'text' ? offset : offset - node.getOffset(child)
|
||||
const { characters } = child
|
||||
const oneChars = characters.take(i)
|
||||
const twoChars = characters.skip(i)
|
||||
one = child.merge({ characters: oneChars })
|
||||
two = child.merge({ characters: twoChars, key: uid() })
|
||||
}
|
||||
|
||||
else {
|
||||
const { nodes } = child
|
||||
const oneNodes = nodes.takeUntil(n => n.key == one.key).push(one)
|
||||
const twoNodes = nodes.skipUntil(n => n.key == one.key).rest().unshift(two)
|
||||
one = child.merge({ nodes: oneNodes })
|
||||
two = child.merge({ nodes: twoNodes, key: uid() })
|
||||
}
|
||||
|
||||
child = document.getParent(child)
|
||||
}
|
||||
|
||||
parent = parent.removeNode(index)
|
||||
parent = parent.insertNode(index, two)
|
||||
parent = parent.insertNode(index, one)
|
||||
document = isParent ? parent : document.updateDescendant(parent)
|
||||
state = state.merge({ document })
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'split-node',
|
||||
offset,
|
||||
const operation = {
|
||||
type: 'split_node',
|
||||
path,
|
||||
})
|
||||
offset,
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ import {
|
||||
moveTo,
|
||||
moveToOffsets,
|
||||
moveToRangeOf,
|
||||
updateSelection,
|
||||
setSelection,
|
||||
} from './on-selection'
|
||||
|
||||
/**
|
||||
@ -210,7 +210,7 @@ export default {
|
||||
moveTo,
|
||||
moveToOffsets,
|
||||
moveToRangeOf,
|
||||
updateSelection,
|
||||
setSelection,
|
||||
|
||||
/**
|
||||
* Normalize.
|
||||
|
@ -13,7 +13,15 @@ export function blur(transform) {
|
||||
const { state } = transform
|
||||
const { selection } = state
|
||||
const sel = selection.blur()
|
||||
return transform.updateSelection(sel)
|
||||
|
||||
return transform.operate({
|
||||
type: 'set_selection',
|
||||
selection: sel,
|
||||
inverse: {
|
||||
type: 'set_selection',
|
||||
selection,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -27,7 +35,7 @@ export function collapseToAnchor(transform) {
|
||||
const { state } = transform
|
||||
const { selection } = state
|
||||
const sel = selection.collapseToAnchor()
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,7 +49,7 @@ export function collapseToFocus(transform) {
|
||||
const { state } = transform
|
||||
const { selection } = state
|
||||
const sel = selection.collapseToFocus()
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,7 +64,7 @@ export function collapseToEndOf(transform, node) {
|
||||
const { state } = transform
|
||||
const { selection } = state
|
||||
const sel = selection.collapseToEndOf(node)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,7 +83,7 @@ export function collapseToEndOfNextBlock(transform) {
|
||||
if (!next) return transform
|
||||
|
||||
const sel = selection.collapseToEndOf(next)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,7 +102,7 @@ export function collapseToEndOfNextText(transform) {
|
||||
if (!next) return transform
|
||||
|
||||
const sel = selection.collapseToEndOf(next)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,7 +121,7 @@ export function collapseToEndOfPreviousBlock(transform) {
|
||||
if (!previous) return transform
|
||||
|
||||
const sel = selection.collapseToEndOf(previous)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,7 +140,7 @@ export function collapseToEndOfPreviousText(transform) {
|
||||
if (!previous) return transform
|
||||
|
||||
const sel = selection.collapseToEndOf(previous)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,7 +155,7 @@ export function collapseToStartOf(transform, node) {
|
||||
const { state } = transform
|
||||
const { selection } = state
|
||||
const sel = selection.collapseToStartOf(node)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,7 +174,7 @@ export function collapseToStartOfNextBlock(transform) {
|
||||
if (!next) return transform
|
||||
|
||||
const sel = selection.collapseToStartOf(next)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,7 +193,7 @@ export function collapseToStartOfNextText(transform) {
|
||||
if (!next) return transform
|
||||
|
||||
const sel = selection.collapseToStartOf(next)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,7 +212,7 @@ export function collapseToStartOfPreviousBlock(transform) {
|
||||
if (!previous) return transform
|
||||
|
||||
const sel = selection.collapseToStartOf(previous)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -223,7 +231,7 @@ export function collapseToStartOfPreviousText(transform) {
|
||||
if (!previous) return transform
|
||||
|
||||
const sel = selection.collapseToStartOf(previous)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -238,7 +246,7 @@ export function extendBackward(transform, n) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.extendBackward(n).normalize(document)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,7 +261,7 @@ export function extendForward(transform, n) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.extendForward(n).normalize(document)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,7 +276,7 @@ export function extendToEndOf(transform, node) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.extendToEndOf(node).normalize(document)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -283,7 +291,7 @@ export function extendToStartOf(transform, node) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.extendToStartOf(node).normalize(document)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -297,7 +305,7 @@ export function focus(transform) {
|
||||
const { state } = transform
|
||||
const { selection } = state
|
||||
const sel = selection.focus()
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,7 +320,7 @@ export function moveBackward(transform, n) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.moveBackward(n).normalize(document)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -327,7 +335,7 @@ export function moveForward(transform, n) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.moveForward(n).normalize(document)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -343,7 +351,7 @@ export function moveTo(transform, properties) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.merge(properties).normalize(document)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,7 +367,7 @@ export function moveToOffsets(transform, anchor, fokus) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.moveToOffsets(anchor, fokus)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -375,27 +383,31 @@ export function moveToRangeOf(transform, start, end) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
const sel = selection.moveToRangeOf(start, end).normalize(document)
|
||||
return transform.updateSelection(sel)
|
||||
return transform.setSelection(sel)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the selection with a new `selection`.
|
||||
* Set the selection to a new `selection`.
|
||||
*
|
||||
* @param {Transform} transform
|
||||
* @param {Mixed} sel
|
||||
* @param {Mixed} selection
|
||||
* @return {Transform}
|
||||
*/
|
||||
|
||||
export function updateSelection(transform, sel) {
|
||||
sel = Normalize.selection(sel)
|
||||
export function setSelection(transform, selection) {
|
||||
selection = Normalize.selection(selection)
|
||||
const { state } = transform
|
||||
|
||||
let { state } = transform
|
||||
let { selection } = state
|
||||
selection = selection.merge(sel)
|
||||
state = state.merge({ selection })
|
||||
const inverse = {
|
||||
type: 'set_selection',
|
||||
selection: state.selection,
|
||||
}
|
||||
|
||||
return transform.add(state, {
|
||||
type: 'update-selection',
|
||||
const operation = {
|
||||
type: 'set_selection',
|
||||
selection,
|
||||
})
|
||||
inverse,
|
||||
}
|
||||
|
||||
return transform.operate(operation)
|
||||
}
|
||||
|
279
lib/transforms/operations.js
Normal file
279
lib/transforms/operations.js
Normal file
@ -0,0 +1,279 @@
|
||||
|
||||
import uid from '../utils/uid'
|
||||
|
||||
/**
|
||||
* Add mark to text at `offset` and `length` in node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function addMark(state, operation) {
|
||||
const { path, offset, length, mark } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
node = node.addMark(offset, length, mark)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a `node` at `index` in a node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function insertNode(state, operation) {
|
||||
const { path, index, node } = operation
|
||||
let { document } = state
|
||||
let parent = document.assertPath(path)
|
||||
const isParent = document == parent
|
||||
const nodes = parent.nodes.splice(index, 0, node)
|
||||
parent = parent.merge({ nodes })
|
||||
document = isParent ? parent : document.updateDescendant(parent)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert `text` at `offset` in node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function insertText(state, operation) {
|
||||
const { path, offset, text, marks } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
node = node.insertText(offset, text, marks)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a node by `key` to a new parent by `key` and `index`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function moveNode(state, operation) {
|
||||
const { path, newPath, newIndex } = operation
|
||||
let { document } = state
|
||||
const node = document.assertPath(path)
|
||||
|
||||
let parent = document.getParent(node)
|
||||
const isParent = document == parent
|
||||
const index = parent.nodes.indexOf(node)
|
||||
parent = parent.removeNode(index)
|
||||
document = isParent ? parent : document.updateDescendant(parent)
|
||||
|
||||
let target = document.assertPath(newPath)
|
||||
const isTarget = document == target
|
||||
target = target.insertNode(newIndex, node)
|
||||
document = isTarget ? target : document.updateDescendant(target)
|
||||
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove mark from text at `offset` and `length` in node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function removeMark(state, operation) {
|
||||
const { path, offset, length, mark } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
node = node.removeMark(offset, length, mark)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function removeNode(state, operation) {
|
||||
const { path } = operation
|
||||
let { document } = state
|
||||
const node = document.assertPath(path)
|
||||
let parent = document.getParent(node)
|
||||
const index = parent.nodes.indexOf(node)
|
||||
const isParent = document == parent
|
||||
parent = parent.removeNode(index)
|
||||
document = isParent ? parent : document.updateDescendant(parent)
|
||||
document = document.normalize()
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove text at `offset` and `length` in node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function removeText(state, operation) {
|
||||
const { path, offset, length } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
node = node.removeText(offset, length)
|
||||
document = document.updateDescendant(node)
|
||||
document = document.normalize()
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `properties` on mark on text at `offset` and `length` in node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function setMark(state, operation) {
|
||||
const { path, offset, length, mark, properties } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
node = node.updateMark(offset, length, mark, properties)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `properties` on a node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function setNode(state, operation) {
|
||||
const { path, properties } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
node = node.merge(properties)
|
||||
document = document.updateDescendant(node)
|
||||
document = document.normalize()
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selection to a new `selection`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function setSelection(state, operation) {
|
||||
let { selection } = operation
|
||||
selection = state.selection.merge(selection)
|
||||
state = state.merge({ selection })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a node by `key` at `offset`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Object} operation
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
function splitNode(state, operation) {
|
||||
const { path, offset } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
let parent = document.getParent(node)
|
||||
const isParent = document == parent
|
||||
const index = parent.nodes.indexOf(node)
|
||||
|
||||
let child = node
|
||||
let one
|
||||
let two
|
||||
|
||||
if (node.kind != 'text') {
|
||||
child = node.getTextAtOffset(offset)
|
||||
}
|
||||
|
||||
while (child && child != parent) {
|
||||
if (child.kind == 'text') {
|
||||
const i = node.kind == 'text' ? offset : offset - node.getOffset(child)
|
||||
const { characters } = child
|
||||
const oneChars = characters.take(i)
|
||||
const twoChars = characters.skip(i)
|
||||
one = child.merge({ characters: oneChars })
|
||||
two = child.merge({ characters: twoChars, key: uid() })
|
||||
}
|
||||
|
||||
else {
|
||||
const { nodes } = child
|
||||
const oneNodes = nodes.takeUntil(n => n.key == one.key).push(one)
|
||||
const twoNodes = nodes.skipUntil(n => n.key == one.key).rest().unshift(two)
|
||||
one = child.merge({ nodes: oneNodes })
|
||||
two = child.merge({ nodes: twoNodes, key: uid() })
|
||||
}
|
||||
|
||||
child = document.getParent(child)
|
||||
}
|
||||
|
||||
parent = parent.removeNode(index)
|
||||
parent = parent.insertNode(index, two)
|
||||
parent = parent.insertNode(index, one)
|
||||
document = isParent ? parent : document.updateDescendant(parent)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
export default {
|
||||
|
||||
// Text operations.
|
||||
insert_text: insertText,
|
||||
remove_text: removeText,
|
||||
|
||||
// Mark operations.
|
||||
add_mark: addMark,
|
||||
remove_mark: removeMark,
|
||||
set_mark: setMark,
|
||||
|
||||
// Node operations.
|
||||
insert_node: insertNode,
|
||||
move_node: moveNode,
|
||||
remove_node: removeNode,
|
||||
set_node: setNode,
|
||||
split_node: splitNode,
|
||||
|
||||
// Selection operations.
|
||||
set_selection: setSelection,
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user