1
0
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:
Ian Storm Taylor 2016-06-30 11:13:56 -07:00
parent aba40a2aaf
commit 1069fee13a
38 changed files with 329 additions and 94 deletions

View File

@ -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 })
}

View File

@ -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": [

View File

@ -371,7 +371,7 @@ class Content extends React.Component {
</Component>
)
return node.data.get('isVoid')
return node.isVoid
? this.renderVoid(element, node)
: element
}

View File

@ -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()

View File

@ -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)

View File

@ -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.

View File

@ -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
}

View File

@ -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()
},
/**

View File

@ -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))
})
}

View File

@ -13,6 +13,6 @@ export default function (state) {
return state
.transform()
.setBlockAtRange(range, 'code')
.setBlockAtRange(range, { type: 'code' })
.apply()
}

View File

@ -13,6 +13,6 @@ export default function (state) {
return state
.transform()
.setBlockAtRange(range, 'code')
.setBlockAtRange(range, { type: 'code' })
.apply()
}

View File

@ -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()
}

View File

@ -12,6 +12,6 @@ export default function (state) {
return state
.transform()
.setBlockAtRange(range, 'code')
.setBlockAtRange(range, { type: 'code' })
.apply()
}

View File

@ -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()
}

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: word

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: code
nodes:
- kind: text
ranges:
- text: word

View File

@ -12,6 +12,6 @@ export default function (state) {
return state
.transform()
.setBlockAtRange(range, 'code')
.setBlockAtRange(range, { type: 'code' })
.apply()
}

View File

@ -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()
}

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: word

View File

@ -0,0 +1,10 @@
nodes:
- kind: block
type: code
data:
key: value
nodes:
- kind: text
ranges:
- text: word

View File

@ -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()
}

View File

@ -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()
}

View File

@ -0,0 +1,8 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
ranges:
- text: word

View File

@ -0,0 +1,9 @@
nodes:
- kind: block
type: image
isVoid: true
nodes:
- kind: text
ranges:
- text: ""

View File

@ -13,6 +13,6 @@ export default function (state) {
return state
.transform()
.setInlineAtRange(range, 'code')
.setInlineAtRange(range, { type: 'code' })
.apply()
}

View File

@ -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()
}

View File

@ -12,6 +12,6 @@ export default function (state) {
return state
.transform()
.setInlineAtRange(range, 'code')
.setInlineAtRange(range, { type: 'code' })
.apply()
}

View File

@ -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()
}

View File

@ -0,0 +1,11 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: link
nodes:
- kind: text
ranges:
- text: word

View File

@ -0,0 +1,11 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: code
nodes:
- kind: text
ranges:
- text: word

View File

@ -12,6 +12,6 @@ export default function (state) {
return state
.transform()
.setInlineAtRange(range, 'code')
.setInlineAtRange(range, { type: 'code' })
.apply()
}

View File

@ -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()
}

View File

@ -0,0 +1,11 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: link
nodes:
- kind: text
ranges:
- text: word

View File

@ -0,0 +1,13 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: code
data:
key: value
nodes:
- kind: text
ranges:
- text: word

View File

@ -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()
}

View File

@ -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()
}

View File

@ -0,0 +1,11 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: link
nodes:
- kind: text
ranges:
- text: word

View File

@ -0,0 +1,12 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: inline
type: emoji
isVoid: true
nodes:
- kind: text
ranges:
- text: ""