1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-04-16 11:22:15 +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
###### 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.
### `render`
Renders all of the default contents of the editor!
### `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.

View File

@ -20,6 +20,8 @@ When the editor needs to resolve a plugin-related handler, it will loop through
- [Other Properties](#other-properties)
- [`onChange`](#onchange)
- [`onBeforeChange`](#onbeforechange)
- [`render`](#render)
- [`renderPortal`](#renderportal)
- [`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).
### `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.
@ -216,6 +218,16 @@ The `onBeforeChange` handler isn't a native browser event handler. Instead, it i
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`
`Object`

View File

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

View File

@ -41,16 +41,6 @@ const STATE_ACCUMULATOR_METHODS = [
'onChange',
]
/**
* Methods that accumulate an array.
*
* @type {Array}
*/
const ARRAY_ACCUMULATOR_METHODS = [
'render'
]
/**
* Default properties.
*
@ -95,6 +85,53 @@ class Stack extends new Record(DEFAULTS) {
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.
*

View File

@ -1,5 +1,6 @@
import Base64 from '../serializers/base-64'
import Content from '../components/content'
import Character from '../models/character'
import Debug from 'debug'
import Placeholder from '../components/placeholder'
@ -814,6 +815,43 @@ function Plugin(options = {}) {
.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.
*
@ -892,6 +930,7 @@ function Plugin(options = {}) {
onKeyDown,
onPaste,
onSelect,
render,
schema,
}
}