mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-21 14:41:23 +02:00
refactor more transforms
This commit is contained in:
@@ -49,6 +49,20 @@ export function addMarkAtRange(transform, range, mark) {
|
||||
*/
|
||||
|
||||
export function deleteAtRange(transform, range) {
|
||||
// if (range.isCollapsed) return transform
|
||||
|
||||
// const { state } = transform
|
||||
// const { document } = state
|
||||
// const { startKey, startOffset, endKey, endOffset } = range
|
||||
// const startText = document.assertDescendant(startKey)
|
||||
// const endText = document.assertDescendant(endKey)
|
||||
|
||||
// if (startKey == endKey) {
|
||||
// const index = startOffset
|
||||
// const length = endOffset - startOffset
|
||||
// return transform.removeTextByKey(startKey, index, length)
|
||||
// }
|
||||
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
|
||||
@@ -147,65 +161,56 @@ export function deleteAtRange(transform, range) {
|
||||
*/
|
||||
|
||||
export function deleteBackwardAtRange(transform, range, n = 1) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
const { startKey, startOffset } = range
|
||||
|
||||
// When the range is still expanded, just do a regular delete.
|
||||
if (range.isExpanded) return deleteAtRange(transform, range)
|
||||
|
||||
// When collapsed at the start of the node, there's nothing to do.
|
||||
if (range.isAtStartOf(document)) return transform
|
||||
|
||||
// When collapsed in a void node, remove that node.
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const { startKey, focusOffset } = range
|
||||
const text = document.getDescendant(startKey)
|
||||
const block = document.getClosestBlock(startKey)
|
||||
if (block && block.isVoid) {
|
||||
document = document.removeDescendant(block)
|
||||
state = state.merge({ document })
|
||||
transform.state = state
|
||||
return transform
|
||||
}
|
||||
|
||||
const inline = document.getClosestInline(startKey)
|
||||
|
||||
if (range.isExpanded) {
|
||||
return transform.deleteAtRange(range)
|
||||
}
|
||||
|
||||
if (block && block.isVoid) {
|
||||
return transform.removeNodeByKey(block.key)
|
||||
}
|
||||
|
||||
if (inline && inline.isVoid) {
|
||||
document = document.removeDescendant(inline)
|
||||
state = state.merge({ document })
|
||||
transform.state = state
|
||||
return transform.removeNodeByKey(inline.key)
|
||||
}
|
||||
|
||||
if (range.isAtStartOf(document)) {
|
||||
return transform
|
||||
}
|
||||
|
||||
// When at start of a text node, merge forwards into the next text node.
|
||||
const startNode = document.getDescendant(startKey)
|
||||
if (range.isAtStartOf(text)) {
|
||||
const prev = document.getPreviousText(text)
|
||||
const prevBlock = document.getClosestBlock(prev)
|
||||
const prevInline = document.getClosestInline(prev)
|
||||
|
||||
if (range.isAtStartOf(startNode)) {
|
||||
const previous = document.getPreviousText(startNode)
|
||||
|
||||
// If the previous descendant is void, remove it.
|
||||
const prevBlock = document.getClosestBlock(previous)
|
||||
if (prevBlock && prevBlock.isVoid) {
|
||||
document = document.removeDescendant(prevBlock)
|
||||
state = state.merge({ document })
|
||||
transform.state = state
|
||||
return transform
|
||||
return transform.removeNodeByKey(prevBlock.key)
|
||||
}
|
||||
|
||||
const prevInline = document.getClosestInline(previous)
|
||||
if (prevInline && prevInline.isVoid) {
|
||||
document = document.removeDescendant(prevInline)
|
||||
state = state.merge({ document })
|
||||
transform.state = state
|
||||
return transform
|
||||
return transform.removeNodeByKey(prevInline.key)
|
||||
}
|
||||
|
||||
range = range.extendToEndOf(previous)
|
||||
range = range.normalize(document)
|
||||
return deleteAtRange(transform, range)
|
||||
range = range.merge({
|
||||
anchorKey: prev.key,
|
||||
anchorOffset: prev.length,
|
||||
})
|
||||
|
||||
return transform.deleteAtRange(range)
|
||||
}
|
||||
|
||||
// Otherwise, remove `n` characters behind of the cursor.
|
||||
range = range.extendBackward(n)
|
||||
range = range.normalize(document)
|
||||
return deleteAtRange(transform, range)
|
||||
range = range.merge({
|
||||
focusOffset: focusOffset - 1,
|
||||
isBackward: true,
|
||||
})
|
||||
|
||||
return transform.deleteAtRange(range)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,46 +223,45 @@ export function deleteBackwardAtRange(transform, range, n = 1) {
|
||||
*/
|
||||
|
||||
export function deleteForwardAtRange(transform, range, n = 1) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
const { startKey } = range
|
||||
|
||||
// When the range is still expanded, just do a regular delete.
|
||||
if (range.isExpanded) return deleteAtRange(transform, range)
|
||||
|
||||
// When collapsed at the end of the node, there's nothing to do.
|
||||
if (range.isAtEndOf(document)) return transform
|
||||
|
||||
// When collapsed in a void node, remove that node.
|
||||
const block = document.getClosestBlock(startKey)
|
||||
if (block && block.isVoid) {
|
||||
document = document.removeDescendant(block)
|
||||
state = state.merge({ document })
|
||||
transform.state = state
|
||||
return transform
|
||||
}
|
||||
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
const { startKey, focusOffset } = range
|
||||
const text = document.getDescendant(startKey)
|
||||
const inline = document.getClosestInline(startKey)
|
||||
const block = document.getClosestBlock(startKey)
|
||||
|
||||
if (range.isExpanded) {
|
||||
return transform.deleteAtRange(range)
|
||||
}
|
||||
|
||||
if (block && block.isVoid) {
|
||||
return transform.removeNodeByKey(block.key)
|
||||
}
|
||||
|
||||
if (inline && inline.isVoid) {
|
||||
document = document.removeDescendant(inline)
|
||||
state = state.merge({ document })
|
||||
transform.state = state
|
||||
return transform.removeNodeByKey(inline.key)
|
||||
}
|
||||
|
||||
if (range.isAtEndOf(document)) {
|
||||
return transform
|
||||
}
|
||||
|
||||
// When at end of a text node, merge forwards into the next text node.
|
||||
const startNode = document.getDescendant(startKey)
|
||||
if (range.isAtEndOf(startNode)) {
|
||||
const next = document.getNextText(startNode)
|
||||
range = range.extendToStartOf(next)
|
||||
range = range.normalize(document)
|
||||
return deleteAtRange(transform, range)
|
||||
if (range.isAtEndOf(text)) {
|
||||
const next = document.getNextText(text)
|
||||
|
||||
range = range.merge({
|
||||
focusKey: next.key,
|
||||
focusOffset: 0
|
||||
})
|
||||
|
||||
return transform.deleteAtRange(range)
|
||||
}
|
||||
|
||||
// Otherwise, remove `n` characters ahead of the cursor.
|
||||
range = range.extendForward(n)
|
||||
range = range.normalize(document)
|
||||
return deleteAtRange(transform, range)
|
||||
range = range.merge({
|
||||
focusOffset: focusOffset + 1
|
||||
})
|
||||
|
||||
return transform.deleteAtRange(range)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -689,58 +693,33 @@ export function splitBlockAtRange(transform, range, depth = 1) {
|
||||
*/
|
||||
|
||||
export function splitInlineAtRange(transform, range, depth = Infinity) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
|
||||
// If the range is expanded, remove it first.
|
||||
if (range.isExpanded) {
|
||||
transform = deleteAtRange(transform, range)
|
||||
state = transform.state
|
||||
document = state.document
|
||||
transform.deleteAtRange(range)
|
||||
range = range.collapseToStart()
|
||||
}
|
||||
|
||||
// First split the text nodes.
|
||||
transform = splitTextAtRange(transform, range)
|
||||
state = transform.state
|
||||
document = state.document
|
||||
|
||||
// Find the children that were split.
|
||||
const { startKey } = range
|
||||
let firstChild = document.getDescendant(startKey)
|
||||
let secondChild = document.getNextText(firstChild)
|
||||
let parent = document.getClosestInline(firstChild)
|
||||
const { startKey, startOffset } = range
|
||||
const { state } = transform
|
||||
const { document } = state
|
||||
let node = document.assertDescendant(startKey)
|
||||
let parent = document.getClosestInline(node)
|
||||
let offset = startOffset
|
||||
let d = 0
|
||||
|
||||
// While the parent is an inline parent, split the inline nodes.
|
||||
while (parent && d < depth) {
|
||||
firstChild = parent.merge({ nodes: Inline.createList([firstChild]) })
|
||||
secondChild = Inline.create({
|
||||
nodes: [secondChild],
|
||||
type: parent.type,
|
||||
data: parent.data
|
||||
})
|
||||
debugger
|
||||
|
||||
// Split the children.
|
||||
const grandparent = document.getParent(parent)
|
||||
const nodes = grandparent.nodes
|
||||
.takeUntil(n => n.key == firstChild.key)
|
||||
.push(firstChild)
|
||||
.push(secondChild)
|
||||
.concat(grandparent.nodes.skipUntil(n => n.key == firstChild.key).rest())
|
||||
|
||||
// Update the grandparent.
|
||||
document = grandparent == document
|
||||
? document.merge({ nodes })
|
||||
: document.updateDescendant(grandparent.merge({ nodes }))
|
||||
while (parent && parent.kind == 'inline' && d < depth) {
|
||||
const index = parent.nodes.indexOf(node)
|
||||
const befores = parent.nodes.take(index)
|
||||
const length = befores.reduce((l, n) => n.length, 0)
|
||||
|
||||
offset += length
|
||||
node = parent
|
||||
parent = document.getClosestInline(parent)
|
||||
d++
|
||||
parent = document.getClosestInline(firstChild)
|
||||
}
|
||||
|
||||
state = state.merge({ document })
|
||||
transform.state = state
|
||||
return transform
|
||||
return transform.splitNodeByKey(node.key, offset)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,18 +1,19 @@
|
||||
|
||||
import Normalize from '../utils/normalize'
|
||||
import uid from '../utils/uid'
|
||||
|
||||
/**
|
||||
* Add mark to text at `index` and `length` in node by `key`.
|
||||
* Add mark to text at `offset` and `length` in node by `key`.
|
||||
*
|
||||
* @param {Transform} transform
|
||||
* @param {String} key
|
||||
* @param {Number} index
|
||||
* @param {Number} offset
|
||||
* @param {Number} length
|
||||
* @param {Mixed} mark
|
||||
* @return {Transform}
|
||||
*/
|
||||
|
||||
export function addMarkByKey(transform, key, index, length, mark) {
|
||||
export function addMarkByKey(transform, key, offset, length, mark) {
|
||||
mark = Normalize.mark(mark)
|
||||
|
||||
let { state } = transform
|
||||
@@ -20,14 +21,14 @@ export function addMarkByKey(transform, key, index, length, mark) {
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
|
||||
node = node.addMark(index, length, mark)
|
||||
node = node.addMark(offset, length, mark)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
|
||||
transform.state = state
|
||||
transform.operations.push({
|
||||
type: 'add-mark',
|
||||
index,
|
||||
offset,
|
||||
length,
|
||||
mark,
|
||||
path,
|
||||
@@ -70,30 +71,30 @@ export function insertNodeByKey(transform, key, index, node) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert `text` at `index` in node by `key`.
|
||||
* Insert `text` at `offset` in node by `key`.
|
||||
*
|
||||
* @param {Transform} transform
|
||||
* @param {String} key
|
||||
* @param {Number} index
|
||||
* @param {Number} offset
|
||||
* @param {String} text
|
||||
* @param {Set} marks (optional)
|
||||
* @return {Transform}
|
||||
*/
|
||||
|
||||
export function insertTextByKey(transform, key, index, text, marks) {
|
||||
export function insertTextByKey(transform, key, offset, text, marks) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
|
||||
node = node.insertText(index, text, marks)
|
||||
node = node.insertText(offset, text, marks)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
|
||||
transform.state = state
|
||||
transform.operations.push({
|
||||
type: 'insert-text',
|
||||
index,
|
||||
offset,
|
||||
marks,
|
||||
path,
|
||||
text,
|
||||
@@ -145,17 +146,17 @@ export function moveNodeByKey(transform, key, newKey, newIndex) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove mark from text at `index` and `length` in node by `key`.
|
||||
* Remove mark from text at `offset` and `length` in node by `key`.
|
||||
*
|
||||
* @param {Transform} transform
|
||||
* @param {String} key
|
||||
* @param {Number} index
|
||||
* @param {Number} offset
|
||||
* @param {Number} length
|
||||
* @param {Mark} mark
|
||||
* @return {Transform}
|
||||
*/
|
||||
|
||||
export function removeMarkByKey(transform, key, index, length, mark) {
|
||||
export function removeMarkByKey(transform, key, offset, length, mark) {
|
||||
mark = Normalize.mark(mark)
|
||||
|
||||
let { state } = transform
|
||||
@@ -163,14 +164,14 @@ export function removeMarkByKey(transform, key, index, length, mark) {
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
|
||||
node = node.removeMark(index, length, mark)
|
||||
node = node.removeMark(offset, length, mark)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
|
||||
transform.state = state
|
||||
transform.operations.push({
|
||||
type: 'remove-mark',
|
||||
index,
|
||||
offset,
|
||||
length,
|
||||
mark,
|
||||
path,
|
||||
@@ -211,22 +212,22 @@ export function removeNodeByKey(transform, key) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove text at `index` and `length` in node by `key`.
|
||||
* Remove text at `offset` and `length` in node by `key`.
|
||||
*
|
||||
* @param {Transform} transform
|
||||
* @param {String} key
|
||||
* @param {Number} index
|
||||
* @param {Number} offset
|
||||
* @param {Number} length
|
||||
* @return {Transform}
|
||||
*/
|
||||
|
||||
export function removeTextByKey(transform, key, index, length) {
|
||||
export function removeTextByKey(transform, key, offset, length) {
|
||||
let { state } = transform
|
||||
let { document } = state
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
|
||||
node = node.removeText(index, length)
|
||||
node = node.removeText(offset, length)
|
||||
document = document.updateDescendant(node)
|
||||
document = document.normalize()
|
||||
state = state.merge({ document })
|
||||
@@ -234,7 +235,7 @@ export function removeTextByKey(transform, key, index, length) {
|
||||
transform.state = state
|
||||
transform.operations.push({
|
||||
type: 'remove-text',
|
||||
index,
|
||||
offset,
|
||||
length,
|
||||
path,
|
||||
})
|
||||
@@ -243,17 +244,17 @@ export function removeTextByKey(transform, key, index, length) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `properties` on mark on text at `index` and `length` in node by `key`.
|
||||
* Set `properties` on mark on text at `offset` and `length` in node by `key`.
|
||||
*
|
||||
* @param {Transform} transform
|
||||
* @param {String} key
|
||||
* @param {Number} index
|
||||
* @param {Number} offset
|
||||
* @param {Number} length
|
||||
* @param {Mark} mark
|
||||
* @return {Transform}
|
||||
*/
|
||||
|
||||
export function setMarkByKey(transform, key, index, length, mark, properties) {
|
||||
export function setMarkByKey(transform, key, offset, length, mark, properties) {
|
||||
mark = Normalize.mark(mark)
|
||||
properties = Normalize.markProperties(properties)
|
||||
|
||||
@@ -262,14 +263,14 @@ export function setMarkByKey(transform, key, index, length, mark, properties) {
|
||||
let node = document.assertDescendant(key)
|
||||
const path = document.getPath(node)
|
||||
|
||||
node = node.updateMark(index, length, mark, properties)
|
||||
node = node.updateMark(offset, length, mark, properties)
|
||||
document = document.updateDescendant(node)
|
||||
state = state.merge({ document })
|
||||
|
||||
transform.state = state
|
||||
transform.operations.push({
|
||||
type: 'set-mark',
|
||||
index,
|
||||
offset,
|
||||
length,
|
||||
mark,
|
||||
path,
|
||||
@@ -310,3 +311,66 @@ export function setNodeByKey(transform, key, properties) {
|
||||
|
||||
return transform
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a node by `key` at `offset`.
|
||||
*
|
||||
* @param {Transform} transform
|
||||
* @param {String} key
|
||||
* @param {Number} offset
|
||||
* @return {Transform}
|
||||
*/
|
||||
|
||||
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)
|
||||
|
||||
let child = node
|
||||
let one
|
||||
let two
|
||||
|
||||
if (node.kind != 'text') {
|
||||
child = node.getTextAtOffset(offset)
|
||||
}
|
||||
|
||||
while (child && child != parent) {
|
||||
if (child.kind == 'text') {
|
||||
const { characters } = child
|
||||
const oneChars = characters.take(offset)
|
||||
const twoChars = characters.skip(offset)
|
||||
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 })
|
||||
|
||||
transform.state = state
|
||||
transform.operations.push({
|
||||
type: 'split-node',
|
||||
offset,
|
||||
path,
|
||||
})
|
||||
|
||||
return transform
|
||||
}
|
||||
|
||||
|
@@ -56,15 +56,16 @@ import {
|
||||
*/
|
||||
|
||||
import {
|
||||
insertTextByKey,
|
||||
removeTextByKey,
|
||||
addMarkByKey,
|
||||
removeMarkByKey,
|
||||
setMarkByKey,
|
||||
insertNodeAfterNodeByKey,
|
||||
removeNodeByKey,
|
||||
setNodeByKey,
|
||||
insertTextByKey,
|
||||
moveNodeByKey,
|
||||
removeMarkByKey,
|
||||
removeNodeByKey,
|
||||
removeTextByKey,
|
||||
setMarkByKey,
|
||||
setNodeByKey,
|
||||
splitNodeByKey,
|
||||
} from './by-key'
|
||||
|
||||
/**
|
||||
@@ -159,15 +160,16 @@ export default {
|
||||
* By key.
|
||||
*/
|
||||
|
||||
insertTextByKey,
|
||||
removeTextByKey,
|
||||
addMarkByKey,
|
||||
removeMarkByKey,
|
||||
setMarkByKey,
|
||||
insertNodeAfterNodeByKey,
|
||||
removeNodeByKey,
|
||||
setNodeByKey,
|
||||
insertTextByKey,
|
||||
moveNodeByKey,
|
||||
removeMarkByKey,
|
||||
removeNodeByKey,
|
||||
removeTextByKey,
|
||||
setMarkByKey,
|
||||
setNodeByKey,
|
||||
splitNodeByKey,
|
||||
|
||||
/**
|
||||
* On selection.
|
||||
|
Reference in New Issue
Block a user