1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-11 01:33:58 +02:00

rename plugin.render to renderPortal, and add plugin.render

This commit is contained in:
Ian Storm Taylor
2017-03-02 18:19:39 -08:00
parent fa4f9dff6c
commit 2a5d3ee556
6 changed files with 119 additions and 77 deletions

View File

@@ -4,6 +4,17 @@ This document maintains a list of changes to Slate with each new version. Until
--- ---
### `0.18.0` — March 2, 2017
###### BREAKING CHANGES
- **The `plugin.render` property is now called `plugin.renderPortal`.** This is to make way for the new `plugin.render` property that offers HOC-like behavior, so that plugins can augment the editor however they choose.
---
### `0.17.0` — February 27, 2017 ### `0.17.0` — February 27, 2017
###### DEPRECATION CHANGES ###### DEPRECATION CHANGES

View File

@@ -43,6 +43,10 @@ When the user pastes content into the editor, the core plugin handles all pastes
When the user makes a new selection in the DOM, the core plugin updates that selection in Slate's internal data model, re-rendering if it needs to. When the user makes a new selection in the DOM, the core plugin updates that selection in Slate's internal data model, re-rendering if it needs to.
### `render`
Renders all of the default contents of the editor!
### `schema` ### `schema`
The core plugin defines a schema that enforces a few constraints on the content and defines default block and inline node renderer components—wrapping in a `<div>` and `<span>`, respectively. Each of these components contains `shouldComponentUpdate` logic that prevents unnecessary re-renders. The core plugin defines a schema that enforces a few constraints on the content and defines default block and inline node renderer components—wrapping in a `<div>` and `<span>`, respectively. Each of these components contains `shouldComponentUpdate` logic that prevents unnecessary re-renders.

View File

@@ -20,6 +20,8 @@ When the editor needs to resolve a plugin-related handler, it will loop through
- [Other Properties](#other-properties) - [Other Properties](#other-properties)
- [`onChange`](#onchange) - [`onChange`](#onchange)
- [`onBeforeChange`](#onbeforechange) - [`onBeforeChange`](#onbeforechange)
- [`render`](#render)
- [`renderPortal`](#renderportal)
- [`schema`](#schema) - [`schema`](#schema)
@@ -181,7 +183,7 @@ The `data` object is a convenience object created to standardize the paste metad
If no other plugin handles this event, it will be handled by the [Core plugin](./core.md). If no other plugin handles this event, it will be handled by the [Core plugin](./core.md).
### `onSelect` ### `onSelect`
`Function onSelect(event: Event, data: Object, state: State, editor: Editor => State || Void` `Function onSelect(event: Event, data: Object, state: State, editor: Editor) => State || Void`
This handler is called whenever the native DOM selection changes. This handler is called whenever the native DOM selection changes.
@@ -216,6 +218,16 @@ The `onBeforeChange` handler isn't a native browser event handler. Instead, it i
Like `onChange`, `onBeforeChange` is cummulative. Like `onChange`, `onBeforeChange` is cummulative.
### `render`
`Function render(props: Object, state: State, editor: Editor) => Object || Void`
The `render` property allows you to define higher-order-component-like behavior. It is passed all of the properties of the editor, including `props.children`. You can then choose to wrap the existing `children` in any custom elements or proxy the properties however you choose. This can be useful for rendering toolbars, styling the editor, rendering validation, etc.
### `renderPortal`
`Function renderPortal(state: State, editor: Editor) => Object || Void`
The `renderPortal` property allows you to define extra elements that will render outside of the editor, in a separate [portal](). This is useful for rendering hovering menus, or other cases where you don't need to render inside the editor, but want to add elements to the DOM.
### `schema` ### `schema`
`Object` `Object`

View File

@@ -1,5 +1,4 @@
import Content from './content'
import Debug from 'debug' import Debug from 'debug'
import Portal from 'react-portal' import Portal from 'react-portal'
import React from 'react' import React from 'react'
@@ -47,23 +46,6 @@ const PLUGINS_PROPS = [
'schema', 'schema',
] ]
/**
* Pass-through properties of the editor.
*
* @type {Array}
*/
const PASS_THROUGH_PROPS = [
'autoCorrect',
'autoFocus',
'className',
'readOnly',
'role',
'spellCheck',
'style',
'tabIndex',
]
/** /**
* Editor. * Editor.
* *
@@ -258,32 +240,14 @@ class Editor extends React.Component {
render = () => { render = () => {
const { props, state } = this const { props, state } = this
const { stack } = state const { stack } = state
const handlers = {} const children = stack
const passes = {} .renderPortal(state.state, this)
const children = stack.render(state.state, this) .map((child, i) => <Portal key={i} isOpened>{child}</Portal>)
for (const property of EVENT_HANDLERS) {
handlers[property] = this[property]
}
for (const property of PASS_THROUGH_PROPS) {
passes[property] = this.props[property]
}
debug('render', { props, state }) debug('render', { props, state })
return ( const tree = stack.render(state.state, this, { ...props, children })
<Content return tree
{...handlers}
{...passes}
editor={this}
onChange={this.onChange}
schema={this.getSchema()}
state={this.getState()}
>
{children.map((child, i) => <Portal key={i} isOpened>{child}</Portal>)}
</Content>
)
} }
} }

View File

@@ -41,16 +41,6 @@ const STATE_ACCUMULATOR_METHODS = [
'onChange', 'onChange',
] ]
/**
* Methods that accumulate an array.
*
* @type {Array}
*/
const ARRAY_ACCUMULATOR_METHODS = [
'render'
]
/** /**
* Default properties. * Default properties.
* *
@@ -95,6 +85,53 @@ class Stack extends new Record(DEFAULTS) {
return 'stack' return 'stack'
} }
/**
* Invoke `render` on all of the plugins in reverse, building up a tree of
* higher-order components.
*
* @param {State} state
* @param {Editor} editor
* @param {Object} children
* @param {Object} props
* @return {Component}
*/
render = (state, editor, props) => {
debug('render')
const plugins = this.plugins.slice().reverse()
let children
for (const plugin of plugins) {
if (!plugin.render) continue
children = plugin.render(props, state, editor)
props.children = children
}
return children
}
/**
* Invoke `renderPortal` on all of the plugins, building a list of portals.
*
* @param {State} state
* @param {Editor} editor
* @return {Array}
*/
renderPortal = (state, editor) => {
debug('renderPortal')
const portals = []
for (const plugin of this.plugins) {
if (!plugin.renderPortal) continue
const portal = plugin.renderPortal(state, editor)
if (portal == null) continue
portals.push(portal)
}
return portals
}
} }
/** /**
@@ -151,31 +188,6 @@ for (const method of STATE_ACCUMULATOR_METHODS) {
} }
} }
/**
* Mix in the array accumulator methods.
*
* @param {State} state
* @param {Editor} editor
* @param {Mixed} ...args
* @return {Array}
*/
for (const method of ARRAY_ACCUMULATOR_METHODS) {
Stack.prototype[method] = function (state, editor, ...args) {
debug(method)
const array = []
for (const plugin of this.plugins) {
if (!plugin[method]) continue
const next = plugin[method](...args, state, editor)
if (next == null) continue
array.push(next)
}
return array
}
}
/** /**
* Assert that a `value` is a state object. * Assert that a `value` is a state object.
* *

View File

@@ -1,5 +1,6 @@
import Base64 from '../serializers/base-64' import Base64 from '../serializers/base-64'
import Content from '../components/content'
import Character from '../models/character' import Character from '../models/character'
import Debug from 'debug' import Debug from 'debug'
import Placeholder from '../components/placeholder' import Placeholder from '../components/placeholder'
@@ -814,6 +815,43 @@ function Plugin(options = {}) {
.apply() .apply()
} }
/**
* Render.
*
* @param {Object} props
* @param {State} state
* @param {Editor} editor
* @return {Object}
*/
function render(props, state, editor) {
return (
<Content
autoCorrect={props.autoCorrect}
autoFocus={props.autoFocus}
className={props.className}
children={props.children}
editor={editor}
onBeforeInput={editor.onBeforeInput}
onBlur={editor.onBlur}
onChange={editor.onChange}
onCopy={editor.onCopy}
onCut={editor.onCut}
onDrop={editor.onDrop}
onKeyDown={editor.onKeyDown}
onPaste={editor.onPaste}
onSelect={editor.onSelect}
readOnly={props.readOnly}
role={props.role}
schema={editor.getSchema()}
spellCheck={props.spellCheck}
state={state}
style={props.style}
tabIndex={props.tabIndex}
/>
)
}
/** /**
* A default schema rule to render block nodes. * A default schema rule to render block nodes.
* *
@@ -892,6 +930,7 @@ function Plugin(options = {}) {
onKeyDown, onKeyDown,
onPaste, onPaste,
onSelect, onSelect,
render,
schema, schema,
} }
} }