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

update docs

This commit is contained in:
Ian Storm Taylor
2016-07-08 14:10:06 -07:00
parent b0b3c3f907
commit 3cf14527af
5 changed files with 564 additions and 132 deletions

View File

@@ -1,128 +0,0 @@
<br/>
<p align="center"><strong>Previous:</strong><br/><a href="./adding-event-handlers.md">Adding Event Handlers</a></p>
<br/>
### Adding Custom Formatting
In the last guide we learned how to use Slate's event handlers to change its content, but we didn't do anything really that useful with it.
In this guide, we'll show you how to add custom formatting options, like **bold**, _italic_, `code` or ~~strikethrough~~.
So we start with our app from earlier, but we'll get rid of that useless `onKeyDown` handler logic, and we'll replace it with logic that will add a **bold** format to the currently selected text:
```js
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
onChange={state => this.onChange(state)}
onKeyDown={e, state => this.onKeyDown(e, state)}
/>
)
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
// Return with no changes if it's not the "B" key with cmd/ctrl pressed.
if (event.which != 66 || !event.metaKey) return
// Otherwise, transform the state by adding a `bold` mark to the selection.
const newState = state
.transform()
.mark('bold')
.apply()
// Return the new state, which will cause the editor to update it.
return newState
}
}
```
Now if you read through `onKeyDown`, it should be fairly clear.
But! If you happen to now try selecting text and hitting **⌘-B**, you'll get an error in your console. That's because we haven't told Slate how to render a "bold" mark.
For every mark type you want to add to your schema, you need to give Slate a "renderer" for that mark, which is just an object of styles that will be passed to React's `style=` property.
So let's define our `bold` mark:
```js
// Define a set of styles that make text bold.
const BOLD_MARK = {
fontWeight: 'bold'
}
```
Pretty simple, right?
And now, let's tell Slate about that mark. To do that, we'll need to pass in a `renderMark` function to the `Editor`, which it will call with any mark it finds. And when it calls it with a bold mark, we'll return our `BOLD_MARK` styles. Like so:
```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}
onChange={state => this.onChange(state)}
onKeyDown={e, state => this.onKeyDown(e, state)}
renderMark={mark => this.renderMark(mark)}
/>
)
}
// Define a mark rendering function that knows about "bold" marks.
renderMark(mark) {
if (mark.type == 'bold') return BOLD_MARK
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
if (event.which != 66 || !event.metaKey) return
const newState = state
.transform()
.mark('bold')
.apply()
return newState
}
}
```
Now, if you try selecting a piece of text and hitting **⌘-B** you should see it turn bold! Magic!
<br/>
<p align="center"><strong>Next:</strong><br/><a href="./adding-custom-block-types.md">Adding Custom Block Types</a></p>
<br/>

View File

@@ -27,11 +27,16 @@ class App extends React.Component {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
/>
)
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
@@ -55,12 +60,17 @@ class App extends React.Component {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={e, state => this.onKeyDown(e, state)}
onKeyDown={(e, state) => this.onKeyDown(e, state)}
/>
)
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
@@ -93,12 +103,17 @@ class App extends React.Component {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={e, state => this.onKeyDown(e, state)}
onKeyDown={(e, state) => this.onKeyDown(e, state)}
/>
)
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
@@ -125,5 +140,5 @@ With that added, try typing `&`, and you should see it automatically become `and
That gives you a sense for what you can do with Slate's event handlers. Each one will be called with the `event` object, and the current `state` of the editor.
<br/>
<p align="center"><strong>Next:</strong><br/><a href="./adding-custom-formatting.md">Adding Custom Formatting</a></p>
<p align="center"><strong>Next:</strong><br/><a href="./adding-custom-block-nodes.md">Adding Custom Block Nodes</a></p>
<br/>

View File

@@ -0,0 +1,276 @@
<br/>
<p align="center"><strong>Previous:</strong><br/><a href="./defining-custom-block-nodes.md">Defining Custom Block Nodes</a></p>
<br/>
### Applying Custom Formatting
In the previous guide we learned how to create custom block types that render chunks of text inside different containers. But Slate allows for more than just "blocks".
In this guide, we'll show you how to add custom formatting options, like **bold**, _italic_, `code` or ~~strikethrough~~.
So we start with our app from earlier:
```js
const CodeNode = (props) => {
return <pre><code>{props.children}</code></pre>
}
const ParagraphNode = (props) => {
return <p>{props.children}</p>
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={e, state => this.onKeyDown(e, state)}
/>
)
}
renderNode(node) {
switch (node.type) {
case 'code': return CodeNode
case 'paragraph': return ParagraphNode
}
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
if (event.which != 192 || !event.metaKey) return
const isCode = state.blocks.some(block => block.type == 'code')
return state
.transform()
.setBlock(isCode ? 'paragraph' : 'code')
.apply()
}
}
}
```
And now, we'll edit the `onKeyDown` handler to make it so that when you press **⌘-B**, it will add a "bold" mark to the currently selected text:
```js
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={(e, state) => this.onKeyDown(e, state)}
/>
)
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
if (!event.metaKey) return
// Decide what to do based on the key code...
switch (event.which) {
// When "B" is pressed, add a "bold" mark to the text.
case 66: {
return state
.transform()
.mark('bold')
.apply()
}
// When "`" is pressed, keep our existing code block logic.
case 192: {
const isCode = state.blocks.some(block => block.type == 'code')
return state
.transform()
.setBlock(isCode ? 'paragraph' : 'code')
.apply()
}
}
}
}
```
Okay, so we've got the hotkey handler setup... but! If you happen to now try selecting text and hitting **⌘-B**, you'll get an error in your console. That's because we haven't told Slate how to render a "bold" mark.
For every mark type you want to add to your schema, you need to give Slate a "renderer" for that mark, just like nodes. Except unlike nodes, mark renderers are just simple objects of styles that will be passed directly to React's `style=` property.
So let's define our `bold` mark:
```js
// Define a set of styles that make text bold.
const BOLD_MARK = {
fontWeight: 'bold'
}
```
Pretty simple, right?
And now, let's tell Slate about that mark. To do that, we'll need to pass in a `renderMark` function to the `Editor`, which it will call with any mark it finds. And when it calls it with a bold mark, we'll return our `BOLD_MARK` styles. Like so:
```js
const BOLD_MARK = {
fontWeight: 'bold'
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
// Add the `renderMark` handler to the editor.
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)}
/>
)
}
// Define a mark rendering function that knows about "bold" marks.
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) return
switch (event.which) {
case 66: {
return state
.transform()
.mark('bold')
.apply()
}
case 192: {
const isCode = state.blocks.some(block => block.type == 'code')
return state
.transform()
.setBlock(isCode ? 'paragraph' : 'code')
.apply()
}
}
}
}
```
Now, if you try selecting a piece of text and hitting **⌘-B** you should see it turn bold! Magic!
But we forgot one thing. When you hit **⌘-B** again, it should remove the bold formatting. To do that, we'll need to add a bit of logic to either add or remove the `bold` mark depending on what the currently selected marks on the text are:
```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) return
switch (event.which) {
case 66: {
// Loop through the current marks, check to see if any are bold.
const isBold = state.marks.some(mark => mark.type == 'bold')
return state
.transform()
[isBold ? 'mark' : 'unmark']('bold')
.apply()
}
case 192: {
const isCode = state.blocks.some(block => block.type == 'code')
return state
.transform()
.setBlock(isCode ? 'paragraph' : 'code')
.apply()
}
}
}
}
```
Now when you repeatedly press **⌘-B** you should see the bold formatting be added and removed!
<br/>
<p align="center"><strong>Next:</strong><br/><a href="./using-plugins.md">Using Plugins</a></p>
<br/>

View File

@@ -0,0 +1,249 @@
<br/>
<p align="center"><strong>Previous:</strong><br/><a href="./adding-event-handlers.md">Adding Event Handlers</a></p>
<br/>
### Defining Custom Block Nodes
In our previous example, we started with a `paragraph` block node, but that's not all you can do. Slate lets you define any type of custom blocks you want, like block quotes, code blocks, list items, etc.
We'll show you how. Let's start with our app from earlier:
```js
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={(e, state) => this.onKeyDown(e, state)}
/>
)
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
if (event.which != 55 || !event.shiftKey) return
const newState = state
.transform()
.insertText('and')
.apply()
return newState
}
}
```
Now let's add "code blocks" to our editor.
Just like when we defined our `paragraph` block, we'll do the same except this time for code blocks:
```js
// Define a React component renderer for our code blocks.
const CodeNode = (props) => {
return <pre><code>{props.children}</code></pre>
}
```
Pretty simple.
See that `props.children` reference? We glossed over it earlier. Slate will automatically render all of the children of a block for you, and then pass them to you just like any other React component would, via `props.children`. That way you don't have to muck around with rendering the proper text nodes or anything like that.
Now, let's add that renderer to our `Editor`:
```js
const CodeNode = (props) => {
return <pre><code>{props.children}</code></pre>
}
const ParagraphNode = (props) => {
return <p>{props.children}</p>
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={e, state => this.onKeyDown(e, state)}
/>
)
}
// Render the right component depending on the node `type`.
renderNode(node) {
switch (node.type) {
case 'code': return CodeNode
case 'paragraph': return ParagraphNode
}
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
if (event.which != 55 || !event.shiftKey) return
const newState = state
.transform()
.insertText('and')
.apply()
return newState
}
}
```
Okay, but now we'll need a way for the user to actually turn a block into a code block. So let's change our `onKeyDown` function to add a **⌘-`** shortcut that does just that:
```js
const CodeNode = (props) => {
return <pre><code>{props.children}</code></pre>
}
const ParagraphNode = (props) => {
return <p>{props.children}</p>
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={e, state => this.onKeyDown(e, state)}
/>
)
}
renderNode(node) {
switch (node.type) {
case 'code': return CodeNode
case 'paragraph': return ParagraphNode
}
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
// Return with no changes if it's not the "`" key with cmd/ctrl pressed.
if (event.which != 192 || !event.metaKey) return
// Otherwise, set the currently selected blocks type to "code".
return state
.transform()
.setBlock('code')
.apply()
}
}
}
```
Now, if you press **⌘-`**, the block your cursor is in should turn into a code block! Magic!
But we forgot one thing. When you hit **⌘-`** again, it should change the code block back into a paragraph. To do that, we'll need to add a bit of logic to change the type we set based on whether any of the currently selected blocks are already a code block:
```js
const CodeNode = (props) => {
return <pre><code>{props.children}</code></pre>
}
const ParagraphNode = (props) => {
return <p>{props.children}</p>
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
state: initialState
}
}
render() {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
onKeyDown={e, state => this.onKeyDown(e, state)}
/>
)
}
renderNode(node) {
switch (node.type) {
case 'code': return CodeNode
case 'paragraph': return ParagraphNode
}
}
onChange(state) {
this.setState({ state })
}
onKeyDown(event, state) {
if (event.which != 192 || !event.metaKey) return
// Determine whether any of the currently selected blocks are code blocks.
const isCode = state.blocks.some(block => block.type == 'code')
// Toggle the block type depending on `isCode`.
return state
.transform()
.setBlock(isCode ? 'paragraph' : 'code')
.apply()
}
}
}
```
And there you have it! If you press **⌘-`** while inside a code block, it should turn back into a paragraph!
<br/>
<p align="center"><strong>Next:</strong><br/><a href="./adding-custom-formatting.md">Applying Custom Formatting</a></p>
<br/>

View File

@@ -47,7 +47,18 @@ const initialState = Raw.deserialize([
])
```
Once you've got a `State` object create, via the `Raw` serializer, or any other serialization method you want, you can pass it into the `Editor` component inside your application:
Okay, now we've got our initial state in a format that Slate understands. But Slate doesn't know anything about our schema, specifically how to render `paragraph` block nodes.
So we need to define a paragraph node renderer, which is just a simple React component, like so:
```js
// A simple React component that wraps text in a `<p>` element.
const ParagraphNode = (props) => {
return <p>{props.children}</p>
}
```
And now that we've our initial state and our paragraph renderer, we define our `App` and pass them into Slate's `Editor` component, like so:
```js
import React from 'react'
@@ -70,6 +81,10 @@ const initialState = Raw.deserialize([
}
])
const ParagraphNode = (props) => {
return <p>{props.children}</p>
}
// Define our app...
class App extends React.Component {
@@ -86,11 +101,16 @@ class App extends React.Component {
return (
<Editor
state={this.state.state}
renderNode={node => this.renderNode(node)}
onChange={state => this.onChange(state)}
/>
)
}
renderNode(node) {
if (node.type == 'paragraph') return ParagraphNode
}
onChange(state) {
// Update the app's React state with the new editor state.
this.setState({ state })