mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-25 16:20:49 +02:00
refactor transform, simplify selection updating logic
This commit is contained in:
@@ -108,9 +108,7 @@ export function _delete(transform) {
|
|||||||
export function deleteBackward(transform, n = 1) {
|
export function deleteBackward(transform, n = 1) {
|
||||||
const { state } = transform
|
const { state } = transform
|
||||||
const { selection } = state
|
const { selection } = state
|
||||||
transform
|
transform.deleteBackwardAtRange(selection, n)
|
||||||
.deleteBackwardAtRange(selection, n)
|
|
||||||
.collapseToEnd()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,9 +121,7 @@ export function deleteBackward(transform, n = 1) {
|
|||||||
export function deleteForward(transform, n = 1) {
|
export function deleteForward(transform, n = 1) {
|
||||||
const { state } = transform
|
const { state } = transform
|
||||||
const { selection } = state
|
const { selection } = state
|
||||||
transform
|
transform.deleteForwardAtRange(selection, n)
|
||||||
.deleteForwardAtRange(selection, n)
|
|
||||||
.collapseToEnd()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,19 +132,11 @@ export function deleteForward(transform, n = 1) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export function insertBlock(transform, block) {
|
export function insertBlock(transform, block) {
|
||||||
let { state } = transform
|
block = Normalize.block(block)
|
||||||
let { document, selection } = state
|
const { state } = transform
|
||||||
const keys = document.getTexts().map(text => text.key)
|
const { selection } = state
|
||||||
|
|
||||||
transform.unsetSelection()
|
|
||||||
transform.insertBlockAtRange(selection, block)
|
transform.insertBlockAtRange(selection, block)
|
||||||
state = transform.state
|
transform.collapseToEndOf(block)
|
||||||
document = state.document
|
|
||||||
|
|
||||||
const text = document.getTexts().find(n => !keys.includes(n.key))
|
|
||||||
const after = selection.collapseToEndOf(text)
|
|
||||||
|
|
||||||
transform.moveTo(after)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,34 +197,18 @@ export function insertFragment(transform, fragment) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export function insertInline(transform, inline) {
|
export function insertInline(transform, inline) {
|
||||||
let { state } = transform
|
inline = Normalize.inline(inline)
|
||||||
let { document, selection, startText } = state
|
const { state } = transform
|
||||||
let after
|
const { selection, startBlock } = state
|
||||||
|
|
||||||
const hasVoid = document.hasVoidParent(startText.key)
|
|
||||||
const keys = document.getTexts().map(text => text.key)
|
|
||||||
|
|
||||||
transform.unsetSelection()
|
|
||||||
transform.insertInlineAtRange(selection, inline)
|
transform.insertInlineAtRange(selection, inline)
|
||||||
state = transform.state
|
|
||||||
document = state.document
|
|
||||||
|
|
||||||
if (hasVoid) {
|
// If the start block is void, it won't have inserted at all.
|
||||||
after = selection
|
if (startBlock.isVoid) return
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
// Otherwise, find the inserted inline node, and collapse to the start of it.
|
||||||
const text = document.getTexts().find((n) => {
|
const { document } = transform.state
|
||||||
if (keys.includes(n.key)) return false
|
inline = document.assertNode(inline.key)
|
||||||
const parent = document.getParent(n.key)
|
transform.collapseToEndOf(inline)
|
||||||
if (parent.kind != 'inline') return false
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
after = selection.collapseToEndOf(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
transform.moveTo(after)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -4,6 +4,16 @@ import Normalize from '../utils/normalize'
|
|||||||
import SCHEMA from '../schemas/core'
|
import SCHEMA from '../schemas/core'
|
||||||
import { List } from 'immutable'
|
import { List } from 'immutable'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An options object with normalize set to `false`.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const OPTS = {
|
||||||
|
normalize: false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new `mark` to the characters at `range`.
|
* Add a new `mark` to the characters at `range`.
|
||||||
*
|
*
|
||||||
@@ -68,8 +78,8 @@ export function deleteAtRange(transform, range, options = {}) {
|
|||||||
const startOff = (startChild.kind == 'text' ? 0 : startChild.getOffset(startKey)) + startOffset
|
const startOff = (startChild.kind == 'text' ? 0 : startChild.getOffset(startKey)) + startOffset
|
||||||
const endOff = (endChild.kind == 'text' ? 0 : endChild.getOffset(endKey)) + endOffset
|
const endOff = (endChild.kind == 'text' ? 0 : endChild.getOffset(endKey)) + endOffset
|
||||||
|
|
||||||
transform.splitNodeByKey(startChild.key, startOff, { normalize: false })
|
transform.splitNodeByKey(startChild.key, startOff, OPTS)
|
||||||
transform.splitNodeByKey(endChild.key, endOff, { normalize: false })
|
transform.splitNodeByKey(endChild.key, endOff, OPTS)
|
||||||
|
|
||||||
state = transform.state
|
state = transform.state
|
||||||
document = state.document
|
document = state.document
|
||||||
@@ -87,7 +97,7 @@ export function deleteAtRange(transform, range, options = {}) {
|
|||||||
if (middles.size) {
|
if (middles.size) {
|
||||||
// remove first nodes directly so the document is not normalized
|
// remove first nodes directly so the document is not normalized
|
||||||
middles.forEach(child => {
|
middles.forEach(child => {
|
||||||
transform.removeNodeByKey(child.key, { normalize: false })
|
transform.removeNodeByKey(child.key, OPTS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,11 +105,11 @@ export function deleteAtRange(transform, range, options = {}) {
|
|||||||
endBlock.nodes.forEach((child, i) => {
|
endBlock.nodes.forEach((child, i) => {
|
||||||
const newKey = startBlock.key
|
const newKey = startBlock.key
|
||||||
const newIndex = startBlock.nodes.size + i
|
const newIndex = startBlock.nodes.size + i
|
||||||
transform.moveNodeByKey(child.key, newKey, newIndex, { normalize: false })
|
transform.moveNodeByKey(child.key, newKey, newIndex, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
const lonely = document.getFurthest(endBlock.key, p => p.nodes.size == 1) || endBlock
|
const lonely = document.getFurthest(endBlock.key, p => p.nodes.size == 1) || endBlock
|
||||||
transform.removeNodeByKey(lonely.key, { normalize: false })
|
transform.removeNodeByKey(lonely.key, OPTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalize) {
|
if (normalize) {
|
||||||
@@ -316,7 +326,7 @@ export function insertFragmentAtRange(transform, range, fragment, options = {})
|
|||||||
|
|
||||||
// If the range is expanded, delete it first.
|
// If the range is expanded, delete it first.
|
||||||
if (range.isExpanded) {
|
if (range.isExpanded) {
|
||||||
transform.deleteAtRange(range, { normalize: false })
|
transform.deleteAtRange(range, OPTS)
|
||||||
range = range.collapseToStart()
|
range = range.collapseToStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,13 +367,13 @@ export function insertFragmentAtRange(transform, range, fragment, options = {})
|
|||||||
|
|
||||||
fragment.nodes.forEach((node, i) => {
|
fragment.nodes.forEach((node, i) => {
|
||||||
const newIndex = startIndex + i + 1
|
const newIndex = startIndex + i + 1
|
||||||
transform.insertNodeByKey(parent.key, newIndex, node, { normalize: false })
|
transform.insertNodeByKey(parent.key, newIndex, node, OPTS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to split the node.
|
// Check if we need to split the node.
|
||||||
if (startOffset != 0) {
|
if (startOffset != 0) {
|
||||||
transform.splitNodeByKey(startChild.key, offset, { normalize: false })
|
transform.splitNodeByKey(startChild.key, offset, OPTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our variables with the new state.
|
// Update our variables with the new state.
|
||||||
@@ -383,15 +393,15 @@ export function insertFragmentAtRange(transform, range, fragment, options = {})
|
|||||||
|
|
||||||
nextNodes.forEach((node, i) => {
|
nextNodes.forEach((node, i) => {
|
||||||
const newIndex = lastIndex + i
|
const newIndex = lastIndex + i
|
||||||
transform.moveNodeByKey(node.key, lastBlock.key, newIndex, { normalize: false })
|
transform.moveNodeByKey(node.key, lastBlock.key, newIndex, OPTS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the starting block is empty, we replace it entirely with the first block
|
// If the starting block is empty, we replace it entirely with the first block
|
||||||
// of the fragment, since this leads to a more expected behavior for the user.
|
// of the fragment, since this leads to a more expected behavior for the user.
|
||||||
if (startBlock.isEmpty) {
|
if (startBlock.isEmpty) {
|
||||||
transform.removeNodeByKey(startBlock.key, { normalize: false })
|
transform.removeNodeByKey(startBlock.key, OPTS)
|
||||||
transform.insertNodeByKey(parent.key, index, firstBlock, { normalize: false })
|
transform.insertNodeByKey(parent.key, index, firstBlock, OPTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we maintain the starting block, and insert all of the first
|
// Otherwise, we maintain the starting block, and insert all of the first
|
||||||
@@ -403,7 +413,7 @@ export function insertFragmentAtRange(transform, range, fragment, options = {})
|
|||||||
firstBlock.nodes.forEach((inline, i) => {
|
firstBlock.nodes.forEach((inline, i) => {
|
||||||
const o = startOffset == 0 ? 0 : 1
|
const o = startOffset == 0 ? 0 : 1
|
||||||
const newIndex = inlineIndex + i + o
|
const newIndex = inlineIndex + i + o
|
||||||
transform.insertNodeByKey(startBlock.key, newIndex, inline, { normalize: false })
|
transform.insertNodeByKey(startBlock.key, newIndex, inline, OPTS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,7 +438,7 @@ export function insertInlineAtRange(transform, range, inline, options = {}) {
|
|||||||
inline = Normalize.inline(inline)
|
inline = Normalize.inline(inline)
|
||||||
|
|
||||||
if (range.isExpanded) {
|
if (range.isExpanded) {
|
||||||
transform.deleteAtRange(range, { normalize: false })
|
transform.deleteAtRange(range, OPTS)
|
||||||
range = range.collapseToStart()
|
range = range.collapseToStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,8 +451,8 @@ export function insertInlineAtRange(transform, range, inline, options = {}) {
|
|||||||
|
|
||||||
if (parent.isVoid) return
|
if (parent.isVoid) return
|
||||||
|
|
||||||
transform.splitNodeByKey(startKey, startOffset, { normalize: false })
|
transform.splitNodeByKey(startKey, startOffset, OPTS)
|
||||||
transform.insertNodeByKey(parent.key, index + 1, inline, { normalize: false })
|
transform.insertNodeByKey(parent.key, index + 1, inline, OPTS)
|
||||||
|
|
||||||
if (normalize) {
|
if (normalize) {
|
||||||
transform.normalizeNodeByKey(parent.key, SCHEMA)
|
transform.normalizeNodeByKey(parent.key, SCHEMA)
|
||||||
@@ -470,7 +480,7 @@ export function insertTextAtRange(transform, range, text, marks, options = {}) {
|
|||||||
if (parent.isVoid) return
|
if (parent.isVoid) return
|
||||||
|
|
||||||
if (range.isExpanded) {
|
if (range.isExpanded) {
|
||||||
transform.deleteAtRange(range, { normalize: false })
|
transform.deleteAtRange(range, OPTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PERF: Unless specified, don't normalize if only inserting text.
|
// PERF: Unless specified, don't normalize if only inserting text.
|
||||||
@@ -702,17 +712,17 @@ export function unwrapBlockAtRange(transform, range, properties, options = {}) {
|
|||||||
|
|
||||||
if (first == firstMatch && last == lastMatch) {
|
if (first == firstMatch && last == lastMatch) {
|
||||||
block.nodes.forEach((child, i) => {
|
block.nodes.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, parent.key, index + i, { normalize: false })
|
transform.moveNodeByKey(child.key, parent.key, index + i, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
transform.removeNodeByKey(block.key, { normalize: false })
|
transform.removeNodeByKey(block.key, OPTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (last == lastMatch) {
|
else if (last == lastMatch) {
|
||||||
block.nodes
|
block.nodes
|
||||||
.skipUntil(n => n == firstMatch)
|
.skipUntil(n => n == firstMatch)
|
||||||
.forEach((child, i) => {
|
.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, parent.key, index + 1 + i, { normalize: false })
|
transform.moveNodeByKey(child.key, parent.key, index + 1 + i, OPTS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,23 +731,23 @@ export function unwrapBlockAtRange(transform, range, properties, options = {}) {
|
|||||||
.takeUntil(n => n == lastMatch)
|
.takeUntil(n => n == lastMatch)
|
||||||
.push(lastMatch)
|
.push(lastMatch)
|
||||||
.forEach((child, i) => {
|
.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, parent.key, index + i, { normalize: false })
|
transform.moveNodeByKey(child.key, parent.key, index + i, OPTS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
const offset = block.getOffset(firstMatch.key)
|
const offset = block.getOffset(firstMatch.key)
|
||||||
|
|
||||||
transform.splitNodeByKey(block.key, offset, { normalize: false })
|
transform.splitNodeByKey(block.key, offset, OPTS)
|
||||||
state = transform.state
|
state = transform.state
|
||||||
document = state.document
|
document = state.document
|
||||||
const extra = document.getPreviousSibling(firstMatch.key)
|
const extra = document.getPreviousSibling(firstMatch.key)
|
||||||
|
|
||||||
children.forEach((child, i) => {
|
children.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, parent.key, index + 1 + i, { normalize: false })
|
transform.moveNodeByKey(child.key, parent.key, index + 1 + i, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
transform.removeNodeByKey(extra.key, { normalize: false })
|
transform.removeNodeByKey(extra.key, OPTS)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -783,7 +793,7 @@ export function unwrapInlineAtRange(transform, range, properties, options = {})
|
|||||||
const index = parent.nodes.indexOf(inline)
|
const index = parent.nodes.indexOf(inline)
|
||||||
|
|
||||||
inline.nodes.forEach((child, i) => {
|
inline.nodes.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, parent.key, index + i, { normalize: false })
|
transform.moveNodeByKey(child.key, parent.key, index + i, OPTS)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -850,11 +860,11 @@ export function wrapBlockAtRange(transform, range, block, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// inject the new block node into the parent
|
// inject the new block node into the parent
|
||||||
transform.insertNodeByKey(parent.key, index, block, { normalize: false })
|
transform.insertNodeByKey(parent.key, index, block, OPTS)
|
||||||
|
|
||||||
// move the sibling nodes into the new block node
|
// move the sibling nodes into the new block node
|
||||||
siblings.forEach((node, i) => {
|
siblings.forEach((node, i) => {
|
||||||
transform.moveNodeByKey(node.key, block.key, i, { normalize: false })
|
transform.moveNodeByKey(node.key, block.key, i, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (normalize) {
|
if (normalize) {
|
||||||
@@ -900,11 +910,11 @@ export function wrapInlineAtRange(transform, range, inline, options = {}) {
|
|||||||
|
|
||||||
if (startBlock == endBlock) {
|
if (startBlock == endBlock) {
|
||||||
if (endOff != endChild.length) {
|
if (endOff != endChild.length) {
|
||||||
transform.splitNodeByKey(endChild.key, endOff, { normalize: false })
|
transform.splitNodeByKey(endChild.key, endOff, OPTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startOff != 0) {
|
if (startOff != 0) {
|
||||||
transform.splitNodeByKey(startChild.key, startOff, { normalize: false })
|
transform.splitNodeByKey(startChild.key, startOff, OPTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
state = transform.state
|
state = transform.state
|
||||||
@@ -926,10 +936,10 @@ export function wrapInlineAtRange(transform, range, inline, options = {}) {
|
|||||||
|
|
||||||
const node = inline.regenerateKey()
|
const node = inline.regenerateKey()
|
||||||
|
|
||||||
transform.insertNodeByKey(startBlock.key, startInnerIndex, node, { normalize: false })
|
transform.insertNodeByKey(startBlock.key, startInnerIndex, node, OPTS)
|
||||||
|
|
||||||
inlines.forEach((child, i) => {
|
inlines.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, node.key, i, { normalize: false })
|
transform.moveNodeByKey(child.key, node.key, i, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (normalize) {
|
if (normalize) {
|
||||||
@@ -938,8 +948,8 @@ export function wrapInlineAtRange(transform, range, inline, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
transform.splitNodeByKey(startChild.key, startOff, { normalize: false })
|
transform.splitNodeByKey(startChild.key, startOff, OPTS)
|
||||||
transform.splitNodeByKey(endChild.key, endOff, { normalize: false })
|
transform.splitNodeByKey(endChild.key, endOff, OPTS)
|
||||||
|
|
||||||
state = transform.state
|
state = transform.state
|
||||||
document = state.document
|
document = state.document
|
||||||
@@ -951,15 +961,15 @@ export function wrapInlineAtRange(transform, range, inline, options = {}) {
|
|||||||
const startNode = inline.regenerateKey()
|
const startNode = inline.regenerateKey()
|
||||||
const endNode = inline.regenerateKey()
|
const endNode = inline.regenerateKey()
|
||||||
|
|
||||||
transform.insertNodeByKey(startBlock.key, startIndex - 1, startNode, { normalize: false })
|
transform.insertNodeByKey(startBlock.key, startIndex - 1, startNode, OPTS)
|
||||||
transform.insertNodeByKey(endBlock.key, endIndex, endNode, { normalize: false })
|
transform.insertNodeByKey(endBlock.key, endIndex, endNode, OPTS)
|
||||||
|
|
||||||
startInlines.forEach((child, i) => {
|
startInlines.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, startNode.key, i, { normalize: false })
|
transform.moveNodeByKey(child.key, startNode.key, i, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
endInlines.forEach((child, i) => {
|
endInlines.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, endNode.key, i, { normalize: false })
|
transform.moveNodeByKey(child.key, endNode.key, i, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (normalize) {
|
if (normalize) {
|
||||||
@@ -970,10 +980,10 @@ export function wrapInlineAtRange(transform, range, inline, options = {}) {
|
|||||||
|
|
||||||
blocks.slice(1, -1).forEach((block) => {
|
blocks.slice(1, -1).forEach((block) => {
|
||||||
const node = inline.regenerateKey()
|
const node = inline.regenerateKey()
|
||||||
transform.insertNodeByKey(block.key, 0, node, { normalize: false })
|
transform.insertNodeByKey(block.key, 0, node, OPTS)
|
||||||
|
|
||||||
block.nodes.forEach((child, i) => {
|
block.nodes.forEach((child, i) => {
|
||||||
transform.moveNodeByKey(child.key, node.key, i, { normalize: false })
|
transform.moveNodeByKey(child.key, node.key, i, OPTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (normalize) {
|
if (normalize) {
|
||||||
|
Reference in New Issue
Block a user