mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-21 22:45:18 +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 Types from 'prop-types'
|
||||||
|
|
||||||
import TRANSFER_TYPES from '../constants/transfer-types'
|
import TRANSFER_TYPES from '../constants/transfer-types'
|
||||||
import Leaf from './leaf'
|
|
||||||
import Void from './void'
|
import Void from './void'
|
||||||
|
import Text from './text'
|
||||||
import setTransferData from '../utils/set-transfer-data'
|
import setTransferData from '../utils/set-transfer-data'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,7 +54,7 @@ class Node extends React.Component {
|
|||||||
super(props)
|
super(props)
|
||||||
const { node, schema } = props
|
const { node, schema } = props
|
||||||
this.state = {}
|
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) => {
|
debug = (message, ...args) => {
|
||||||
const { node } = this.props
|
const { node } = this.props
|
||||||
const { key, kind, type } = node
|
const { key, type } = node
|
||||||
const id = kind == 'text' ? `${key} (${kind})` : `${key} (${type})`
|
debug(message, `${key} (${type})`, ...args)
|
||||||
debug(message, `${id}`, ...args)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +77,6 @@ class Node extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
componentWillReceiveProps = (props) => {
|
componentWillReceiveProps = (props) => {
|
||||||
if (props.node.kind == 'text') return
|
|
||||||
if (props.node == this.props.node) return
|
if (props.node == this.props.node) return
|
||||||
const Component = props.node.getComponent(props.schema)
|
const Component = props.node.getComponent(props.schema)
|
||||||
this.setState({ Component })
|
this.setState({ Component })
|
||||||
@@ -138,24 +136,6 @@ class Node extends React.Component {
|
|||||||
// need to be rendered again.
|
// need to be rendered again.
|
||||||
if (n.isSelected || p.isSelected) return true
|
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.
|
// Otherwise, don't update.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -190,48 +170,10 @@ class Node extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { props } = this
|
const { props } = this
|
||||||
const { node } = this.props
|
|
||||||
|
|
||||||
this.debug('render', { props })
|
this.debug('render', { props })
|
||||||
|
|
||||||
return node.kind == 'text'
|
const { editor, isSelected, node, parent, readOnly, state } = props
|
||||||
? 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 { Component } = this.state
|
const { Component } = this.state
|
||||||
const { selection } = state
|
const { selection } = state
|
||||||
const indexes = node.getSelectionIndexes(selection, isSelected)
|
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}
|
* @return {Element}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
renderText = () => {
|
renderNode = (child, isSelected) => {
|
||||||
const { node, schema, state } = this.props
|
const { block, editor, node, readOnly, schema, state } = this.props
|
||||||
const { document } = state
|
const Component = child.kind === 'text' ? Text : Node
|
||||||
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 (
|
return (
|
||||||
<span data-key={node.key}>
|
<Component
|
||||||
{leaves}
|
block={node.kind == 'block' ? node : block}
|
||||||
</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}
|
editor={editor}
|
||||||
index={index}
|
isSelected={isSelected}
|
||||||
marks={marks}
|
key={child.key}
|
||||||
node={node}
|
node={child}
|
||||||
offset={offset}
|
parent={node}
|
||||||
parent={parent}
|
readOnly={readOnly}
|
||||||
ranges={ranges}
|
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={state}
|
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