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:
parent
33ddd30486
commit
808848e22e
@ -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}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user