mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-04-19 21:01:57 +02:00
add isVoid property to nodes
This commit is contained in:
parent
aba40a2aaf
commit
1069fee13a
@ -34,40 +34,25 @@ class Images extends React.Component {
|
||||
}
|
||||
|
||||
const { anchorBlock, selection } = state
|
||||
let transform = state.transform()
|
||||
|
||||
if (anchorBlock.text == '') {
|
||||
state = state
|
||||
.transform()
|
||||
.setBlock('image', { src })
|
||||
.apply()
|
||||
if (anchorBlock.text != '') {
|
||||
if (selection.isAtEndOf(anchorBlock)) {
|
||||
transform = transform.splitBlock()
|
||||
} else if (selection.isAtStartOf(anchorBlock)) {
|
||||
transform = transform.splitBlock().moveToStartOfPreviousBlock()
|
||||
} else {
|
||||
transform = transform.splitBlock().splitBlock().moveToStartOfPreviousBlock()
|
||||
}
|
||||
}
|
||||
|
||||
else if (selection.isAtEndOf(anchorBlock)) {
|
||||
state = state
|
||||
.transform()
|
||||
.splitBlock()
|
||||
.setBlock('image', { src })
|
||||
.apply()
|
||||
}
|
||||
|
||||
else if (selection.isAtStartOf(anchorBlock)) {
|
||||
state = state
|
||||
.transform()
|
||||
.splitBlock()
|
||||
.moveToStartOfPreviousBlock()
|
||||
.setBlock('image', { src })
|
||||
.apply()
|
||||
}
|
||||
|
||||
else {
|
||||
state = state
|
||||
.transform()
|
||||
.splitBlock()
|
||||
.splitBlock()
|
||||
.moveToStartOfPreviousBlock()
|
||||
.setBlock('image', { src })
|
||||
.apply()
|
||||
}
|
||||
state = transform
|
||||
.setBlock({
|
||||
type: 'image',
|
||||
isVoid: true,
|
||||
data: { src }
|
||||
})
|
||||
.apply()
|
||||
|
||||
this.setState({ state })
|
||||
}
|
||||
|
@ -17,8 +17,8 @@
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "image",
|
||||
"isVoid": true,
|
||||
"data": {
|
||||
"isVoid": true,
|
||||
"src": "https://img.washingtonpost.com/wp-apps/imrs.php?src=https://img.washingtonpost.com/news/speaking-of-science/wp-content/uploads/sites/36/2015/10/as12-49-7278-1024x1024.jpg&w=1484"
|
||||
},
|
||||
"nodes": [
|
||||
|
@ -371,7 +371,7 @@ class Content extends React.Component {
|
||||
</Component>
|
||||
)
|
||||
|
||||
return node.data.get('isVoid')
|
||||
return node.isVoid
|
||||
? this.renderVoid(element, node)
|
||||
: element
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import Immutable, { Map, List, Record } from 'immutable'
|
||||
|
||||
const DEFAULTS = {
|
||||
data: new Map(),
|
||||
isVoid: false,
|
||||
key: null,
|
||||
nodes: new List(),
|
||||
type: null
|
||||
@ -49,6 +50,7 @@ class Block extends Record(DEFAULTS) {
|
||||
|
||||
properties.key = uid(4)
|
||||
properties.data = Data.create(properties.data)
|
||||
properties.isVoid = !! properties.isVoid
|
||||
properties.nodes = Block.createList(properties.nodes)
|
||||
|
||||
return new Block(properties).normalize()
|
||||
|
@ -23,6 +23,7 @@ import { List, Map, Record } from 'immutable'
|
||||
|
||||
const DEFAULTS = {
|
||||
data: new Map(),
|
||||
isVoid: false,
|
||||
key: null,
|
||||
nodes: new List(),
|
||||
type: null
|
||||
@ -49,6 +50,7 @@ class Inline extends Record(DEFAULTS) {
|
||||
|
||||
properties.key = uid(4)
|
||||
properties.data = Data.create(properties.data)
|
||||
properties.isVoid = !! properties.isVoid
|
||||
properties.nodes = Inline.createList(properties.nodes)
|
||||
|
||||
let inline = new Inline(properties)
|
||||
|
@ -806,19 +806,30 @@ const Node = {
|
||||
normalize() {
|
||||
let node = this
|
||||
|
||||
// If this node has no children, add a text node.
|
||||
if (node.nodes.size == 0) {
|
||||
const text = Text.create()
|
||||
const nodes = node.nodes.push(text)
|
||||
return node.merge({ nodes })
|
||||
}
|
||||
|
||||
// Map this node's descendants, ensuring there are no duplicate keys.
|
||||
// Map this node's descendants, ensuring... ensuring there are no duplicate keys.
|
||||
const keys = []
|
||||
node = node.mapDescendants((node) => {
|
||||
if (keys.includes(node.key)) node = node.set('key', uid())
|
||||
keys.push(node.key)
|
||||
return node
|
||||
|
||||
node = node.mapDescendants((desc) => {
|
||||
|
||||
// That there are no duplicate keys.
|
||||
if (keys.includes(desc.key)) desc = desc.set('key', uid())
|
||||
keys.push(desc.key)
|
||||
|
||||
// That void nodes contain no text.
|
||||
if (desc.isVoid && desc.length) {
|
||||
const text = Text.create()
|
||||
const nodes = desc.nodes.clear().push(text)
|
||||
desc = desc.merge({ nodes })
|
||||
}
|
||||
|
||||
// That no block or inline node is empty.
|
||||
if (desc.kind != 'text' && desc.nodes.size == 0) {
|
||||
const text = Text.create()
|
||||
const nodes = desc.nodes.push(text)
|
||||
desc = desc.merge({ nodes })
|
||||
}
|
||||
|
||||
return desc
|
||||
})
|
||||
|
||||
// See if there are any adjacent text nodes.
|
||||
|
@ -562,31 +562,31 @@ class State extends Record(DEFAULTS) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block nodes in the current selection to `type`.
|
||||
* Set `properties` of the block nodes in the current selection.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {Object} properties
|
||||
* @return {State} state
|
||||
*/
|
||||
|
||||
setBlock(type, data) {
|
||||
setBlock(properties) {
|
||||
let state = this
|
||||
let { document, selection } = state
|
||||
document = document.setBlockAtRange(selection, type, data)
|
||||
document = document.setBlockAtRange(selection, properties)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the inline nodes in the current selection to `type`.
|
||||
* Set `properties` of the inline nodes in the current selection.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {Object} properties
|
||||
* @return {State} state
|
||||
*/
|
||||
|
||||
setInline(type, data) {
|
||||
setInline(properties) {
|
||||
let state = this
|
||||
let { document, selection } = state
|
||||
document = document.setInlineAtRange(selection, type, data)
|
||||
document = document.setInlineAtRange(selection, properties)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
@ -324,65 +324,57 @@ const Transforms = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the block nodes in a range to `type`, with optional `data`.
|
||||
* Set the `properties` of block nodes in a `range`.
|
||||
*
|
||||
* @param {Selection} range
|
||||
* @param {String} type (optional)
|
||||
* @param {Data} data (optional)
|
||||
* @param {Object or String} properties
|
||||
* @return {Node} node
|
||||
*/
|
||||
|
||||
setBlockAtRange(range, type, data) {
|
||||
setBlockAtRange(range, properties = {}) {
|
||||
let node = this
|
||||
|
||||
// Allow for passing data only.
|
||||
if (typeof type == 'object') {
|
||||
data = type
|
||||
type = null
|
||||
// Allow for properties to be a string `type` for convenience.
|
||||
if (typeof properties == 'string') {
|
||||
properties = { type: properties }
|
||||
}
|
||||
|
||||
// Update each of the blocks.
|
||||
const blocks = node.getBlocksAtRange(range)
|
||||
blocks.forEach((block) => {
|
||||
const obj = {}
|
||||
if (type) obj.type = type
|
||||
if (data) obj.data = Data.create(data)
|
||||
block = block.merge(obj)
|
||||
if (properties.data) properties.data = Data.create(properties.data)
|
||||
block = block.merge(properties)
|
||||
node = node.updateDescendant(block)
|
||||
})
|
||||
|
||||
return node
|
||||
return node.normalize()
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the inline nodes in a range to `type`, with optional `data`.
|
||||
* Set the `properties` of inline nodes in a `range`.
|
||||
*
|
||||
* @param {Selection} range
|
||||
* @param {String} type (optional)
|
||||
* @param {Data} data (optional)
|
||||
* @param {Object or String} properties
|
||||
* @return {Node} node
|
||||
*/
|
||||
|
||||
setInlineAtRange(range, type, data) {
|
||||
setInlineAtRange(range, properties = {}) {
|
||||
let node = this
|
||||
|
||||
// Allow for passing data only.
|
||||
if (typeof type == 'object') {
|
||||
data = type
|
||||
type = null
|
||||
// Allow for properties to be a string `type` for convenience.
|
||||
if (typeof properties == 'string') {
|
||||
properties = { type: properties }
|
||||
}
|
||||
|
||||
// Update each of the inlines.
|
||||
const inlines = node.getInlinesAtRange(range)
|
||||
inlines.forEach((inline) => {
|
||||
const obj = {}
|
||||
if (type) obj.type = type
|
||||
if (data) obj.data = Data.create(data)
|
||||
inline = inline.merge(obj)
|
||||
if (properties.data) properties.data = Data.create(properties.data)
|
||||
inline = inline.merge(properties)
|
||||
node = node.updateDescendant(inline)
|
||||
})
|
||||
|
||||
return node
|
||||
return node.normalize()
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -46,6 +46,7 @@ function serializeNode(node) {
|
||||
obj.kind = node.kind
|
||||
obj.type = node.type
|
||||
obj.nodes = node.nodes.toArray().map(node => serializeNode(node))
|
||||
if (node.isVoid) obj.isVoid = node.isVoid
|
||||
if (node.data.size) obj.data = node.data.toJSON()
|
||||
return obj
|
||||
}
|
||||
@ -112,6 +113,7 @@ function deserializeNode(object) {
|
||||
return Block.create({
|
||||
type: object.type,
|
||||
data: object.data,
|
||||
isVoid: object.isVoid,
|
||||
nodes: Block.createList(object.nodes.map(deserializeNode))
|
||||
})
|
||||
}
|
||||
@ -119,6 +121,7 @@ function deserializeNode(object) {
|
||||
return Inline.create({
|
||||
type: object.type,
|
||||
data: object.data,
|
||||
isVoid: object.isVoid,
|
||||
nodes: Inline.createList(object.nodes.map(deserializeNode))
|
||||
})
|
||||
}
|
||||
|
@ -13,6 +13,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, 'code')
|
||||
.setBlockAtRange(range, { type: 'code' })
|
||||
.apply()
|
||||
}
|
||||
|
@ -13,6 +13,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, 'code')
|
||||
.setBlockAtRange(range, { type: 'code' })
|
||||
.apply()
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { Map } from 'immutable'
|
||||
import { Data } from '../../../../..'
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
@ -14,6 +14,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, new Map({ key: 'value' }))
|
||||
.setBlockAtRange(range, { data: Data.create({ key: 'value' }) })
|
||||
.apply()
|
||||
}
|
||||
|
@ -12,6 +12,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, 'code')
|
||||
.setBlockAtRange(range, { type: 'code' })
|
||||
.apply()
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 0,
|
||||
focusKey: first.key,
|
||||
focusOffset: 0
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, 'code')
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: code
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -12,6 +12,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, 'code')
|
||||
.setBlockAtRange(range, { type: 'code' })
|
||||
.apply()
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 0,
|
||||
focusKey: first.key,
|
||||
focusOffset: 0
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, {
|
||||
type: 'code',
|
||||
data: { key: 'value' }
|
||||
})
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,10 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: code
|
||||
data:
|
||||
key: value
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { Map } from 'immutable'
|
||||
import { Data } from '../../../../..'
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
@ -14,6 +14,9 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, 'code', new Map({ key: 'value' }))
|
||||
.setBlockAtRange(range, {
|
||||
type: 'code',
|
||||
data: Data.create({ key: 'value' })
|
||||
})
|
||||
.apply()
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 0,
|
||||
focusKey: first.key,
|
||||
focusOffset: 0
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setBlockAtRange(range, {
|
||||
type: 'image',
|
||||
isVoid: true
|
||||
})
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,9 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: image
|
||||
isVoid: true
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: ""
|
@ -13,6 +13,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setInlineAtRange(range, 'code')
|
||||
.setInlineAtRange(range, { type: 'code' })
|
||||
.apply()
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { Map } from 'immutable'
|
||||
import { Data } from '../../../../..'
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
@ -14,6 +14,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setInlineAtRange(range, new Map({ key: 'value' }))
|
||||
.setInlineAtRange(range, { data: Data.create({ key: 'value' }) })
|
||||
.apply()
|
||||
}
|
||||
|
@ -12,6 +12,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setInlineAtRange(range, 'code')
|
||||
.setInlineAtRange(range, { type: 'code' })
|
||||
.apply()
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 0,
|
||||
focusKey: first.key,
|
||||
focusOffset: 0
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setInlineAtRange(range, 'code')
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: inline
|
||||
type: link
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,11 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: inline
|
||||
type: code
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -12,6 +12,6 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setInlineAtRange(range, 'code')
|
||||
.setInlineAtRange(range, { type: 'code' })
|
||||
.apply()
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 0,
|
||||
focusKey: first.key,
|
||||
focusOffset: 0
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setInlineAtRange(range, {
|
||||
type: 'code',
|
||||
data: { key: 'value' }
|
||||
})
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: inline
|
||||
type: link
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,13 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: inline
|
||||
type: code
|
||||
data:
|
||||
key: value
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { Map } from 'immutable'
|
||||
import { Data } from '../../../../..'
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
@ -14,6 +14,9 @@ export default function (state) {
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setInlineAtRange(range, 'code', new Map({ key: 'value' }))
|
||||
.setInlineAtRange(range, {
|
||||
type: 'code',
|
||||
data: Data.create({ key: 'value' })
|
||||
})
|
||||
.apply()
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
|
||||
export default function (state) {
|
||||
const { document, selection } = state
|
||||
const texts = document.getTextNodes()
|
||||
const first = texts.first()
|
||||
const range = selection.merge({
|
||||
anchorKey: first.key,
|
||||
anchorOffset: 0,
|
||||
focusKey: first.key,
|
||||
focusOffset: 0
|
||||
})
|
||||
|
||||
return state
|
||||
.transform()
|
||||
.setInlineAtRange(range, {
|
||||
type: 'emoji',
|
||||
isVoid: true
|
||||
})
|
||||
.apply()
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: inline
|
||||
type: link
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: word
|
@ -0,0 +1,12 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: paragraph
|
||||
nodes:
|
||||
- kind: inline
|
||||
type: emoji
|
||||
isVoid: true
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: ""
|
Loading…
x
Reference in New Issue
Block a user