mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-09-01 03:11:44 +02:00
Add node.getNodesAtRange
(#2398)
* Add `Node.getNodesAtRange` * Add tests for `Node.getNodesAtRange`
This commit is contained in:
committed by
Ian Storm Taylor
parent
f2f97e502e
commit
e6d611d726
@@ -253,6 +253,12 @@ Get the next [`Text`](./text.md) node after a descendant by `path` or `key`.
|
|||||||
|
|
||||||
Get a node in the tree by `path` or `key`.
|
Get a node in the tree by `path` or `key`.
|
||||||
|
|
||||||
|
### `getNodesAtRange`
|
||||||
|
|
||||||
|
`getNodesAtRange(range: Range) => List`
|
||||||
|
|
||||||
|
Get all of the nodes in a `range`. This includes all of the [`Text`](./text.md) nodes inside the range and all ancestors of those [`Text`](./text.md) nodes up to this node.
|
||||||
|
|
||||||
### `getOffset`
|
### `getOffset`
|
||||||
|
|
||||||
`getOffset(path: List|Array) => Number`
|
`getOffset(path: List|Array) => Number`
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import direction from 'direction'
|
import direction from 'direction'
|
||||||
import invariant from 'tiny-invariant'
|
import invariant from 'tiny-invariant'
|
||||||
import { List, OrderedSet, Set } from 'immutable'
|
import { List, OrderedSet, Set, Stack } from 'immutable'
|
||||||
|
|
||||||
import mixin from '../utils/mixin'
|
import mixin from '../utils/mixin'
|
||||||
import Block from '../models/block'
|
import Block from '../models/block'
|
||||||
@@ -913,6 +913,75 @@ class ElementInterface {
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all of the nodes in a `range`. This includes all of the
|
||||||
|
* text nodes inside the range and all ancestors of those text
|
||||||
|
* nodes up to this node.
|
||||||
|
*
|
||||||
|
* @param {Range} range
|
||||||
|
* @return {List<Node>}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getNodesAtRange(range) {
|
||||||
|
range = this.resolveRange(range)
|
||||||
|
if (range.isUnset) return List()
|
||||||
|
const { start, end } = range
|
||||||
|
|
||||||
|
// Do a depth-first stack-based search for all nodes in the range
|
||||||
|
// Nodes that are pushed to the stack are inside the range
|
||||||
|
|
||||||
|
// Start with the nodes that are on the highest level in the tree
|
||||||
|
let stack = Stack(
|
||||||
|
this.nodes
|
||||||
|
.slice(start.path.get(0), end.path.get(0) + 1)
|
||||||
|
.map((node, index) => ({
|
||||||
|
node,
|
||||||
|
onStartEdge: index === 0,
|
||||||
|
onEndEdge: index === end.path.get(0) - start.path.get(0),
|
||||||
|
relativeStartPath: start.path.slice(1),
|
||||||
|
relativeEndPath: end.path.slice(1),
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
while (stack.size > 0) {
|
||||||
|
const {
|
||||||
|
node,
|
||||||
|
onStartEdge,
|
||||||
|
onEndEdge,
|
||||||
|
relativeStartPath,
|
||||||
|
relativeEndPath,
|
||||||
|
} = stack.peek()
|
||||||
|
|
||||||
|
stack = stack.shift()
|
||||||
|
result.push(node)
|
||||||
|
|
||||||
|
if (node.object === 'text') continue
|
||||||
|
|
||||||
|
// Modify indexes to exclude children that are outside of the range
|
||||||
|
const startIndex = onStartEdge ? relativeStartPath.get(0) : 0
|
||||||
|
const endIndex = onEndEdge ? relativeEndPath.get(0) : node.nodes.size - 1
|
||||||
|
|
||||||
|
// Push children that are inside the range to the stack
|
||||||
|
stack = stack.pushAll(
|
||||||
|
node.nodes.slice(startIndex, endIndex + 1).map((n, i) => ({
|
||||||
|
node: n,
|
||||||
|
onStartEdge: onStartEdge && i === 0,
|
||||||
|
onEndEdge: onEndEdge && i === endIndex - startIndex,
|
||||||
|
relativeStartPath:
|
||||||
|
onStartEdge && i === 0 ? relativeStartPath.slice(1) : null,
|
||||||
|
relativeEndPath:
|
||||||
|
onEndEdge && i === endIndex - startIndex
|
||||||
|
? relativeEndPath.slice(1)
|
||||||
|
: null,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return List(result)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the offset for a descendant text node by `key`.
|
* Get the offset for a descendant text node by `key`.
|
||||||
*
|
*
|
||||||
@@ -1797,6 +1866,7 @@ memoize(ElementInterface.prototype, [
|
|||||||
'getInlinesByTypeAsArray',
|
'getInlinesByTypeAsArray',
|
||||||
'getMarksAsArray',
|
'getMarksAsArray',
|
||||||
'getMarksAtPosition',
|
'getMarksAtPosition',
|
||||||
|
'getNodesAtRange',
|
||||||
'getOrderedMarksBetweenPositions',
|
'getOrderedMarksBetweenPositions',
|
||||||
'getInsertMarksAtRange',
|
'getInsertMarksAtRange',
|
||||||
'getMarksByTypeAsArray',
|
'getMarksByTypeAsArray',
|
||||||
|
@@ -0,0 +1,38 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph key="a">
|
||||||
|
<text key="b">one</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">
|
||||||
|
tw<anchor />o
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
<image key="e" src="https://example.com/image2.png">
|
||||||
|
<text key="f" />
|
||||||
|
</image>
|
||||||
|
<paragraph key="g">
|
||||||
|
<inline type="link" key="h">
|
||||||
|
<text key="i">three</text>
|
||||||
|
</inline>
|
||||||
|
<text key="j">
|
||||||
|
<focus />four
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
|
@@ -0,0 +1,31 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<quote key="a">
|
||||||
|
<quote key="b">
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">
|
||||||
|
on<cursor />e
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
</quote>
|
||||||
|
<paragraph key="e">
|
||||||
|
<text key="f">two</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'b', 'c', 'd']
|
@@ -0,0 +1,37 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<quote key="a">
|
||||||
|
<quote key="b">
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">one</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="e">
|
||||||
|
<text key="f">two</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
<quote key="g">
|
||||||
|
<paragraph key="h">
|
||||||
|
<text key="i">
|
||||||
|
three
|
||||||
|
<cursor />
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
</quote>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'g', 'h', 'i']
|
@@ -0,0 +1,35 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<quote key="a">
|
||||||
|
<quote key="b">
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">one</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="e">
|
||||||
|
<text key="f">
|
||||||
|
<cursor />
|
||||||
|
two
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
</quote>
|
||||||
|
<paragraph key="g">
|
||||||
|
<text key="h">three</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'b', 'e', 'f']
|
@@ -0,0 +1,46 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph key="a">
|
||||||
|
<text key="b">
|
||||||
|
<focus />
|
||||||
|
one
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
<quote key="c">
|
||||||
|
<quote key="d">
|
||||||
|
<paragraph key="e">
|
||||||
|
<text key="f">two</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
<quote key="g">
|
||||||
|
<paragraph key="h">
|
||||||
|
<text key="i">three</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="j">
|
||||||
|
<text key="k">
|
||||||
|
<anchor />
|
||||||
|
four
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="l">
|
||||||
|
<text key="m">five</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
</quote>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
|
@@ -0,0 +1,44 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<quote key="a">
|
||||||
|
<quote key="b">
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">one</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
<quote key="e">
|
||||||
|
<paragraph key="f">
|
||||||
|
<text key="g">
|
||||||
|
<anchor />two
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="h">
|
||||||
|
<text key="i">three</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="j">
|
||||||
|
<text key="k">
|
||||||
|
f<focus />our
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
</quote>
|
||||||
|
<paragraph key="l">
|
||||||
|
<text key="m">five</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
|
@@ -0,0 +1,31 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<quote key="a">
|
||||||
|
<quote key="b">
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">
|
||||||
|
<anchor />one<focus />
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
</quote>
|
||||||
|
</quote>
|
||||||
|
<paragraph key="e">
|
||||||
|
<text key="f">two</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'b', 'c', 'd']
|
@@ -0,0 +1,28 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph key="a">
|
||||||
|
<text key="b">one</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">
|
||||||
|
<cursor />
|
||||||
|
two
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['c', 'd']
|
@@ -0,0 +1,31 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph key="a">
|
||||||
|
<text key="b">one</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">
|
||||||
|
two
|
||||||
|
<cursor />
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="e">
|
||||||
|
<text key="f">three</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['c', 'd']
|
@@ -0,0 +1,27 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph key="a">
|
||||||
|
<text key="b">
|
||||||
|
on<cursor />e
|
||||||
|
</text>
|
||||||
|
</paragraph>
|
||||||
|
<paragraph key="c">
|
||||||
|
<text key="d">two</text>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'b']
|
@@ -0,0 +1,27 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<paragraph key="a">
|
||||||
|
<text key="b">one</text>
|
||||||
|
<inline type="link" key="c">
|
||||||
|
<text key="d">
|
||||||
|
tw<cursor />o
|
||||||
|
</text>
|
||||||
|
</inline>
|
||||||
|
</paragraph>
|
||||||
|
</document>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'c', 'd']
|
@@ -0,0 +1,26 @@
|
|||||||
|
/** @jsx h */
|
||||||
|
|
||||||
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
|
export const input = (
|
||||||
|
<value>
|
||||||
|
<document>
|
||||||
|
<image key="a" src="https://example.com/image2.png">
|
||||||
|
<text key="b" />
|
||||||
|
</image>
|
||||||
|
</document>
|
||||||
|
<selection isFocused={false}>
|
||||||
|
<anchor key="b" offset={0} />
|
||||||
|
<focus key="b" offset={0} />
|
||||||
|
</selection>
|
||||||
|
</value>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function({ document, selection }) {
|
||||||
|
return document
|
||||||
|
.getNodesAtRange(selection)
|
||||||
|
.map(n => n.key)
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = ['a', 'b']
|
Reference in New Issue
Block a user