mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-04-20 13:22:04 +02:00
loosen default shouldComponentUpdate for nodes, and make Void implicit
This commit is contained in:
parent
18acc638fd
commit
9a70188f34
@ -5,6 +5,13 @@ This document maintains a list of changes to Slate with each new version. Until
|
||||
---
|
||||
|
||||
|
||||
### `0.11.0` — _August 4, 2016_
|
||||
|
||||
#### BREAKING CHANGES
|
||||
|
||||
- **Void nodes are renderered implicitly again!** Previously Slate had required that you wrap void node renderers yourself with the exposed `<Void>` wrapping component. This was to allow for selection styling, but a change was made to make selection styling able to handled in Javascript. Now the `<Void>` wrapper will be implicitly rendered by Slate, so you do not need to worry about it, and "voidness" only needs to toggled in one place, the `isVoid: true` property of a node.
|
||||
|
||||
|
||||
### `0.10.0` — _July 29, 2016_
|
||||
|
||||
#### BREAKING CHANGES
|
||||
|
@ -1,6 +1,5 @@
|
||||
|
||||
import React from 'react'
|
||||
import { Void } from '../..'
|
||||
|
||||
/**
|
||||
* An video embed component.
|
||||
@ -50,10 +49,10 @@ class Video extends React.Component {
|
||||
|
||||
render = () => {
|
||||
return (
|
||||
<Void {...this.props}>
|
||||
<div {...this.props.attributes}>
|
||||
{this.renderVideo()}
|
||||
{this.renderInput()}
|
||||
</Void>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -15,11 +15,11 @@ import isUrl from 'is-url'
|
||||
const NODES = {
|
||||
image: (props) => {
|
||||
const { node, state } = props
|
||||
const isFocused = state.selection.hasEdgeIn(node)
|
||||
const src = node.data.get('src')
|
||||
const className = isFocused ? 'active' : null
|
||||
return (
|
||||
<Void {...props} className="image-block">
|
||||
<img src={src} {...props.attributes} />
|
||||
</Void>
|
||||
<img src={src} className={className} {...props.attributes} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,10 @@ img {
|
||||
max-height: 20em;
|
||||
}
|
||||
|
||||
img.active {
|
||||
box-shadow: 0 0 0 2px blue;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 2px solid #ddd;
|
||||
margin-left: 0;
|
||||
@ -156,11 +160,3 @@ input:focus {
|
||||
.hover-menu .button[data-active="true"] {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.image-block:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.image-block:focus img {
|
||||
box-shadow: 0 0 0 2px blue;
|
||||
}
|
||||
|
@ -772,7 +772,7 @@ class Content extends React.Component {
|
||||
|
||||
function isNonEditable(event) {
|
||||
const { target, currentTarget } = event
|
||||
const nonEditable = target.closest('[contenteditable="false"]:not([data-void="true"])')
|
||||
const nonEditable = target.closest('[contenteditable="false"]')
|
||||
const isContained = currentTarget.contains(nonEditable)
|
||||
return isContained
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ class Editor extends React.Component {
|
||||
*/
|
||||
|
||||
onBeforeChange = (state) => {
|
||||
if (state == this.state.state) return
|
||||
if (state == this.state.state) return state
|
||||
|
||||
for (const plugin of this.state.plugins) {
|
||||
if (!plugin.onBeforeChange) continue
|
||||
|
@ -5,10 +5,11 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import TYPES from '../utils/types'
|
||||
import Text from './text'
|
||||
import Void from './void'
|
||||
import scrollTo from 'element-scroll-to'
|
||||
|
||||
/**
|
||||
* Debugger.
|
||||
* Debug.
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
@ -23,6 +24,12 @@ const debug = Debug('slate:node')
|
||||
|
||||
class Node extends React.Component {
|
||||
|
||||
/**
|
||||
* Property types.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
static propTypes = {
|
||||
editor: React.PropTypes.object.isRequired,
|
||||
node: React.PropTypes.object.isRequired,
|
||||
@ -32,10 +39,31 @@ class Node extends React.Component {
|
||||
state: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Default properties.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
static defaultProps = {
|
||||
style: {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param {Object} props
|
||||
*/
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
|
||||
if (props.node.kind != 'text') {
|
||||
this.state.Component = props.renderNode(props.node)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug.
|
||||
*
|
||||
@ -50,6 +78,21 @@ class Node extends React.Component {
|
||||
debug(message, `${id}`, ...args)
|
||||
}
|
||||
|
||||
/**
|
||||
* On receiving new props, update the `Component` renderer.
|
||||
*
|
||||
* @param {Object} props
|
||||
*/
|
||||
|
||||
componentWillReceiveProps = (props) => {
|
||||
if (props.node.kind == 'text') return
|
||||
if (props.node == this.props.node) return
|
||||
|
||||
this.setState({
|
||||
Component: props.renderNode(props.node)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the node update?
|
||||
*
|
||||
@ -59,10 +102,36 @@ class Node extends React.Component {
|
||||
*/
|
||||
|
||||
shouldComponentUpdate = (props) => {
|
||||
return (
|
||||
const { Component } = this.state
|
||||
|
||||
// If the node is rendered with a `Component` that has enabled suppression
|
||||
// of update checking, always return true so that it can deal with update
|
||||
// checking itself.
|
||||
if (Component && Component.suppressShouldComponentUpdate) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise, perform a peformant update check by default.
|
||||
if (
|
||||
props.node != this.props.node ||
|
||||
(props.state.isFocused && props.state.selection.hasEdgeIn(props.node))
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
// For block and inline nodes, which can have custom renderers, we need to
|
||||
// include another check for whether the previous selection had an edge in
|
||||
// the node, to allow for intuitive selection-based rendering.
|
||||
if (
|
||||
this.props.node.kind != 'text' &&
|
||||
this.props.state.isFocused &&
|
||||
this.props.state.selection.hasEdgeIn(this.props.node)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise, don't update.
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,8 +237,8 @@ class Node extends React.Component {
|
||||
*/
|
||||
|
||||
renderElement = () => {
|
||||
const { editor, node, renderNode, state } = this.props
|
||||
const Component = renderNode(node)
|
||||
const { editor, node, state } = this.props
|
||||
const { Component } = this.state
|
||||
const children = node.nodes
|
||||
.map(child => this.renderNode(child))
|
||||
.toArray()
|
||||
@ -188,7 +257,7 @@ class Node extends React.Component {
|
||||
if (direction == 'rtl') attributes.dir = 'rtl'
|
||||
}
|
||||
|
||||
return (
|
||||
const element = (
|
||||
<Component
|
||||
attributes={attributes}
|
||||
key={node.key}
|
||||
@ -199,6 +268,10 @@ class Node extends React.Component {
|
||||
{children}
|
||||
</Component>
|
||||
)
|
||||
|
||||
return node.isVoid
|
||||
? <Void {...this.props}>{element}</Void>
|
||||
: element
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,36 +29,11 @@ class Void extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
children: React.PropTypes.any.isRequired,
|
||||
className: React.PropTypes.string,
|
||||
editor: React.PropTypes.object.isRequired,
|
||||
node: React.PropTypes.object.isRequired,
|
||||
state: React.PropTypes.object.isRequired,
|
||||
style: React.PropTypes.object
|
||||
state: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Default properties.
|
||||
*/
|
||||
|
||||
static defaultProps = {
|
||||
style: {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the component update?
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Object} state
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
shouldComponentUpdate = (props, state) => {
|
||||
return (
|
||||
props.node != this.props.node ||
|
||||
(props.state.isFocused && props.state.selection.hasEdgeIn(props.node))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* When one of the wrapper elements it clicked, select the void node.
|
||||
*
|
||||
@ -84,29 +59,19 @@ class Void extends React.Component {
|
||||
*/
|
||||
|
||||
render = () => {
|
||||
const { children, node, className, style } = this.props
|
||||
const { children, node } = this.props
|
||||
const Tag = node.kind == 'block' ? 'div' : 'span'
|
||||
|
||||
// Make the outer wrapper relative, so the spacer can overlay it.
|
||||
const styles = {
|
||||
...style,
|
||||
const style = {
|
||||
position: 'relative'
|
||||
}
|
||||
|
||||
return (
|
||||
<Tag
|
||||
contentEditable={false}
|
||||
data-void="true"
|
||||
onClick={this.onClick}
|
||||
>
|
||||
<Tag
|
||||
contentEditable
|
||||
suppressContentEditableWarning
|
||||
className={className}
|
||||
style={styles}
|
||||
>
|
||||
{this.renderSpacer()}
|
||||
<Tag contentEditable={false}>{children}</Tag>
|
||||
<Tag style={style} onClick={this.onClick}>
|
||||
{this.renderSpacer()}
|
||||
<Tag contentEditable={false}>
|
||||
{children}
|
||||
</Tag>
|
||||
</Tag>
|
||||
)
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
import Editor from './components/editor'
|
||||
import Placeholder from './components/placeholder'
|
||||
import Void from './components/void'
|
||||
|
||||
/**
|
||||
* Models.
|
||||
@ -54,7 +53,6 @@ export {
|
||||
Selection,
|
||||
State,
|
||||
Text,
|
||||
Void,
|
||||
findDOMNode
|
||||
}
|
||||
|
||||
@ -73,6 +71,5 @@ export default {
|
||||
Selection,
|
||||
State,
|
||||
Text,
|
||||
Void,
|
||||
findDOMNode
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user