forked from ianstormtaylor/slate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathleaf.js
155 lines (128 loc) · 4 KB
/
leaf.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import OffsetKey from '../utils/offset-key'
import React from 'react'
import ReactDOM from 'react-dom'
/**
* Leaf.
*/
class Leaf extends React.Component {
/**
* Properties.
*/
static propTypes = {
index: React.PropTypes.number.isRequired,
marks: React.PropTypes.object.isRequired,
node: React.PropTypes.object.isRequired,
renderMark: React.PropTypes.func.isRequired,
state: React.PropTypes.object.isRequired,
text: React.PropTypes.string.isRequired
};
/**
* Should component update?
*
* @param {Object} props
* @return {Boolean} shouldUpdate
*/
shouldComponentUpdate(props) {
const { index, node, state } = props
const { selection } = state
if (
props.index != this.props.index ||
props.text != this.props.text ||
props.marks != this.props.marks
) {
return true
}
const { start, end } = OffsetKey.findBounds(node.key, index, state)
return selection.hasEdgeBetween(node, start, end)
}
componentDidMount() {
this.updateSelection()
}
componentDidUpdate() {
this.updateSelection()
}
updateSelection() {
const { state } = this.props
const { selection } = state
// If the selection is not focused we have nothing to do.
if (!selection.isFocused) return
const { anchorOffset, focusOffset } = selection
const { node, index } = this.props
const { start, end } = OffsetKey.findBounds(node.key, index, state)
// If neither matches, the selection doesn't start or end here, so exit.
const hasAnchor = selection.hasAnchorBetween(node, start, end)
const hasFocus = selection.hasFocusBetween(node, start, end)
if (!hasAnchor && !hasFocus) return
// We have a selection to render, so prepare a few things...
const native = window.getSelection()
const el = ReactDOM.findDOMNode(this).firstChild
// If both the start and end are here, set the selection all at once.
if (hasAnchor && hasFocus) {
native.removeAllRanges()
const range = window.document.createRange()
range.setStart(el, anchorOffset - start)
native.addRange(range)
native.extend(el, focusOffset - start)
return
}
// If the selection is forward, we can set things in sequence. In
// the first leaf to render, reset the selection and set the new start. And
// then in the second leaf to render, extend to the new end.
if (selection.isForward) {
if (hasAnchor) {
native.removeAllRanges()
const range = window.document.createRange()
range.setStart(el, anchorOffset - start)
native.addRange(range)
} else if (hasFocus) {
native.extend(el, focusOffset - start)
}
}
// Otherwise, if the selection is backward, we need to hack the order a bit.
// In the first leaf to render, set a phony start anchor to store the true
// end position. And then in the second leaf to render, set the start and
// extend the end to the stored value.
else {
if (hasFocus) {
native.removeAllRanges()
const range = window.document.createRange()
range.setStart(el, focusOffset - start)
native.addRange(range)
} else if (hasAnchor) {
const endNode = native.focusNode
const endOffset = native.focusOffset
native.removeAllRanges()
const range = window.document.createRange()
range.setStart(el, anchorOffset - start)
native.addRange(range)
native.extend(endNode, endOffset)
}
}
}
render() {
const { node, index, text, marks, renderMark } = this.props
const offsetKey = OffsetKey.stringify({
key: node.key,
index
})
const style = marks.reduce((memo, mark) => {
const styles = renderMark(mark, marks)
for (const key in styles) {
memo[key] = styles[key]
}
return memo
}, {})
return (
<span
data-offset-key={offsetKey}
style={style}
>
{text || <br />}
</span>
)
}
}
/**
* Export.
*/
export default Leaf