2016-07-08 14:40:45 -07:00
< br / >
< p align = "center" > < strong > Previous:< / strong > < br / > < a href = "./applying-custom-formatting.md" > Applying Custom Formatting< / a > < / p >
< br / >
2016-07-12 21:34:31 -07:00
# Using Plugins
2016-07-08 14:40:45 -07:00
Up to now, everything we've learned has been about how to write one-off logic for your specific Slate editor. But one of the most beautiful things about Slate is actually its plugin system, and how it lets you write less one-off code.
2016-07-08 14:54:52 -07:00
In the previous guide, we actually wrote some pretty useful code for adding **bold** formatting to ranges of text when a key is pressed. But most of that code wasn't really specific to **bold** text; it could just as easily have applied to _italic_ text or `code` text if we switched a few variables.
2016-07-08 14:40:45 -07:00
2016-07-08 14:54:52 -07:00
So let's break that logic out into it's a reusable plugin that can toggle _any_ mark on _any_ key press.
2016-07-08 14:40:45 -07:00
Starting with our app from earlier:
```js
class App extends React.Component {
2016-08-14 18:25:12 -07:00
state = {
state: initialState,
schema: {
marks: {
bold: props => < strong > {props.children}< / strong >
}
2016-07-08 14:40:45 -07:00
}
}
2017-09-06 17:51:40 -07:00
onChange = ({ state }) => {
2016-11-17 18:56:09 -08:00
this.setState({ state })
2016-07-08 14:40:45 -07:00
}
2017-09-06 17:51:40 -07:00
onKeyDown = (event, data, change) => {
2016-07-08 14:40:45 -07:00
if (!event.metaKey || event.which != 66) return
2017-02-16 10:16:19 -08:00
event.preventDefault()
2017-09-06 17:51:40 -07:00
change.toggleMark('bold')
return true
2016-07-08 14:40:45 -07:00
}
2017-08-02 18:36:33 +02:00
render() {
2016-11-17 18:56:09 -08:00
return (
< Editor
schema={this.state.schema}
state={this.state.state}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
/>
)
}
2016-07-08 14:40:45 -07:00
}
```
Let's write a new function, that takes a set of options: the mark `type` to toggle and the key `code` to press.
```js
function MarkHotkey(options) {
// Grab our options from the ones passed in.
2017-04-22 07:14:04 +03:00
const { type, code, isAltKey = false } = options
2016-07-08 14:40:45 -07:00
}
```
2016-07-08 14:54:52 -07:00
Okay, that was easy. But it doesn't do anything.
2016-07-08 14:40:45 -07:00
2016-07-08 14:54:52 -07:00
To fix that, we need our plugin function to return a "plugin object" that Slate recognizes. Slate's plugin objects are just plain objects that have properties that map to the same handler on the `Editor` .
2016-07-08 14:40:45 -07:00
2016-07-08 14:54:52 -07:00
In this case our plugin object will have one property: a `onKeyDown` handler, with its logic copied right from our current app's code:
2016-07-08 14:40:45 -07:00
```js
function MarkHotkey(options) {
2017-05-30 22:15:11 +02:00
const { type, code, isAltKey = false } = options
2016-07-08 14:40:45 -07:00
// Return our "plugin" object, containing the `onKeyDown` handler.
return {
2017-09-06 17:51:40 -07:00
onKeyDown(event, data, change) {
2016-07-08 14:40:45 -07:00
// Check that the key pressed matches our `code` option.
2017-04-22 07:14:04 +03:00
if (!event.metaKey || event.which != code || event.altKey != isAltKey) return
2016-07-08 14:40:45 -07:00
2017-02-16 10:16:19 -08:00
// Prevent the default characters from being inserted.
event.preventDefault()
2016-08-14 18:25:12 -07:00
// Toggle the mark `type` .
2017-09-06 17:51:40 -07:00
change.toggleMark(type)
return true
2016-07-08 14:40:45 -07:00
}
}
}
```
Boom! Now we're getting somewhere. That code is reusable for any type of mark.
2016-07-08 14:54:52 -07:00
Now that we have our plugin, let's remove the hard-coded logic from our app, and replace it with our brand new `MarkHotkey` plugin instead, passing in the same options that will keep our **bold** functionality intact:
2016-07-08 14:40:45 -07:00
```js
// Initialize our bold-mark-adding plugin.
const boldPlugin = MarkHotkey({
2017-09-06 17:51:40 -07:00
type: 'bold',
2016-07-08 14:40:45 -07:00
code: 66
})
// Create an array of plugins.
const plugins = [
boldPlugin
]
class App extends React.Component {
2016-08-14 18:25:12 -07:00
state = {
state: initialState,
schema: {
marks: {
bold: props => < strong > {props.children}< / strong >
}
2016-07-08 14:40:45 -07:00
}
}
2017-09-06 17:51:40 -07:00
onChange = ({ state }) => {
2016-11-17 18:56:09 -08:00
this.setState({ state })
}
2016-07-08 14:40:45 -07:00
2017-08-02 18:36:33 +02:00
render() {
2016-07-08 14:40:45 -07:00
return (
2016-08-14 18:25:12 -07:00
// Add the `plugins` property to the editor, and remove `onKeyDown` .
2016-07-08 14:40:45 -07:00
< Editor
plugins={plugins}
2016-08-14 18:25:12 -07:00
schema={this.state.schema}
state={this.state.state}
2016-11-17 18:56:09 -08:00
onChange={this.onChange}
2016-07-08 14:40:45 -07:00
/>
)
}
}
```
Awesome. If you test out the editor now, you'll notice that everything still works just as it did before. But the beauty of the logic being encapsulated in a plugin is that we can add more mark types _extremely_ easily now!
Let's add _italic_ , `code` , ~~strikethrough~~ and underline marks:
```js
2016-08-14 18:25:12 -07:00
// Initialize a plugin for each mark...
2016-07-08 14:40:45 -07:00
const plugins = [
MarkHotkey({ code: 66, type: 'bold' }),
2017-04-22 07:14:04 +03:00
MarkHotkey({ code: 67, type: 'code', isAltKey: true }),
2016-07-08 14:40:45 -07:00
MarkHotkey({ code: 73, type: 'italic' }),
MarkHotkey({ code: 68, type: 'strikethrough' }),
MarkHotkey({ code: 85, type: 'underline' })
]
class App extends React.Component {
2016-08-14 18:25:12 -07:00
state = {
state: initialState,
schema: {
marks: {
bold: props => < strong > {props.children}< / strong > ,
// Add our new mark renderers...
code: props => < code > {props.children}< / code > ,
italic: props => < em > {props.children}< / em > ,
strikethrough: props => < del > {props.children}< / del > ,
underline: props => < u > {props.children}< / u > ,
}
2016-07-08 14:40:45 -07:00
}
}
2017-09-06 17:51:40 -07:00
onChange = ({ state }) => {
2016-11-17 18:56:09 -08:00
this.setState({ state })
}
2016-07-08 14:40:45 -07:00
2017-08-02 18:36:33 +02:00
render() {
2016-07-08 14:40:45 -07:00
return (
< Editor
plugins={plugins}
2016-08-14 18:25:12 -07:00
schema={this.state.schema}
state={this.state.state}
2016-11-17 18:56:09 -08:00
onChange={this.onChange}
2016-07-08 14:40:45 -07:00
/>
)
}
}
```
And there you have it! We just added a ton of functionality to the editor with very little work. And we can keep all of our mark hotkey logic tested and isolated in a single place, making maintaining the code easier.
Of course... now that it's reusable, we could actually make our `MarkHotkey` plugin a little easier to use. What if instead of a `code` argument it took the text of the `key` itself? That would make the calling code a lot clearer, since key codes are really obtuse.
2016-07-08 14:54:52 -07:00
In fact, unless you have weirdly good keycode knowledge, you probably have no idea what our current hotkeys actually are.
2016-07-08 14:40:45 -07:00
Let's fix that.
Using the `keycode` module in npm makes this dead simple.
First install it:
```
npm install keycode
```
And then we can add it our plugin:
```js
// Import the keycode module.
import keycode from `keycode`
function MarkHotkey(options) {
// Change the options to take a `key` .
2017-05-30 22:15:11 +02:00
const { type, key, isAltKey = false } = options
2016-07-08 14:40:45 -07:00
return {
2017-09-06 17:51:40 -07:00
onKeyDown(event, data, change) {
2016-07-08 14:40:45 -07:00
// Change the comparison to use the key name.
2017-05-30 22:15:11 +02:00
if (!event.metaKey || keycode(event.which) != key || event.altKey != isAltKey) return
2017-02-16 10:16:19 -08:00
event.preventDefault()
2017-09-06 17:51:40 -07:00
change.toggleMark(type)
return true
2016-07-08 14:40:45 -07:00
}
}
}
```
2016-07-08 14:54:52 -07:00
And now we can make our app code much clearer for the next person who reads it:
2016-07-08 14:40:45 -07:00
```js
// Use the much clearer key names instead of key codes!
const plugins = [
MarkHotkey({ key: 'b', type: 'bold' }),
2017-04-22 07:14:04 +03:00
MarkHotkey({ key: 'c', type: 'code', isAltKey: true }),
2016-07-08 14:40:45 -07:00
MarkHotkey({ key: 'i', type: 'italic' }),
MarkHotkey({ key: 'd', type: 'strikethrough' }),
MarkHotkey({ key: 'u', type: 'underline' })
]
class App extends React.Component {
2016-08-14 18:25:12 -07:00
state = {
state: initialState,
schema: {
marks: {
bold: props => < strong > {props.children}< / strong > ,
code: props => < code > {props.children}< / code > ,
italic: props => < em > {props.children}< / em > ,
strikethrough: props => < del > {props.children}< / del > ,
underline: props => < u > {props.children}< / u > ,
}
2016-07-08 14:40:45 -07:00
}
}
2017-09-06 17:51:40 -07:00
onChange = ({ state }) => {
2016-11-17 18:56:09 -08:00
this.setState({ state })
}
2016-07-08 14:40:45 -07:00
2017-08-02 18:36:33 +02:00
render() {
2016-07-08 14:40:45 -07:00
return (
< Editor
plugins={plugins}
2016-08-14 18:25:12 -07:00
schema={this.state.schema}
state={this.state.state}
2016-11-17 18:56:09 -08:00
onChange={this.onChange}
2016-07-08 14:40:45 -07:00
/>
)
}
}
```
2016-07-08 14:54:52 -07:00
That's why plugins are awesome. They let you get really expressive while also making your codebase easier to manage. And since Slate is built with plugins as a primary consideration, using them is dead simple!
2016-07-18 12:55:04 -07:00
< br / >
< p align = "center" > < strong > Next:< / strong > < br / > < a href = "./saving-to-a-database.md" > Saving to a Database< / a > < / p >
< br / >