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:
@@ -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'],
|
||||||
|
146
examples/restore-dom/index.js
Normal file
146
examples/restore-dom/index.js
Normal 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
|
38
examples/restore-dom/value.json
Normal file
38
examples/restore-dom/value.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@@ -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}
|
||||||
|
@@ -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)}
|
||||||
|
@@ -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,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
21
packages/slate-react/src/plugins/react/restore-dom.js
Normal file
21
packages/slate-react/src/plugins/react/restore-dom.js
Normal 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
|
Reference in New Issue
Block a user