1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-13 10:44:02 +02:00

Restore dom (#2782)

* Working version of restore dom

* Fix linting errors
This commit is contained in:
Sunny Hirai
2019-05-16 10:59:51 -07:00
committed by GitHub
parent e4fae23454
commit d24f3b16ec
7 changed files with 221 additions and 3 deletions

View File

@@ -27,6 +27,7 @@ import PlainText from './plain-text'
import Plugins from './plugins' import Plugins from './plugins'
import RTL from './rtl' import RTL from './rtl'
import ReadOnly from './read-only' import ReadOnly from './read-only'
import RestoreDOM from './restore-dom'
import RichText from './rich-text' import RichText from './rich-text'
import SearchHighlighting from './search-highlighting' import SearchHighlighting from './search-highlighting'
import Composition from './composition' import Composition from './composition'
@@ -63,6 +64,7 @@ const EXAMPLES = [
['Plain Text', PlainText, '/plain-text'], ['Plain Text', PlainText, '/plain-text'],
['Plugins', Plugins, '/plugins'], ['Plugins', Plugins, '/plugins'],
['Read-only', ReadOnly, '/read-only'], ['Read-only', ReadOnly, '/read-only'],
['Restore DOM', RestoreDOM, '/restore-dom'],
['Rich Text', RichText, '/rich-text'], ['Rich Text', RichText, '/rich-text'],
['RTL', RTL, '/rtl'], ['RTL', RTL, '/rtl'],
['Search Highlighting', SearchHighlighting, '/search-highlighting'], ['Search Highlighting', SearchHighlighting, '/search-highlighting'],

View File

@@ -0,0 +1,146 @@
import { Editor } from 'slate-react'
import { Value } from 'slate'
import React from 'react'
import initialValue from './value.json'
import { Button, Icon, Toolbar } from '../components'
/**
* The Restore DOM example.
*
* This shows the usage of the `restoreDOM` command to rebuild the editor from
* scratch causing all the nodes to force render even if there are no changes
* to the DOM.
*
* The `onClickHighlight` method changes the internal state but normally the
* change is not rendered because there is no change to Slate's internal
* `value`.
*
* RestoreDOM also blows away the old render which makes it safe if the DOM
* has been altered outside of React.
*
* @type {Component}
*/
class RestoreDOMExample extends React.Component {
/**
* Deserialize the initial editor value and set an initial highlight color.
*
* @type {Object}
*/
state = {
value: Value.fromJSON(initialValue),
bgcolor: '#ffffff',
}
/**
* Store a reference to the `editor`.
*
* @param {Editor} editor
*/
ref = editor => {
this.editor = editor
}
/**
* Render.
*
* @return {Element}
*/
render() {
return (
<div>
<Toolbar>
{this.renderHighlightButton('#ffffff')}
{this.renderHighlightButton('#ffeecc')}
{this.renderHighlightButton('#ffffcc')}
{this.renderHighlightButton('#ccffcc')}
{this.renderHighlightButton('#ccffff')}
</Toolbar>
<Editor
spellCheck
autoFocus
placeholder="Enter some text..."
ref={this.ref}
value={this.state.value}
onChange={this.onChange}
renderBlock={this.renderBlock}
/>
</div>
)
}
/**
* Render a highlight button
*
* @param {String} bgcolor
* @return {Element}
*/
renderHighlightButton = bgcolor => {
const isActive = this.state.bgcolor === bgcolor
return (
<Button
active={isActive}
onMouseDown={event => this.onClickHighlight(bgcolor)}
style={{ backgroundColor: bgcolor }}
>
<Icon>format_paint</Icon>
</Button>
)
}
/**
* Highlight every block with a given background color
*
* @param {String} bgcolor
*/
onClickHighlight = bgcolor => {
const { editor } = this
this.setState({ bgcolor })
editor.restoreDOM()
}
/**
* Render a Slate block.
*
* @param {Object} props
* @return {Element}
*/
renderBlock = (props, editor, next) => {
const { attributes, children, node } = props
const style = { backgroundColor: this.state.bgcolor }
switch (node.type) {
case 'paragraph':
return (
<p {...attributes} style={style}>
{children}
</p>
)
default:
return next()
}
}
/**
* On change, save the new `value`.
*
* @param {Editor} editor
*/
onChange = ({ value }) => {
this.setState({ value })
}
}
/**
* Export.
*/
export default RestoreDOMExample

View File

@@ -0,0 +1,38 @@
{
"object": "value",
"document": {
"object": "document",
"nodes": [
{
"object": "block",
"type": "paragraph",
"nodes": [
{
"object": "text",
"text": "First block of text"
}
]
},
{
"object": "block",
"type": "paragraph",
"nodes": [
{
"object": "text",
"text": "Second block of text"
}
]
},
{
"object": "block",
"type": "paragraph",
"nodes": [
{
"object": "text",
"text": "Third block of text"
}
]
}
]
}
}

View File

@@ -53,6 +53,7 @@ class Content extends React.Component {
static propTypes = { static propTypes = {
autoCorrect: Types.bool.isRequired, autoCorrect: Types.bool.isRequired,
className: Types.string, className: Types.string,
contentKey: Types.number,
editor: Types.object.isRequired, editor: Types.object.isRequired,
id: Types.string, id: Types.string,
readOnly: Types.bool.isRequired, readOnly: Types.bool.isRequired,
@@ -486,6 +487,7 @@ class Content extends React.Component {
return ( return (
<Container <Container
key={this.props.contentKey}
{...handlers} {...handlers}
{...data} {...data}
ref={this.ref} ref={this.ref}

View File

@@ -79,7 +79,7 @@ class Editor extends React.Component {
* @type {Object} * @type {Object}
*/ */
state = { value: this.props.defaultValue } state = { value: this.props.defaultValue, contentKey: 0 }
/** /**
* Temporary values. * Temporary values.
@@ -151,6 +151,7 @@ class Editor extends React.Component {
const { options, readOnly, value: valueFromProps } = this.props const { options, readOnly, value: valueFromProps } = this.props
const { value: valueFromState } = this.state const { value: valueFromState } = this.state
const value = valueFromProps || valueFromState const value = valueFromProps || valueFromState
const { contentKey } = this.state
this.controller.setReadOnly(readOnly) this.controller.setReadOnly(readOnly)
this.controller.setValue(value, options) this.controller.setValue(value, options)
@@ -170,6 +171,7 @@ class Editor extends React.Component {
ref={this.tmp.contentRef} ref={this.tmp.contentRef}
autoCorrect={autoCorrect} autoCorrect={autoCorrect}
className={className} className={className}
contentKey={contentKey}
editor={this} editor={this}
id={id} id={id}
onEvent={(handler, event) => this.run(handler, event)} onEvent={(handler, event) => this.run(handler, event)}

View File

@@ -4,6 +4,7 @@ import EditorPropsPlugin from './editor-props'
import RenderingPlugin from './rendering' import RenderingPlugin from './rendering'
import QueriesPlugin from './queries' import QueriesPlugin from './queries'
import DOMPlugin from '../dom' import DOMPlugin from '../dom'
import RestoreDOMPlugin from './restore-dom'
/** /**
* A plugin that adds the React-specific rendering logic to the editor. * A plugin that adds the React-specific rendering logic to the editor.
@@ -20,7 +21,7 @@ function ReactPlugin(options = {}) {
const domPlugin = DOMPlugin({ const domPlugin = DOMPlugin({
plugins: [editorPropsPlugin, ...plugins], plugins: [editorPropsPlugin, ...plugins],
}) })
const restoreDomPlugin = RestoreDOMPlugin()
const placeholderPlugin = PlaceholderPlugin({ const placeholderPlugin = PlaceholderPlugin({
placeholder, placeholder,
when: (editor, node) => when: (editor, node) =>
@@ -30,7 +31,13 @@ function ReactPlugin(options = {}) {
Array.from(node.texts()).length === 1, Array.from(node.texts()).length === 1,
}) })
return [domPlugin, placeholderPlugin, renderingPlugin, queriesPlugin] return [
domPlugin,
restoreDomPlugin,
placeholderPlugin,
renderingPlugin,
queriesPlugin,
]
} }
/** /**

View File

@@ -0,0 +1,21 @@
function RestoreDOMPlugin() {
/**
* Makes sure that on the next Content `render` the DOM is restored.
* This gets us around issues where the DOM is in a different state than
* React's virtual DOM and would crash.
*
* @param {Editor} editor
*/
function restoreDOM(editor) {
editor.setState({ contentKey: editor.state.contentKey + 1 })
}
return {
commands: {
restoreDOM,
},
}
}
export default RestoreDOMPlugin