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:
committed by
Ian Storm Taylor
parent
13fd6d3a62
commit
4212c63872
@@ -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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
157
packages/slate-react/src/components/text.js
Normal file
157
packages/slate-react/src/components/text.js
Normal 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
|
Reference in New Issue
Block a user