1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-24 09:13:24 +01:00
slate/lib/components/leaf.js

156 lines
4.0 KiB
JavaScript
Raw Normal View History

2016-06-15 19:46:53 -07:00
2016-06-17 00:09:54 -07:00
import OffsetKey from '../utils/offset-key'
2016-06-15 19:46:53 -07:00
import React from 'react'
import ReactDOM from 'react-dom'
/**
* Leaf.
2016-06-15 19:46:53 -07:00
*/
class Leaf extends React.Component {
2016-06-15 19:46:53 -07:00
/**
* Properties.
*/
2016-06-15 19:46:53 -07:00
static propTypes = {
index: React.PropTypes.number.isRequired,
marks: React.PropTypes.object.isRequired,
2016-06-15 19:46:53 -07:00
node: React.PropTypes.object.isRequired,
renderMark: React.PropTypes.func.isRequired,
state: React.PropTypes.object.isRequired,
2016-06-17 18:20:26 -07:00
text: React.PropTypes.string.isRequired
2016-06-15 19:46:53 -07:00
};
/**
* Should component update?
*
* @param {Object} props
* @return {Boolean} shouldUpdate
*/
2016-07-07 19:37:34 -07:00
shouldComponentUpdate(props) {
const { index, node, state } = props
2016-07-07 19:37:34 -07:00
const { selection } = state
if (
props.index != this.props.index ||
props.text != this.props.text ||
props.marks != this.props.marks
) {
return true
}
2016-07-07 19:37:34 -07:00
const { start, end } = OffsetKey.findBounds(node.key, index, state)
return selection.hasEdgeBetween(node, start, end)
}
componentDidMount() {
2016-06-15 19:46:53 -07:00
this.updateSelection()
}
componentDidUpdate() {
2016-06-15 19:46:53 -07:00
this.updateSelection()
}
updateSelection() {
const { state } = this.props
const { selection } = state
// If the selection is not focused we have nothing to do.
2016-06-15 20:13:02 -07:00
if (!selection.isFocused) return
2016-06-15 19:46:53 -07:00
2016-07-07 19:37:34 -07:00
const { anchorOffset, focusOffset } = selection
const { node, index } = this.props
const { start, end } = OffsetKey.findBounds(node.key, index, state)
2016-06-15 19:46:53 -07:00
// 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
2016-06-15 19:46:53 -07:00
// 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) {
2016-06-15 19:46:53 -07:00
native.removeAllRanges()
const range = window.document.createRange()
2016-06-17 18:36:47 -07:00
range.setStart(el, anchorOffset - start)
2016-06-15 19:46:53 -07:00
native.addRange(range)
2016-06-17 18:36:47 -07:00
native.extend(el, focusOffset - start)
2016-06-15 19:46:53 -07:00
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) {
2016-06-15 19:46:53 -07:00
native.removeAllRanges()
const range = window.document.createRange()
2016-06-17 18:36:47 -07:00
range.setStart(el, anchorOffset - start)
2016-06-15 19:46:53 -07:00
native.addRange(range)
} else if (hasFocus) {
2016-06-17 18:36:47 -07:00
native.extend(el, focusOffset - start)
2016-06-15 19:46:53 -07:00
}
}
// 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) {
2016-06-15 19:46:53 -07:00
native.removeAllRanges()
const range = window.document.createRange()
2016-06-17 18:36:47 -07:00
range.setStart(el, focusOffset - start)
2016-06-15 19:46:53 -07:00
native.addRange(range)
} else if (hasAnchor) {
2016-06-15 19:46:53 -07:00
const endNode = native.focusNode
const endOffset = native.focusOffset
native.removeAllRanges()
const range = window.document.createRange()
2016-06-17 18:36:47 -07:00
range.setStart(el, anchorOffset - start)
2016-06-15 19:46:53 -07:00
native.addRange(range)
native.extend(endNode, endOffset)
}
}
}
render() {
const { node, index, text, marks, renderMark } = this.props
2016-06-17 00:09:54 -07:00
const offsetKey = OffsetKey.stringify({
key: node.key,
index
2016-06-17 00:09:54 -07:00
})
2016-06-15 19:46:53 -07:00
2016-07-06 20:19:19 -07:00
const style = marks.reduce((memo, mark) => {
2016-07-22 14:16:17 -07:00
const styles = renderMark(mark, marks)
for (const key in styles) {
memo[key] = styles[key]
}
2016-07-22 22:15:12 +01:00
return memo
}, {})
2016-06-15 19:46:53 -07:00
return (
<span
2016-06-17 00:09:54 -07:00
data-offset-key={offsetKey}
2016-06-20 17:38:56 -07:00
style={style}
2016-06-15 19:46:53 -07:00
>
2016-07-06 20:19:19 -07:00
{text || <br />}
2016-06-15 19:46:53 -07:00
</span>
)
}
}
/**
* Export.
*/
export default Leaf