mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-17 20:51:20 +02:00
Convert remaining Node.get*
methods to use paths (#2557)
* tests * faster getSelectionIndeces by passing path during render * rename var * fix variables * revert node component changes * revert changes to content * changes to element and path-utils * fix point normalize * cleanup * more cleanup, clearer getDescendant
This commit is contained in:
@@ -4,8 +4,8 @@
|
|||||||
const h = require('../../helpers/h')
|
const h = require('../../helpers/h')
|
||||||
const { Editor } = require('slate')
|
const { Editor } = require('slate')
|
||||||
|
|
||||||
module.exports.default = function(change) {
|
module.exports.default = function(editor) {
|
||||||
change
|
editor
|
||||||
.normalize()
|
.normalize()
|
||||||
.moveForward(5)
|
.moveForward(5)
|
||||||
.normalize()
|
.normalize()
|
||||||
|
@@ -112,7 +112,7 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively find all descendant nodes by `iterator`.
|
* Recursively find a descendant node by `iterator`.
|
||||||
*
|
*
|
||||||
* @param {Function} iterator
|
* @param {Function} iterator
|
||||||
* @return {Node|Null}
|
* @return {Node|Null}
|
||||||
@@ -131,6 +131,45 @@ class ElementInterface {
|
|||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively find a descendant node and its path by `iterator`.
|
||||||
|
*
|
||||||
|
* @param {Function} iterator
|
||||||
|
* @return {Null|[Node, List]}
|
||||||
|
*/
|
||||||
|
|
||||||
|
findDescendantAndPath(
|
||||||
|
iterator,
|
||||||
|
pathToThisNode = PathUtils.create([]),
|
||||||
|
findLast = false
|
||||||
|
) {
|
||||||
|
let found
|
||||||
|
let foundPath
|
||||||
|
|
||||||
|
this.forEachDescendantWithPath(
|
||||||
|
(node, path, nodes) => {
|
||||||
|
if (iterator(node, path, nodes)) {
|
||||||
|
found = node
|
||||||
|
foundPath = path
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pathToThisNode,
|
||||||
|
findLast
|
||||||
|
)
|
||||||
|
|
||||||
|
return found ? [found, foundPath] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Easy helpers to avoid needing to pass findLast boolean
|
||||||
|
findFirstDescendantAndPath(iterator, pathToThisNode) {
|
||||||
|
return this.findDescendantAndPath(iterator, pathToThisNode, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
findLastDescendantAndPath(iterator, pathToThisNode) {
|
||||||
|
return this.findDescendantAndPath(iterator, pathToThisNode, true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively iterate over all descendant nodes with `iterator`. If the
|
* Recursively iterate over all descendant nodes with `iterator`. If the
|
||||||
* iterator returns false it will break the loop.
|
* iterator returns false it will break the loop.
|
||||||
@@ -156,6 +195,39 @@ class ElementInterface {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively iterate over all descendant nodes with `iterator`. If the
|
||||||
|
* iterator returns false it will break the loop.
|
||||||
|
* Calls iterator with node and path.
|
||||||
|
*
|
||||||
|
* @param {Function} iterator
|
||||||
|
* @param {List} path
|
||||||
|
* @param {Boolean} findLast - whether to iterate in reverse order
|
||||||
|
*/
|
||||||
|
|
||||||
|
forEachDescendantWithPath(iterator, path = PathUtils.create([]), findLast) {
|
||||||
|
let nodes = this.nodes
|
||||||
|
let ret
|
||||||
|
|
||||||
|
if (findLast) nodes = nodes.reverse()
|
||||||
|
|
||||||
|
nodes.forEach((child, i) => {
|
||||||
|
const childPath = path.concat(i)
|
||||||
|
|
||||||
|
if (iterator(child, childPath, nodes) === false) {
|
||||||
|
ret = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child.object !== 'text') {
|
||||||
|
ret = child.forEachDescendantWithPath(iterator, childPath, findLast)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a set of the active marks in a `range`.
|
* Get a set of the active marks in a `range`.
|
||||||
*
|
*
|
||||||
@@ -169,31 +241,33 @@ class ElementInterface {
|
|||||||
|
|
||||||
if (range.isCollapsed) {
|
if (range.isCollapsed) {
|
||||||
const { start } = range
|
const { start } = range
|
||||||
return this.getMarksAtPosition(start.key, start.offset).toSet()
|
return this.getMarksAtPosition(start.path, start.offset).toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { start, end } = range
|
const { start, end } = range
|
||||||
let startKey = start.key
|
let startPath = start.path
|
||||||
let startOffset = start.offset
|
let startOffset = start.offset
|
||||||
let endKey = end.key
|
let endPath = end.path
|
||||||
let endOffset = end.offset
|
let endOffset = end.offset
|
||||||
let startText = this.getDescendant(startKey)
|
let startText = this.getDescendant(startPath)
|
||||||
|
let endText = this.getDescendant(endPath)
|
||||||
|
|
||||||
if (startKey !== endKey) {
|
if (!PathUtils.isEqual(startPath, endPath)) {
|
||||||
while (startKey !== endKey && endOffset === 0) {
|
while (!PathUtils.isEqual(startPath, endPath) && endOffset === 0) {
|
||||||
const endText = this.getPreviousText(endKey)
|
;[endText, endPath] = this.getPreviousTextAndPath(endPath)
|
||||||
endKey = endText.key
|
|
||||||
endOffset = endText.text.length
|
endOffset = endText.text.length
|
||||||
}
|
}
|
||||||
|
|
||||||
while (startKey !== endKey && startOffset === startText.text.length) {
|
while (
|
||||||
startText = this.getNextText(startKey)
|
!PathUtils.isEqual(startPath, endPath) &&
|
||||||
startKey = startText.key
|
startOffset === startText.text.length
|
||||||
|
) {
|
||||||
|
;[startText, startPath] = this.getNextTextAndPath(startPath)
|
||||||
startOffset = 0
|
startOffset = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startKey === endKey) {
|
if (PathUtils.isEqual(startPath, endPath)) {
|
||||||
return startText.getActiveMarksBetweenOffsets(startOffset, endOffset)
|
return startText.getActiveMarksBetweenOffsets(startOffset, endOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,21 +276,23 @@ class ElementInterface {
|
|||||||
startText.text.length
|
startText.text.length
|
||||||
)
|
)
|
||||||
if (startMarks.size === 0) return Set()
|
if (startMarks.size === 0) return Set()
|
||||||
const endText = this.getDescendant(endKey)
|
|
||||||
const endMarks = endText.getActiveMarksBetweenOffsets(0, endOffset)
|
const endMarks = endText.getActiveMarksBetweenOffsets(0, endOffset)
|
||||||
let marks = startMarks.intersect(endMarks)
|
let marks = startMarks.intersect(endMarks)
|
||||||
|
|
||||||
// If marks is already empty, the active marks is empty
|
// If marks is already empty, the active marks is empty
|
||||||
if (marks.size === 0) return marks
|
if (marks.size === 0) {
|
||||||
|
return marks
|
||||||
|
}
|
||||||
|
|
||||||
let text = this.getNextText(startKey)
|
;[startText, startPath] = this.getNextTextAndPath(startPath)
|
||||||
|
|
||||||
while (text.key !== endKey) {
|
while (!PathUtils.isEqual(startPath, endPath)) {
|
||||||
if (text.text.length !== 0) {
|
if (startText.text.length !== 0) {
|
||||||
marks = marks.intersect(text.getActiveMarks())
|
marks = marks.intersect(startText.getActiveMarks())
|
||||||
if (marks.size === 0) return Set()
|
if (marks.size === 0) return Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
text = this.getNextText(text.key)
|
;[startText, startPath] = this.getNextTextAndPath(startPath)
|
||||||
}
|
}
|
||||||
return marks
|
return marks
|
||||||
}
|
}
|
||||||
@@ -342,8 +418,8 @@ class ElementInterface {
|
|||||||
|
|
||||||
getChild(path) {
|
getChild(path) {
|
||||||
path = this.resolvePath(path)
|
path = this.resolvePath(path)
|
||||||
if (!path) return null
|
if (!path || path.size > 1) return null
|
||||||
const child = path.size === 1 ? this.nodes.get(path.first()) : null
|
const child = this.nodes.get(path.first())
|
||||||
return child
|
return child
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,11 +551,16 @@ class ElementInterface {
|
|||||||
|
|
||||||
getDescendant(path) {
|
getDescendant(path) {
|
||||||
path = this.resolvePath(path)
|
path = this.resolvePath(path)
|
||||||
if (!path) return null
|
if (!path || !path.size) return null
|
||||||
|
|
||||||
const deep = path.flatMap(x => ['nodes', x])
|
let node = this
|
||||||
const ret = this.getIn(deep)
|
|
||||||
return ret
|
path.forEach(index => {
|
||||||
|
node = node.getIn(['nodes', index])
|
||||||
|
return !!node
|
||||||
|
})
|
||||||
|
|
||||||
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -546,14 +627,14 @@ class ElementInterface {
|
|||||||
/**
|
/**
|
||||||
* Get the furthest ancestor of a node.
|
* Get the furthest ancestor of a node.
|
||||||
*
|
*
|
||||||
* @param {Path} path
|
* @param {List|String} path
|
||||||
* @return {Node|Null}
|
* @return {Node|Null}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getFurthestAncestor(path) {
|
getFurthestAncestor(path) {
|
||||||
path = this.resolvePath(path)
|
path = this.resolvePath(path)
|
||||||
if (!path) return null
|
if (!path || !path.size) return null
|
||||||
const furthest = path.size ? this.nodes.get(path.first()) : null
|
const furthest = this.nodes.get(path.first())
|
||||||
return furthest
|
return furthest
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,7 +663,7 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the furthest ancestor of a node that has only one child.
|
* Get the furthest ancestor of a node, where all ancestors to that point only have one child.
|
||||||
*
|
*
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
* @return {Node|Null}
|
* @return {Node|Null}
|
||||||
@@ -616,7 +697,7 @@ class ElementInterface {
|
|||||||
/**
|
/**
|
||||||
* Get the closest inline nodes for each text node in the node, as an array.
|
* Get the closest inline nodes for each text node in the node, as an array.
|
||||||
*
|
*
|
||||||
* @return {List<Node>}
|
* @return {Array<Node>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getInlinesAsArray() {
|
getInlinesAsArray() {
|
||||||
@@ -719,10 +800,10 @@ class ElementInterface {
|
|||||||
|
|
||||||
if (range.isCollapsed) {
|
if (range.isCollapsed) {
|
||||||
// PERF: range is not cachable, use key and offset as proxies for cache
|
// PERF: range is not cachable, use key and offset as proxies for cache
|
||||||
return this.getMarksAtPosition(start.key, start.offset)
|
return this.getMarksAtPosition(start.path, start.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = this.getDescendant(start.key)
|
const text = this.getDescendant(start.path)
|
||||||
const marks = text.getMarksAtIndex(start.offset + 1)
|
const marks = text.getMarksAtIndex(start.offset + 1)
|
||||||
return marks
|
return marks
|
||||||
}
|
}
|
||||||
@@ -744,7 +825,7 @@ class ElementInterface {
|
|||||||
* Get the bottom-most descendants in a `range` as an array
|
* Get the bottom-most descendants in a `range` as an array
|
||||||
*
|
*
|
||||||
* @param {Range} range
|
* @param {Range} range
|
||||||
* @return {Array}
|
* @return {Array<Node>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getLeafBlocksAtRangeAsArray(range) {
|
getLeafBlocksAtRangeAsArray(range) {
|
||||||
@@ -752,17 +833,57 @@ class ElementInterface {
|
|||||||
if (range.isUnset) return []
|
if (range.isUnset) return []
|
||||||
|
|
||||||
const { start, end } = range
|
const { start, end } = range
|
||||||
const startBlock = this.getClosestBlock(start.key)
|
|
||||||
|
|
||||||
|
return this.getLeafBlocksBetweenPathPositionsAsArray(start.path, end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bottom-most descendants between two paths as an array
|
||||||
|
*
|
||||||
|
* @param {List|Null} startPath
|
||||||
|
* @param {List|Null} endPath
|
||||||
|
* @return {Array<Node>}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getLeafBlocksBetweenPathPositionsAsArray(startPath, endPath) {
|
||||||
// PERF: the most common case is when the range is in a single block node,
|
// PERF: the most common case is when the range is in a single block node,
|
||||||
// where we can avoid a lot of iterating of the tree.
|
// where we can avoid a lot of iterating of the tree.
|
||||||
if (start.key === end.key) return [startBlock]
|
if (startPath && endPath && PathUtils.isEqual(startPath, endPath)) {
|
||||||
|
return [this.getClosestBlock(startPath)]
|
||||||
|
} else if (!startPath && !endPath) {
|
||||||
|
return this.getBlocksAsArray()
|
||||||
|
}
|
||||||
|
|
||||||
const endBlock = this.getClosestBlock(end.key)
|
const startIndex = startPath ? startPath.get(0, 0) : 0
|
||||||
const blocks = this.getBlocksAsArray()
|
const endIndex = endPath
|
||||||
const startIndex = blocks.indexOf(startBlock)
|
? endPath.get(0, this.nodes.size - 1)
|
||||||
const endIndex = blocks.indexOf(endBlock)
|
: this.nodes.size - 1
|
||||||
return blocks.slice(startIndex, endIndex + 1)
|
|
||||||
|
let array = []
|
||||||
|
|
||||||
|
this.nodes.slice(startIndex, endIndex + 1).forEach((node, i) => {
|
||||||
|
if (node.object !== 'block') {
|
||||||
|
return
|
||||||
|
} else if (node.isLeafBlock()) {
|
||||||
|
array.push(node)
|
||||||
|
} else {
|
||||||
|
const childStartPath =
|
||||||
|
startPath && i === 0 ? PathUtils.drop(startPath) : null
|
||||||
|
const childEndPath =
|
||||||
|
endPath && i === endIndex - startIndex
|
||||||
|
? PathUtils.drop(endPath)
|
||||||
|
: null
|
||||||
|
|
||||||
|
array = array.concat(
|
||||||
|
node.getLeafBlocksBetweenPathPositionsAsArray(
|
||||||
|
childStartPath,
|
||||||
|
childEndPath
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -783,7 +904,7 @@ class ElementInterface {
|
|||||||
* Get the bottom-most inline nodes for each text node in a `range` as an array.
|
* Get the bottom-most inline nodes for each text node in a `range` as an array.
|
||||||
*
|
*
|
||||||
* @param {Range} range
|
* @param {Range} range
|
||||||
* @return {Array}
|
* @return {Array<Node>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getLeafInlinesAtRangeAsArray(range) {
|
getLeafInlinesAtRangeAsArray(range) {
|
||||||
@@ -829,27 +950,30 @@ class ElementInterface {
|
|||||||
/**
|
/**
|
||||||
* Get a set of marks in a `position`, the equivalent of a collapsed range
|
* Get a set of marks in a `position`, the equivalent of a collapsed range
|
||||||
*
|
*
|
||||||
* @param {string} key
|
* @param {List|string} key
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
* @return {Set}
|
* @return {Set}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getMarksAtPosition(key, offset) {
|
getMarksAtPosition(path, offset) {
|
||||||
const text = this.getDescendant(key)
|
path = this.resolvePath(path)
|
||||||
|
const text = this.getDescendant(path)
|
||||||
const currentMarks = text.getMarksAtIndex(offset)
|
const currentMarks = text.getMarksAtIndex(offset)
|
||||||
if (offset !== 0) return currentMarks
|
if (offset !== 0) return currentMarks
|
||||||
const closestBlock = this.getClosestBlock(key)
|
const closestBlock = this.getClosestBlock(path)
|
||||||
|
|
||||||
if (closestBlock.text === '') {
|
if (closestBlock.text === '') {
|
||||||
// insert mark for empty block; the empty block are often created by split node or add marks in a range including empty blocks
|
// insert mark for empty block; the empty block are often created by split node or add marks in a range including empty blocks
|
||||||
return currentMarks
|
return currentMarks
|
||||||
}
|
}
|
||||||
|
|
||||||
const previous = this.getPreviousText(key)
|
const previous = this.getPreviousTextAndPath(path)
|
||||||
if (!previous) return Set()
|
if (!previous) return Set()
|
||||||
|
|
||||||
if (closestBlock.hasDescendant(previous.key)) {
|
const [previousText, previousPath] = previous
|
||||||
return previous.getMarksAtIndex(previous.text.length)
|
|
||||||
|
if (closestBlock.hasDescendant(previousPath)) {
|
||||||
|
return previous.getMarksAtIndex(previousText.text.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentMarks
|
return currentMarks
|
||||||
@@ -897,28 +1021,20 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the block node before a descendant text node by `key`.
|
* Get the block node after a descendant text node by `path`.
|
||||||
*
|
*
|
||||||
* @param {String} key
|
* @param {List|String} path
|
||||||
* @return {Node|Null}
|
* @return {Node|Null}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getNextBlock(key) {
|
getNextBlock(path) {
|
||||||
const child = this.assertDescendant(key)
|
path = this.resolvePath(path)
|
||||||
let last
|
const match = this.getNextDeepMatchingNodeAndPath(
|
||||||
|
path,
|
||||||
|
n => n.object === 'block'
|
||||||
|
)
|
||||||
|
|
||||||
if (child.object === 'block') {
|
return match ? match[0] : null
|
||||||
last = child.getLastText()
|
|
||||||
} else {
|
|
||||||
const block = this.getClosestBlock(key)
|
|
||||||
last = block.getLastText()
|
|
||||||
}
|
|
||||||
|
|
||||||
const next = this.getNextText(last.key)
|
|
||||||
if (!next) return null
|
|
||||||
|
|
||||||
const closest = this.getClosestBlock(next.key)
|
|
||||||
return closest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -946,6 +1062,74 @@ class ElementInterface {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next node in the tree from a node that matches iterator
|
||||||
|
*
|
||||||
|
* This will not only check for siblings but instead move up the tree
|
||||||
|
* returning the next ancestor if no sibling is found.
|
||||||
|
*
|
||||||
|
* @param {List} path
|
||||||
|
* @return {Node|Null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getNextMatchingNodeAndPath(path, iterator = () => true) {
|
||||||
|
if (!path) return null
|
||||||
|
|
||||||
|
for (let i = path.size; i > 0; i--) {
|
||||||
|
const p = path.slice(0, i)
|
||||||
|
|
||||||
|
let nextPath = PathUtils.increment(p)
|
||||||
|
let nextNode = this.getNode(nextPath)
|
||||||
|
|
||||||
|
while (nextNode && !iterator(nextNode)) {
|
||||||
|
nextPath = PathUtils.increment(nextPath)
|
||||||
|
nextNode = this.getNode(nextPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextNode) return [nextNode, nextPath]
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next, deepest node in the tree from a node that matches iterator
|
||||||
|
*
|
||||||
|
* This will not only check for siblings but instead move up the tree
|
||||||
|
* returning the next ancestor if no sibling is found.
|
||||||
|
*
|
||||||
|
* @param {List} path
|
||||||
|
* @param {Function} iterator
|
||||||
|
* @return {Node|Null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getNextDeepMatchingNodeAndPath(path, iterator = () => true) {
|
||||||
|
const match = this.getNextMatchingNodeAndPath(path)
|
||||||
|
|
||||||
|
if (!match) return null
|
||||||
|
|
||||||
|
let [nextNode, nextPath] = match
|
||||||
|
|
||||||
|
let childMatch
|
||||||
|
|
||||||
|
const assign = () => {
|
||||||
|
childMatch =
|
||||||
|
nextNode.object !== 'text' &&
|
||||||
|
nextNode.findFirstDescendantAndPath(iterator, nextPath)
|
||||||
|
return childMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
while (assign(childMatch)) {
|
||||||
|
;[nextNode, nextPath] = childMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nextNode) return null
|
||||||
|
|
||||||
|
return iterator(nextNode)
|
||||||
|
? [nextNode, nextPath]
|
||||||
|
: this.getNextDeepMatchingNodeAndPath(match[1], iterator)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next sibling of a node.
|
* Get the next sibling of a node.
|
||||||
*
|
*
|
||||||
@@ -979,6 +1163,16 @@ class ElementInterface {
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNextTextAndPath(path) {
|
||||||
|
if (!path) return null
|
||||||
|
if (!path.size) return null
|
||||||
|
const match = this.getNextDeepMatchingNodeAndPath(
|
||||||
|
path,
|
||||||
|
n => n.object === 'text'
|
||||||
|
)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all of the nodes in a `range`. This includes all of the
|
* Get all of the nodes in a `range`. This includes all of the
|
||||||
* text nodes inside the range and all ancestors of those text
|
* text nodes inside the range and all ancestors of those text
|
||||||
@@ -1049,23 +1243,28 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the offset for a descendant text node by `key`.
|
* Get the offset for a descendant text node by `path` or `key`.
|
||||||
*
|
*
|
||||||
* @param {String} key
|
* @param {List|string} path
|
||||||
* @return {Number}
|
* @return {Number}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getOffset(key) {
|
getOffset(path) {
|
||||||
this.assertDescendant(key)
|
path = this.resolvePath(path)
|
||||||
|
this.assertDescendant(path)
|
||||||
|
|
||||||
// Calculate the offset of the nodes before the highest child.
|
// Calculate the offset of the nodes before the highest child.
|
||||||
const child = this.getFurthestAncestor(key)
|
const index = path.first()
|
||||||
|
|
||||||
const offset = this.nodes
|
const offset = this.nodes
|
||||||
.takeUntil(n => n === child)
|
.slice(0, index)
|
||||||
.reduce((memo, n) => memo + n.text.length, 0)
|
.reduce((memo, n) => memo + n.text.length, 0)
|
||||||
|
|
||||||
// Recurse if need be.
|
// Recurse if need be.
|
||||||
const ret = this.hasChild(key) ? offset : offset + child.getOffset(key)
|
const ret =
|
||||||
|
path.size === 1
|
||||||
|
? offset
|
||||||
|
: offset + this.nodes.get(index).getOffset(PathUtils.drop(path))
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1088,7 +1287,7 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { start } = range
|
const { start } = range
|
||||||
const offset = this.getOffset(start.key) + start.offset
|
const offset = this.getOffset(start.path) + start.offset
|
||||||
return offset
|
return offset
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1119,14 +1318,14 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (range.isCollapsed) {
|
if (range.isCollapsed) {
|
||||||
// PERF: range is not cachable, use key and offset as proxies for cache
|
// PERF: range is not cachable, use path? and offset as proxies for cache
|
||||||
return this.getMarksAtPosition(start.key, start.offset)
|
return this.getMarksAtPosition(start.path, start.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
const marks = this.getOrderedMarksBetweenPositions(
|
const marks = this.getOrderedMarksBetweenPositions(
|
||||||
start.key,
|
start.path,
|
||||||
start.offset,
|
start.offset,
|
||||||
end.key,
|
end.path,
|
||||||
end.offset
|
end.offset
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1137,28 +1336,34 @@ class ElementInterface {
|
|||||||
* Get a set of the marks in a `range`.
|
* Get a set of the marks in a `range`.
|
||||||
* PERF: arguments use key and offset for utilizing cache
|
* PERF: arguments use key and offset for utilizing cache
|
||||||
*
|
*
|
||||||
* @param {string} startKey
|
* @param {List|string} startPath
|
||||||
* @param {number} startOffset
|
* @param {number} startOffset
|
||||||
* @param {string} endKey
|
* @param {List|string} endPath
|
||||||
* @param {number} endOffset
|
* @param {number} endOffset
|
||||||
* @returns {OrderedSet<Mark>}
|
* @returns {OrderedSet<Mark>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getOrderedMarksBetweenPositions(startKey, startOffset, endKey, endOffset) {
|
getOrderedMarksBetweenPositions(startPath, startOffset, endPath, endOffset) {
|
||||||
if (startKey === endKey) {
|
startPath = this.resolvePath(startPath)
|
||||||
const startText = this.getDescendant(startKey)
|
endPath = this.resolvePath(endPath)
|
||||||
|
|
||||||
|
const startText = this.getDescendant(startPath)
|
||||||
|
|
||||||
|
if (PathUtils.isEqual(startPath, endPath)) {
|
||||||
return startText.getMarksBetweenOffsets(startOffset, endOffset)
|
return startText.getMarksBetweenOffsets(startOffset, endOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
const texts = this.getTextsBetweenPositionsAsArray(startKey, endKey)
|
const endText = this.getDescendant(endPath)
|
||||||
|
|
||||||
|
const texts = this.getTextsBetweenPathPositionsAsArray(startPath, endPath)
|
||||||
|
|
||||||
return OrderedSet().withMutations(result => {
|
return OrderedSet().withMutations(result => {
|
||||||
texts.forEach(text => {
|
texts.forEach(text => {
|
||||||
if (text.key === startKey) {
|
if (text.key === startText.key) {
|
||||||
result.union(
|
result.union(
|
||||||
text.getMarksBetweenOffsets(startOffset, text.text.length)
|
text.getMarksBetweenOffsets(startOffset, text.text.length)
|
||||||
)
|
)
|
||||||
} else if (text.key === endKey) {
|
} else if (text.key === endText.key) {
|
||||||
result.union(text.getMarksBetweenOffsets(0, endOffset))
|
result.union(text.getMarksBetweenOffsets(0, endOffset))
|
||||||
} else {
|
} else {
|
||||||
result.union(text.getMarks())
|
result.union(text.getMarks())
|
||||||
@@ -1196,28 +1401,20 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the block node before a descendant text node by `key`.
|
* Get the block node before a descendant text node by `path`.
|
||||||
*
|
*
|
||||||
* @param {String} key
|
* @param {List|String} path
|
||||||
* @return {Node|Null}
|
* @return {Node|Null}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getPreviousBlock(key) {
|
getPreviousBlock(path) {
|
||||||
const child = this.assertDescendant(key)
|
path = this.resolvePath(path)
|
||||||
let first
|
const match = this.getPreviousDeepMatchingNodeAndPath(
|
||||||
|
path,
|
||||||
|
n => n.object === 'block'
|
||||||
|
)
|
||||||
|
|
||||||
if (child.object === 'block') {
|
return match ? match[0] : null
|
||||||
first = child.getFirstText()
|
|
||||||
} else {
|
|
||||||
const block = this.getClosestBlock(key)
|
|
||||||
first = block.getFirstText()
|
|
||||||
}
|
|
||||||
|
|
||||||
const previous = this.getPreviousText(first.key)
|
|
||||||
if (!previous) return null
|
|
||||||
|
|
||||||
const closest = this.getClosestBlock(previous.key)
|
|
||||||
return closest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1232,16 +1429,8 @@ class ElementInterface {
|
|||||||
if (range.isUnset) return List()
|
if (range.isUnset) return List()
|
||||||
|
|
||||||
const { start, end } = range
|
const { start, end } = range
|
||||||
const startBlock = this.getFurthestBlock(start.key)
|
|
||||||
|
|
||||||
// PERF: the most common case is when the range is in a single block node,
|
return this.nodes.slice(start.path.first(), end.path.first() + 1)
|
||||||
// where we can avoid a lot of iterating of the tree.
|
|
||||||
if (start.key === end.key) return List([startBlock])
|
|
||||||
|
|
||||||
const endBlock = this.getFurthestBlock(end.key)
|
|
||||||
const startIndex = this.nodes.indexOf(startBlock)
|
|
||||||
const endIndex = this.nodes.indexOf(endBlock)
|
|
||||||
return this.nodes.slice(startIndex, endIndex + 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1303,6 +1492,76 @@ class ElementInterface {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the previous node in the tree from a node that matches iterator
|
||||||
|
*
|
||||||
|
* This will not only check for siblings but instead move up the tree
|
||||||
|
* returning the previous ancestor if no sibling is found.
|
||||||
|
*
|
||||||
|
* @param {List} path
|
||||||
|
* @return {Node|Null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getPreviousMatchingNodeAndPath(path, iterator = () => true) {
|
||||||
|
if (!path) return null
|
||||||
|
|
||||||
|
for (let i = path.size; i > 0; i--) {
|
||||||
|
const p = path.slice(0, i)
|
||||||
|
if (p.last() === 0) continue
|
||||||
|
|
||||||
|
let previousPath = PathUtils.decrement(p)
|
||||||
|
let previousNode = this.getNode(previousPath)
|
||||||
|
|
||||||
|
while (previousNode && !iterator(previousNode)) {
|
||||||
|
previousPath = PathUtils.decrement(previousPath)
|
||||||
|
previousNode = this.getNode(previousPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousNode) return [previousNode, previousPath]
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next previous in the tree from a node that matches iterator
|
||||||
|
*
|
||||||
|
* This will not only check for siblings but instead move up the tree
|
||||||
|
* returning the previous ancestor if no sibling is found.
|
||||||
|
* Once a node is found, the last deepest child matching is returned
|
||||||
|
*
|
||||||
|
* @param {List} path
|
||||||
|
* @param {Function} iterator
|
||||||
|
* @return {Node|Null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getPreviousDeepMatchingNodeAndPath(path, iterator = () => true) {
|
||||||
|
const match = this.getPreviousMatchingNodeAndPath(path)
|
||||||
|
|
||||||
|
if (!match) return null
|
||||||
|
|
||||||
|
let [previousNode, previousPath] = match
|
||||||
|
|
||||||
|
let childMatch
|
||||||
|
|
||||||
|
const assign = () => {
|
||||||
|
childMatch =
|
||||||
|
previousNode.object !== 'text' &&
|
||||||
|
previousNode.findLastDescendantAndPath(iterator, previousPath)
|
||||||
|
return childMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
while (assign(childMatch)) {
|
||||||
|
;[previousNode, previousPath] = childMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!previousNode) return null
|
||||||
|
|
||||||
|
return iterator(previousNode)
|
||||||
|
? [previousNode, previousPath]
|
||||||
|
: this.getPreviousDeepMatchingNodeAndPath(match[1], iterator)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the previous sibling of a node.
|
* Get the previous sibling of a node.
|
||||||
*
|
*
|
||||||
@@ -1321,7 +1580,7 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text node after a descendant text node.
|
* Get the text node before a descendant text node.
|
||||||
*
|
*
|
||||||
* @param {List|String} path
|
* @param {List|String} path
|
||||||
* @return {Node|Null}
|
* @return {Node|Null}
|
||||||
@@ -1333,8 +1592,18 @@ class ElementInterface {
|
|||||||
if (!path.size) return null
|
if (!path.size) return null
|
||||||
const previous = this.getPreviousNode(path)
|
const previous = this.getPreviousNode(path)
|
||||||
if (!previous) return null
|
if (!previous) return null
|
||||||
const text = previous.getLastText()
|
const match = previous.getLastText()
|
||||||
return text
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
getPreviousTextAndPath(path) {
|
||||||
|
if (!path) return null
|
||||||
|
if (!path.size) return null
|
||||||
|
const match = this.getPreviousDeepMatchingNodeAndPath(
|
||||||
|
path,
|
||||||
|
n => n.object === 'text'
|
||||||
|
)
|
||||||
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1455,58 +1724,92 @@ class ElementInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all of the text nodes in a `range`.
|
* Get all of the text nodes in a `range` as a List.
|
||||||
*
|
*
|
||||||
* @param {Range} range
|
* @param {Range} range
|
||||||
* @return {List<Node>}
|
* @return {List<Node>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTextsAtRange(range) {
|
getTextsAtRange(range) {
|
||||||
range = this.resolveRange(range)
|
const arr = this.getTextsAtRangeAsArray(range)
|
||||||
if (range.isUnset) return List()
|
return List(arr)
|
||||||
const { start, end } = range
|
|
||||||
const list = List(this.getTextsBetweenPositionsAsArray(start.key, end.key))
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all of the text nodes in a `range` as an array.
|
* Get all of the text nodes in a `range` as an array.
|
||||||
*
|
*
|
||||||
* @param {Range} range
|
* @param {Range} range
|
||||||
* @return {Array}
|
* @return {Array<Node>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTextsAtRangeAsArray(range) {
|
getTextsAtRangeAsArray(range) {
|
||||||
range = this.resolveRange(range)
|
range = this.resolveRange(range)
|
||||||
if (range.isUnset) return []
|
if (range.isUnset) return []
|
||||||
const { start, end } = range
|
const { start, end } = range
|
||||||
const texts = this.getTextsBetweenPositionsAsArray(start.key, end.key)
|
const texts = this.getTextsBetweenPathPositionsAsArray(start.path, end.path)
|
||||||
return texts
|
return texts
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all of the text nodes in a `range` as an array.
|
* Get all of the text nodes in a `range` as an array.
|
||||||
* PERF: use key in arguments for cache
|
* PERF: use key / path in arguments for cache
|
||||||
*
|
*
|
||||||
* @param {string} startKey
|
* @param {List|string} startPath
|
||||||
* @param {string} endKey
|
* @param {List|string} endPath
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTextsBetweenPositionsAsArray(startKey, endKey) {
|
getTextsBetweenPositionsAsArray(startPath, endPath) {
|
||||||
const startText = this.getDescendant(startKey)
|
startPath = this.resolvePath(startPath)
|
||||||
|
endPath = this.resolvePath(endPath)
|
||||||
|
|
||||||
|
return this.getTextsBetweenPathPositionsAsArray(startPath, endPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all of the text nodes in a `range` as an array.
|
||||||
|
*
|
||||||
|
* @param {List|falsey} startPath
|
||||||
|
* @param {List|falsey} endPath
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getTextsBetweenPathPositionsAsArray(startPath, endPath) {
|
||||||
// PERF: the most common case is when the range is in a single text node,
|
// PERF: the most common case is when the range is in a single text node,
|
||||||
// where we can avoid a lot of iterating of the tree.
|
// where we can avoid a lot of iterating of the tree.
|
||||||
if (startKey === endKey) return [startText]
|
if (startPath && endPath && PathUtils.isEqual(startPath, endPath)) {
|
||||||
|
return [this.getDescendant(startPath)]
|
||||||
|
} else if (!startPath && !endPath) {
|
||||||
|
return this.getTextsAsArray()
|
||||||
|
}
|
||||||
|
|
||||||
const endText = this.getDescendant(endKey)
|
const startIndex = startPath ? startPath.get(0, 0) : 0
|
||||||
const texts = this.getTextsAsArray()
|
const endIndex = endPath
|
||||||
const start = texts.indexOf(startText)
|
? endPath.get(0, this.nodes.size - 1)
|
||||||
const end = texts.indexOf(endText, start)
|
: this.nodes.size - 1
|
||||||
const ret = texts.slice(start, end + 1)
|
|
||||||
return ret
|
let array = []
|
||||||
|
|
||||||
|
this.nodes.slice(startIndex, endIndex + 1).forEach((node, i) => {
|
||||||
|
if (node.object === 'text') {
|
||||||
|
array.push(node)
|
||||||
|
} else {
|
||||||
|
// For the node at start and end of this list, we want to provide a start and end path
|
||||||
|
// For other nodes, we can just get all their text nodes, they are between the paths
|
||||||
|
const childStartPath =
|
||||||
|
startPath && i === 0 ? PathUtils.drop(startPath) : null
|
||||||
|
const childEndPath =
|
||||||
|
endPath && i === endIndex - startIndex
|
||||||
|
? PathUtils.drop(endPath)
|
||||||
|
: null
|
||||||
|
|
||||||
|
array = array.concat(
|
||||||
|
node.getTextsBetweenPathPositionsAsArray(childStartPath, childEndPath)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1619,9 +1922,10 @@ class ElementInterface {
|
|||||||
|
|
||||||
isLeafBlock() {
|
isLeafBlock() {
|
||||||
const { object, nodes } = this
|
const { object, nodes } = this
|
||||||
|
if (object !== 'block') return false
|
||||||
if (!nodes.size) return true
|
if (!nodes.size) return true
|
||||||
const first = nodes.first()
|
|
||||||
return object === 'block' && first.object !== 'block'
|
return nodes.first().object !== 'block'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1632,16 +1936,17 @@ class ElementInterface {
|
|||||||
|
|
||||||
isLeafInline() {
|
isLeafInline() {
|
||||||
const { object, nodes } = this
|
const { object, nodes } = this
|
||||||
|
if (object !== 'inline') return false
|
||||||
if (!nodes.size) return true
|
if (!nodes.size) return true
|
||||||
const first = nodes.first()
|
|
||||||
return object === 'inline' && first.object !== 'inline'
|
return nodes.first().object !== 'inline'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a descendant node is inside a range. This will return true for all
|
* Check whether a descendant node is inside a range. This will return true for all
|
||||||
* text nodes inside the range and all ancestors of those text nodes up to this node.
|
* text nodes inside the range and all ancestors of those text nodes up to this node.
|
||||||
*
|
*
|
||||||
* @param {List|Key} path
|
* @param {List|string} path
|
||||||
* @param {Range} range
|
* @param {Range} range
|
||||||
* @return {Node}
|
* @return {Node}
|
||||||
*/
|
*/
|
||||||
@@ -2005,7 +2310,7 @@ for (const method of ASSERTS) {
|
|||||||
|
|
||||||
memoize(ElementInterface.prototype, [
|
memoize(ElementInterface.prototype, [
|
||||||
'getBlocksAsArray',
|
'getBlocksAsArray',
|
||||||
'getBlocksAtRangeAsArray',
|
'getLeafBlocksAtRangeAsArray',
|
||||||
'getBlocksByTypeAsArray',
|
'getBlocksByTypeAsArray',
|
||||||
'getDecorations',
|
'getDecorations',
|
||||||
'getFragmentAtRange',
|
'getFragmentAtRange',
|
||||||
@@ -2028,7 +2333,7 @@ memoize(ElementInterface.prototype, [
|
|||||||
'getTextAtOffset',
|
'getTextAtOffset',
|
||||||
'getTextDirection',
|
'getTextDirection',
|
||||||
'getTextsAsArray',
|
'getTextsAsArray',
|
||||||
'getTextsBetweenPositionsAsArray',
|
'getTextsBetweenPathPositionsAsArray',
|
||||||
])
|
])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -355,7 +355,23 @@ class Point extends Record(DEFAULTS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { key, offset, path } = this
|
const { key, offset, path } = this
|
||||||
const target = node.getNode(key || path)
|
|
||||||
|
// PERF: this function gets called a lot.
|
||||||
|
// to avoid creating the key -> path lookup table, we attempt to look up by path first.
|
||||||
|
let target = path && node.getNode(path)
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
target = node.getNode(key)
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
// There is a misalignment of path and key
|
||||||
|
const point = this.merge({
|
||||||
|
path: node.getPath(key),
|
||||||
|
})
|
||||||
|
|
||||||
|
return point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
warning(false, "A point's `path` or `key` invalid and was reset!")
|
warning(false, "A point's `path` or `key` invalid and was reset!")
|
||||||
@@ -388,6 +404,8 @@ class Point extends Record(DEFAULTS) {
|
|||||||
|
|
||||||
if (target && path && key && key !== target.key) {
|
if (target && path && key && key !== target.key) {
|
||||||
warning(false, "A point's `key` did not match its `path`!")
|
warning(false, "A point's `key` did not match its `path`!")
|
||||||
|
|
||||||
|
// TODO: if we look up by path above and it differs by key, do we want to reset it to looking up by key?
|
||||||
}
|
}
|
||||||
|
|
||||||
const point = this.merge({
|
const point = this.merge({
|
||||||
|
@@ -212,7 +212,7 @@ function isYounger(path, target) {
|
|||||||
* Lift a `path` to refer to its parent.
|
* Lift a `path` to refer to its parent.
|
||||||
*
|
*
|
||||||
* @param {List} path
|
* @param {List} path
|
||||||
* @return {Array}
|
* @return {List}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function lift(path) {
|
function lift(path) {
|
||||||
@@ -220,6 +220,18 @@ function lift(path) {
|
|||||||
return parent
|
return parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop a `path`, returning the path from the first child.
|
||||||
|
*
|
||||||
|
* @param {List} path
|
||||||
|
* @return {List}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function drop(path) {
|
||||||
|
const relative = path.slice(1)
|
||||||
|
return relative
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maximum length of paths `a` and `b`.
|
* Get the maximum length of paths `a` and `b`.
|
||||||
*
|
*
|
||||||
@@ -391,6 +403,7 @@ export default {
|
|||||||
isSibling,
|
isSibling,
|
||||||
isYounger,
|
isYounger,
|
||||||
lift,
|
lift,
|
||||||
|
drop,
|
||||||
max,
|
max,
|
||||||
min,
|
min,
|
||||||
relate,
|
relate,
|
||||||
|
@@ -0,0 +1,34 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import { Set } from 'immutable'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
wo<anchor />
|
||||||
|
<mark type="a">rd</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="b">middle</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>unmarked</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="c">
|
||||||
|
an<focus />other
|
||||||
|
</mark>
|
||||||
|
<mark type="d">unselected marked text</mark>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getActiveMarksAtRange(selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = Set()
|
@@ -0,0 +1,38 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import { Set } from 'immutable'
|
||||||
|
import { Mark } from 'slate'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
wo<anchor />
|
||||||
|
<mark type="a">rd</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="b">
|
||||||
|
<mark type="a">middle</mark>
|
||||||
|
</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="b">
|
||||||
|
<mark type="a">
|
||||||
|
an<focus />other
|
||||||
|
</mark>
|
||||||
|
</mark>
|
||||||
|
<mark type="c">unselected marked text</mark>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getActiveMarksAtRange(selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = Set.of(Mark.create('a'))
|
@@ -0,0 +1,34 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import { Set } from 'immutable'
|
||||||
|
import { Mark } from 'slate'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
wo<anchor />
|
||||||
|
<mark type="a">rd</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="a">middle</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="a">
|
||||||
|
an<focus />other
|
||||||
|
</mark>
|
||||||
|
<mark type="b">unselected marked text</mark>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getActiveMarksAtRange(selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = Set.of(Mark.create('a'))
|
26
packages/slate/test/models/node/get-closest/by-key.js
Normal file
26
packages/slate/test/models/node/get-closest/by-key.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getClosestBlock(selection.end.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = (
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
)
|
@@ -0,0 +1,30 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<inline type="inline_type">
|
||||||
|
<anchor />two<focus />
|
||||||
|
</inline>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getClosestBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = (
|
||||||
|
<paragraph>
|
||||||
|
<inline type="inline_type">
|
||||||
|
<anchor />two<focus />
|
||||||
|
</inline>
|
||||||
|
</paragraph>
|
||||||
|
)
|
21
packages/slate/test/models/node/get-closest/top-level.js
Normal file
21
packages/slate/test/models/node/get-closest/top-level.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import { PathUtils } from 'slate'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>two</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getClosestBlock(PathUtils.create([1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = null
|
@@ -0,0 +1,40 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
wo<anchor />
|
||||||
|
<b>rd</b>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<b>middle</b>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<b>
|
||||||
|
an<focus />other
|
||||||
|
</b>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getFragmentAtRange(selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = (
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
<b>rd</b>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<b>middle</b>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<b>an</b>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
)
|
@@ -0,0 +1,23 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<inline type="test">one</inline>
|
||||||
|
<text key="a">two</text>
|
||||||
|
</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function(value) {
|
||||||
|
const { document } = value
|
||||||
|
return document.getFurthestOnlyChildAncestor('a')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = null
|
@@ -0,0 +1,21 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
<inline type="test">one</inline>
|
||||||
|
<text key="a">two</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function(value) {
|
||||||
|
const { document } = value
|
||||||
|
return document.getFurthestOnlyChildAncestor('a')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = null
|
22
packages/slate/test/models/node/get-next-block/by-key.js
Normal file
22
packages/slate/test/models/node/get-next-block/by-key.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextBlock(selection.end.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>three</paragraph>
|
@@ -0,0 +1,24 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
<inline type="inline_type" />
|
||||||
|
<inline type="inline_type" />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>three</paragraph>
|
@@ -0,0 +1,22 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>three</paragraph>
|
@@ -0,0 +1,27 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>1</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />2<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>3</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>3.1</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>4</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>3</paragraph>
|
@@ -0,0 +1,24 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>three</paragraph>
|
@@ -0,0 +1,21 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import PathUtils from '../../../../src/utils/path-utils'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>two</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document }) {
|
||||||
|
return document.getNextBlock(PathUtils.create([1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>three</paragraph>
|
@@ -0,0 +1,25 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>two</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />four<focus />
|
||||||
|
<inline type="inline_type">five</inline>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = null
|
@@ -0,0 +1,24 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>two</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />four<focus />
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = null
|
@@ -0,0 +1,28 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextNode(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = (
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
)
|
@@ -0,0 +1,22 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getNextNode(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>three</paragraph>
|
22
packages/slate/test/models/node/get-previous-block/by-key.js
Normal file
22
packages/slate/test/models/node/get-previous-block/by-key.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getPreviousBlock(selection.end.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>one</paragraph>
|
@@ -0,0 +1,24 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<inline type="inline_type" />
|
||||||
|
<inline type="inline_type" />
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getPreviousBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>one</paragraph>
|
@@ -0,0 +1,25 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
<inline type="inline_type">zer</inline>
|
||||||
|
<anchor />one<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>two</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getPreviousBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = null
|
@@ -0,0 +1,24 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />one<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>two</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getPreviousBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = null
|
@@ -0,0 +1,22 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>two</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />three<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getPreviousBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>two</paragraph>
|
@@ -0,0 +1,32 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>1</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>2</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>2.1</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>3.1</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>3.2</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />4><focus />
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getPreviousBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>3.2</paragraph>
|
@@ -0,0 +1,24 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<anchor />two<focus />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getPreviousBlock(selection.end.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>one</paragraph>
|
@@ -0,0 +1,21 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import PathUtils from '../../../../src/utils/path-utils'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>one</paragraph>
|
||||||
|
<paragraph>two</paragraph>
|
||||||
|
<paragraph>three</paragraph>
|
||||||
|
<paragraph>four</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document }) {
|
||||||
|
return document.getPreviousBlock(PathUtils.create([2]))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = <paragraph>two</paragraph>
|
@@ -0,0 +1,35 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import PathUtils from '../../../../src/utils/path-utils'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
wo<anchor />
|
||||||
|
<mark type="a">rd</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="b">middle</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>unmarked</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="c">
|
||||||
|
an<focus />other
|
||||||
|
</mark>
|
||||||
|
<mark type="d">unselected marked text</mark>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
const node = document.getDescendant(PathUtils.create([1]))
|
||||||
|
return node.getSelectionIndexes(selection, [1])
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = { start: 0, end: 2 }
|
@@ -0,0 +1,33 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
wo<anchor />
|
||||||
|
<mark type="a">rd</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="b">middle</mark>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph />
|
||||||
|
</paragraph>
|
||||||
|
<paragraph>unmarked</paragraph>
|
||||||
|
<paragraph>
|
||||||
|
<mark type="c">
|
||||||
|
an<focus />other
|
||||||
|
</mark>
|
||||||
|
<mark type="d">unselected marked text</mark>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getSelectionIndexes(selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = { start: 0, end: 4 }
|
@@ -0,0 +1,26 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import PathUtils from '../../../../src/utils/path-utils'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
before
|
||||||
|
<anchor />start
|
||||||
|
<inline type="inline_type" />
|
||||||
|
<inline type="inline_with_text">inline text</inline>
|
||||||
|
end<focus />
|
||||||
|
after
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
const node = document.getDescendant(PathUtils.create([0, 2]))
|
||||||
|
return node.getSelectionIndexes(selection, [0, 2])
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = { start: 0, end: 1 }
|
@@ -0,0 +1,26 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
import PathUtils from '../../../../src/utils/path-utils'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
before
|
||||||
|
<anchor />start
|
||||||
|
<inline type="inline_type" />
|
||||||
|
<inline type="inline_with_text">inline text</inline>
|
||||||
|
end<focus />
|
||||||
|
after
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
const node = document.getDescendant(PathUtils.create([0]))
|
||||||
|
return node.getSelectionIndexes(selection, [0])
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = { start: 0, end: 4 }
|
@@ -0,0 +1,24 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph>
|
||||||
|
before
|
||||||
|
<anchor />start
|
||||||
|
<inline type="inline_type" />
|
||||||
|
<inline type="inline_with_text">inline text</inline>
|
||||||
|
end<focus />
|
||||||
|
after
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document.getSelectionIndexes(selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = { start: 0, end: 1 }
|
Reference in New Issue
Block a user