mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-22 06:53:25 +02:00
refactor normalization of arguments with better error handling
This commit is contained in:
@@ -7,6 +7,7 @@ import Inline from './inline'
|
||||
import Mark from './mark'
|
||||
import Selection from './selection'
|
||||
import Text from './text'
|
||||
import typeOf from 'type-of'
|
||||
import uid from '../utils/uid'
|
||||
import { List, Map, Set } from 'immutable'
|
||||
|
||||
@@ -22,7 +23,7 @@ const Transforms = {
|
||||
* Add a new `mark` to the characters at `range`.
|
||||
*
|
||||
* @param {Selection} range
|
||||
* @param {Mark or String} mark
|
||||
* @param {Mark or String or Object} mark
|
||||
* @return {Node} node
|
||||
*/
|
||||
|
||||
@@ -216,33 +217,28 @@ const Transforms = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert a block `node` at `range`.
|
||||
* Insert a `block` node at `range`.
|
||||
*
|
||||
* @param {Selection} range
|
||||
* @param {Node} node
|
||||
* @param {Block or String or Object} block
|
||||
* @return {Node} node
|
||||
*/
|
||||
|
||||
insertBlockAtRange(range, node) {
|
||||
let doc = this
|
||||
insertBlockAtRange(range, block) {
|
||||
block = normalizeBlock(block)
|
||||
let node = this
|
||||
|
||||
// If expanded, delete the range first.
|
||||
if (range.isExpanded) {
|
||||
doc = doc.deleteAtRange(range)
|
||||
node = node.deleteAtRange(range)
|
||||
range = range.collapseToStart()
|
||||
}
|
||||
|
||||
// Allow for passing just a type string.
|
||||
if (typeof node == 'string') node = { type: node }
|
||||
|
||||
// Allow for passing a plain object of properties.
|
||||
node = Block.create(node)
|
||||
|
||||
const { startKey, startOffset } = range
|
||||
let startBlock = doc.getClosestBlock(startKey)
|
||||
let parent = doc.getParent(startBlock)
|
||||
let nodes = Block.createList([node])
|
||||
const isParent = parent == doc
|
||||
let startBlock = node.getClosestBlock(startKey)
|
||||
let parent = node.getParent(startBlock)
|
||||
let nodes = Block.createList([block])
|
||||
const isParent = parent == node
|
||||
|
||||
// If the start block is void, insert after it.
|
||||
if (startBlock.isVoid) {
|
||||
@@ -268,21 +264,21 @@ const Transforms = {
|
||||
|
||||
// Otherwise, split the block and insert between.
|
||||
else {
|
||||
doc = doc.splitBlockAtRange(range)
|
||||
parent = doc.getParent(startBlock)
|
||||
startBlock = doc.getClosestBlock(startKey)
|
||||
node = node.splitBlockAtRange(range)
|
||||
parent = node.getParent(startBlock)
|
||||
startBlock = node.getClosestBlock(startKey)
|
||||
nodes = parent.nodes.takeUntil(n => n == startBlock)
|
||||
.push(startBlock)
|
||||
.push(node)
|
||||
.push(block)
|
||||
.concat(parent.nodes.skipUntil(n => n == startBlock).rest())
|
||||
parent = parent.merge({ nodes })
|
||||
}
|
||||
|
||||
doc = isParent
|
||||
node = isParent
|
||||
? parent
|
||||
: doc.updateDescendant(parent)
|
||||
: node.updateDescendant(parent)
|
||||
|
||||
return doc.normalize()
|
||||
return node.normalize()
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -385,51 +381,46 @@ const Transforms = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert an inline `node` at `range`.
|
||||
* Insert an `inline` node at `range`.
|
||||
*
|
||||
* @param {Selection} range
|
||||
* @param {Node} node
|
||||
* @param {Inline or String or Object} inline
|
||||
* @return {Node} node
|
||||
*/
|
||||
|
||||
insertInlineAtRange(range, node) {
|
||||
let doc = this
|
||||
insertInlineAtRange(range, inline) {
|
||||
inline = normalizeInline(inline)
|
||||
let node = this
|
||||
|
||||
// If expanded, delete the range first.
|
||||
if (range.isExpanded) {
|
||||
doc = doc.deleteAtRange(range)
|
||||
node = node.deleteAtRange(range)
|
||||
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 startBlock = node.getClosestBlock(startKey)
|
||||
if (startBlock && startBlock.isVoid) return node
|
||||
|
||||
const inline = doc.getClosestInline(startKey)
|
||||
if (inline && inline.isVoid) return doc
|
||||
|
||||
// Allow for passing a type string.
|
||||
if (typeof node == 'string') node = { type: node }
|
||||
|
||||
// Allow for passing a plain object of properties.
|
||||
node = Inline.create(node)
|
||||
const startInline = node.getClosestInline(startKey)
|
||||
if (startInline && startInline.isVoid) return node
|
||||
|
||||
// Split the text nodes at the cursor.
|
||||
doc = doc.splitTextAtRange(range)
|
||||
node = node.splitTextAtRange(range)
|
||||
|
||||
// Insert the node between the split text nodes.
|
||||
const startText = doc.getDescendant(startKey)
|
||||
let parent = doc.getParent(startKey)
|
||||
// Insert the inline between the split text nodes.
|
||||
const startText = node.getDescendant(startKey)
|
||||
let parent = node.getParent(startKey)
|
||||
const nodes = parent.nodes.takeUntil(n => n == startText)
|
||||
.push(startText)
|
||||
.push(node)
|
||||
.push(inline)
|
||||
.concat(parent.nodes.skipUntil(n => n == startText).rest())
|
||||
|
||||
parent = parent.merge({ nodes })
|
||||
doc = doc.updateDescendant(parent)
|
||||
return doc.normalize()
|
||||
node = node.updateDescendant(parent)
|
||||
return node.normalize()
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -521,20 +512,10 @@ const Transforms = {
|
||||
*/
|
||||
|
||||
setBlockAtRange(range, properties = {}) {
|
||||
properties = normalizeProperties(properties)
|
||||
let node = this
|
||||
|
||||
// Allow for properties to be a string `type` for convenience.
|
||||
if (typeof properties == 'string') {
|
||||
properties = { type: properties }
|
||||
}
|
||||
if (properties.data) {
|
||||
properties.data = Data.create(properties.data)
|
||||
} else {
|
||||
delete properties.data
|
||||
}
|
||||
|
||||
// Update each of the blocks.
|
||||
const blocks = node.getBlocksAtRange(range)
|
||||
|
||||
blocks.forEach((block) => {
|
||||
block = block.merge(properties)
|
||||
node = node.updateDescendant(block)
|
||||
@@ -552,17 +533,11 @@ const Transforms = {
|
||||
*/
|
||||
|
||||
setInlineAtRange(range, properties = {}) {
|
||||
properties = normalizeProperties(properties)
|
||||
let node = this
|
||||
|
||||
// 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) => {
|
||||
if (properties.data) properties.data = Data.create(properties.data)
|
||||
inline = inline.merge(properties)
|
||||
node = node.updateDescendant(inline)
|
||||
})
|
||||
@@ -579,15 +554,9 @@ const Transforms = {
|
||||
*/
|
||||
|
||||
setNodeByKey(key, properties) {
|
||||
properties = normalizeProperties(properties)
|
||||
let node = this
|
||||
let descendant = node.assertDescendant(key)
|
||||
|
||||
// Allow for properties to be a string `type` for convenience.
|
||||
if (typeof properties == 'string') properties = { type: properties }
|
||||
|
||||
// Ensure that `data` is immutable.
|
||||
if (properties.data) properties.data = Data.create(properties.data)
|
||||
|
||||
descendant = descendant.merge(properties)
|
||||
node = node.updateDescendant(descendant)
|
||||
return node
|
||||
@@ -1093,13 +1062,103 @@ function isInRange(index, text, range) {
|
||||
*/
|
||||
|
||||
function normalizeMark(mark) {
|
||||
if (typeof mark == 'string') {
|
||||
return Mark.create({ type: mark })
|
||||
} else {
|
||||
return Mark.create(mark)
|
||||
if (mark instanceof Mark) return mark
|
||||
|
||||
const type = typeOf(mark)
|
||||
|
||||
switch (type) {
|
||||
case 'string':
|
||||
case 'object': {
|
||||
return Mark.create(normalizeProperties(mark))
|
||||
}
|
||||
default: {
|
||||
throw new Error(`A \`mark\` argument must be a mark, an object or a string, but you passed: "${type}".`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a `block` argument, which can be a string or plain object too.
|
||||
*
|
||||
* @param {Block or String or Object} block
|
||||
* @return {Block}
|
||||
*/
|
||||
|
||||
function normalizeBlock(block) {
|
||||
if (block instanceof Block) return block
|
||||
|
||||
const type = typeOf(block)
|
||||
|
||||
switch (type) {
|
||||
case 'string':
|
||||
case 'object': {
|
||||
return Block.create(normalizeProperties(block))
|
||||
}
|
||||
default: {
|
||||
throw new Error(`A \`block\` argument must be a block, an object or a string, but you passed: "${type}".`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize an `inline` argument, which can be a string or plain object too.
|
||||
*
|
||||
* @param {Inline or String or Object} inline
|
||||
* @return {Inline}
|
||||
*/
|
||||
|
||||
function normalizeInline(inline) {
|
||||
if (inline instanceof Inline) return inline
|
||||
|
||||
const type = typeOf(inline)
|
||||
|
||||
switch (type) {
|
||||
case 'string':
|
||||
case 'object': {
|
||||
return Inline.create(normalizeProperties(inline))
|
||||
}
|
||||
default: {
|
||||
throw new Error(`An \`inline\` argument must be an inline, an object or a string, but you passed: "${type}".`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the `properties` of a node or mark, which can be either a type
|
||||
* string or a dictionary of properties. If it's a dictionary, `data` is
|
||||
* optional and shouldn't be set if null or undefined.
|
||||
*
|
||||
* @param {String or Object} properties
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
function normalizeProperties(properties = {}) {
|
||||
const ret = {}
|
||||
const type = typeOf(properties)
|
||||
|
||||
switch (type) {
|
||||
case 'string': {
|
||||
ret.type = properties
|
||||
break
|
||||
}
|
||||
case 'object': {
|
||||
for (const key in properties) {
|
||||
if (key == 'data') {
|
||||
if (properties[key] != null) ret[key] = Data.create(properties[key])
|
||||
} else {
|
||||
ret[key] = properties[key]
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
default: {
|
||||
throw new Error(`A \`properties\` argument must be an object or a string, but you passed: "${type}".`)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*/
|
||||
|
@@ -14,6 +14,7 @@
|
||||
"keycode": "^2.1.2",
|
||||
"lodash": "^4.13.1",
|
||||
"react-portal": "^2.2.0",
|
||||
"type-of": "^2.0.1",
|
||||
"ua-parser-js": "^0.7.10",
|
||||
"uid": "0.0.2"
|
||||
},
|
||||
|
Reference in New Issue
Block a user