1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-20 14:11:35 +02:00

refactor more transforms

This commit is contained in:
Ian Storm Taylor
2016-08-17 02:19:13 -07:00
parent 3ad03538fd
commit cb204ae8e9
7 changed files with 80 additions and 134 deletions

View File

@@ -12,6 +12,7 @@ import './inline'
import Block from './block' import Block from './block'
import Node from './node' import Node from './node'
import uid from '../utils/uid'
import { OrderedMap, Record } from 'immutable' import { OrderedMap, Record } from 'immutable'
/** /**
@@ -19,7 +20,8 @@ import { OrderedMap, Record } from 'immutable'
*/ */
const DEFAULTS = { const DEFAULTS = {
nodes: new OrderedMap() key: null,
nodes: new OrderedMap(),
} }
/** /**
@@ -37,7 +39,10 @@ class Document extends new Record(DEFAULTS) {
static create(properties = {}) { static create(properties = {}) {
if (properties instanceof Document) return properties if (properties instanceof Document) return properties
properties.key = properties.key || uid(4)
properties.nodes = Block.createList(properties.nodes) properties.nodes = Block.createList(properties.nodes)
return new Document(properties).normalize() return new Document(properties).normalize()
} }

View File

@@ -765,6 +765,11 @@ const Node = {
*/ */
getPath(key) { getPath(key) {
debugger
key = Normalize.key(key)
if (key == this.key) return []
let child = this.assertDescendant(key) let child = this.assertDescendant(key)
let path = [] let path = []
let parent let parent

View File

@@ -274,72 +274,44 @@ export function deleteForwardAtRange(transform, range, n = 1) {
*/ */
export function insertBlockAtRange(transform, range, block) { export function insertBlockAtRange(transform, range, block) {
let { state } = transform
let { document } = state
// Normalize the block argument.
block = Normalize.block(block) block = Normalize.block(block)
// If expanded, delete the range first.
if (range.isExpanded) { if (range.isExpanded) {
transform = deleteAtRange(transform, range) transform.deleteAtRange(range)
state = transform.state
document = state.document
range = range.collapseToStart() range = range.collapseToStart()
} }
const { state } = transform
const { document } = state
const { startKey, startOffset } = range const { startKey, startOffset } = range
let startBlock = document.getClosestBlock(startKey) const startText = document.assertDescendant(startKey)
let parent = document.getParent(startBlock) const startBlock = document.getClosestBlock(startKey)
let nodes = Block.createList([block]) const parent = document.getParent(startBlock)
const isParent = parent == document const index = parent.nodes.indexOf(startBlock)
// If the start block is void, insert after it.
if (startBlock.isVoid) { if (startBlock.isVoid) {
parent = parent.insertChildrenAfter(startBlock, nodes) transform.insertNodeByKey(parent.key, index + 1, block)
} }
// If the block is empty, replace it.
else if (startBlock.isEmpty) { else if (startBlock.isEmpty) {
parent = parent.insertChildrenAfter(startBlock, nodes) transform.removeNodeByKey(startBlock.key)
parent = parent.removeDescendant(startBlock) transform.insertNodeByKey(parent.key, index, block)
} }
// If the range is at the start of the block, insert before.
else if (range.isAtStartOf(startBlock)) { else if (range.isAtStartOf(startBlock)) {
parent = parent.insertChildrenBefore(startBlock, nodes) transform.insertNodeByKey(parent.key, index, block)
} }
// If the range is at the end of the block, insert after.
else if (range.isAtEndOf(startBlock)) { else if (range.isAtEndOf(startBlock)) {
parent = parent.insertChildrenAfter(startBlock, nodes) transform.insertNodeByKey(parent.key, index + 1, block)
} }
// Otherwise, split the block and insert between.
else { else {
transform = splitBlockAtRange(transform, range) const offset = startBlock.getOffset(startText) + startOffset
state = transform.state transform.splitNodeByKey(startBlock.key, offset)
document = state.document transform.insertNodeByKey(parent.key, index + 1, block)
parent = document.getParent(startBlock)
startBlock = document.getClosestBlock(startKey)
nodes = parent.nodes.takeUntil(n => n == startBlock)
.push(startBlock)
.push(block)
.concat(parent.nodes.skipUntil(n => n == startBlock).rest())
parent = parent.merge({ nodes })
} }
// Update the document.
document = isParent
? parent
: document.updateDescendant(parent)
// Normalize the document.
document = document.normalize()
// Return the updated state.
state = state.merge({ document })
transform.state = state
return transform return transform
} }
@@ -467,47 +439,26 @@ export function insertFragmentAtRange(transform, range, fragment) {
*/ */
export function insertInlineAtRange(transform, range, inline) { export function insertInlineAtRange(transform, range, inline) {
let { state } = transform
let { document } = state
// Normalize the inline argument.
inline = Normalize.inline(inline) inline = Normalize.inline(inline)
// If expanded, delete the range first.
if (range.isExpanded) { if (range.isExpanded) {
transform = deleteAtRange(transform, range) transform.deleteAtRange(range)
state = transform.state
document = state.document
range = range.collapseToStart() range = range.collapseToStart()
} }
const { startKey, endKey, startOffset, endOffset } = range const { state } = transform
const { document } = state
const { startKey, startOffset } = range
const parent = document.getParent(startKey)
const startText = document.assertDescendant(startKey)
const index = parent.nodes.indexOf(startText)
// If the range is inside a void, abort. if (parent.isVoid) {
const startBlock = document.getClosestBlock(startKey) return transform
if (startBlock && startBlock.isVoid) return transform }
const startInline = document.getClosestInline(startKey) transform.splitNodeByKey(startKey, startOffset)
if (startInline && startInline.isVoid) return transform transform.insertNodeByKey(parent.key, index + 1, inline)
// Split the text nodes at the cursor.
transform = splitTextAtRange(transform, range)
state = transform.state
document = state.document
// Insert the inline between the split text nodes.
const startText = document.getDescendant(startKey)
let parent = document.getParent(startKey)
const nodes = parent.nodes.takeUntil(n => n == startText)
.push(startText)
.push(inline)
.concat(parent.nodes.skipUntil(n => n == startText).rest())
parent = parent.merge({ nodes })
document = document.updateDescendant(parent)
document = document.normalize()
state = state.merge({ document })
transform.state = state
return transform return transform
} }
@@ -525,9 +476,9 @@ export function insertTextAtRange(transform, range, text, marks) {
const { state } = transform const { state } = transform
const { document } = state const { document } = state
const { startKey, startOffset } = range const { startKey, startOffset } = range
const isVoid = document.hasVoidParent(startKey) const parent = document.getParent(startKey)
if (isVoid) { if (parent.isVoid) {
return transform return transform
} }
@@ -681,51 +632,6 @@ export function splitInlineAtRange(transform, range, height = Infinity) {
return transform.splitNodeByKey(node.key, offset) return transform.splitNodeByKey(node.key, offset)
} }
/**
* Split the text nodes at a `range`.
*
* @param {Transform} transform
* @param {Selection} range
* @return {Transform}
*/
export function splitTextAtRange(transform, range) {
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
range = range.collapseToStart()
}
// Split the text node's characters.
const { startKey, startOffset } = range
const text = document.getDescendant(startKey)
const { characters } = text
const firstChars = characters.take(startOffset)
const secondChars = characters.skip(startOffset)
let firstChild = text.merge({ characters: firstChars })
let secondChild = Text.create({ characters: secondChars })
// Split the text nodes.
let parent = document.getParent(text)
const nodes = parent.nodes
.takeUntil(c => c.key == firstChild.key)
.push(firstChild)
.push(secondChild)
.concat(parent.nodes.skipUntil(n => n.key == firstChild.key).rest())
// Update the nodes.
parent = parent.merge({ nodes })
document = document.updateDescendant(parent)
state = state.merge({ document })
transform.state = state
return transform
}
/** /**
* Add or remove a `mark` from the characters at `range`, depending on whether * Add or remove a `mark` from the characters at `range`, depending on whether
* it's already there. * it's already there.
@@ -1048,12 +954,11 @@ export function wrapInlineAtRange(transform, range, properties) {
* @param {Transform} transform * @param {Transform} transform
* @param {Selection} range * @param {Selection} range
* @param {String} prefix * @param {String} prefix
* @param {String} suffix * @param {String} suffix (optional)
* @return {Transform} * @return {Transform}
*/ */
export function wrapTextAtRange(transform, range, prefix, suffix = prefix) { export function wrapTextAtRange(transform, range, prefix, suffix = prefix) {
const { state } = transform
const { startKey, endKey } = range const { startKey, endKey } = range
const start = range.collapseToStart() const start = range.collapseToStart()
let end = range.collapseToEnd() let end = range.collapseToEnd()

View File

@@ -50,12 +50,13 @@ export function addMarkByKey(transform, key, offset, length, mark) {
export function insertNodeByKey(transform, key, index, node) { export function insertNodeByKey(transform, key, index, node) {
let { state } = transform let { state } = transform
let { document } = state let { document } = state
let parent = document.assertDescendant(key) let parent = document.key == key ? document : document.assertDescendant(key)
const isParent = document == parent
const path = document.getPath(parent) const path = document.getPath(parent)
const nodes = parent.nodes.splice(index + 1, 0, node) const nodes = parent.nodes.splice(index, 0, node)
parent = parent.merge({ nodes }) parent = parent.merge({ nodes })
document = document.updateDescendant(parent) document = isParent ? parent : document.updateDescendant(parent)
document = document.normalize() document = document.normalize()
state = state.merge({ document }) state = state.merge({ document })

View File

@@ -57,7 +57,7 @@ import {
import { import {
addMarkByKey, addMarkByKey,
insertNodeAfterNodeByKey, insertNodeByKey,
insertTextByKey, insertTextByKey,
moveNodeByKey, moveNodeByKey,
removeMarkByKey, removeMarkByKey,
@@ -170,7 +170,7 @@ export default {
*/ */
addMarkByKey, addMarkByKey,
insertNodeAfterNodeByKey, insertNodeByKey,
insertTextByKey, insertTextByKey,
moveNodeByKey, moveNodeByKey,
removeMarkByKey, removeMarkByKey,

View File

@@ -0,0 +1,32 @@
/**
* Normalize the document.
*
* @param {Transform} transform
* @return {Transform}
*/
export function normalizeDocument(transform) {
let { state } = transform
let { document } = state
document = document.normalize()
state = state.merge({ document })
transform.state = state
return transform
}
/**
* Normalize the selection.
*
* @param {Transform} transform
* @return {Transform}
*/
export function normalizeSelection(transform) {
let { state } = transform
let { document, selection } = state
selection = selection.normalize(document)
state = state.merge({ selection })
transform.state = state
return transform
}

View File

@@ -60,12 +60,10 @@ function inline(value) {
function key(value) { function key(value) {
if (value instanceof Block) return value.key if (value instanceof Block) return value.key
if (value instanceof Document) return value.key
if (value instanceof Inline) return value.key if (value instanceof Inline) return value.key
if (value instanceof Text) return value.key if (value instanceof Text) return value.key
// Special-case document for now.
if (value instanceof Document) return
const type = typeOf(value) const type = typeOf(value)
if (type == 'string') return value if (type == 'string') return value