1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-03-05 21:49:06 +01:00

a handful of performance improvements ()

* update large example

* pass block down to <Text> for performance, closes 

* add get-ranges benchmark

* optimize getRanges(), closes 

* add serialization benchmarks

* optimize Raw.deserializeRanges() by computing marks once, closes 

* change .merge calls to .set for performance

* change updateDescendant() to use getAncestors() for memoization

* change getPath() to use getAncestors() for memoization

* switch getTexts() and friends to use arrays while iterating

* rename split-block benchmark

* update benchmark compare script
This commit is contained in:
Ian Storm Taylor 2017-04-02 14:57:36 -07:00 committed by GitHub
parent 8687fac1a2
commit 059ee96db8
30 changed files with 3071 additions and 147 deletions
benchmark
compare.js
fixtures
models
rendering/normal
serializers
raw-deserialize
raw-serialize
transforms/split-block
examples/large-document
src

@ -22,6 +22,8 @@ baseline.forEach((suite, i) => {
suite.benchmarks.forEach((base, j) => {
const comp = comparison[i].benchmarks[j]
if (!comp) return
const b = base.iterations / base.elapsed * 100
const c = comp.iterations / comp.elapsed * 100
const threshold = b * THRESHOLD

@ -0,0 +1,17 @@
import { __clear } from '../../../../lib/utils/memoize'
export default function ({ state, text }) {
state.document.getPath(text.key)
}
export function before(state) {
const text = state.document.getLastText()
__clear()
return { state, text }
}
export function after() {
__clear()
}

@ -0,0 +1,17 @@
import { __clear } from '../../../../lib/utils/memoize'
export default function (text) {
text.getRanges()
}
export function before(state) {
const text = state.document.getFirstText()
__clear()
return text
}
export function after() {
__clear()
}

@ -0,0 +1,19 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'

@ -0,0 +1,20 @@
import { __clear } from '../../../../lib/utils/memoize'
export default function ({ state, next }) {
state.document.updateDescendant(next)
}
export function before(state) {
const texts = state.document.getTexts()
const { size } = texts
const text = texts.get(Math.round(size / 2))
const next = text.insertText(0, 'some text')
__clear()
return { state, next }
}
export function after() {
__clear()
}

@ -0,0 +1,683 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
key: _cursor_
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'

@ -0,0 +1,10 @@
import { Raw } from '../../../..'
export default function (json) {
Raw.deserialize(json, { normalize: false })
}
export function before(state) {
return Raw.serialize(state)
}

@ -0,0 +1,683 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
key: _cursor_
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'

@ -0,0 +1,6 @@
import { Raw } from '../../../..'
export default function (state) {
Raw.serialize(state)
}

@ -0,0 +1,683 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
key: _cursor_
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'

@ -0,0 +1,683 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
key: _cursor_
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'This is editable '
- text: 'rich'
marks:
- type: bold
- text: ' text, '
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'much'
marks:
- type: italic
- text: ' better than a '
- text: '<textarea>'
marks:
- type: code
- text: '!'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: 'Since it''s rich text, you can do things like turn a selection of text '
- text: 'bold'
marks:
- type: bold
- text: ', or add a semantically rendered block quote in the middle of the page,
like this:'
- kind: block
type: block-quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'A wise quote.'
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 'Try it out for yourself!'

@ -17,6 +17,23 @@ const schema = {
nodes: {
'heading': props => <h1 {...props.attributes}>{props.children}</h1>,
'paragraph': props => <p {...props.attributes} style={{ marginBottom: 20 }}>{props.children}</p>,
},
marks: {
bold: {
fontWeight: 'bold'
},
code: {
fontFamily: 'monospace',
backgroundColor: '#eee',
padding: '3px',
borderRadius: '4px'
},
italic: {
fontStyle: 'italic'
},
underlined: {
textDecoration: 'underline'
}
}
}
@ -26,14 +43,14 @@ for (let h = 0; h < HEADINGS; h++) {
nodes.push({
kind: 'block',
type: 'heading',
nodes: [ { kind: 'text', text: faker.lorem.sentence() } ]
nodes: [{ kind: 'text', text: faker.lorem.sentence() }]
})
for (let p = 0; p < PARAGRAPHS; p++) {
nodes.push({
kind: 'block',
type: 'paragraph',
nodes: [ { kind: 'text', text: faker.lorem.paragraph() } ]
nodes: [{ kind: 'text', text: faker.lorem.paragraph() }]
})
}
}
@ -55,7 +72,7 @@ class LargeDocument extends React.Component {
constructor() {
super()
console.time('deserializeLargeDocument')
this.state = { state: Raw.deserialize({ nodes }, { terse: true }) }
this.state = { state: Raw.deserialize({ nodes }, { normalize: false, terse: true }) }
console.timeEnd('deserializeLargeDocument')
}
@ -69,6 +86,45 @@ class LargeDocument extends React.Component {
this.setState({ state })
}
/**
* On key down, if it's a formatting command toggle a mark.
*
* @param {Event} e
* @param {Object} data
* @param {State} state
* @return {State}
*/
onKeyDown = (e, data, state) => {
if (!data.isMod) return
let mark
switch (data.key) {
case 'b':
mark = 'bold'
break
case 'i':
mark = 'italic'
break
case 'u':
mark = 'underlined'
break
case '`':
mark = 'code'
break
default:
return
}
state = state
.transform()
.toggleMark(mark)
.apply()
e.preventDefault()
return state
}
/**
* Render the editor.
*
@ -83,6 +139,7 @@ class LargeDocument extends React.Component {
spellCheck={false}
state={this.state.state}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
/>
)
}

@ -726,7 +726,7 @@ class Content extends React.Component {
// If there are no ranges, the editor was blurred natively.
if (!native.rangeCount) {
data.selection = selection.merge({ isFocused: false })
data.selection = selection.set('isFocused', false)
data.isNative = true
}

@ -29,6 +29,7 @@ class Leaf extends React.Component {
*/
static propTypes = {
block: React.PropTypes.object.isRequired,
editor: React.PropTypes.object.isRequired,
index: React.PropTypes.number.isRequired,
marks: React.PropTypes.object.isRequired,
@ -129,7 +130,7 @@ class Leaf extends React.Component {
*/
renderText(props) {
const { node, state, parent, text, index, ranges } = props
const { block, node, parent, text, index, ranges } = props
// COMPAT: If the text is empty and it's the only child, we need to render a
// <br/> to get the block to have the proper height.
@ -147,7 +148,6 @@ class Leaf extends React.Component {
// COMPAT: Browsers will collapse trailing new lines at the end of blocks,
// so we need to add an extra trailing new lines to prevent that.
const block = state.document.getClosestBlock(node.key)
const lastText = block.getLastText()
const lastChar = text.charAt(text.length - 1)
const isLastText = node == lastText

@ -32,6 +32,7 @@ class Node extends React.Component {
*/
static propTypes = {
block: React.PropTypes.object,
editor: React.PropTypes.object.isRequired,
node: React.PropTypes.object.isRequired,
parent: React.PropTypes.object.isRequired,
@ -229,6 +230,7 @@ class Node extends React.Component {
<Node
key={child.key}
node={child}
block={this.props.node.kind == 'block' ? this.props.node : this.props.block}
parent={this.props.node}
editor={this.props.editor}
readOnly={this.props.readOnly}
@ -247,9 +249,7 @@ class Node extends React.Component {
renderElement = () => {
const { editor, node, parent, readOnly, state } = this.props
const { Component } = this.state
const children = node.nodes
.map(child => this.renderNode(child))
.toArray()
const children = node.nodes.map(this.renderNode).toArray()
// Attributes that the developer must to mix into the element in their
// custom node renderer component.
@ -321,12 +321,13 @@ class Node extends React.Component {
*/
renderLeaf = (ranges, range, index, offset) => {
const { node, parent, schema, state, editor } = this.props
const { block, node, parent, schema, state, editor } = this.props
const { text, marks } = range
return (
<Leaf
key={`${node.key}-${index}`}
block={block}
editor={editor}
index={index}
marks={marks}

@ -214,12 +214,23 @@ const Node = {
*/
getBlocks() {
return this.nodes.reduce((blocks, node) => {
if (node.kind != 'block') return blocks
return node.isLeafBlock()
? blocks.push(node)
: blocks.concat(node.getBlocks())
}, new List())
const array = this.getBlocksAsArray()
return new List(array)
},
/**
* Get the leaf block descendants of the node.
*
* @return {List<Node>}
*/
getBlocksAsArray() {
return this.nodes.reduce((array, child) => {
if (child.kind != 'block') return array
if (!child.isLeafBlock()) return array.concat(child.getBlocksAsArray())
array.push(child)
return array
}, [])
},
/**
@ -635,12 +646,29 @@ const Node = {
*/
getInlines() {
return this.nodes.reduce((inlines, node) => {
if (node.kind == 'text') return inlines
return node.isLeafInline()
? inlines.push(node)
: inlines.concat(node.getInlines())
}, new List())
const array = this.getInlinesAsArray()
return new List(array)
},
/**
* Get the closest inline nodes for each text node in the node, as an array.
*
* @return {List<Node>}
*/
getInlinesAsArray() {
let array = []
this.nodes.forEach((child) => {
if (child.kind == 'text') return
if (child.isLeafInline()) {
array.push(child)
} else {
array = array.concat(child.getInlinesAsArray())
}
})
return array
},
/**
@ -909,27 +937,17 @@ const Node = {
*/
getPath(key) {
key = Normalize.key(key)
if (key == this.key) return []
let child = this.assertNode(key)
const ancestors = this.getAncestors(key)
const path = []
let childKey = key
let parent
// Efficient with getParent memoization
while (parent = this.getParent(childKey)) {
const index = parent.nodes.findIndex(n => n.key === childKey)
ancestors.reverse().forEach((ancestor) => {
const index = ancestor.nodes.indexOf(child)
path.unshift(index)
childKey = parent.key
}
child = ancestor
})
if (childKey === key) {
// Did not loop once, meaning we could not find the child
throw new Error(`Could not find a descendant node with key "${key}".`)
} else {
return path
}
return path
},
/**
@ -1043,11 +1061,28 @@ const Node = {
*/
getTexts() {
return this.nodes.reduce((texts, node) => {
return node.kind == 'text'
? texts.push(node)
: texts.concat(node.getTexts())
}, new List())
const array = this.getTextsAsArray()
return new List(array)
},
/**
* Recursively get all the leaf text nodes in order of appearance, as array.
*
* @return {List<Node>}
*/
getTextsAsArray() {
let array = []
this.nodes.forEach((node) => {
if (node.kind == 'text') {
array.push(node)
} else {
array = array.concat(node.getTextsAsArray())
}
})
return array
},
/**
@ -1136,7 +1171,7 @@ const Node = {
}
const nodes = this.nodes.insert(index, node)
return this.merge({ nodes })
return this.set('nodes', nodes)
},
/**
@ -1187,7 +1222,7 @@ const Node = {
if (second.kind == 'text') {
let { characters } = first
characters = characters.concat(second.characters)
first = first.merge({ characters })
first = first.set('characters', characters)
}
else {
@ -1224,7 +1259,7 @@ const Node = {
if (ret != node) nodes = nodes.set(ret.key, ret)
})
return this.merge({ nodes })
return this.set('nodes', nodes)
},
/**
@ -1248,7 +1283,7 @@ const Node = {
nodes = nodes.set(index, ret)
})
return this.merge({ nodes })
return this.set('nodes', nodes)
},
/**
@ -1258,7 +1293,8 @@ const Node = {
*/
regenerateKey() {
return this.merge({ key: generateKey() })
const key = generateKey()
return this.set('key', key)
},
/**
@ -1279,11 +1315,8 @@ const Node = {
const isParent = node == parent
const nodes = parent.nodes.splice(index, 1)
parent = parent.merge({ nodes })
node = isParent
? parent
: node.updateDescendant(parent)
parent = parent.set('nodes', nodes)
node = isParent ? parent : node.updateDescendant(parent)
return node
},
@ -1296,7 +1329,7 @@ const Node = {
removeNode(index) {
const nodes = this.nodes.splice(index, 1)
return this.merge({ nodes })
return this.set('nodes', nodes)
},
/**
@ -1356,8 +1389,8 @@ const Node = {
const { characters } = child
const oneChars = characters.take(i)
const twoChars = characters.skip(i)
one = child.merge({ characters: oneChars })
two = child.merge({ characters: twoChars }).regenerateKey()
one = child.set('characters', oneChars)
two = child.set('characters', twoChars).regenerateKey()
}
else {
@ -1369,8 +1402,8 @@ const Node = {
oneNodes = (oneNodes.size == (nodes.size - 1) && one == nodes.last()) ? nodes : oneNodes.push(one)
const twoNodes = nodes.skipUntil(n => n.key == one.key).rest().unshift(two)
one = child.merge({ nodes: oneNodes })
two = child.merge({ nodes: twoNodes }).regenerateKey()
one = child.set('nodes', oneNodes)
two = child.set('nodes', twoNodes).regenerateKey()
}
child = base.getParent(child.key)
@ -1404,8 +1437,8 @@ const Node = {
const oneNodes = nodes.take(count)
const twoNodes = nodes.skip(count)
const one = node.merge({ nodes: oneNodes })
const two = node.merge({ nodes: twoNodes }).regenerateKey()
const one = node.set('nodes', oneNodes)
const two = node.set('nodes', twoNodes).regenerateKey()
const nodeIndex = parent.nodes.indexOf(node)
@ -1425,22 +1458,19 @@ const Node = {
*/
updateDescendant(node) {
let found = false
let child = this.assertDescendant(node.key)
const ancestors = this.getAncestors(node.key)
const result = this.mapDescendants((d) => {
if (d.key == node.key) {
found = true
return node
} else {
return d
}
ancestors.reverse().forEach((parent) => {
let { nodes } = parent
const index = nodes.indexOf(child)
child = parent
nodes = nodes.set(index, node)
parent = parent.set('nodes', nodes)
node = parent
})
if (!found) {
throw new Error(`Could not update descendant node with key "${node.key}".`)
} else {
return result
}
return node
},
/**
@ -1478,7 +1508,7 @@ const Node = {
concatChildren(nodes) {
warn('The `Node.concatChildren(nodes)` method is deprecated.')
nodes = this.nodes.concat(nodes)
return this.merge({ nodes })
return this.set('nodes', nodes)
},
/**
@ -1620,6 +1650,7 @@ memoize(Node, [
'areDescendantsSorted',
'getAncestors',
'getBlocks',
'getBlocksAsArray',
'getBlocksAtRange',
'getBlocksByType',
'getCharacters',
@ -1645,6 +1676,7 @@ memoize(Node, [
'getFurthestAncestor',
'getFurthestOnlyChildAncestor',
'getInlines',
'getInlinesAsArray',
'getInlinesAtRange',
'getInlinesByType',
'getKeys',
@ -1667,6 +1699,7 @@ memoize(Node, [
'getTextAtOffset',
'getTextDirection',
'getTexts',
'getTextsAsArray',
'getTextsAtRange',
'hasChild',
'hasDescendant',

@ -4,7 +4,7 @@ import Mark from './mark'
import Range from './range'
import memoize from '../utils/memoize'
import generateKey from '../utils/generate-key'
import { List, Record, OrderedSet, Set } from 'immutable'
import { List, Record, OrderedSet, Set, is } from 'immutable'
/**
* Default properties.
@ -118,8 +118,7 @@ class Text extends new Record(DEFAULTS) {
*/
get text() {
return this.characters
.reduce((result, char) => result + char.text, '')
return this.characters.reduce((string, char) => string + char.text, '')
}
/**
@ -137,11 +136,11 @@ class Text extends new Record(DEFAULTS) {
if (i >= index + length) return char
let { marks } = char
marks = marks.add(mark)
char = char.merge({ marks })
char = char.set('marks', marks)
return char
})
return this.merge({ characters })
return this.set('characters', characters)
}
/**
@ -223,43 +222,51 @@ class Text extends new Record(DEFAULTS) {
*/
getRanges(decorators = []) {
const list = new List()
const characters = this.getDecorations(decorators)
let ranges = []
// PERF: cache previous values for faster lookup.
let prevChar
let prevRange
// If there are no characters, return one empty range.
if (characters.size == 0) {
return list.push(new Range())
ranges.push({})
}
// Convert the now-decorated characters into ranges.
const ranges = characters.reduce((memo, char, i) => {
const { marks, text } = char
// Otherwise, loop the characters and build the ranges...
else {
characters.forEach((char, i) => {
const { marks, text } = char
// The first one can always just be created.
if (i == 0) {
return memo.push(new Range({ text, marks }))
}
// The first one can always just be created.
if (i == 0) {
prevChar = char
prevRange = { text, marks }
ranges.push(prevRange)
return
}
// Otherwise, compare to the previous and see if a new range should be
// created, or whether the text should be added to the previous range.
const previous = characters.get(i - 1)
const prevMarks = previous.marks
const added = marks.filterNot(mark => prevMarks.includes(mark))
const removed = prevMarks.filterNot(mark => marks.includes(mark))
const isSame = !added.size && !removed.size
// Otherwise, compare the current and previous marks.
const prevMarks = prevChar.marks
const isSame = is(marks, prevMarks)
// If the marks are the same, add the text to the previous range.
if (isSame) {
const index = memo.size - 1
let prevRange = memo.get(index)
let prevText = prevRange.get('text')
prevRange = prevRange.set('text', prevText += text)
return memo.set(index, prevRange)
}
// If the marks are the same, add the text to the previous range.
if (isSame) {
prevChar = char
prevRange.text += text
return
}
// Otherwise, create a new range.
return memo.push(new Range({ text, marks }))
}, list)
// Otherwise, create a new range.
prevChar = char
prevRange = { text, marks }
ranges.push(prevRange)
}, [])
}
// PERF: convert the ranges to immutable objects after iterating.
ranges = new List(ranges.map(object => new Range(object)))
// Return the ranges.
return ranges
@ -294,7 +301,7 @@ class Text extends new Record(DEFAULTS) {
.concat(chars)
.concat(characters.slice(index))
return this.merge({ characters })
return this.set('characters', characters)
}
/**
@ -304,7 +311,8 @@ class Text extends new Record(DEFAULTS) {
*/
regenerateKey() {
return this.merge({ key: generateKey() })
const key = generateKey()
return this.set('key', key)
}
/**
@ -322,11 +330,11 @@ class Text extends new Record(DEFAULTS) {
if (i >= index + length) return char
let { marks } = char
marks = marks.remove(mark)
char = char.merge({ marks })
char = char.set('marks', marks)
return char
})
return this.merge({ characters })
return this.set('characters', characters)
}
/**
@ -342,7 +350,7 @@ class Text extends new Record(DEFAULTS) {
const start = index
const end = index + length
characters = characters.filterNot((char, i) => start <= i && i < end)
return this.merge({ characters })
return this.set('characters', characters)
}
/**
@ -363,11 +371,11 @@ class Text extends new Record(DEFAULTS) {
if (!marks.has(mark)) return char
marks = marks.remove(mark)
marks = marks.add(newMark)
char = char.merge({ marks })
char = char.set('marks', marks)
return char
})
return this.merge({ characters })
return this.set('characters', characters)
}
/**

@ -85,7 +85,7 @@ class Transform {
if (save) this.save({ merge })
// Return the new state with the `isNative` flag set.
return this.state.merge({ isNative: !!isNative })
return this.state.set('isNative', !!isNative)
}
}

@ -167,7 +167,7 @@ function Plugin(options = {}) {
// Add the `isNative` flag directly, so we don't have to re-transform.
if (isNative) {
next = next.merge({ isNative })
next = next.set('isNative', isNative)
}
// If not native, prevent default so that the DOM remains untouched.

@ -134,14 +134,16 @@ const Raw = {
deserializeRange(object, options = {}) {
if (options.terse) object = Raw.untersifyRange(object)
const marks = Mark.createSet(object.marks.map((mark) => {
return Raw.deserializeMark(mark, options)
}))
return Character.createList(object.text
.split('')
.map((char) => {
return Character.create({
text: char,
marks: Mark.createSet(object.marks.map((mark) => {
return Raw.deserializeMark(mark, options)
}))
marks,
})
}))
},

@ -78,7 +78,7 @@ function addMark(state, operation) {
let node = document.assertPath(path)
node = node.addMark(offset, length, mark)
document = document.updateDescendant(node)
state = state.merge({ document })
state = state.set('document', document)
return state
}
@ -97,7 +97,7 @@ function insertNode(state, operation) {
const isParent = document == parent
parent = parent.insertNode(index, node)
document = isParent ? parent : document.updateDescendant(parent)
state = state.merge({ document })
state = state.set('document', document)
return state
}
@ -127,7 +127,7 @@ function insertText(state, operation) {
selection = selection.moveFocus(text.length)
}
state = state.merge({ document, selection })
state = state.set('document', document).set('selection', selection)
return state
}
@ -171,7 +171,7 @@ function joinNode(state, operation) {
}
}
state = state.merge({ document, selection })
state = state.set('document', document).set('selection', selection)
return state
}
@ -201,7 +201,7 @@ function moveNode(state, operation) {
target = target.insertNode(newIndex, node)
document = isTarget ? target : document.updateDescendant(target)
state = state.merge({ document })
state = state.set('document', document)
return state
}
@ -219,7 +219,7 @@ function removeMark(state, operation) {
let node = document.assertPath(path)
node = node.removeMark(offset, length, mark)
document = document.updateDescendant(node)
state = state.merge({ document })
state = state.set('document', document)
return state
}
@ -278,7 +278,7 @@ function removeNode(state, operation) {
document = isParent ? parent : document.updateDescendant(parent)
// Update the document and selection.
state = state.merge({ document, selection })
state = state.set('document', document).set('selection', selection)
return state
}
@ -307,7 +307,7 @@ function removeText(state, operation) {
node = node.removeText(offset, length)
document = document.updateDescendant(node)
state = state.merge({ document, selection })
state = state.set('document', document).set('selection', selection)
return state
}
@ -325,7 +325,7 @@ function setMark(state, operation) {
let node = document.assertPath(path)
node = node.updateMark(offset, length, mark, newMark)
document = document.updateDescendant(node)
state = state.merge({ document })
state = state.set('document', document)
return state
}
@ -356,7 +356,7 @@ function setNode(state, operation) {
node = node.merge(properties)
document = node.kind === 'document' ? node : document.updateDescendant(node)
state = state.merge({ document })
state = state.set('document', document)
return state
}
@ -388,7 +388,7 @@ function setSelection(state, operation) {
selection = selection.merge(properties)
selection = selection.normalize(document)
state = state.merge({ selection })
state = state.set('selection', selection)
return state
}
@ -411,7 +411,7 @@ function splitNode(state, operation) {
// If there's no offset, it's using the `count` instead.
if (offset == null) {
document = document.splitNodeAfter(path, count)
state = state.merge({ document })
state = state.set('document', document)
return state
}
@ -446,7 +446,7 @@ function splitNode(state, operation) {
}
}
state = state.merge({ document, selection })
state = state.set('document', document).set('selection', selection)
return state
}

@ -28,13 +28,13 @@ Transforms.addMark = (transform, mark) => {
if (selection.marks) {
const marks = selection.marks.add(mark)
const sel = selection.merge({ marks })
const sel = selection.set('marks', marks)
transform.select(sel)
return
}
const marks = document.getMarksAtRange(selection).add(mark)
const sel = selection.merge({ marks })
const sel = selection.set('marks', marks)
transform.select(sel)
}
@ -341,13 +341,13 @@ Transforms.removeMark = (transform, mark) => {
if (selection.marks) {
const marks = selection.marks.remove(mark)
const sel = selection.merge({ marks })
const sel = selection.set('marks', marks)
transform.select(sel)
return
}
const marks = document.getMarksAtRange(selection).remove(mark)
const sel = selection.merge({ marks })
const sel = selection.set('marks', marks)
transform.select(sel)
}

@ -1058,7 +1058,7 @@ Transforms.unwrapInlineAtRange = (transform, range, properties, options = {}) =>
Transforms.wrapBlockAtRange = (transform, range, block, options = {}) => {
block = Normalize.block(block)
block = block.merge({ nodes: block.nodes.clear() })
block = block.set('nodes', block.nodes.clear())
const { normalize = true } = options
const { state } = transform
@ -1144,7 +1144,7 @@ Transforms.wrapInlineAtRange = (transform, range, inline, options = {}) => {
}
inline = Normalize.inline(inline)
inline = inline.merge({ nodes: inline.nodes.clear() })
inline = inline.set('nodes', inline.nodes.clear())
const blocks = document.getBlocksAtRange(range)
let startBlock = document.getClosestBlock(startKey)

@ -401,7 +401,7 @@ Transforms.unwrapNodeByKey = (transform, key, options = {}) => {
Transforms.wrapInlineByKey = (transform, key, inline, options) => {
inline = Normalize.inline(inline)
inline = inline.merge({ nodes: inline.nodes.clear() })
inline = inline.set('nodes', inline.nodes.clear())
const { document } = transform.state
const node = document.assertDescendant(key)
@ -424,7 +424,7 @@ Transforms.wrapInlineByKey = (transform, key, inline, options) => {
Transforms.wrapBlockByKey = (transform, key, block, options) => {
block = Normalize.block(block)
block = block.merge({ nodes: block.nodes.clear() })
block = block.set('nodes', block.nodes.clear())
const { document } = transform.state
const node = document.assertDescendant(key)

@ -97,7 +97,7 @@ Transforms.normalizeSelection = (transform) => {
})
}
state = state.merge({ selection })
state = state.set('selection', selection)
transform.state = state
}

@ -33,8 +33,8 @@ Transforms.redo = (transform) => {
// Update the history.
state = transform.state
history = history.merge({ undos, redos })
state = state.merge({ history })
history = history.set('undos', undos).set('redos', redos)
state = state.set('history', history)
// Update the transform.
transform.state = state
@ -71,8 +71,8 @@ Transforms.save = (transform, options = {}) => {
redos = redos.clear()
// Update the state.
history = history.merge({ undos, redos })
state = state.merge({ history })
history = history.set('undos', undos).set('redos', redos)
state = state.set('history', history)
// Update the transform.
transform.state = state
@ -106,8 +106,8 @@ Transforms.undo = (transform) => {
// Update the history.
state = transform.state
history = history.merge({ undos, redos })
state = state.merge({ history })
history = history.set('undos', undos).set('redos', redos)
state = state.set('history', history)
// Update the transform.
transform.state = state