1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-02 22:28:46 +01:00

fix the performance of calculating isSelected (#1087)

* fix the performance of calculating isSelected

* ocd
This commit is contained in:
Ian Storm Taylor 2017-09-07 18:04:40 -07:00 committed by GitHub
parent 33ddd30486
commit 808848e22e
3 changed files with 67 additions and 45 deletions

View File

@ -861,10 +861,12 @@ class Content extends React.Component {
const { props } = this
const { className, readOnly, state, tabIndex, role, tagName } = props
const Container = tagName
const { document } = state
const children = document.nodes
.map(node => this.renderNode(node))
.toArray()
const { document, selection } = state
const indexes = document.getSelectionIndexes(selection, selection.isFocused)
const children = document.nodes.toArray().map((child, i) => {
const isSelected = !!indexes && indexes.start <= i && i < indexes.end
return this.renderNode(child, isSelected)
})
const style = {
// Prevent the default outline styles.
@ -933,27 +935,13 @@ class Content extends React.Component {
* Render a `child` node of the document.
*
* @param {Node} child
* @param {Boolean} isSelected
* @return {Element}
*/
renderNode = (child) => {
renderNode = (child, isSelected) => {
const { editor, readOnly, schema, state } = this.props
const { document, selection } = state
const { startKey, endKey, isBlurred } = selection
let isSelected
if (isBlurred) {
isSelected = false
}
else {
isSelected = document.nodes
.skipUntil(n => n.kind == 'text' ? n.key == startKey : n.hasDescendant(startKey))
.reverse()
.skipUntil(n => n.kind == 'text' ? n.key == endKey : n.hasDescendant(endKey))
.includes(child)
}
const { document } = state
return (
<Node
block={null}

View File

@ -244,36 +244,17 @@ class Node extends React.Component {
* Render a `child` node.
*
* @param {Node} child
* @param {Boolean} isSelected
* @return {Element}
*/
renderNode = (child) => {
const { block, editor, isSelected, node, readOnly, schema, state } = this.props
const { selection } = state
const { startKey, endKey } = selection
let isChildSelected
if (!isSelected) {
isChildSelected = false
}
else if (node.kind == 'text') {
isChildSelected = node.key == startKey || node.key == endKey
}
else {
isChildSelected = node.nodes
.skipUntil(n => n.kind == 'text' ? n.key == startKey : n.hasDescendant(startKey))
.reverse()
.skipUntil(n => n.kind == 'text' ? n.key == endKey : n.hasDescendant(endKey))
.includes(child)
}
renderNode = (child, isSelected) => {
const { block, editor, node, readOnly, schema, state } = this.props
return (
<Node
block={node.kind == 'block' ? node : block}
editor={editor}
isSelected={isChildSelected}
isSelected={isSelected}
key={child.key}
node={child}
parent={node}
@ -293,7 +274,12 @@ class Node extends React.Component {
renderElement = () => {
const { editor, isSelected, node, parent, readOnly, state } = this.props
const { Component } = this.state
const children = node.nodes.map(this.renderNode).toArray()
const { selection } = state
const indexes = node.getSelectionIndexes(selection, isSelected)
const children = node.nodes.toArray().map((child, i) => {
const isChildSelected = !!indexes && indexes.start <= i && i < indexes.end
return this.renderNode(child, isChildSelected)
})
// Attributes that the developer must to mix into the element in their
// custom node renderer component.

View File

@ -1375,6 +1375,54 @@ class Node {
.last()
}
/**
* Get the indexes of the selection for a `range`, given an extra flag for
* whether the node `isSelected`, to determine whether not finding matches
* means everything is selected or nothing is.
*
* @param {Selection} range
* @param {Boolean} isSelected
* @return {Object|Null}
*/
getSelectionIndexes(range, isSelected = false) {
const { startKey, endKey } = range
// PERF: if we're not selected, or the range is blurred, we can exit early.
if (!isSelected || range.isBlurred) {
return null
}
// PERF: if the start and end keys are the same, just check for the child
// that contains that single key.
if (startKey == endKey) {
const child = this.getFurthestAncestor(startKey)
const index = child ? this.nodes.indexOf(child) : null
return { start: index, end: index + 1 }
}
// Otherwise, check all of the children...
let start = null
let end = null
this.nodes.forEach((child, i) => {
if (child.kind == 'text') {
if (start == null && child.key == startKey) start = i
if (end == null && child.key == endKey) end = i + 1
} else {
if (start == null && child.hasDescendant(startKey)) start = i
if (end == null && child.hasDescendant(endKey)) end = i + 1
}
// PERF: exit early if both start and end have been found.
return start != null && end != null
})
if (isSelected && start == null) start = 0
if (isSelected && end == null) end = this.nodes.size
return start == null ? null : { start, end }
}
/**
* Get the concatenated text string of all child nodes.
*