1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-04-22 14:21:54 +02:00

change void nodes to have a single space, prevent text-less inlines

This commit is contained in:
Ian Storm Taylor 2016-07-27 16:21:55 -07:00
parent 807d9f93e4
commit 42cbcb7e8d
40 changed files with 128 additions and 140 deletions

View File

@ -158,9 +158,16 @@ class Leaf extends React.Component {
}
renderText() {
const { text } = this.props
if (!text) return <br />
const { text, parent } = this.props
// If the text is empty, we need to render a <br/> to get the block to have
// the proper height.
if (text == '') return <br />
// COMPAT: Browsers will collapse trailing new lines, so we need to add an
// extra trailing new lines to prevent that.
if (text.charAt(text.length - 1) == '\n') return `${text}\n`
return text
}

View File

@ -59,18 +59,18 @@ class Node extends React.Component {
}
/**
* Render a `node`.
* Render a `child` node.
*
* @param {Node} node
* @param {Node} child
* @return {Element} element
*/
renderNode = (node) => {
renderNode = (child) => {
const { editor, renderDecorations, renderMark, renderNode, state } = this.props
return (
<Node
key={node.key}
node={node}
key={child.key}
node={child}
state={state}
editor={editor}
renderDecorations={renderDecorations}

View File

@ -141,7 +141,6 @@ class Void extends React.Component {
return (
<Leaf
ref={this.renderLeafRefs}
renderMark={this.renderLeafMark}
key={offsetKey}
state={state}

View File

@ -85,11 +85,7 @@ class Block extends new Record(DEFAULTS) {
*/
get isEmpty() {
return (
this.nodes.size == 1 &&
this.nodes.first().kind == 'text' &&
this.length == 0
)
return this.text == ''
}
/**

View File

@ -52,7 +52,17 @@ class Document extends new Record(DEFAULTS) {
}
/**
* Get the length of the concatenated text of the node.
* Is the document empty?
*
* @return {Boolean} isEmpty
*/
get isEmpty() {
return this.text == ''
}
/**
* Get the length of the concatenated text of the document.
*
* @return {Number} length
*/

View File

@ -85,11 +85,7 @@ class Inline extends new Record(DEFAULTS) {
*/
get isEmpty() {
return (
this.nodes.size == 1 &&
this.nodes.first().kind == 'text' &&
this.length == 0
)
return this.text == ''
}
/**

View File

@ -882,28 +882,34 @@ const Node = {
// Map this node's descendants, ensuring...
node = node.mapDescendants((desc) => {
if (removals.has(desc.key)) return desc
// ...that there are no duplicate keys.
if (keys.has(desc.key)) desc = desc.set('key', uid())
keys = keys.add(desc.key)
// ...that void nodes contain no text.
if (desc.isVoid && desc.length) {
let text = desc.getTexts().first()
let characters = text.characters.clear()
text = text.merge({ characters })
const nodes = desc.nodes.clear().push(text)
desc = desc.merge({ nodes })
// ...that void nodes contain a single space of content.
if (desc.isVoid && desc.text != ' ') {
desc = desc.merge({
nodes: Text.createList([{
characters: Character.createList([{ text: ' ' }])
}])
})
}
// ...that no block or inline node is empty.
// ...that no block or inline has no text node inside it.
if (desc.kind != 'text' && desc.nodes.size == 0) {
const text = Text.create()
const nodes = desc.nodes.push(text)
desc = desc.merge({ nodes })
}
if (desc.kind == 'text' && !removals.has(desc.key)) {
// ...that no inline node is empty.
if (desc.kind == 'inline' && desc.text == '') {
removals = removals.add(desc.key)
}
if (desc.kind == 'text') {
let next = node.getNextSibling(desc)
// ...that there are no adjacent text nodes.
@ -919,7 +925,9 @@ const Node = {
// ...that there are no extra empty text nodes.
else if (desc.length == 0) {
const parent = node.getParent(desc)
if (parent.nodes.size > 1) removals = removals.add(desc.key)
if (!removals.has(parent.key) && parent.nodes.size > 1) {
removals = removals.add(desc.key)
}
}
}

View File

@ -21,9 +21,7 @@ const Range = new Record({
const DEFAULTS = {
characters: new List(),
decorations: null,
key: null,
cache: null
key: null
}
/**
@ -43,8 +41,6 @@ class Text extends new Record(DEFAULTS) {
if (properties instanceof Text) return properties
properties.key = properties.key || uid(4)
properties.characters = Character.createList(properties.characters)
properties.decorations = null
properties.cache = null
return new Text(properties)
}
@ -77,7 +73,7 @@ class Text extends new Record(DEFAULTS) {
*/
get isEmpty() {
return this.length == 0
return this.text == ''
}
/**

View File

@ -401,6 +401,15 @@ const Transforms = {
range = range.collapseToStart()
}
const { startKey, endKey, startOffset, endOffset } = range
// If the range is inside a void, abort.
const block = doc.getClosestBlock(startKey)
if (block && block.isVoid) return doc
const inline = doc.getClosestInline(startKey)
if (inline && inline.isVoid) return doc
// Allow for passing a type string.
if (typeof node == 'string') node = { type: node }
@ -411,7 +420,6 @@ const Transforms = {
doc = doc.splitTextAtRange(range)
// Insert the node between the split text nodes.
const { startKey, endKey, startOffset, endOffset } = range
const startText = doc.getDescendant(startKey)
let parent = doc.getParent(startKey)
const nodes = parent.nodes.takeUntil(n => n == startText)

View File

@ -72,7 +72,7 @@
"lint": "eslint --ignore-pattern 'build.js' '{examples,lib}/**/*.js'",
"prepublish": "npm run dist",
"start": "http-server ./examples",
"test": "mocha --compilers js:babel-core/register --require source-map-support/register --reporter spec ./test/server.js"
"test": "mocha --compilers js:babel-core/register --reporter spec ./test/server.js"
},
"keywords": [
"canvas",

View File

@ -4,4 +4,6 @@ nodes:
isVoid: true
data: {}
nodes:
- characters: []
- characters:
- text: " "
marks: []

View File

@ -4,8 +4,10 @@ nodes:
isVoid: false
data: {}
nodes:
- type: link
isVoid: true
data: {}
nodes:
- characters: []
- type: link
isVoid: true
data: {}
nodes:
- characters:
- text: " "
marks: []

View File

@ -4,4 +4,6 @@ nodes:
isVoid: true
data: {}
nodes:
- characters: []
- characters:
- text: " "
marks: []

View File

@ -8,4 +8,6 @@ nodes:
isVoid: true
data: {}
nodes:
- characters: []
- characters:
- text: " "
marks: []

View File

@ -4,4 +4,6 @@ nodes:
isVoid: true
data: {}
nodes:
- characters: []
- characters:
- text: " "
marks: []

View File

@ -4,8 +4,10 @@ nodes:
isVoid: false
data: {}
nodes:
- type: link
isVoid: true
data: {}
nodes:
- characters: []
- type: link
isVoid: true
data: {}
nodes:
- characters:
- text: " "
marks: []

View File

@ -11,5 +11,5 @@ document:
- kind: text
ranges:
- kind: range
text: ""
text: " "
marks: []

View File

@ -16,5 +16,5 @@ document:
- kind: text
ranges:
- kind: range
text: ""
text: " "
marks: []

View File

@ -60,7 +60,7 @@ describe('serializers', () => {
const innerDir = resolve(dir, test)
const expected = readMetadata.sync(resolve(innerDir, 'output.yaml'))
const input = fs.readFileSync(resolve(innerDir, 'input.txt'), 'utf8')
const state = Plain.deserialize(input.trim())
const state = Plain.deserialize(input.replace(/\n$/m, ''))
const json = state.document.toJS()
strictEqual(strip(json), expected)
})
@ -77,7 +77,7 @@ describe('serializers', () => {
const input = require(resolve(innerDir, 'input.js')).default
const expected = fs.readFileSync(resolve(innerDir, 'output.txt'), 'utf8')
const serialized = Plain.serialize(input)
strictEqual(serialized, expected.trim())
strictEqual(serialized, expected.replace(/\n$/m, ''))
})
}
})

View File

@ -12,6 +12,9 @@ export default function (state) {
return state
.transform()
.insertInlineAtRange(range, 'hashtag')
.insertInlineAtRange(range, {
type: 'hashtag',
isVoid: true
})
.apply()
}

View File

@ -7,6 +7,4 @@ nodes:
text: word
- kind: inline
type: hashtag
nodes:
- kind: text
text: ""
isVoid: true

View File

@ -12,6 +12,9 @@ export default function (state) {
return state
.transform()
.insertBlockAtRange(range, 'image')
.insertInlineAtRange(range, {
type: 'hashtag',
isVoid: true
})
.apply()
}

View File

@ -5,13 +5,8 @@ nodes:
nodes:
- kind: text
text: wo
- kind: block
type: image
nodes:
- kind: text
text: ""
- kind: block
type: paragraph
nodes:
- kind: inline
type: hashtag
isVoid: true
- kind: text
text: rd

View File

@ -12,6 +12,9 @@ export default function (state) {
return state
.transform()
.insertBlockAtRange(range, 'image')
.insertInlineAtRange(range, {
type: 'hashtag',
isVoid: true
})
.apply()
}

View File

@ -1,12 +1,10 @@
nodes:
- kind: block
type: image
nodes:
- kind: text
text: ""
- kind: block
type: paragraph
nodes:
- kind: inline
type: hashtag
isVoid: true
- kind: text
text: word

View File

@ -12,6 +12,9 @@ export default function (state) {
return state
.transform()
.insertBlockAtRange(range, 'image')
.insertInlineAtRange(range, {
type: 'hashtag',
isVoid: true
})
.apply()
}

View File

@ -1,7 +1,8 @@
nodes:
- kind: block
type: image
type: paragraph
nodes:
- kind: text
text: ""
- kind: inline
type: hashtag
isVoid: true

View File

@ -12,6 +12,9 @@ export default function (state) {
return state
.transform()
.insertBlockAtRange(range, 'image')
.insertInlineAtRange(range, {
type: 'hashtag',
isVoid: true
})
.apply()
}

View File

@ -3,6 +3,3 @@ nodes:
- kind: block
type: image
isVoid: true
nodes:
- kind: text
text: ""

View File

@ -3,8 +3,3 @@ nodes:
- kind: block
type: image
isVoid: true
- kind: block
type: image
nodes:
- kind: text
text: ""

View File

@ -1,12 +0,0 @@
nodes:
- kind: block
type: image
nodes:
- kind: text
text: ""
- kind: block
type: paragraph
nodes:
- kind: text
text: word

View File

@ -1,5 +1,5 @@
import { Block } from '../../../../..'
import { Inline } from '../../../../..'
export default function (state) {
const { document, selection } = state
@ -14,6 +14,9 @@ export default function (state) {
return state
.transform()
.insertBlockAtRange(range, Block.create({ type: 'image' }))
.insertInlineAtRange(range, Inline.create({
type: 'image',
isVoid: true
}))
.apply()
}

View File

@ -3,5 +3,8 @@ nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: image
isVoid: true
- kind: text
text: word

View File

@ -1,17 +0,0 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 0,
focusKey: first.key,
focusOffset: 0
})
return state
.transform()
.insertBlockAtRange(range, { type: 'image' })
.apply()
}

View File

@ -1,12 +0,0 @@
nodes:
- kind: block
type: image
nodes:
- kind: text
text: ""
- kind: block
type: paragraph
nodes:
- kind: text
text: word

View File

@ -11,11 +11,6 @@ nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: one
nodes:
- kind: text
text: ""
- kind: inline
type: two
nodes:

View File

@ -11,11 +11,6 @@ nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: one
nodes:
- kind: text
text: ""
- kind: inline
type: two
nodes: