1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-09-03 12:12:39 +02:00

update docs

This commit is contained in:
Ian Storm Taylor
2016-07-08 14:40:45 -07:00
parent 3cf14527af
commit 8c07941d56
4 changed files with 345 additions and 7 deletions

View File

@@ -55,9 +55,9 @@ If you're using Slate for the first time, check out the [Getting Started](./docs
- [**Guides**](docs/guides/installing-slate.md)
- [Installing Slate](docs/guides/installing-slate.md)
- [Adding Event Handlers](docs/guides/adding-event-handlers.md)
- [Adding Custom Formatting](docs/guides/adding-custom-formatting.md)
- [Adding Custom Block Types](docs/guides/adding-custom-block-types.md)
- Adding Plugins
- [Defining Custom Block Nodes](docs/guides/defining-custom-block-nodes.md)
- [Applying Custom Formatting](docs/guides/applying-custom-formatting.md)
- [Using Plugins](docs/guides/using-plugins.md)
- [**Concepts**](docs/concepts.md)
- Statelessness & Immutability

View File

@@ -3,8 +3,8 @@ These guides introduce you to the different parts of Slate in a step-by-step way
- [Installing Slate](./installing-slate.md)
- [Adding Event Handlers](./adding-event-handlers.md)
- [Adding Custom Formatting](./adding-custom-formatting.md)
- [Adding Custom Block Types](./adding-custom-block-types.md)
- Adding Plugins
- [Defining Custom Block Nodes](./defining-custom-block-nodes.md)
- [Applying Custom Formatting](./applying-custom-formatting.md)
- [Using Plugins](./using-plugins.md)
_If you have an idea for a guide, or notice something that isn't clear, submit a pull request!_

View File

@@ -253,7 +253,7 @@ class App extends React.Component {
const isBold = state.marks.some(mark => mark.type == 'bold')
return state
.transform()
[isBold ? 'mark' : 'unmark']('bold')
[isBold ? 'unmark' : 'mark']('bold')
.apply()
}
case 192: {

View File

@@ -0,0 +1,338 @@
<br/>
<p align="center"><strong>Previous:</strong><br/><a href="./applying-custom-formatting.md">Applying Custom Formatting</a></p>
<br/>
### Using Formatting
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.
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.
So let's look at how you'd break that code out into it's own reusable plugin that can toggle _any_ formatting mark on _any_ key press.
Starting with our app from earlier:
```js
const BOLD_MARK = {
fontWeight: 'bold'
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
renderMark={mark => this.renderMark(mark)}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={(e, state) => this.onKeyDown(e, state)}
/>
)
}
renderMark(mark) {
if (mark.type == 'bold') return BOLD_MARK
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
if (!event.metaKey || event.which != 66) return
const isBold = state.marks.some(mark => mark.type == 'bold')
return state
.transform()
[isBold ? 'unmark' : 'mark']('bold')
.apply()
}
}
```
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.
const { type, code } = options
}
```
Okay, that was easy.
Now we want to have it return a dictionary of handlers, in this case specifically containing a `onKeyDown` handler with logic pulled from our app:
```js
function MarkHotkey(options) {
const { type, code } = options
// Return our "plugin" object, containing the `onKeyDown` handler.
return {
onKeyDown(event, state) {
// Check that the key pressed matches our `code` option.
if (!event.metaKey || event.which != code) return
// Determine whether our `type` option mark is currently active.
const isActive = state.marks.some(mark => mark.type == type)
// Toggle the mark `type` based on whether it is active.
return state
.transform()
[isActive ? 'unmark' : 'mark'](type)
.apply()
}
}
}
```
Boom! Now we're getting somewhere. That code is reusable for any type of mark.
So now, let's remove the old code from our app, and pass the editor our brand new `MarkHotkey` plugin instead, giving it the same options that will keep our bold functionality intact:
```js
const BOLD_MARK = {
fontWeight: 'bold'
}
// Initialize our bold-mark-adding plugin.
const boldPlugin = MarkHotkey({
type: 'bold',
code: 66
})
// Create an array of plugins.
const plugins = [
boldPlugin
]
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
// Add the `plugins` property to the editor, and remove `onKeyDown`.
render() {
return (
<Editor
state={this.state.state}
plugins={plugins}
renderMark={mark => this.renderMark(mark)}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
/>
)
}
renderMark(mark) {
if (mark.type == 'bold') return BOLD_MARK
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
}
```
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
// Add our new mark renderers...
const MARKS = {
bold: {
fontWeight: 'bold'
},
code: {
fontFamily: 'monospace'
},
italic: {
fontStyle: 'italic'
},
strikethrough: {
textDecoration: 'strikethrough'
},
underline: {
textDecoration: 'underline'
}
}
// Initialize our plugins...
const plugins = [
MarkHotkey({ code: 66, type: 'bold' }),
MarkHotkey({ code: 192, type: 'code' }),
MarkHotkey({ code: 73, type: 'italic' }),
MarkHotkey({ code: 68, type: 'strikethrough' }),
MarkHotkey({ code: 85, type: 'underline' })
]
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
plugins={plugins}
renderMark={mark => this.renderMark(mark)}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
/>
)
}
// Update our render function to handle all the new marks...
renderMark(mark) {
return MARKS[mark.type]
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
}
```
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.
In fact, unless you have weirdly good keycode guessing, there's a good chance you had no idea what our current hotkeys bindings actually mapped to.
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`.
const { type, key } = options
return {
onKeyDown(event, state) {
// Change the comparison to use the key name.
if (!event.metaKey || keycode(event.which) != key) return
const isActive = state.marks.some(mark => mark.type == type)
return state
.transform()
[isActive ? 'unmark' : 'mark'](type)
.apply()
}
}
}
```
And finally, we can make our app code clearer:
```js
const MARKS = {
bold: {
fontWeight: 'bold'
},
code: {
fontFamily: 'monospace'
},
italic: {
fontStyle: 'italic'
},
strikethrough: {
textDecoration: 'strikethrough'
},
underline: {
textDecoration: 'underline'
}
}
// Use the much clearer key names instead of key codes!
const plugins = [
MarkHotkey({ key: 'b', type: 'bold' }),
MarkHotkey({ key: '`', type: 'code' }),
MarkHotkey({ key: 'i', type: 'italic' }),
MarkHotkey({ key: 'd', type: 'strikethrough' }),
MarkHotkey({ key: 'u', type: 'underline' })
]
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
plugins={plugins}
renderMark={mark => this.renderMark(mark)}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
/>
)
}
renderMark(mark) {
return MARKS[mark.type]
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
}
```
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.