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

add onDocumentChange and onSelectionChange properties to editor

This commit is contained in:
Ian Storm Taylor
2016-07-18 13:30:37 -07:00
parent c717f8429f
commit c41046ac17
4 changed files with 78 additions and 9 deletions

View File

@@ -81,7 +81,6 @@ Now whenever you edit the page, if you look in Local Storage, you should see the
But... if you refresh the page, everything is still reset. That's because we need to make sure the initial state is pulled from that same Local Storage location, like so:
```js
class App extends React.Component {
@@ -114,3 +113,40 @@ class App extends React.Component {
Now you should be able to save changes across refreshes!
However, if you inspect the change handler, you'll notice that it's actually saving the Local Storage value on _every_ change to the editor, even when only the selection changes! This is because `onChange` is called for _every_ change. For Local Storage this doesn't really matter, but if you're saving things to a database via HTTP request this would result in a lot of unnecessary requests.
Instead of using `onChange`, Slate's editor also accepts an `onDocumentChange` convenience handler that you can use to isolate saving logic to only happen when the document itself has changed, like so:
```js
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
// Update the initial value to be pulled from Local Storage.
state: Plain.deserialize(localStorage.getItem('content'))
}
}
render() {
return (
<Editor
state={this.state.state}
onChange={state => this.onChange(state)}
/>
)
}
onChange(state) {
this.setState({ state })
}
onDocumentChange(document, state) {
const string = Plain.serialize(state)
localStorage.setItem('content', string)
}
}
```
Now you're content will be saved only when the content itself changes!

View File

@@ -10,6 +10,8 @@ The top-level React component that renders the Slate editor itself.
- [Properties](#properties)
- [`className`](#classname)
- [`onChange`](#onchange)
- [`onDocumentChange`](#ondocumentchange)
- [`onSelectionChange`](#onselectionchange)
- [`plugins`](#plugins)
- [`readOnly`](#readonly)
- [`state`](#state)
@@ -49,10 +51,20 @@ The top-level React component that renders the Slate editor itself.
An optional class name to apply to the content editable element.
### `onChange`
`Function`
`Function onChange(state: State)`
A change handler that will be called with the newly-changed editor `state`. You should usually pass the newly changed `state` back into the editor through its `state` property. This hook allows you to add persistence logic to your editor.
### `onDocumentChange`
`Function onDocumentChange(document: Document, state: State)`
A convenience handler property that will only be called for changes in state where the document has changed. It is called with the changed `document` and `state`.
### `onSelectionChange`
`Function onSelectionChange(selection: Selection, state: State)`
A convenience handler property that will only be called for changes in state where the selection has changed. It is called with the changed `selection` and `state`.
### `plugins`
`Array`

View File

@@ -48,21 +48,21 @@ All of the event handler properties are passed the same React `event` object you
Each event handler can choose to return a new `state` object, in which case the editor's state will be updated. If nothing is returned, the editor will simply continue resolving the plugin stack.
### `onBeforeInput`
`onBeforeInput(event: Event, state: State, editor: Editor) => State || Void`
`Function onBeforeInput(event: Event, state: State, editor: Editor) => State || Void`
This handler is called right before a string of text is inserted into the `contenteditable` element. The `event.data` property will be the string of text that is being inserted.
Make sure to `event.preventDefault()` if you do not want the default insertion behavior to occur! If no other plugin handles this event, it will be handled by the [Core plugin](./core.md).
### `onKeyDown`
`onKeyDown(event: Event, state: State, editor: Editor) => State || Void`
`Function onKeyDown(event: Event, state: State, editor: Editor) => State || Void`
This handler is called when any key is pressed in the `contenteditable` element, before any action is taken. Use the `event.which` property to determine which key was pressed.
Make sure to `event.preventDefault()` if you do not want the default insertion behavior to occur! If no other plugin handles this event, it will be handled by the [Core plugin](./core.md).
### `onPaste`
`onPaste(event: Event, paste: Object, state: State, editor: Editor) => State || Void`
`Function onPaste(event: Event, paste: Object, state: State, editor: Editor) => State || Void`
This handler is called when the user pastes content into the `contenteditable` element. The event is already prevented by default, so you must define a state change to have any affect occur.
@@ -102,14 +102,14 @@ If no other plugin handles this event, it will be handled by the [Core plugin](.
To customize the renderer output of the editor, plugins can define a set of "renderer" properties.
### `renderDecorations`
`renderDecorations(text: Text) => Characters || Void`
`Function renderDecorations(text: Text) => Characters || Void`
The `renderDecorations` handler allows you to add dynamic, content-aware [`Marks`](../models/mark.md) to ranges of text, without having them show up in the serialized state of the editor. This is useful for things like code highlighting, where the marks will change as the user types.
`renderDecorations` is called for every `text` node in the document, and should return a set of updated [`Characters`](../models/character.md) for the text node in question. Every plugin's decoration logic is called, and the resulting characters are unioned, such that multiple plugins can apply decorations to the same pieces of text.
### `renderMark`
`renderMark(mark: Mark) => Object || Void`
`Function renderMark(mark: Mark) => Object || Void`
The `renderMark` handler allows you to define the styles that each mark should be rendered with. It takes a [`Mark`](../models/mark.md) object, and should return a dictionary of styles that will be applied via React's `style=` property. For example:
@@ -121,7 +121,7 @@ The `renderMark` handler allows you to define the styles that each mark should b
```
### `renderNode`
`renderNode(node: Block || Inline) => Component || Void`
`Function renderNode(node: Block || Inline) => Component || Void`
The `renderNode` handler allows you to define the component that will be used to render a node—both blocks and inlines. It takes a [`Node`](../models/node.md) object, and should return a React component.
@@ -167,7 +167,7 @@ The `node` itself is passed in, so you can access any custom data associated wit
```
### `onChange`
`onChange(state: State) => State || Void`
`Function onChange(state: State) => State || Void`
The `onChange` handler isn't a native browser event handler. Instead, it is invoked whenever the editor state changes. Returning a new state will update the editor's state, continuing down the plugin stack.

View File

@@ -4,6 +4,12 @@ import CorePlugin from '../plugins/core'
import React from 'react'
import State from '../models/state'
/**
* Noop.
*/
function noop() {}
/**
* Editor.
*/
@@ -18,8 +24,10 @@ class Editor extends React.Component {
className: React.PropTypes.string,
onBeforeInput: React.PropTypes.func,
onChange: React.PropTypes.func.isRequired,
onDocumentChange: React.PropTypes.func,
onKeyDown: React.PropTypes.func,
onPaste: React.PropTypes.func,
onSelectionChange: React.PropTypes.func,
placeholder: React.PropTypes.any,
placeholderClassName: React.PropTypes.string,
placeholderStyle: React.PropTypes.object,
@@ -33,6 +41,8 @@ class Editor extends React.Component {
};
static defaultProps = {
onDocumentChange: noop,
onSelectionChange: noop,
plugins: [],
readOnly: false
};
@@ -45,6 +55,7 @@ class Editor extends React.Component {
constructor(props) {
super(props)
this.tmp = {}
this.state = {}
this.state.plugins = this.resolvePlugins(props)
this.state.state = this.resolveState(props.state)
@@ -119,6 +130,16 @@ class Editor extends React.Component {
}
this.props.onChange(state)
if (state.document != this.tmp.document) {
this.props.onDocumentChange(state.document, state)
this.tmp.document = state.document
}
if (state.selection != this.tmp.selection) {
this.props.onSelectionChange(state.selection, state)
this.tmp.selection = state.selection
}
}
/**