1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-30 02:19:52 +02:00

fix text nodes to rerender with changes decorations

This commit is contained in:
Ian Storm Taylor
2016-08-09 16:17:06 -07:00
parent 0fbf840649
commit 0886242657
4 changed files with 99 additions and 121 deletions

View File

@@ -11,7 +11,41 @@ import initialState from './state.json'
*/ */
const NODES = { const NODES = {
code: props => <pre><code {...props.attributes}>{props.children}</code></pre> code: (props) => {
const { attributes, children, editor, node } = props
const language = node.data.get('language')
function onChange(e) {
const state = editor.getState()
const next = state
.transform()
.setNodeByKey(node.key, {
data: {
language: e.target.value
}
})
.apply()
editor.onChange(next)
}
return (
<div style={{ position: 'relative' }}>
<pre>
<code {...props.attributes}>{props.children}</code>
</pre>
<div
contentEditable={false}
style={{ position: 'absolute', top: '5px', right: '5px' }}
>
<select value={language} onChange={onChange} >
<option value="css">CSS</option>
<option value="js">JavaScript</option>
<option value="html">HTML</option>
</select>
</div>
</div>
)
}
} }
/** /**
@@ -136,8 +170,9 @@ class CodeHighlighting extends React.Component {
if (block.type != 'code') return text.characters if (block.type != 'code') return text.characters
let characters = text.characters.asMutable() let characters = text.characters.asMutable()
const language = block.data.get('language')
const string = text.text const string = text.text
const grammar = Prism.languages.javascript const grammar = Prism.languages[language]
const tokens = Prism.tokenize(string, grammar) const tokens = Prism.tokenize(string, grammar)
let offset = 0 let offset = 0

View File

@@ -13,6 +13,9 @@
{ {
"kind": "block", "kind": "block",
"type": "code", "type": "code",
"data": {
"language": "js"
},
"nodes": [ "nodes": [
{ {
"kind": "text", "kind": "text",

View File

@@ -4,7 +4,7 @@ import Debug from 'debug'
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import TYPES from '../utils/types' import TYPES from '../utils/types'
import Text from './text' import Leaf from './leaf'
import Void from './void' import Void from './void'
import scrollTo from '../utils/scroll-to' import scrollTo from '../utils/scroll-to'
@@ -31,6 +31,7 @@ class Node extends React.Component {
*/ */
static propTypes = { static propTypes = {
block: React.PropTypes.object,
editor: React.PropTypes.object.isRequired, editor: React.PropTypes.object.isRequired,
node: React.PropTypes.object.isRequired, node: React.PropTypes.object.isRequired,
renderDecorations: React.PropTypes.func.isRequired, renderDecorations: React.PropTypes.func.isRequired,
@@ -47,7 +48,7 @@ class Node extends React.Component {
static defaultProps = { static defaultProps = {
style: {} style: {}
} };
/** /**
* Constructor. * Constructor.
@@ -130,6 +131,17 @@ class Node extends React.Component {
return true return true
} }
// For text nodes, which can have custom decorations, we need to check to
// see if the block has changed, which has caused the decorations to change.
if (
props.node.kind == 'text' &&
props.block != this.props.block
) {
const nextRanges = props.node.getDecoratedRanges(props.block, props.renderDecorations)
const ranges = this.props.node.getDecoratedRanges(this.props.block, this.props.renderDecorations)
if (!ranges.equals(nextRanges)) return true
}
// Otherwise, don't update. // Otherwise, don't update.
return false return false
} }
@@ -216,10 +228,12 @@ class Node extends React.Component {
*/ */
renderNode = (child) => { renderNode = (child) => {
const { editor, renderDecorations, renderMark, renderNode, state } = this.props const { editor, node, renderDecorations, renderMark, renderNode, state } = this.props
const block = node.kind == 'block' ? node : this.props.block
return ( return (
<Node <Node
key={child.key} key={child.key}
block={block}
node={child} node={child}
state={state} state={state}
editor={editor} editor={editor}
@@ -281,15 +295,48 @@ class Node extends React.Component {
*/ */
renderText = () => { renderText = () => {
const { node, editor, renderDecorations, renderMark, state } = this.props const { node, block, renderDecorations } = this.props
const ranges = node.getDecoratedRanges(block, renderDecorations)
let offset = 0
const leaves = ranges.map((range, i, original) => {
const leaf = this.renderLeaf(ranges, range, i, offset)
offset += range.text.length
return leaf
})
return ( return (
<Text <span data-key={node.key}>
key={node.key} {leaves}
editor={editor} </span>
node={node} )
renderDecorations={renderDecorations} }
renderMark={renderMark}
/**
* 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 { node, renderMark, state } = this.props
const text = range.text
const marks = range.marks
return (
<Leaf
key={`${node.key}-${index}`}
index={index}
state={state} state={state}
node={node}
text={text}
marks={marks}
ranges={ranges}
renderMark={renderMark}
/> />
) )
} }
@@ -298,6 +345,8 @@ class Node extends React.Component {
/** /**
* Export. * Export.
*
* @type {Component}
*/ */
export default Node export default Node

View File

@@ -1,109 +0,0 @@
import Leaf from './leaf'
import React from 'react'
import { List } from 'immutable'
/**
* Text.
*/
class Text extends React.Component {
/**
* Properties.
*/
static propTypes = {
editor: React.PropTypes.object.isRequired,
node: React.PropTypes.object.isRequired,
renderDecorations: React.PropTypes.func.isRequired,
renderMark: React.PropTypes.func.isRequired,
state: React.PropTypes.object.isRequired
};
/**
* Should the component update?
*
* @param {Object} props
* @param {Object} state
* @return {Boolean} shouldUpdate
*/
shouldComponentUpdate(props, state) {
return (
props.node != this.props.node ||
(props.state.isFocused && props.state.selection.hasEdgeIn(props.node))
)
}
/**
* Render.
*
* @return {Element} element
*/
render() {
const { node } = this.props
return (
<span data-key={node.key}>
{this.renderLeaves()}
</span>
)
}
/**
* Render the leaf nodes.
*
* @return {Array} leaves
*/
renderLeaves() {
const { node, state, renderDecorations } = this.props
const block = state.document.getClosestBlock(node)
const ranges = node.getDecoratedRanges(block, renderDecorations)
return ranges.map((range, i, original) => {
const previous = original.slice(0, i)
const offset = previous.size
? previous.map(r => r.text).join('').length
: 0
return this.renderLeaf(ranges, range, i, offset)
})
}
/**
* 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 { node, renderMark, state } = this.props
const text = range.text
const marks = range.marks
return (
<Leaf
key={`${node.key}-${index}`}
index={index}
state={state}
node={node}
text={text}
marks={marks}
ranges={ranges}
renderMark={renderMark}
/>
)
}
}
/**
* Export.
*/
export default Text