1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-31 02:49:56 +02:00

distinguish between block and inline nodes

This commit is contained in:
Ian Storm Taylor
2016-06-21 16:44:11 -07:00
parent dbdf3760e9
commit c3257a37d4
13 changed files with 286 additions and 121 deletions

View File

@@ -186,7 +186,8 @@ class App extends React.Component {
onBackspace(e, state) { onBackspace(e, state) {
if (state.isCurrentlyExpanded) return if (state.isCurrentlyExpanded) return
if (state.currentStartOffset != 0) return if (state.currentStartOffset != 0) return
const node = state.currentWrappingNodes.first() const node = state.currentBlockNodes.first()
if (!node) debugger
if (node.type == 'paragraph') return if (node.type == 'paragraph') return
e.preventDefault() e.preventDefault()
@@ -207,7 +208,8 @@ class App extends React.Component {
onEnter(e, state) { onEnter(e, state) {
if (state.isCurrentlyExpanded) return if (state.isCurrentlyExpanded) return
const node = state.currentWrappingNodes.first() const node = state.currentBlockNodes.first()
if (!node) debugger
if (state.currentStartOffset == 0 && node.length == 0) return this.onBackspace(e, state) if (state.currentStartOffset == 0 && node.length == 0) return this.onBackspace(e, state)
if (state.currentEndOffset != node.length) return if (state.currentEndOffset != node.length) return

View File

@@ -1,10 +1,11 @@
{ {
"nodes": [ "nodes": [
{ {
"kind": "block",
"type": "paragraph", "type": "paragraph",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "The editor gives you full control over the logic you can add. For example, it's fairly common to want to add markdown-like shortcuts to editors. So that, when you start a line with \"> \" you get a blockquote that looks like this:" "text": "The editor gives you full control over the logic you can add. For example, it's fairly common to want to add markdown-like shortcuts to editors. So that, when you start a line with \"> \" you get a blockquote that looks like this:"
@@ -14,10 +15,11 @@
] ]
}, },
{ {
"kind": "block",
"type": "block-quote", "type": "block-quote",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "A wise quote." "text": "A wise quote."
@@ -27,10 +29,11 @@
] ]
}, },
{ {
"kind": "block",
"type": "paragraph", "type": "paragraph",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "Order when you start a line with \"## \" you get a level-two heading, like this:" "text": "Order when you start a line with \"## \" you get a level-two heading, like this:"
@@ -40,10 +43,11 @@
] ]
}, },
{ {
"kind": "block",
"type": "heading-two", "type": "heading-two",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "Try it out!" "text": "Try it out!"
@@ -53,10 +57,11 @@
] ]
}, },
{ {
"kind": "block",
"type": "paragraph", "type": "paragraph",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "Try it out for yourself! Try starting a new line with \">\", \"-\", or \"#\"s." "text": "Try it out for yourself! Try starting a new line with \">\", \"-\", or \"#\"s."

View File

@@ -1,5 +1,5 @@
import Editor, { Character, Document, Element, State, Text } from '../..' import Editor, { Character, Document, Block, State, Text } from '../..'
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import state from './state.json' import state from './state.json'
@@ -19,13 +19,13 @@ function deserialize(string) {
}, Character.createList()) }, Character.createList())
const text = Text.create({ characters }) const text = Text.create({ characters })
const texts = Element.createMap([text]) const texts = Block.createMap([text])
const node = Element.create({ const node = Block.create({
type: 'paragraph', type: 'paragraph',
nodes: texts, nodes: texts,
}) })
const nodes = Element.createMap([node]) const nodes = Block.createMap([node])
const document = Document.create({ nodes }) const document = Document.create({ nodes })
const state = State.create({ document }) const state = State.create({ document })
return state return state

View File

@@ -1,10 +1,11 @@
{ {
"nodes": [ "nodes": [
{ {
"kind": "block",
"type": "paragraph", "type": "paragraph",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "This is editable " "text": "This is editable "
@@ -47,10 +48,11 @@
] ]
}, },
{ {
"kind": "block",
"type": "paragraph", "type": "paragraph",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "Since it's rich text, you can do things like turn a selection of text ", "text": "Since it's rich text, you can do things like turn a selection of text ",
@@ -70,10 +72,11 @@
] ]
}, },
{ {
"kind": "block",
"type": "block-quote", "type": "block-quote",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "A wise quote." "text": "A wise quote."
@@ -83,10 +86,11 @@
] ]
}, },
{ {
"kind": "block",
"type": "paragraph", "type": "paragraph",
"nodes": [ "nodes": [
{ {
"type": "text", "kind": "text",
"ranges": [ "ranges": [
{ {
"text": "Try it out for yourself!" "text": "Try it out for yourself!"

View File

@@ -10,9 +10,10 @@ export default Editor
* Models. * Models.
*/ */
export { default as Block } from './models/block'
export { default as Character } from './models/character' export { default as Character } from './models/character'
export { default as Element } from './models/element'
export { default as Document } from './models/document' export { default as Document } from './models/document'
export { default as Inline } from './models/inline'
export { default as Mark } from './models/mark' export { default as Mark } from './models/mark'
export { default as Selection } from './models/selection' export { default as Selection } from './models/selection'
export { default as State } from './models/state' export { default as State } from './models/state'

97
lib/models/block.js Normal file
View File

@@ -0,0 +1,97 @@
import Node from './node'
import uid from 'uid'
import { OrderedMap, Record } from 'immutable'
/**
* Default properties.
*/
const DEFAULTS = {
data: new Map(),
key: null,
nodes: new OrderedMap(),
type: null
}
/**
* Block.
*/
class Block extends Record(DEFAULTS) {
/**
* Create a new `Block` with `properties`.
*
* @param {Object} properties
* @return {Block} element
*/
static create(properties = {}) {
if (!properties.type) throw new Error('You must pass a block `type`.')
properties.key = uid(4)
let block = new Block(properties)
return block.normalize()
}
/**
* Create an ordered map of `Blocks` from an array of `Blocks`.
*
* @param {Array} elements
* @return {OrderedMap} map
*/
static createMap(elements = []) {
return elements.reduce((map, element) => {
return map.set(element.key, element)
}, new OrderedMap())
}
/**
* Get the node's kind.
*
* @return {String} kind
*/
get kind() {
return 'block'
}
/**
* Get the length of the concatenated text of the node.
*
* @return {Number} length
*/
get length() {
return this.text.length
}
/**
* Get the concatenated text `string` of all child nodes.
*
* @return {String} text
*/
get text() {
return this.nodes
.map(node => node.text)
.join('')
}
}
/**
* Mix in `Node` methods.
*/
for (const method in Node) {
Block.prototype[method] = Node[method]
}
/**
* Export.
*/
export default Block

View File

@@ -7,7 +7,8 @@ import { OrderedMap, Record } from 'immutable'
*/ */
const DEFAULTS = { const DEFAULTS = {
nodes: new OrderedMap() nodes: new OrderedMap(),
parent: null
} }
/** /**
@@ -24,7 +25,18 @@ class Document extends Record(DEFAULTS) {
*/ */
static create(properties = {}) { static create(properties = {}) {
return new Document(properties) let document = new Document(properties)
return document.normalize()
}
/**
* Get the node's kind.
*
* @return {String} kind
*/
get kind() {
return 'document'
} }
/** /**
@@ -49,16 +61,6 @@ class Document extends Record(DEFAULTS) {
.join('') .join('')
} }
/**
* Type.
*
* @return {String} type
*/
get type() {
return 'document'
}
} }
/** /**

View File

@@ -15,26 +15,27 @@ const DEFAULTS = {
} }
/** /**
* Element. * Inline.
*/ */
class Element extends Record(DEFAULTS) { class Inline extends Record(DEFAULTS) {
/** /**
* Create a new `Element` with `properties`. * Create a new `Inline` with `properties`.
* *
* @param {Object} properties * @param {Object} properties
* @return {Element} element * @return {Inline} element
*/ */
static create(properties = {}) { static create(properties = {}) {
if (!properties.type) throw new Error('You must pass an element `type`.') if (!properties.type) throw new Error('You must pass an inline `type`.')
properties.key = uid(4) properties.key = uid(4)
return new Element(properties) let inline = new Inline(properties)
return inline.normalize()
} }
/** /**
* Create an ordered map of `Elements` from an array of `Elements`. * Create an ordered map of `Inlines` from an array of `Inlines`.
* *
* @param {Array} elements * @param {Array} elements
* @return {OrderedMap} map * @return {OrderedMap} map
@@ -46,6 +47,16 @@ class Element extends Record(DEFAULTS) {
}, new OrderedMap()) }, new OrderedMap())
} }
/**
* Get the node's kind.
*
* @return {String} kind
*/
get kind() {
return 'inline'
}
/** /**
* Get the length of the concatenated text of the node. * Get the length of the concatenated text of the node.
* *
@@ -75,7 +86,7 @@ class Element extends Record(DEFAULTS) {
*/ */
for (const method in Node) { for (const method in Node) {
Element.prototype[method] = Node[method] Inline.prototype[method] = Node[method]
} }
@@ -83,4 +94,4 @@ for (const method in Node) {
* Export. * Export.
*/ */
export default Element export default Inline

View File

@@ -1,6 +1,6 @@
import Block from './block'
import Character from './character' import Character from './character'
import Element from './element'
import Mark from './mark' import Mark from './mark'
import Selection from './selection' import Selection from './selection'
import Text from './text' import Text from './text'
@@ -9,8 +9,8 @@ import { List, OrderedMap, OrderedSet, Set } from 'immutable'
/** /**
* Node. * Node.
* *
* And interface that `Document` and `Element` both implement, to make working * And interface that `Document`, `Block` and `Inline` all implement, to make
* recursively easier with the tree easier. * working with the recursive node tree easier.
*/ */
const Node = { const Node = {
@@ -202,7 +202,7 @@ const Node = {
if (shallow != null) return shallow if (shallow != null) return shallow
return this.nodes return this.nodes
.map(node => node.type == 'text' ? null : node.findNode(iterator)) .map(node => node.kind == 'text' ? null : node.findNode(iterator))
.filter(node => node) .filter(node => node)
.first() .first()
}, },
@@ -217,7 +217,7 @@ const Node = {
filterNodes(iterator) { filterNodes(iterator) {
const shallow = this.nodes.filter(iterator) const shallow = this.nodes.filter(iterator)
const deep = this.nodes const deep = this.nodes
.map(node => node.type == 'text' ? null : node.filterNodes(iterator)) .map(node => node.kind == 'text' ? null : node.filterNodes(iterator))
.filter(node => node) .filter(node => node)
.reduce((all, map) => { .reduce((all, map) => {
return all.concat(map) return all.concat(map)
@@ -254,7 +254,7 @@ const Node = {
*/ */
getFirstTextNode() { getFirstTextNode() {
return this.findNode(node => node.type == 'text') || null return this.findNode(node => node.kind == 'text') || null
}, },
/** /**
@@ -264,7 +264,7 @@ const Node = {
*/ */
getLastTextNode() { getLastTextNode() {
const texts = this.filterNodes(node => node.type == 'text') const texts = this.filterNodes(node => node.kind == 'text')
return texts.last() || null return texts.last() || null
}, },
@@ -335,7 +335,7 @@ const Node = {
// Get all of the nodes that come before the matching child. // Get all of the nodes that come before the matching child.
const child = this.nodes.find((node) => { const child = this.nodes.find((node) => {
if (node == match) return true if (node == match) return true
return node.type == 'text' return node.kind == 'text'
? false ? false
: node.hasNode(match) : node.hasNode(match)
}) })
@@ -372,7 +372,7 @@ const Node = {
if (shallow != null) return shallow if (shallow != null) return shallow
return this.nodes return this.nodes
.map(node => node.type == 'text' ? null : node.getNextNode(key)) .map(node => node.kind == 'text' ? null : node.getNextNode(key))
.filter(node => node) .filter(node => node)
.first() .first()
}, },
@@ -396,7 +396,7 @@ const Node = {
} }
return this.nodes return this.nodes
.map(node => node.type == 'text' ? null : node.getPreviousNode(key)) .map(node => node.kind == 'text' ? null : node.getPreviousNode(key))
.filter(node => node) .filter(node => node)
.first() .first()
}, },
@@ -412,7 +412,7 @@ const Node = {
key = normalizeKey(key) key = normalizeKey(key)
// Create a new selection starting at the first text node. // Create a new selection starting at the first text node.
const first = this.findNode(node => node.type == 'text') const first = this.findNode(node => node.kind == 'text')
const range = Selection.create({ const range = Selection.create({
anchorKey: first.key, anchorKey: first.key,
anchorOffset: 0, anchorOffset: 0,
@@ -439,7 +439,7 @@ const Node = {
let node = null let node = null
this.nodes.forEach((child) => { this.nodes.forEach((child) => {
if (child.type == 'text') return if (child.kind == 'text') return
const match = child.getParentNode(key) const match = child.getParentNode(key)
if (match) node = match if (match) node = match
}) })
@@ -455,13 +455,11 @@ const Node = {
*/ */
getTextNodeAtOffset(offset) { getTextNodeAtOffset(offset) {
let match = null let length = 0
let i let texts = this.filterNodes(node => node.kind == 'text')
let match = texts.find((node) => {
this.nodes.forEach((node) => { length += node.length
if (!node.length > offset + i) return return length >= offset
match = node.type == 'text' ? node : node.getNodeAtOffset(offset - i)
i += node.length
}) })
return match return match
@@ -477,14 +475,17 @@ const Node = {
getTextNodesAtRange(range) { getTextNodesAtRange(range) {
range = range.normalize(this) range = range.normalize(this)
const { startKey, endKey } = range const { startKey, endKey } = range
// If the selection isn't formed, return an empty map.
if (startKey == null || endKey == null) return new OrderedMap() if (startKey == null || endKey == null) return new OrderedMap()
// Assert that the nodes exist before searching.
this.assertHasNode(startKey) this.assertHasNode(startKey)
this.assertHasNode(endKey) this.assertHasNode(endKey)
// Return the text nodes after the start offset and before the end offset. // Return the text nodes after the start offset and before the end offset.
const endNode = this.getNode(endKey) const endNode = this.getNode(endKey)
const texts = this.filterNodes(node => node.type == 'text') const texts = this.filterNodes(node => node.kind == 'text')
const afterStart = texts.skipUntil(node => node.key == startKey) const afterStart = texts.skipUntil(node => node.key == startKey)
const upToEnd = afterStart.takeUntil(node => node.key == endKey) const upToEnd = afterStart.takeUntil(node => node.key == endKey)
let matches = upToEnd.set(endNode.key, endNode) let matches = upToEnd.set(endNode.key, endNode)
@@ -492,22 +493,51 @@ const Node = {
}, },
/** /**
* Get all of the wrapping nodes in a `range`. * Get the closets block nodes for each text node in a `range`.
* *
* @param {Selection} range * @param {Selection} range
* @return {OrderedMap} nodes * @return {OrderedMap} nodes
*/ */
getWrappingNodesAtRange(range) { getBlockNodesAtRange(range) {
const node = this const node = this
range = range.normalize(node) range = range.normalize(node)
const texts = node.getTextNodesAtRange(range) const texts = node.getTextNodesAtRange(range)
const parents = texts.map((text) => { const blocks = texts.map(text => node.getClosestBlockNode(text))
return node.nodes.includes(text) ? node : node.getParentNode(text) return blocks
}) },
return parents /**
* Get the node's closest block parent node.
*
* @param {Node} node
* @return {Node} node
*/
getClosestBlockNode(node) {
let parent = this.getParentNode(node)
while (parent && parent.kind != 'block') {
parent = this.getParentNode(parent)
}
return parent
},
/**
* Get the node's closest inline parent node.
*
* @return {Node} node
*/
getClosestInlineNode() {
let parent = this.getParentNode(node)
while (parent && parent.kind != 'inline') {
parent = this.getParentNode(parent)
}
return parent
}, },
/** /**
@@ -524,7 +554,7 @@ const Node = {
if (shallow) return true if (shallow) return true
const deep = this.nodes const deep = this.nodes
.map(node => node.type == 'text' ? false : node.hasNode(key)) .map(node => node.kind == 'text' ? false : node.hasNode(key))
.some(has => has) .some(has => has)
if (deep) return true if (deep) return true
@@ -625,29 +655,31 @@ const Node = {
}, },
/** /**
* Normalize the node, joining any two adjacent text child nodes. * Normalize the node by joining any two adjacent text child nodes.
* *
* @return {Node} node * @return {Node} node
*/ */
normalize() { normalize() {
let node = this let node = this
let first = node.findNode((child) => {
if (child.type != 'text') return // See if there are any adjacent text nodes.
let firstAdjacent = node.findNode((child) => {
if (child.kind != 'text') return
const parent = node.getParentNode(child) const parent = node.getParentNode(child)
const next = parent.getNextNode(child) const next = parent.getNextNode(child)
return next && next.type == 'text' return next && next.kind == 'text'
}) })
// If no text node was followed by another, do nothing. // If no text nodes are adjacent, abort.
if (!first) return node if (!firstAdjacent) return node
// Otherwise, add the text of the second node to the first... // Fix an adjacent text node if one exists.
let parent = node.getParentNode(first) let parent = node.getParentNode(firstAdjacent)
const second = parent.getNextNode(first) const second = parent.getNextNode(firstAdjacent)
const characters = first.characters.concat(second.characters) const characters = firstAdjacent.characters.concat(second.characters)
first = first.merge({ characters }) firstAdjacent = firstAdjacent.merge({ characters })
parent = parent.updateNode(first) parent = parent.updateNode(firstAdjacent)
// Then remove the second node. // Then remove the second node.
parent = parent.removeNode(second) parent = parent.removeNode(second)
@@ -659,7 +691,7 @@ const Node = {
node = parent node = parent
} }
// Finally, recurse by normalizing again. // Recurse by normalizing again.
return node.normalize() return node.normalize()
}, },
@@ -760,7 +792,7 @@ const Node = {
// Create a brand new second element with the second set of characters. // Create a brand new second element with the second set of characters.
let secondText = Text.create({}) let secondText = Text.create({})
let secondElement = Element.create({ let secondElement = Block.create({
type: firstElement.type, type: firstElement.type,
data: firstElement.data data: firstElement.data
}) })
@@ -852,7 +884,7 @@ const Node = {
} }
const nodes = this.nodes.map((child) => { const nodes = this.nodes.map((child) => {
return child.type == 'text' ? child : child.updateNode(key, node) return child.kind == 'text' ? child : child.updateNode(key, node)
}) })
return this.merge({ nodes }) return this.merge({ nodes })
@@ -872,7 +904,7 @@ const Node = {
// Allow for the parent to by just a type. // Allow for the parent to by just a type.
if (typeof parent == 'string') { if (typeof parent == 'string') {
parent = Element.create({ type: parent }) parent = Block.create({ type: parent })
} }
// Add the child to the parent's nodes. // Add the child to the parent's nodes.

View File

@@ -109,7 +109,7 @@ class Selection extends SelectionRecord {
isAtStartOf(node) { isAtStartOf(node) {
const { startKey, startOffset } = this const { startKey, startOffset } = this
const first = node.type == 'text' ? node : node.getFirstTextNode() const first = node.kind == 'text' ? node : node.getFirstTextNode()
return startKey == first.key && startOffset == 0 return startKey == first.key && startOffset == 0
} }
@@ -122,7 +122,7 @@ class Selection extends SelectionRecord {
isAtEndOf(node) { isAtEndOf(node) {
const { endKey, endOffset } = this const { endKey, endOffset } = this
const last = node.type == 'text' ? node : node.getLastTextNode() const last = node.kind == 'text' ? node : node.getLastTextNode()
return endKey == last.key && endOffset == last.length return endKey == last.key && endOffset == last.length
} }
@@ -148,8 +148,8 @@ class Selection extends SelectionRecord {
let focusNode = node.getNode(focusKey) let focusNode = node.getNode(focusKey)
// If the anchor node isn't a text node, match it to one. // If the anchor node isn't a text node, match it to one.
if (anchorNode.type != 'text') { if (anchorNode.kind != 'text') {
anchorNode = node.getNodeAtOffset(anchorOffset) anchorNode = node.getTextNodeAtOffset(anchorOffset)
let parent = node.getParentNode(anchorNode) let parent = node.getParentNode(anchorNode)
let offset = parent.getNodeOffset(anchorNode) let offset = parent.getNodeOffset(anchorNode)
anchorOffset = anchorOffset - offset anchorOffset = anchorOffset - offset
@@ -157,8 +157,8 @@ class Selection extends SelectionRecord {
} }
// If the focus node isn't a text node, match it to one. // If the focus node isn't a text node, match it to one.
if (focusNode.type != 'text') { if (focusNode.kind != 'text') {
focusNode = node.getNodeAtOffset(focusOffset) focusNode = node.getTextNodeAtOffset(focusOffset)
let parent = node.getParentNode(focusNode) let parent = node.getParentNode(focusNode)
let offset = parent.getNodeOffset(focusNode) let offset = parent.getNodeOffset(focusNode)
focusOffset = focusOffset - offset focusOffset = focusOffset - offset

View File

@@ -134,13 +134,13 @@ class State extends Record(DEFAULTS) {
} }
/** /**
* Get the wrapping nodes in the current selection. * Get the block nodes in the current selection.
* *
* @return {OrderedMap} nodes * @return {OrderedMap} nodes
*/ */
get currentWrappingNodes() { get currentBlockNodes() {
return this.document.getWrappingNodesAtRange(this.selection) return this.document.getBlockNodesAtRange(this.selection)
} }
/** /**

View File

@@ -3,19 +3,20 @@ import uid from 'uid'
import { List, Record } from 'immutable' import { List, Record } from 'immutable'
/** /**
* Record. * Default properties.
*/ */
const TextRecord = new Record({ const DEFAULTS = {
characters: new List(), characters: new List(),
key: null key: null,
}) parent: null
}
/** /**
* Text. * Text.
*/ */
class Text extends TextRecord { class Text extends Record(DEFAULTS) {
/** /**
* Create a new `Text` with `properties`. * Create a new `Text` with `properties`.
@@ -29,6 +30,16 @@ class Text extends TextRecord {
return new Text(properties) return new Text(properties)
} }
/**
* Get the node's kind.
*
* @return {String} kind
*/
get kind() {
return 'text'
}
/** /**
* Get the length of the concatenated text of the node. * Get the length of the concatenated text of the node.
* *
@@ -51,16 +62,6 @@ class Text extends TextRecord {
.join('') .join('')
} }
/**
* Immutable type to match other nodes.
*
* @return {String} type
*/
get type() {
return 'text'
}
} }
/** /**

View File

@@ -1,10 +1,11 @@
import Block from '../models/block'
import Character from '../models/character' import Character from '../models/character'
import Document from '../models/document' import Document from '../models/document'
import Inline from '../models/inline'
import Mark from '../models/mark' import Mark from '../models/mark'
import Element from '../models/element'
import Text from '../models/text'
import State from '../models/state' import State from '../models/state'
import Text from '../models/text'
import groupByMarks from '../utils/group-by-marks' import groupByMarks from '../utils/group-by-marks'
import { Map } from 'immutable' import { Map } from 'immutable'
@@ -29,7 +30,7 @@ function serialize(state) {
*/ */
function serializeNode(node) { function serializeNode(node) {
switch (node.type) { switch (node.kind) {
case 'document': { case 'document': {
return { return {
nodes: node.nodes.toArray().map(node => serializeNode(node)) nodes: node.nodes.toArray().map(node => serializeNode(node))
@@ -37,15 +38,17 @@ function serializeNode(node) {
} }
case 'text': { case 'text': {
return { return {
type: 'text', kind: node.kind,
ranges: serializeCharacters(node.characters) ranges: serializeCharacters(node.characters)
} }
} }
default: { case 'block':
case 'inline': {
return { return {
type: node.type,
data: node.data.toJSON(), data: node.data.toJSON(),
nodes: node.nodes.toArray().map(node => serializeNode(node)) kind: node.kind,
nodes: node.nodes.toArray().map(node => serializeNode(node)),
type: node.type
} }
} }
} }
@@ -93,7 +96,7 @@ function serializeMark(mark) {
function deserialize(object) { function deserialize(object) {
return State.create({ return State.create({
document: Document.create({ document: Document.create({
nodes: Element.createMap(object.nodes.map(deserializeNode)) nodes: Block.createMap(object.nodes.map(deserializeNode))
}) })
}) })
} }
@@ -106,19 +109,26 @@ function deserialize(object) {
*/ */
function deserializeNode(object) { function deserializeNode(object) {
switch (object.type) { switch (object.kind) {
case 'block': {
return Block.create({
type: object.type,
data: new Map(object.data),
nodes: Block.createMap(object.nodes.map(deserializeNode))
})
}
case 'inline': {
return Inline.create({
type: object.type,
data: new Map(object.data),
nodes: Inline.createMap(object.nodes.map(deserializeNode))
})
}
case 'text': { case 'text': {
return Text.create({ return Text.create({
characters: deserializeRanges(object.ranges) characters: deserializeRanges(object.ranges)
}) })
} }
default: {
return Element.create({
type: object.type,
data: new Map(object.data),
nodes: Element.createMap(object.nodes.map(deserializeNode))
})
}
} }
} }