mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-29 18:09:49 +02:00
fix text nodes to rerender with changes decorations
This commit is contained in:
@@ -11,7 +11,41 @@ import initialState from './state.json'
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
let characters = text.characters.asMutable()
|
||||
const language = block.data.get('language')
|
||||
const string = text.text
|
||||
const grammar = Prism.languages.javascript
|
||||
const grammar = Prism.languages[language]
|
||||
const tokens = Prism.tokenize(string, grammar)
|
||||
let offset = 0
|
||||
|
||||
|
@@ -13,6 +13,9 @@
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "code",
|
||||
"data": {
|
||||
"language": "js"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "text",
|
||||
|
@@ -4,7 +4,7 @@ import Debug from 'debug'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import TYPES from '../utils/types'
|
||||
import Text from './text'
|
||||
import Leaf from './leaf'
|
||||
import Void from './void'
|
||||
import scrollTo from '../utils/scroll-to'
|
||||
|
||||
@@ -31,6 +31,7 @@ class Node extends React.Component {
|
||||
*/
|
||||
|
||||
static propTypes = {
|
||||
block: React.PropTypes.object,
|
||||
editor: React.PropTypes.object.isRequired,
|
||||
node: React.PropTypes.object.isRequired,
|
||||
renderDecorations: React.PropTypes.func.isRequired,
|
||||
@@ -47,7 +48,7 @@ class Node extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
style: {}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -130,6 +131,17 @@ class Node extends React.Component {
|
||||
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.
|
||||
return false
|
||||
}
|
||||
@@ -216,10 +228,12 @@ class Node extends React.Component {
|
||||
*/
|
||||
|
||||
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 (
|
||||
<Node
|
||||
key={child.key}
|
||||
block={block}
|
||||
node={child}
|
||||
state={state}
|
||||
editor={editor}
|
||||
@@ -281,15 +295,48 @@ class Node extends React.Component {
|
||||
*/
|
||||
|
||||
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 (
|
||||
<Text
|
||||
key={node.key}
|
||||
editor={editor}
|
||||
node={node}
|
||||
renderDecorations={renderDecorations}
|
||||
renderMark={renderMark}
|
||||
<span data-key={node.key}>
|
||||
{leaves}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -298,6 +345,8 @@ class Node extends React.Component {
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*
|
||||
* @type {Component}
|
||||
*/
|
||||
|
||||
export default Node
|
||||
|
@@ -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
|
Reference in New Issue
Block a user