mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-09 00:36:41 +02:00
Upgrade to React v16 lifecycle (#1975)
* Trying to use memoization and upgrade to react v16 * Fix error * Fix error * Fix handlers error * Add annotation * Remove EventHandlers * No state * Remove un-necessary polyfill * Remove un-necessary polyfill * Remove un-necessary handlers settings * Early Return * Fix Early Return * Fix onChange * Do not run onChange stack twice on same change * Update annotation * Better sense of resolve++ * Cache value in onChange and didMount * Remove un-necessary rechack * Renaming * Remove change in leaf.js * Handlers as this.handlers * do not re-initialize change in onChange * Re-run onChange stack only when change happens * Update value when stack changes * Rename to memoize-one * queue changes * Unify interface * Fix bug * Add document * Remove id * Do not use map * Fix bug * Fix eslint * Fix update when props.value changes * Add annotation * Fix stack * Inline queueChange * Restore onChange * restore onChange * Refactor change and onChange * Use onChange as the single interface for update * Do not flushChange if inside event * Give a warning about synchronous editor.change call * Change isInChange in editor.change * refactor resolution and tmp logic, cleanup code
This commit is contained in:
committed by
Ian Storm Taylor
parent
d05e90e546
commit
877dea16bf
@@ -46,6 +46,7 @@
|
|||||||
"lerna": "^2.7.1",
|
"lerna": "^2.7.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"matcha": "^0.7.0",
|
"matcha": "^0.7.0",
|
||||||
|
"memoize-one": "^4.0.0",
|
||||||
"mocha": "^2.5.3",
|
"mocha": "^2.5.3",
|
||||||
"mocha-lcov-reporter": "^1.3.0",
|
"mocha-lcov-reporter": "^1.3.0",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
"is-window": "^1.0.2",
|
"is-window": "^1.0.2",
|
||||||
"keycode": "^2.1.2",
|
"keycode": "^2.1.2",
|
||||||
"lodash": "^4.1.1",
|
"lodash": "^4.1.1",
|
||||||
|
"memoize-one": "^4.0.0",
|
||||||
"prop-types": "^15.5.8",
|
"prop-types": "^15.5.8",
|
||||||
"react-immutable-proptypes": "^2.1.0",
|
"react-immutable-proptypes": "^2.1.0",
|
||||||
"react-portal": "^3.1.0",
|
"react-portal": "^3.1.0",
|
||||||
|
@@ -60,23 +60,26 @@ class Content extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Temporary values.
|
||||||
*
|
*
|
||||||
* @param {Object} props
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
constructor(props) {
|
tmp = {
|
||||||
super(props)
|
isUpdatingSelection: false,
|
||||||
this.tmp = {}
|
|
||||||
this.tmp.isUpdatingSelection = false
|
|
||||||
|
|
||||||
EVENT_HANDLERS.forEach(handler => {
|
|
||||||
this[handler] = event => {
|
|
||||||
this.onEvent(handler, event)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a set of bound event handlers.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
handlers = EVENT_HANDLERS.reduce((obj, handler) => {
|
||||||
|
obj[handler] = event => this.onEvent(handler, event)
|
||||||
|
return obj
|
||||||
|
}, {})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the editor first mounts in the DOM we need to:
|
* When the editor first mounts in the DOM we need to:
|
||||||
*
|
*
|
||||||
@@ -84,7 +87,7 @@ class Content extends React.Component {
|
|||||||
* - Update the selection, in case it starts focused.
|
* - Update the selection, in case it starts focused.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount() {
|
||||||
const window = getWindow(this.element)
|
const window = getWindow(this.element)
|
||||||
|
|
||||||
window.document.addEventListener(
|
window.document.addEventListener(
|
||||||
@@ -124,7 +127,7 @@ class Content extends React.Component {
|
|||||||
* On update, update the selection.
|
* On update, update the selection.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
componentDidUpdate = () => {
|
componentDidUpdate() {
|
||||||
this.updateSelection()
|
this.updateSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +366,7 @@ class Content extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { props } = this
|
const { props, handlers } = this
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
readOnly,
|
readOnly,
|
||||||
@@ -386,11 +389,6 @@ class Content extends React.Component {
|
|||||||
return this.renderNode(child, isSelected, childrenDecorations[i])
|
return this.renderNode(child, isSelected, childrenDecorations[i])
|
||||||
})
|
})
|
||||||
|
|
||||||
const handlers = EVENT_HANDLERS.reduce((obj, handler) => {
|
|
||||||
obj[handler] = this[handler]
|
|
||||||
return obj
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
// Prevent the default outline styles.
|
// Prevent the default outline styles.
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
@@ -417,21 +415,6 @@ class Content extends React.Component {
|
|||||||
contentEditable={readOnly ? null : true}
|
contentEditable={readOnly ? null : true}
|
||||||
suppressContentEditableWarning
|
suppressContentEditableWarning
|
||||||
className={className}
|
className={className}
|
||||||
onBlur={this.onBlur}
|
|
||||||
onFocus={this.onFocus}
|
|
||||||
onCompositionEnd={this.onCompositionEnd}
|
|
||||||
onCompositionStart={this.onCompositionStart}
|
|
||||||
onCopy={this.onCopy}
|
|
||||||
onCut={this.onCut}
|
|
||||||
onDragEnd={this.onDragEnd}
|
|
||||||
onDragOver={this.onDragOver}
|
|
||||||
onDragStart={this.onDragStart}
|
|
||||||
onDrop={this.onDrop}
|
|
||||||
onInput={this.onInput}
|
|
||||||
onKeyDown={this.onKeyDown}
|
|
||||||
onKeyUp={this.onKeyUp}
|
|
||||||
onPaste={this.onPaste}
|
|
||||||
onSelect={this.onSelect}
|
|
||||||
autoCorrect={props.autoCorrect ? 'on' : 'off'}
|
autoCorrect={props.autoCorrect ? 'on' : 'off'}
|
||||||
spellCheck={spellCheck}
|
spellCheck={spellCheck}
|
||||||
style={style}
|
style={style}
|
||||||
|
@@ -5,6 +5,7 @@ import SlateTypes from 'slate-prop-types'
|
|||||||
import Types from 'prop-types'
|
import Types from 'prop-types'
|
||||||
import logger from 'slate-dev-logger'
|
import logger from 'slate-dev-logger'
|
||||||
import { Schema, Stack } from 'slate'
|
import { Schema, Stack } from 'slate'
|
||||||
|
import memoizeOne from 'memoize-one'
|
||||||
|
|
||||||
import EVENT_HANDLERS from '../constants/event-handlers'
|
import EVENT_HANDLERS from '../constants/event-handlers'
|
||||||
import PLUGINS_PROPS from '../constants/plugin-props'
|
import PLUGINS_PROPS from '../constants/plugin-props'
|
||||||
@@ -66,94 +67,61 @@ class Editor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Initial state.
|
||||||
*
|
*
|
||||||
* @param {Object} props
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
constructor(props) {
|
state = {}
|
||||||
super(props)
|
|
||||||
this.state = {}
|
|
||||||
this.tmp = {}
|
|
||||||
this.tmp.updates = 0
|
|
||||||
this.tmp.resolves = 0
|
|
||||||
|
|
||||||
// Resolve the plugins and create a stack and schema from them.
|
/**
|
||||||
const plugins = this.resolvePlugins(props.plugins, props.schema)
|
* Temporary values.
|
||||||
const stack = Stack.create({ plugins })
|
*
|
||||||
const schema = Schema.create({ plugins })
|
* @type {Object}
|
||||||
this.state.schema = schema
|
*/
|
||||||
this.state.stack = stack
|
|
||||||
|
|
||||||
// Run `onChange` on the passed-in value because we need to ensure that it
|
tmp = {
|
||||||
// is normalized, and queue the resulting change.
|
change: null,
|
||||||
const change = props.value.change()
|
isChanging: false,
|
||||||
stack.run('onChange', change, this)
|
operationsSize: null,
|
||||||
this.queueChange(change)
|
plugins: null,
|
||||||
this.state.value = change.value
|
resolves: 0,
|
||||||
|
updates: 0,
|
||||||
// Create a bound event handler for each event.
|
value: null,
|
||||||
EVENT_HANDLERS.forEach(handler => {
|
|
||||||
this[handler] = (...args) => {
|
|
||||||
this.onEvent(handler, ...args)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the `props` are updated, create a new `Stack` if necessary and run
|
* Create a set of bound event handlers.
|
||||||
* `onChange` to ensure the value is normalized.
|
|
||||||
*
|
*
|
||||||
* @param {Object} props
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
componentWillReceiveProps = props => {
|
handlers = EVENT_HANDLERS.reduce((obj, handler) => {
|
||||||
let { schema, stack } = this
|
obj[handler] = event => this.onEvent(handler, event)
|
||||||
|
return obj
|
||||||
|
}, {})
|
||||||
|
|
||||||
// Increment the updates counter as a baseline.
|
/**
|
||||||
|
* When the component first mounts, flush any temporary changes, and then,
|
||||||
|
* focus the editor if `autoFocus` is set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
this.tmp.updates++
|
this.tmp.updates++
|
||||||
|
|
||||||
// If the plugins or the schema have changed, we need to re-resolve the
|
const { autoFocus } = this.props
|
||||||
// plugins, since it will result in a new stack and new validations.
|
const { change } = this.tmp
|
||||||
if (
|
|
||||||
props.plugins != this.props.plugins ||
|
|
||||||
props.schema != this.props.schema
|
|
||||||
) {
|
|
||||||
const plugins = this.resolvePlugins(props.plugins, props.schema)
|
|
||||||
stack = Stack.create({ plugins })
|
|
||||||
schema = Schema.create({ plugins })
|
|
||||||
this.setState({ schema, stack })
|
|
||||||
|
|
||||||
// Increment the resolves counter.
|
if (autoFocus) {
|
||||||
this.tmp.resolves++
|
if (change) {
|
||||||
|
change.focus()
|
||||||
// If we've resolved a few times already, and it's exactly in line with
|
} else {
|
||||||
// the updates, then warn the user that they may be doing something wrong.
|
this.focus()
|
||||||
if (this.tmp.resolves > 5 && this.tmp.resolves == this.tmp.updates) {
|
|
||||||
logger.warn(
|
|
||||||
'A Slate <Editor> is re-resolving `props.plugins` or `props.schema` on each update, which leads to poor performance. This is often due to passing in a new `schema` or `plugins` prop with each render by declaring them inline in your render function. Do not do this!'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run `onChange` on the passed-in value because we need to ensure that it
|
if (change) {
|
||||||
// is normalized, and queue the resulting change.
|
this.onChange(change)
|
||||||
const change = props.value.change()
|
|
||||||
stack.run('onChange', change, this)
|
|
||||||
this.queueChange(change)
|
|
||||||
this.setState({ value: change.value })
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the component first mounts, flush any temporary changes,
|
|
||||||
* and then, focus the editor if `autoFocus` is set.
|
|
||||||
*/
|
|
||||||
|
|
||||||
componentDidMount = () => {
|
|
||||||
this.flushChange()
|
|
||||||
|
|
||||||
if (this.props.autoFocus) {
|
|
||||||
this.focus()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,113 +129,24 @@ class Editor extends React.Component {
|
|||||||
* When the component updates, flush any temporary change.
|
* When the component updates, flush any temporary change.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
componentDidUpdate = () => {
|
componentDidUpdate(prevProps) {
|
||||||
this.flushChange()
|
this.tmp.updates++
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
const { change, resolves, updates } = this.tmp
|
||||||
* Queue a `change` object, to be able to flush it later. This is required for
|
|
||||||
* when a change needs to be applied to the value, but because of the React
|
|
||||||
* lifecycle we can't apply that change immediately. So we cache it here and
|
|
||||||
* later can call `this.flushChange()` to flush it.
|
|
||||||
*
|
|
||||||
* @param {Change} change
|
|
||||||
*/
|
|
||||||
|
|
||||||
queueChange = change => {
|
// If we've resolved a few times already, and it's exactly in line with
|
||||||
if (change.operations.size) {
|
// the updates, then warn the user that they may be doing something wrong.
|
||||||
debug('queueChange', { change })
|
if (resolves > 5 && resolves === updates) {
|
||||||
this.tmp.change = change
|
logger.warn(
|
||||||
|
'A Slate <Editor> component is re-resolving `props.plugins` or `props.schema` on each update, which leads to poor performance. This is often due to passing in a new `schema` or `plugins` prop with each render by declaring them inline in your render function. Do not do this!'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flush a temporarily stored `change` object, for when a change needed to be
|
|
||||||
* made but couldn't because of React's lifecycle.
|
|
||||||
*/
|
|
||||||
|
|
||||||
flushChange = () => {
|
|
||||||
const { change } = this.tmp
|
|
||||||
|
|
||||||
if (change) {
|
if (change) {
|
||||||
debug('flushChange', { change })
|
this.onChange(change)
|
||||||
delete this.tmp.change
|
|
||||||
this.props.onChange(change)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform a change on the editor, passing `...args` to `change.call`.
|
|
||||||
*
|
|
||||||
* @param {Mixed} ...args
|
|
||||||
*/
|
|
||||||
|
|
||||||
change = (...args) => {
|
|
||||||
const change = this.value.change().call(...args)
|
|
||||||
this.onChange(change)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Programmatically blur the editor.
|
|
||||||
*/
|
|
||||||
|
|
||||||
blur = () => {
|
|
||||||
this.change(c => c.blur())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Programmatically focus the editor.
|
|
||||||
*/
|
|
||||||
|
|
||||||
focus = () => {
|
|
||||||
this.change(c => c.focus())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getters for exposing public properties of the editor's state.
|
|
||||||
*/
|
|
||||||
|
|
||||||
get schema() {
|
|
||||||
return this.state.schema
|
|
||||||
}
|
|
||||||
|
|
||||||
get stack() {
|
|
||||||
return this.state.stack
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
|
||||||
return this.state.value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On event.
|
|
||||||
*
|
|
||||||
* @param {String} handler
|
|
||||||
* @param {Event} event
|
|
||||||
*/
|
|
||||||
|
|
||||||
onEvent = (handler, event) => {
|
|
||||||
this.change(change => {
|
|
||||||
this.stack.run(handler, event, change, this)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On change.
|
|
||||||
*
|
|
||||||
* @param {Change} change
|
|
||||||
*/
|
|
||||||
|
|
||||||
onChange = change => {
|
|
||||||
debug('onChange', { change })
|
|
||||||
|
|
||||||
this.stack.run('onChange', change, this)
|
|
||||||
const { value } = change
|
|
||||||
const { onChange } = this.props
|
|
||||||
if (value == this.value) return
|
|
||||||
onChange(change)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the editor.
|
* Render the editor.
|
||||||
*
|
*
|
||||||
@@ -291,7 +170,162 @@ class Editor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve an array of plugins from `plugins` and `schema` props.
|
* Get the editor's current plugins.
|
||||||
|
*
|
||||||
|
* @return {Array}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get plugins() {
|
||||||
|
const plugins = this.resolvePlugins(this.props.plugins, this.props.schema)
|
||||||
|
return plugins
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the editor's current schema.
|
||||||
|
*
|
||||||
|
* @return {Schema}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get schema() {
|
||||||
|
const schema = this.resolveSchema(this.plugins)
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the editor's current stack.
|
||||||
|
*
|
||||||
|
* @return {Stack}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get stack() {
|
||||||
|
const stack = this.resolveStack(this.plugins)
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the editor's current value.
|
||||||
|
*
|
||||||
|
* @return {Value}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
// If the current `plugins` and `value` are the same as the last seen ones
|
||||||
|
// that were saved in `tmp`, don't re-resolve because that will trigger
|
||||||
|
// extra `onChange` runs.
|
||||||
|
if (
|
||||||
|
this.plugins === this.tmp.plugins &&
|
||||||
|
this.props.value === this.tmp.value
|
||||||
|
) {
|
||||||
|
return this.tmp.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = this.resolveValue(this.plugins, this.props.value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a change on the editor, passing `...args` to `change.call`.
|
||||||
|
*
|
||||||
|
* @param {Mixed} ...args
|
||||||
|
*/
|
||||||
|
|
||||||
|
change = (...args) => {
|
||||||
|
if (this.tmp.isChanging) {
|
||||||
|
logger.warn(
|
||||||
|
"The `editor.change` method was called from within an existing `editor.change` callback. This is not allowed, and often due to calling `editor.change` directly from a plugin's event handler which is unnecessary."
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const change = this.value.change()
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.tmp.isChanging = true
|
||||||
|
change.call(...args)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
} finally {
|
||||||
|
this.tmp.isChanging = false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onChange(change)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Programmatically blur the editor.
|
||||||
|
*/
|
||||||
|
|
||||||
|
blur = () => {
|
||||||
|
this.change(c => c.blur())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Programmatically focus the editor.
|
||||||
|
*/
|
||||||
|
|
||||||
|
focus = () => {
|
||||||
|
this.change(c => c.focus())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On change.
|
||||||
|
*
|
||||||
|
* @param {Change} change
|
||||||
|
*/
|
||||||
|
|
||||||
|
onChange = change => {
|
||||||
|
// If the change doesn't define any operations to apply, abort.
|
||||||
|
if (change.operations.size === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('onChange', { change })
|
||||||
|
change = this.resolveChange(this.plugins, change, change.operations.size)
|
||||||
|
|
||||||
|
// Store a reference to the last `value` and `plugins` that were seen by the
|
||||||
|
// editor, so we can know whether to normalize a new unknown value if one
|
||||||
|
// is passed in via `this.props`.
|
||||||
|
this.tmp.value = change.value
|
||||||
|
this.tmp.plugins = this.plugins
|
||||||
|
|
||||||
|
// Remove the temporary `change`, since it's being flushed.
|
||||||
|
delete this.tmp.change
|
||||||
|
delete this.tmp.operationsSize
|
||||||
|
|
||||||
|
this.props.onChange(change)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On event.
|
||||||
|
*
|
||||||
|
* @param {String} handler
|
||||||
|
* @param {Event} event
|
||||||
|
*/
|
||||||
|
|
||||||
|
onEvent = (handler, event) => {
|
||||||
|
this.change(change => {
|
||||||
|
this.stack.run(handler, event, change, this)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a change from the current `plugins`, a potential `change` and its
|
||||||
|
* current operations `size`.
|
||||||
|
*
|
||||||
|
* @param {Array} plugins
|
||||||
|
* @param {Change} change
|
||||||
|
* @param {Number} size
|
||||||
|
*/
|
||||||
|
|
||||||
|
resolveChange = memoizeOne((plugins, change, size) => {
|
||||||
|
const stack = this.resolveStack(plugins)
|
||||||
|
stack.run('onChange', change, this)
|
||||||
|
return change
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a set of plugins from potential `plugins` and a `schema`.
|
||||||
*
|
*
|
||||||
* In addition to the plugins provided in props, this will initialize three
|
* In addition to the plugins provided in props, this will initialize three
|
||||||
* other plugins:
|
* other plugins:
|
||||||
@@ -304,19 +338,20 @@ class Editor extends React.Component {
|
|||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
resolvePlugins = (plugins, schema) => {
|
resolvePlugins = memoizeOne((plugins = [], schema = {}) => {
|
||||||
|
debug('resolvePlugins', { plugins, schema })
|
||||||
|
this.tmp.resolves++
|
||||||
|
|
||||||
const beforePlugin = BeforePlugin()
|
const beforePlugin = BeforePlugin()
|
||||||
const afterPlugin = AfterPlugin()
|
const afterPlugin = AfterPlugin()
|
||||||
const editorPlugin = {
|
const editorPlugin = { schema }
|
||||||
schema: schema || {},
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const prop of PLUGINS_PROPS) {
|
for (const prop of PLUGINS_PROPS) {
|
||||||
// Skip `onChange` because the editor's `onChange` is special.
|
// Skip `onChange` because the editor's `onChange` is special.
|
||||||
if (prop == 'onChange') continue
|
if (prop == 'onChange') continue
|
||||||
|
|
||||||
// Skip `schema` because it can't be proxied easily, so it must be
|
// Skip `schema` because it can't be proxied easily, so it must be passed
|
||||||
// passed in as an argument to this function instead.
|
// in as an argument to this function instead.
|
||||||
if (prop == 'schema') continue
|
if (prop == 'schema') continue
|
||||||
|
|
||||||
// Define a function that will just proxies into `props`.
|
// Define a function that will just proxies into `props`.
|
||||||
@@ -325,12 +360,59 @@ class Editor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [beforePlugin, editorPlugin, ...(plugins || []), afterPlugin]
|
return [beforePlugin, editorPlugin, ...plugins, afterPlugin]
|
||||||
}
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a schema from the current `plugins`.
|
||||||
|
*
|
||||||
|
* @param {Array} plugins
|
||||||
|
* @return {Schema}
|
||||||
|
*/
|
||||||
|
|
||||||
|
resolveSchema = memoizeOne(plugins => {
|
||||||
|
debug('resolveSchema', { plugins })
|
||||||
|
const schema = Schema.create({ plugins })
|
||||||
|
return schema
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a stack from the current `plugins`.
|
||||||
|
*
|
||||||
|
* @param {Array} plugins
|
||||||
|
* @return {Stack}
|
||||||
|
*/
|
||||||
|
|
||||||
|
resolveStack = memoizeOne(plugins => {
|
||||||
|
debug('resolveStack', { plugins })
|
||||||
|
const stack = Stack.create({ plugins })
|
||||||
|
return stack
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a value from the current `plugins` and a potential `value`.
|
||||||
|
*
|
||||||
|
* @param {Array} plugins
|
||||||
|
* @param {Value} value
|
||||||
|
* @return {Change}
|
||||||
|
*/
|
||||||
|
|
||||||
|
resolveValue = memoizeOne((plugins, value) => {
|
||||||
|
debug('resolveValue', { plugins, value })
|
||||||
|
let change = value.change()
|
||||||
|
change = this.resolveChange(plugins, change, change.operations.size)
|
||||||
|
|
||||||
|
// Store the change and it's operations count so that it can be flushed once
|
||||||
|
// the component next updates.
|
||||||
|
this.tmp.change = change
|
||||||
|
this.tmp.operationsSize = change.operations.size
|
||||||
|
|
||||||
|
return change.value
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix in the property types for the event handlers.
|
* Mix in the prop types for the event handlers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (const prop of EVENT_HANDLERS) {
|
for (const prop of EVENT_HANDLERS) {
|
||||||
|
@@ -62,7 +62,7 @@ class Node extends React.Component {
|
|||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
shouldComponentUpdate = nextProps => {
|
shouldComponentUpdate(nextProps) {
|
||||||
const { props } = this
|
const { props } = this
|
||||||
const { stack } = props.editor
|
const { stack } = props.editor
|
||||||
const shouldUpdate = stack.find(
|
const shouldUpdate = stack.find(
|
||||||
|
@@ -7,7 +7,6 @@ import getWindow from 'get-window'
|
|||||||
import { Block, Inline, Text } from 'slate'
|
import { Block, Inline, Text } from 'slate'
|
||||||
import Hotkeys from 'slate-hotkeys'
|
import Hotkeys from 'slate-hotkeys'
|
||||||
|
|
||||||
import EVENT_HANDLERS from '../constants/event-handlers'
|
|
||||||
import Content from '../components/content'
|
import Content from '../components/content'
|
||||||
import cloneFragment from '../utils/clone-fragment'
|
import cloneFragment from '../utils/clone-fragment'
|
||||||
import findDOMNode from '../utils/find-dom-node'
|
import findDOMNode from '../utils/find-dom-node'
|
||||||
@@ -683,10 +682,7 @@ function AfterPlugin() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function renderEditor(props, editor) {
|
function renderEditor(props, editor) {
|
||||||
const handlers = EVENT_HANDLERS.reduce((obj, handler) => {
|
const { handlers } = editor
|
||||||
obj[handler] = editor[handler]
|
|
||||||
return obj
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Content
|
<Content
|
||||||
|
@@ -5532,6 +5532,10 @@ mem@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn "^1.0.0"
|
mimic-fn "^1.0.0"
|
||||||
|
|
||||||
|
memoize-one@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.0.0.tgz#fc5e2f1427a216676a62ec652cf7398cfad123db"
|
||||||
|
|
||||||
memory-fs@^0.4.0, memory-fs@~0.4.1:
|
memory-fs@^0.4.0, memory-fs@~0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
|
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
|
||||||
|
Reference in New Issue
Block a user