mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-07-31 04:20:26 +02:00
loosen default shouldComponentUpdate for nodes, and make Void implicit
This commit is contained in:
@@ -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_
|
### `0.10.0` — _July 29, 2016_
|
||||||
|
|
||||||
#### BREAKING CHANGES
|
#### BREAKING CHANGES
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Void } from '../..'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An video embed component.
|
* An video embed component.
|
||||||
@@ -50,10 +49,10 @@ class Video extends React.Component {
|
|||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
return (
|
return (
|
||||||
<Void {...this.props}>
|
<div {...this.props.attributes}>
|
||||||
{this.renderVideo()}
|
{this.renderVideo()}
|
||||||
{this.renderInput()}
|
{this.renderInput()}
|
||||||
</Void>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,11 +15,11 @@ import isUrl from 'is-url'
|
|||||||
const NODES = {
|
const NODES = {
|
||||||
image: (props) => {
|
image: (props) => {
|
||||||
const { node, state } = props
|
const { node, state } = props
|
||||||
|
const isFocused = state.selection.hasEdgeIn(node)
|
||||||
const src = node.data.get('src')
|
const src = node.data.get('src')
|
||||||
|
const className = isFocused ? 'active' : null
|
||||||
return (
|
return (
|
||||||
<Void {...props} className="image-block">
|
<img src={src} className={className} {...props.attributes} />
|
||||||
<img src={src} {...props.attributes} />
|
|
||||||
</Void>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,10 @@ img {
|
|||||||
max-height: 20em;
|
max-height: 20em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img.active {
|
||||||
|
box-shadow: 0 0 0 2px blue;
|
||||||
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
border-left: 2px solid #ddd;
|
border-left: 2px solid #ddd;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
@@ -156,11 +160,3 @@ input:focus {
|
|||||||
.hover-menu .button[data-active="true"] {
|
.hover-menu .button[data-active="true"] {
|
||||||
color: #fff;
|
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) {
|
function isNonEditable(event) {
|
||||||
const { target, currentTarget } = 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)
|
const isContained = currentTarget.contains(nonEditable)
|
||||||
return isContained
|
return isContained
|
||||||
}
|
}
|
||||||
|
@@ -163,7 +163,7 @@ class Editor extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
onBeforeChange = (state) => {
|
onBeforeChange = (state) => {
|
||||||
if (state == this.state.state) return
|
if (state == this.state.state) return state
|
||||||
|
|
||||||
for (const plugin of this.state.plugins) {
|
for (const plugin of this.state.plugins) {
|
||||||
if (!plugin.onBeforeChange) continue
|
if (!plugin.onBeforeChange) continue
|
||||||
|
@@ -5,10 +5,11 @@ 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 Text from './text'
|
||||||
|
import Void from './void'
|
||||||
import scrollTo from 'element-scroll-to'
|
import scrollTo from 'element-scroll-to'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debugger.
|
* Debug.
|
||||||
*
|
*
|
||||||
* @type {Function}
|
* @type {Function}
|
||||||
*/
|
*/
|
||||||
@@ -23,6 +24,12 @@ const debug = Debug('slate:node')
|
|||||||
|
|
||||||
class Node extends React.Component {
|
class Node extends React.Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property types.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
editor: React.PropTypes.object.isRequired,
|
editor: React.PropTypes.object.isRequired,
|
||||||
node: React.PropTypes.object.isRequired,
|
node: React.PropTypes.object.isRequired,
|
||||||
@@ -32,10 +39,31 @@ class Node extends React.Component {
|
|||||||
state: React.PropTypes.object.isRequired
|
state: React.PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default properties.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
style: {}
|
style: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param {Object} props
|
||||||
|
*/
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {}
|
||||||
|
|
||||||
|
if (props.node.kind != 'text') {
|
||||||
|
this.state.Component = props.renderNode(props.node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug.
|
* Debug.
|
||||||
*
|
*
|
||||||
@@ -50,6 +78,21 @@ class Node extends React.Component {
|
|||||||
debug(message, `${id}`, ...args)
|
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?
|
* Should the node update?
|
||||||
*
|
*
|
||||||
@@ -59,10 +102,36 @@ class Node extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
shouldComponentUpdate = (props) => {
|
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.node != this.props.node ||
|
||||||
(props.state.isFocused && props.state.selection.hasEdgeIn(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 = () => {
|
renderElement = () => {
|
||||||
const { editor, node, renderNode, state } = this.props
|
const { editor, node, state } = this.props
|
||||||
const Component = renderNode(node)
|
const { Component } = this.state
|
||||||
const children = node.nodes
|
const children = node.nodes
|
||||||
.map(child => this.renderNode(child))
|
.map(child => this.renderNode(child))
|
||||||
.toArray()
|
.toArray()
|
||||||
@@ -188,7 +257,7 @@ class Node extends React.Component {
|
|||||||
if (direction == 'rtl') attributes.dir = 'rtl'
|
if (direction == 'rtl') attributes.dir = 'rtl'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const element = (
|
||||||
<Component
|
<Component
|
||||||
attributes={attributes}
|
attributes={attributes}
|
||||||
key={node.key}
|
key={node.key}
|
||||||
@@ -199,6 +268,10 @@ class Node extends React.Component {
|
|||||||
{children}
|
{children}
|
||||||
</Component>
|
</Component>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return node.isVoid
|
||||||
|
? <Void {...this.props}>{element}</Void>
|
||||||
|
: element
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -29,36 +29,11 @@ class Void extends React.Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: React.PropTypes.any.isRequired,
|
children: React.PropTypes.any.isRequired,
|
||||||
className: React.PropTypes.string,
|
|
||||||
editor: React.PropTypes.object.isRequired,
|
editor: React.PropTypes.object.isRequired,
|
||||||
node: React.PropTypes.object.isRequired,
|
node: React.PropTypes.object.isRequired,
|
||||||
state: React.PropTypes.object.isRequired,
|
state: React.PropTypes.object.isRequired
|
||||||
style: React.PropTypes.object
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
* When one of the wrapper elements it clicked, select the void node.
|
||||||
*
|
*
|
||||||
@@ -84,29 +59,19 @@ class Void extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
const { children, node, className, style } = this.props
|
const { children, node } = this.props
|
||||||
const Tag = node.kind == 'block' ? 'div' : 'span'
|
const Tag = node.kind == 'block' ? 'div' : 'span'
|
||||||
|
|
||||||
// Make the outer wrapper relative, so the spacer can overlay it.
|
// Make the outer wrapper relative, so the spacer can overlay it.
|
||||||
const styles = {
|
const style = {
|
||||||
...style,
|
|
||||||
position: 'relative'
|
position: 'relative'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tag
|
<Tag style={style} onClick={this.onClick}>
|
||||||
contentEditable={false}
|
{this.renderSpacer()}
|
||||||
data-void="true"
|
<Tag contentEditable={false}>
|
||||||
onClick={this.onClick}
|
{children}
|
||||||
>
|
|
||||||
<Tag
|
|
||||||
contentEditable
|
|
||||||
suppressContentEditableWarning
|
|
||||||
className={className}
|
|
||||||
style={styles}
|
|
||||||
>
|
|
||||||
{this.renderSpacer()}
|
|
||||||
<Tag contentEditable={false}>{children}</Tag>
|
|
||||||
</Tag>
|
</Tag>
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
)
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import Editor from './components/editor'
|
import Editor from './components/editor'
|
||||||
import Placeholder from './components/placeholder'
|
import Placeholder from './components/placeholder'
|
||||||
import Void from './components/void'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models.
|
* Models.
|
||||||
@@ -54,7 +53,6 @@ export {
|
|||||||
Selection,
|
Selection,
|
||||||
State,
|
State,
|
||||||
Text,
|
Text,
|
||||||
Void,
|
|
||||||
findDOMNode
|
findDOMNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +71,5 @@ export default {
|
|||||||
Selection,
|
Selection,
|
||||||
State,
|
State,
|
||||||
Text,
|
Text,
|
||||||
Void,
|
|
||||||
findDOMNode
|
findDOMNode
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user