1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-01 13:18:29 +01:00

remove the void text content restriction, closes #1504 (#1663)

This commit is contained in:
Ian Storm Taylor 2018-02-21 18:03:41 -08:00 committed by GitHub
parent c5f0626a05
commit 514f3de1be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 82 additions and 150 deletions

View File

@ -10,4 +10,4 @@ export const input = (
</value>
)
export const output = ' '
export const output = ''

View File

@ -12,4 +12,4 @@ export const input = (
</value>
)
export const output = ' '
export const output = ''

View File

@ -116,28 +116,38 @@ class Leaf extends React.Component {
renderText() {
const { block, node, parent, text, index, leaves } = this.props
// COMPAT: If the text is empty and the only child, we need to render a
// line break when copying and pasting to support expected plain text.
if (text == '' && parent.kind == 'block' && parent.text == '') {
return <span data-slate-zero-width="n">{'\u200B'}</span>
}
// COMPAT: Render text inside void nodes with a zero-width space.
// So the node can contain selection but the text is not visible.
if (parent.isVoid) return <span data-slate-zero-width="z">{'\u200B'}</span>
if (parent.isVoid) {
return <span data-slate-zero-width="z">{'\u200B'}</span>
}
// COMPAT: If this is the last text node in an empty block, render a zero-
// width space that will convert into a line break when copying and pasting
// to support expected plain text.
if (
text === '' &&
parent.kind === 'block' &&
parent.text === '' &&
parent.nodes.size === 1
) {
return <span data-slate-zero-width="n">{'\u200B'}</span>
}
// COMPAT: If the text is empty, it's because it's on the edge of an inline
// void node, so we render a zero-width space so that the selection can be
// inserted next to it still.
if (text == '') return <span data-slate-zero-width="z">{'\u200B'}</span>
if (text === '') {
return <span data-slate-zero-width="z">{'\u200B'}</span>
}
// 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 lastText = block.getLastText()
const lastChar = text.charAt(text.length - 1)
const isLastText = node == lastText
const isLastLeaf = index == leaves.size - 1
if (isLastText && isLastLeaf && lastChar == '\n') return `${text}\n`
const isLastText = node === lastText
const isLastLeaf = index === leaves.size - 1
if (isLastText && isLastLeaf && lastChar === '\n') return `${text}\n`
// Otherwise, just return the text.
return text

View File

@ -298,25 +298,8 @@ Changes.deleteLineBackwardAtRange = (change, range, options) => {
const { startKey, startOffset } = range
const startBlock = document.getClosestBlock(startKey)
const offset = startBlock.getOffset(startKey)
const startWithVoidInline =
startBlock.nodes.size > 1 &&
startBlock.nodes.get(0).text == '' &&
startBlock.nodes.get(1).object == 'inline'
let o = offset + startOffset
// If line starts with an void inline node, the text node inside this inline
// node disturbs the offset. Ignore this inline node and delete it afterwards.
if (startWithVoidInline) {
o -= 1
}
const o = offset + startOffset
change.deleteBackwardAtRange(range, o, options)
// Delete the remaining first inline node if needed.
if (startWithVoidInline) {
change.deleteBackward()
}
}
/**
@ -362,27 +345,22 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
return
}
const voidParent = document.getClosestVoid(startKey)
// If there is a void parent, delete it.
if (voidParent) {
change.removeNodeByKey(voidParent.key, { normalize })
return
}
const block = document.getClosestBlock(startKey)
// If the closest block is void, delete it.
if (block && block.isVoid) {
change.removeNodeByKey(block.key, { normalize })
return
}
// If the closest is not void, but empty, remove it
if (block && !block.isVoid && block.isEmpty && document.nodes.size !== 1) {
if (block && block.isEmpty && document.nodes.size !== 1) {
change.removeNodeByKey(block.key, { normalize })
return
}
// If the closest inline is void, delete it.
const inline = document.getClosestInline(startKey)
if (inline && inline.isVoid) {
change.removeNodeByKey(inline.key, { normalize })
return
}
// If the range is at the start of the document, abort.
if (range.isAtStartOf(document)) {
return
@ -394,17 +372,11 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
if (range.isAtStartOf(text)) {
const prev = document.getPreviousText(text.key)
const prevBlock = document.getClosestBlock(prev.key)
const prevInline = document.getClosestInline(prev.key)
const prevVoid = document.getClosestVoid(prev.key)
// If the previous block is void, remove it.
if (prevBlock && prevBlock.isVoid) {
change.removeNodeByKey(prevBlock.key, { normalize })
return
}
// If the previous inline is void, remove it.
if (prevInline && prevInline.isVoid) {
change.removeNodeByKey(prevInline.key, { normalize })
// If the previous text node has a void parent, remove it.
if (prevVoid) {
change.removeNodeByKey(prevVoid.key, { normalize })
return
}
@ -449,13 +421,6 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
}
}
// If the focus node is inside a void, go up until right after it.
if (document.hasVoidParent(node.key)) {
const parent = document.getClosestVoid(node.key)
node = document.getNextText(parent.key)
offset = 0
}
range = range.merge({
focusKey: node.key,
focusOffset: offset,
@ -548,16 +513,18 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
return
}
const block = document.getClosestBlock(startKey)
const voidParent = document.getClosestVoid(startKey)
// If the closest block is void, delete it.
if (block && block.isVoid) {
change.removeNodeByKey(block.key, { normalize })
// If the node has a void parent, delete it.
if (voidParent) {
change.removeNodeByKey(voidParent.key, { normalize })
return
}
const block = document.getClosestBlock(startKey)
// If the closest is not void, but empty, remove it
if (block && !block.isVoid && block.isEmpty && document.nodes.size !== 1) {
if (block && block.isEmpty && document.nodes.size !== 1) {
const nextBlock = document.getNextBlock(block.key)
change.removeNodeByKey(block.key, { normalize })
if (nextBlock && nextBlock.key) {
@ -566,13 +533,6 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
return
}
// If the closest inline is void, delete it.
const inline = document.getClosestInline(startKey)
if (inline && inline.isVoid) {
change.removeNodeByKey(inline.key, { normalize })
return
}
// If the range is at the start of the document, abort.
if (range.isAtEndOf(document)) {
return
@ -584,17 +544,11 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
if (range.isAtEndOf(text)) {
const next = document.getNextText(text.key)
const nextBlock = document.getClosestBlock(next.key)
const nextInline = document.getClosestInline(next.key)
const nextVoid = document.getClosestVoid(next.key)
// If the previous block is void, remove it.
if (nextBlock && nextBlock.isVoid) {
change.removeNodeByKey(nextBlock.key, { normalize })
return
}
// If the previous inline is void, remove it.
if (nextInline && nextInline.isVoid) {
change.removeNodeByKey(nextInline.key, { normalize })
// If the next text node has a void parent, remove it.
if (nextVoid) {
change.removeNodeByKey(nextVoid.key, { normalize })
return
}

View File

@ -93,32 +93,7 @@ const CORE_SCHEMA_RULES = [
},
/**
* Ensure that void nodes contain a text node with a single space of text.
*
* @type {Object}
*/
{
validateNode(node) {
if (!node.isVoid) return
if (node.object != 'block' && node.object != 'inline') return
if (node.text == ' ' && node.nodes.size == 1) return
return change => {
const text = Text.create(' ')
const index = node.nodes.size
change.insertNodeByKey(node.key, index, text, { normalize: false })
node.nodes.forEach(child => {
change.removeNodeByKey(child.key, { normalize: false })
})
}
},
},
/**
* Ensure that inline nodes are never empty.
* Ensure that inline non-void nodes are never empty.
*
* This rule is applied to all blocks, because when they contain an empty
* inline, we need to remove the inline from that parent block. If `validate`
@ -131,9 +106,11 @@ const CORE_SCHEMA_RULES = [
{
validateNode(node) {
if (node.object != 'block') return
const invalids = node.nodes.filter(
n => n.object == 'inline' && n.text == ''
n => n.object === 'inline' && n.isVoid === false && n.text === ''
)
if (!invalids.size) return
return change => {
@ -167,7 +144,9 @@ const CORE_SCHEMA_RULES = [
const prev = index > 0 ? node.nodes.get(index - 1) : null
const next = node.nodes.get(index + 1)
// We don't test if "prev" is inline, since it has already been processed in the loop
// We don't test if "prev" is inline, since it has already been
// processed in the loop
const insertBefore = !prev
const insertAfter = !next || next.object == 'inline'

View File

@ -1692,7 +1692,7 @@ class Node {
*/
hasVoidParent(key) {
return !!this.getClosest(key, parent => parent.isVoid)
return !!this.getClosestVoid(key)
}
/**
@ -2071,7 +2071,6 @@ memoize(
'getPreviousText',
'getTextAtOffset',
'getTextsAtRangeAsArray',
'hasVoidParent',
'validate',
],
{

View File

@ -21,7 +21,6 @@ export const output = (
<value>
<document>
<image>
{' '}
<cursor />
</image>
</document>

View File

@ -21,7 +21,7 @@ export const output = (
<value>
<document>
<image>
<cursor />{' '}
<cursor />
</image>
</document>
</value>

View File

@ -13,7 +13,7 @@ export const input = (
<anchor />one
</paragraph>
<image>
<focus />{' '}
<focus />
</image>
<paragraph>two</paragraph>
</document>

View File

@ -13,7 +13,7 @@ export const input = (
on<anchor />e
</paragraph>
<image>
<focus />{' '}
<focus />
</image>
<paragraph>three</paragraph>
</document>

View File

@ -13,7 +13,6 @@ export const input = (
on<anchor />e
</paragraph>
<image>
{' '}
<focus />
</image>
<paragraph>three</paragraph>

View File

@ -10,7 +10,6 @@ export const input = (
<value>
<document>
<image>
{' '}
<anchor />
</image>
<paragraph>

View File

@ -10,10 +10,10 @@ export const input = (
<value>
<document>
<image>
<anchor />{' '}
<anchor />
</image>
<image>
<focus />{' '}
<focus />
</image>
<paragraph>one</paragraph>
<paragraph>two</paragraph>

View File

@ -11,7 +11,7 @@ export const input = (
<document>
<paragraph>
<emoji>
<anchor />{' '}
<anchor />
</emoji>
<focus />abc
</paragraph>

View File

@ -10,8 +10,7 @@ export const input = (
<value>
<document>
<image>
{' '}
<cursor />
text<cursor />
</image>
<paragraph>text</paragraph>
</document>
@ -21,7 +20,7 @@ export const input = (
export const output = (
<value>
<document>
<image />
<image>text</image>
<quote>
<cursor />
</quote>

View File

@ -10,7 +10,7 @@ export const input = (
<value>
<document>
<image>
<cursor />{' '}
<cursor />text
</image>
<paragraph>text</paragraph>
</document>
@ -23,7 +23,7 @@ export const output = (
<quote>
<cursor />
</quote>
<image />
<image>text</image>
<paragraph>text</paragraph>
</document>
</value>

View File

@ -24,7 +24,6 @@ export const output = (
<document>
<paragraph>
word<emoji>
{' '}
<cursor />
</emoji>
</paragraph>

View File

@ -24,7 +24,6 @@ export const output = (
<document>
<paragraph>
wo<emoji>
{' '}
<cursor />
</emoji>rd
</paragraph>

View File

@ -24,7 +24,6 @@ export const output = (
<document>
<paragraph>
<emoji>
{' '}
<cursor />
</emoji>word
</paragraph>

View File

@ -27,7 +27,6 @@ export const output = (
<paragraph>
<link>
wo<emoji>
{' '}
<cursor />
</emoji>rd
</link>

View File

@ -24,7 +24,6 @@ export const output = (
<document>
<paragraph>
<emoji>
{' '}
<cursor />
</emoji>
</paragraph>

View File

@ -27,7 +27,6 @@ export const output = (
<document>
<paragraph>
wo<emoji>
{' '}
<cursor />
</emoji>rd
</paragraph>

View File

@ -23,7 +23,7 @@ export const output = (
<value>
<document>
<image>
<cursor />{' '}
<cursor />word
</image>
</document>
</value>

View File

@ -21,13 +21,13 @@ export const input = (
</value>
)
// TODO: fix cursor placement
export const output = (
<value>
<document>
<paragraph>
<cursor />
<emoji />
<emoji>
<cursor />word
</emoji>
</paragraph>
</document>
</value>

View File

@ -12,7 +12,7 @@ export const input = (
<paragraph>
<emoji>
<text key="a">
<cursor />{' '}
<cursor />a
</text>
</emoji>
</paragraph>
@ -24,8 +24,9 @@ export const output = (
<value>
<document>
<paragraph>
<cursor />
<emoji />
<emoji>
<cursor />
</emoji>
</paragraph>
</document>
</value>

View File

@ -25,8 +25,9 @@ export const output = (
<value>
<document>
<paragraph>
<cursor />
<emoji />
<emoji>
<cursor />word
</emoji>
</paragraph>
</document>
</value>

View File

@ -28,7 +28,6 @@ export const output = (
<value>
<document>
<image>
{' '}
<cursor />
</image>
<paragraph>one</paragraph>

View File

@ -28,7 +28,6 @@ export const output = (
<value>
<document>
<image>
{' '}
<cursor />
</image>
<paragraph>one</paragraph>

View File

@ -21,7 +21,7 @@ export const input = (
export const output = (
<value>
<document>
<paragraph>{/* prettier-ignore */ ' '}</paragraph>
<paragraph />
</document>
</value>
)

View File

@ -19,7 +19,7 @@ export const input = {
leaves: [
{
object: 'leaf',
text: ' ',
text: '',
marks: [],
},
],

View File

@ -35,7 +35,7 @@ export const input = {
leaves: [
{
object: 'leaf',
text: ' ',
text: '',
marks: [],
},
],

View File

@ -27,7 +27,7 @@ export const output = {
leaves: [
{
object: 'leaf',
text: ' ',
text: '',
marks: [],
},
],

View File

@ -45,7 +45,7 @@ export const output = {
leaves: [
{
object: 'leaf',
text: ' ',
text: '',
marks: [],
},
],