1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-24 01:02:31 +01: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 Node from './node'
import uid from '../utils/uid'
import { OrderedMap, Record } from 'immutable'
/**
@ -19,7 +20,8 @@ import { OrderedMap, Record } from 'immutable'
*/
const DEFAULTS = {
nodes: new OrderedMap()
key: null,
nodes: new OrderedMap(),
}
/**
@ -37,7 +39,10 @@ class Document extends new Record(DEFAULTS) {
static create(properties = {}) {
if (properties instanceof Document) return properties
properties.key = properties.key || uid(4)
properties.nodes = Block.createList(properties.nodes)
return new Document(properties).normalize()
}

View File

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

View File

@ -274,72 +274,44 @@ export function deleteForwardAtRange(transform, range, n = 1) {
*/
export function insertBlockAtRange(transform, range, block) {
let { state } = transform
let { document } = state
// Normalize the block argument.
block = Normalize.block(block)
// If expanded, delete the range first.
if (range.isExpanded) {
transform = deleteAtRange(transform, range)
state = transform.state
document = state.document
transform.deleteAtRange(range)
range = range.collapseToStart()
}
const { state } = transform
const { document } = state
const { startKey, startOffset } = range
let startBlock = document.getClosestBlock(startKey)
let parent = document.getParent(startBlock)
let nodes = Block.createList([block])
const isParent = parent == document
const startText = document.assertDescendant(startKey)
const startBlock = document.getClosestBlock(startKey)
const parent = document.getParent(startBlock)
const index = parent.nodes.indexOf(startBlock)
// If the start block is void, insert after it.
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) {
parent = parent.insertChildrenAfter(startBlock, nodes)
parent = parent.removeDescendant(startBlock)
transform.removeNodeByKey(startBlock.key)
transform.insertNodeByKey(parent.key, index, block)
}
// If the range is at the start of the block, insert before.
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)) {
parent = parent.insertChildrenAfter(startBlock, nodes)
transform.insertNodeByKey(parent.key, index + 1, block)
}
// Otherwise, split the block and insert between.
else {
transform = splitBlockAtRange(transform, range)
state = transform.state
document = state.document
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 })
const offset = startBlock.getOffset(startText) + startOffset
transform.splitNodeByKey(startBlock.key, offset)
transform.insertNodeByKey(parent.key, index + 1, block)
}
// 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
}
@ -467,47 +439,26 @@ export function insertFragmentAtRange(transform, range, fragment) {
*/
export function insertInlineAtRange(transform, range, inline) {
let { state } = transform
let { document } = state
// Normalize the inline argument.
inline = Normalize.inline(inline)
// If expanded, delete the range first.
if (range.isExpanded) {
transform = deleteAtRange(transform, range)
state = transform.state
document = state.document
transform.deleteAtRange(range)
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.
const startBlock = document.getClosestBlock(startKey)
if (startBlock && startBlock.isVoid) return transform
if (parent.isVoid) {
return transform
}
const startInline = document.getClosestInline(startKey)
if (startInline && startInline.isVoid) return transform
// 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
transform.splitNodeByKey(startKey, startOffset)
transform.insertNodeByKey(parent.key, index + 1, inline)
return transform
}
@ -525,9 +476,9 @@ export function insertTextAtRange(transform, range, text, marks) {
const { state } = transform
const { document } = state
const { startKey, startOffset } = range
const isVoid = document.hasVoidParent(startKey)
const parent = document.getParent(startKey)
if (isVoid) {
if (parent.isVoid) {
return transform
}
@ -681,51 +632,6 @@ export function splitInlineAtRange(transform, range, height = Infinity) {
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
* it's already there.
@ -1048,12 +954,11 @@ export function wrapInlineAtRange(transform, range, properties) {
* @param {Transform} transform
* @param {Selection} range
* @param {String} prefix
* @param {String} suffix
* @param {String} suffix (optional)
* @return {Transform}
*/
export function wrapTextAtRange(transform, range, prefix, suffix = prefix) {
const { state } = transform
const { startKey, endKey } = range
const start = range.collapseToStart()
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) {
let { state } = transform
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 nodes = parent.nodes.splice(index + 1, 0, node)
const nodes = parent.nodes.splice(index, 0, node)
parent = parent.merge({ nodes })
document = document.updateDescendant(parent)
document = isParent ? parent : document.updateDescendant(parent)
document = document.normalize()
state = state.merge({ document })

View File

@ -57,7 +57,7 @@ import {
import {
addMarkByKey,
insertNodeAfterNodeByKey,
insertNodeByKey,
insertTextByKey,
moveNodeByKey,
removeMarkByKey,
@ -170,7 +170,7 @@ export default {
*/
addMarkByKey,
insertNodeAfterNodeByKey,
insertNodeByKey,
insertTextByKey,
moveNodeByKey,
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) {
if (value instanceof Block) return value.key
if (value instanceof Document) return value.key
if (value instanceof Inline) return value.key
if (value instanceof Text) return value.key
// Special-case document for now.
if (value instanceof Document) return
const type = typeOf(value)
if (type == 'string') return value