forked from ianstormtaylor/slate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathget-children-decorations.js
132 lines (111 loc) · 3.14 KB
/
get-children-decorations.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { Set } from 'immutable'
/**
* Split the decorations in lists of relevant decorations for each child.
*
* @param {Node} node
* @param {List} decorations
* @return {Array<List<Decoration>>}
*/
function getChildrenDecorations(node, decorations) {
const activeDecorations = Set().asMutable()
const childrenDecorations = []
orderChildDecorations(node, decorations).forEach(item => {
if (item.isRangeStart) {
// Item is a decoration start
activeDecorations.add(item.decoration)
} else if (item.isRangeEnd) {
// item is a decoration end
activeDecorations.remove(item.decoration)
} else {
// Item is a child node
childrenDecorations.push(activeDecorations.toList())
}
})
return childrenDecorations
}
/**
* Orders the children of provided node and its decoration endpoints (start, end)
* so that decorations can be passed only to relevant children (see use in Node.render())
*
* @param {Node} node
* @param {List} decorations
* @return {Array<Item>}
*
* where type Item =
* {
* child: Node,
* // Index of the child in its parent
* index: number
* }
* or {
* // True if this represents the start of the given decoration
* isRangeStart: boolean,
* // True if this represents the end of the given decoration
* isRangeEnd: boolean,
* decoration: Range
* }
*/
function orderChildDecorations(node, decorations) {
if (decorations.isEmpty()) {
return node.nodes.toArray().map((child, index) => ({
child,
index,
}))
}
// Map each key to its global order
const keyOrders = { [node.key]: 0 }
let globalOrder = 1
node.forEachDescendant(child => {
keyOrders[child.key] = globalOrder
globalOrder = globalOrder + 1
})
const childNodes = node.nodes.toArray()
const endPoints = childNodes.map((child, index) => ({
child,
index,
order: keyOrders[child.key],
}))
decorations.forEach(decoration => {
// Range start.
// A rangeStart should be before the child containing its startKey, in order
// to consider it active before going down the child.
const startKeyOrder = keyOrders[decoration.start.key]
const containingChildOrder =
startKeyOrder === undefined
? 0
: getContainingChildOrder(childNodes, keyOrders, startKeyOrder)
endPoints.push({
isRangeStart: true,
order: containingChildOrder - 0.5,
decoration,
})
// Range end.
const endKeyOrder = (keyOrders[decoration.end.key] || globalOrder) + 0.5
endPoints.push({
isRangeEnd: true,
order: endKeyOrder,
decoration,
})
})
return endPoints.sort((a, b) => (a.order > b.order ? 1 : -1))
}
/*
* Returns the key order of the child right before the given order.
*/
function getContainingChildOrder(children, keyOrders, order) {
// Find the first child that is after the given key
const nextChildIndex = children.findIndex(
child => order < keyOrders[child.key]
)
if (nextChildIndex <= 0) {
return 0
}
const containingChild = children[nextChildIndex - 1]
return keyOrders[containingChild.key]
}
/**
* Export.
*
* @type {Function}
*/
export default getChildrenDecorations