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:
parent
3ad03538fd
commit
cb204ae8e9
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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 })
|
||||
|
||||
|
@ -57,7 +57,7 @@ import {
|
||||
|
||||
import {
|
||||
addMarkByKey,
|
||||
insertNodeAfterNodeByKey,
|
||||
insertNodeByKey,
|
||||
insertTextByKey,
|
||||
moveNodeByKey,
|
||||
removeMarkByKey,
|
||||
@ -170,7 +170,7 @@ export default {
|
||||
*/
|
||||
|
||||
addMarkByKey,
|
||||
insertNodeAfterNodeByKey,
|
||||
insertNodeByKey,
|
||||
insertTextByKey,
|
||||
moveNodeByKey,
|
||||
removeMarkByKey,
|
||||
|
32
lib/transforms/normalize.js
Normal file
32
lib/transforms/normalize.js
Normal 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
|
||||
}
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user