1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-21 14:41:23 +02:00

Move code used to render text nodes into a separate React component (#1150)

* Move code used to render text nodes from `Node` into a separate
React component.

* Fix comments.
This commit is contained in:
AlbertHilb
2017-09-21 19:02:29 +02:00
committed by Ian Storm Taylor
parent 13fd6d3a62
commit 4212c63872
2 changed files with 175 additions and 108 deletions

View File

@@ -7,8 +7,8 @@ import logger from 'slate-dev-logger'
import Types from 'prop-types'
import TRANSFER_TYPES from '../constants/transfer-types'
import Leaf from './leaf'
import Void from './void'
import Text from './text'
import setTransferData from '../utils/set-transfer-data'
/**
@@ -54,7 +54,7 @@ class Node extends React.Component {
super(props)
const { node, schema } = props
this.state = {}
this.state.Component = node.kind == 'text' ? null : node.getComponent(schema)
this.state.Component = node.getComponent(schema)
}
/**
@@ -66,9 +66,8 @@ class Node extends React.Component {
debug = (message, ...args) => {
const { node } = this.props
const { key, kind, type } = node
const id = kind == 'text' ? `${key} (${kind})` : `${key} (${type})`
debug(message, `${id}`, ...args)
const { key, type } = node
debug(message, `${key} (${type})`, ...args)
}
/**
@@ -78,7 +77,6 @@ class Node extends React.Component {
*/
componentWillReceiveProps = (props) => {
if (props.node.kind == 'text') return
if (props.node == this.props.node) return
const Component = props.node.getComponent(props.schema)
this.setState({ Component })
@@ -138,24 +136,6 @@ class Node extends React.Component {
// need to be rendered again.
if (n.isSelected || p.isSelected) return true
// If the node is a text node, re-render if the current decorations have
// changed, even if the content of the text node itself hasn't.
if (n.node.kind == 'text' && n.schema.hasDecorators) {
const nDecorators = n.state.document.getDescendantDecorators(n.node.key, n.schema)
const pDecorators = p.state.document.getDescendantDecorators(p.node.key, p.schema)
const nRanges = n.node.getRanges(nDecorators)
const pRanges = p.node.getRanges(pDecorators)
if (!nRanges.equals(pRanges)) return true
}
// If the node is a text node, and its parent is a block node, and it was
// the last child of the block, re-render to cleanup extra `<br/>` or `\n`.
if (n.node.kind == 'text' && n.parent.kind == 'block') {
const pLast = p.parent.nodes.last()
const nLast = n.parent.nodes.last()
if (p.node == pLast && n.node != nLast) return true
}
// Otherwise, don't update.
return false
}
@@ -190,48 +170,10 @@ class Node extends React.Component {
render() {
const { props } = this
const { node } = this.props
this.debug('render', { props })
return node.kind == 'text'
? this.renderText()
: this.renderElement()
}
/**
* Render a `child` node.
*
* @param {Node} child
* @param {Boolean} isSelected
* @return {Element}
*/
renderNode = (child, isSelected) => {
const { block, editor, node, readOnly, schema, state } = this.props
return (
<Node
block={node.kind == 'block' ? node : block}
editor={editor}
isSelected={isSelected}
key={child.key}
node={child}
parent={node}
readOnly={readOnly}
schema={schema}
state={state}
/>
)
}
/**
* Render an element `node`.
*
* @return {Element}
*/
renderElement = () => {
const { editor, isSelected, node, parent, readOnly, state } = this.props
const { editor, isSelected, node, parent, readOnly, state } = props
const { Component } = this.state
const { selection } = state
const indexes = node.getSelectionIndexes(selection, isSelected)
@@ -275,59 +217,27 @@ class Node extends React.Component {
}
/**
* Render a text node.
* Render a `child` node.
*
* @param {Node} child
* @param {Boolean} isSelected
* @return {Element}
*/
renderText = () => {
const { node, schema, state } = this.props
const { document } = state
const decorators = schema.hasDecorators ? document.getDescendantDecorators(node.key, schema) : []
const ranges = node.getRanges(decorators)
let offset = 0
const leaves = ranges.map((range, i) => {
const leaf = this.renderLeaf(ranges, range, i, offset)
offset += range.text.length
return leaf
})
renderNode = (child, isSelected) => {
const { block, editor, node, readOnly, schema, state } = this.props
const Component = child.kind === 'text' ? Text : Node
return (
<span data-key={node.key}>
{leaves}
</span>
)
}
/**
* Render a single leaf node given a `range` and `offset`.
*
* @param {List<Range>} ranges
* @param {Range} range
* @param {Number} index
* @param {Number} offset
* @return {Element} leaf
*/
renderLeaf = (ranges, range, index, offset) => {
const { block, node, parent, schema, state, editor } = this.props
const { text, marks } = range
return (
<Leaf
key={`${node.key}-${index}`}
block={block}
<Component
block={node.kind == 'block' ? node : block}
editor={editor}
index={index}
marks={marks}
node={node}
offset={offset}
parent={parent}
ranges={ranges}
isSelected={isSelected}
key={child.key}
node={child}
parent={node}
readOnly={readOnly}
schema={schema}
state={state}
text={text}
/>
)
}

View File

@@ -0,0 +1,157 @@
import Debug from 'debug'
import React from 'react'
import SlateTypes from 'slate-prop-types'
import Types from 'prop-types'
import Leaf from './leaf'
/**
* Debug.
*
* @type {Function}
*/
const debug = Debug('slate:node')
class Text extends React.Component {
/**
* Property types.
*
* @type {Object}
*/
static propTypes = {
block: SlateTypes.block,
editor: Types.object.isRequired,
node: SlateTypes.node.isRequired,
parent: SlateTypes.node.isRequired,
schema: SlateTypes.schema.isRequired,
state: SlateTypes.state.isRequired,
}
/**
* Debug.
*
* @param {String} message
* @param {Mixed} ...args
*/
debug = (message, ...args) => {
const { node } = this.props
const { key } = node
debug(message, `${key} (text)`, ...args)
}
/**
* Should the node update?
*
* @param {Object} nextProps
* @param {Object} state
* @return {Boolean}
*/
shouldComponentUpdate = (nextProps) => {
const { props } = this
const n = nextProps
const p = props
// If the node has changed, update. PERF: There are cases where it will have
// changed, but it's properties will be exactly the same (eg. copy-paste)
// which this won't catch. But that's rare and not a drag on performance, so
// for simplicity we just let them through.
if (n.node != p.node) return true
// Re-render if the current decorations have changed, even if the content of
// the text node itself hasn't.
if (n.schema.hasDecorators) {
const nDecorators = n.state.document.getDescendantDecorators(n.node.key, n.schema)
const pDecorators = p.state.document.getDescendantDecorators(p.node.key, p.schema)
const nRanges = n.node.getRanges(nDecorators)
const pRanges = p.node.getRanges(pDecorators)
if (!nRanges.equals(pRanges)) return true
}
// If the node parent is a block node, and it was the last child of the
// block, re-render to cleanup extra `<br/>` or `\n`.
if (n.parent.kind == 'block') {
const pLast = p.parent.nodes.last()
const nLast = n.parent.nodes.last()
if (p.node == pLast && n.node != nLast) return true
}
// Otherwise, don't update.
return false
}
/**
* Render.
*
* @return {Element}
*/
render() {
const { props } = this
this.debug('render', { props })
const { node, schema, state } = props
const { document } = state
const decorators = schema.hasDecorators ? document.getDescendantDecorators(node.key, schema) : []
const ranges = node.getRanges(decorators)
let offset = 0
const leaves = ranges.map((range, i) => {
const leaf = this.renderLeaf(ranges, range, i, offset)
offset += range.text.length
return leaf
})
return (
<span data-key={node.key}>
{leaves}
</span>
)
}
/**
* Render a single leaf node given a `range` and `offset`.
*
* @param {List<Range>} ranges
* @param {Range} range
* @param {Number} index
* @param {Number} offset
* @return {Element} leaf
*/
renderLeaf = (ranges, range, index, offset) => {
const { block, node, parent, schema, state, editor } = this.props
const { text, marks } = range
return (
<Leaf
key={`${node.key}-${index}`}
block={block}
editor={editor}
index={index}
marks={marks}
node={node}
offset={offset}
parent={parent}
ranges={ranges}
schema={schema}
state={state}
text={text}
/>
)
}
}
/**
* Export.
*
* @type {Component}
*/
export default Text