From 4212c6387210a0cc2d8ada1366d03aff6647fa03 Mon Sep 17 00:00:00 2001 From: AlbertHilb Date: Thu, 21 Sep 2017 19:02:29 +0200 Subject: [PATCH] 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. --- packages/slate-react/src/components/node.js | 126 +++------------- packages/slate-react/src/components/text.js | 157 ++++++++++++++++++++ 2 files changed, 175 insertions(+), 108 deletions(-) create mode 100644 packages/slate-react/src/components/text.js diff --git a/packages/slate-react/src/components/node.js b/packages/slate-react/src/components/node.js index 163509ce3..4c0df2856 100644 --- a/packages/slate-react/src/components/node.js +++ b/packages/slate-react/src/components/node.js @@ -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 `
` 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 ( - - ) - } - - /** - * 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 ( - - {leaves} - - ) - } - - /** - * Render a single leaf node given a `range` and `offset`. - * - * @param {List} 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 ( - ) } diff --git a/packages/slate-react/src/components/text.js b/packages/slate-react/src/components/text.js new file mode 100644 index 000000000..901601569 --- /dev/null +++ b/packages/slate-react/src/components/text.js @@ -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 `
` 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 ( + + {leaves} + + ) + } + + /** + * Render a single leaf node given a `range` and `offset`. + * + * @param {List} 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 ( + + ) + } + +} + +/** + * Export. + * + * @type {Component} + */ + +export default Text