2016-07-08 14:10:06 -07:00
< br / >
< p align = "center" > < strong > Previous:< / strong > < br / > < a href = "./adding-event-handlers.md" > Adding Event Handlers< / a > < / p >
< br / >
2016-07-12 21:34:31 -07:00
# Defining Custom Block Nodes
2016-07-08 14:10:06 -07:00
2016-08-14 18:25:12 -07:00
In our previous example, we started with a paragraph, but we never actually told Slate anything about the `paragraph` block type. We just let it use its internal default renderer, which uses a plain old `<div>` .
2016-07-12 21:34:31 -07:00
2017-09-06 17:51:40 -07:00
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.
2016-07-08 14:10:06 -07:00
We'll show you how. Let's start with our app from earlier:
```js
class App extends React.Component {
2016-08-14 18:25:12 -07:00
state = {
2018-02-07 15:58:41 +00:00
value: initialValue,
2016-07-08 14:10:06 -07:00
}
2017-09-06 17:51:40 -07:00
2017-10-27 13:39:06 -07:00
onChange = ({ value }) => {
this.setState({ value })
2016-07-08 14:10:06 -07:00
}
2017-10-16 21:13:07 -07:00
onKeyDown = (event, change) => {
2017-10-15 19:23:07 -07:00
if (event.key != '& ') return
2017-02-16 10:16:19 -08:00
event.preventDefault()
2018-02-07 15:58:41 +00:00
change.insertText('and')
2017-09-06 17:51:40 -07:00
return true
2016-07-08 14:10:06 -07:00
}
2017-08-02 18:36:33 +02:00
render() {
2016-11-17 18:56:09 -08:00
return (
< Editor
2017-10-27 13:39:06 -07:00
value={this.state.value}
2016-11-17 18:56:09 -08:00
onChange={this.onChange}
onKeyDown={this.onKeyDown}
/>
)
}
2016-07-08 14:10:06 -07:00
}
```
Now let's add "code blocks" to our editor.
2017-10-15 16:22:00 -05:00
The problem is, code blocks won't just be rendered as a plain paragraph, they'll need to be rendered differently. To make that happen, we need to define a "renderer" for `code` nodes.
2016-07-12 21:34:31 -07:00
Node renderers are just simple React components, like so:
2016-07-08 14:10:06 -07:00
```js
// Define a React component renderer for our code blocks.
2016-08-14 18:25:12 -07:00
function CodeNode(props) {
2018-02-07 15:58:41 +00:00
return (
< pre { . . . props . attributes } >
< code > {props.children}< / code >
< / pre >
)
2016-07-08 14:10:06 -07:00
}
```
2017-09-06 17:51:40 -07:00
Pretty simple.
2016-07-08 14:10:06 -07:00
2016-07-12 21:34:31 -07:00
See the `props.attributes` reference? Slate passes attributes that should be rendered on the top-most element of your blocks, so that you don't have to build them up yourself. You **must** mix the attributes into your component.
And see that `props.children` reference? 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. You **must** render the children as the lowest leaf in your component.
2016-07-08 14:10:06 -07:00
Now, let's add that renderer to our `Editor` :
```js
2016-08-14 18:25:12 -07:00
function CodeNode(props) {
2018-02-07 15:58:41 +00:00
return (
< pre { . . . props . attributes } >
< code > {props.children}< / code >
< / pre >
)
2016-07-08 14:10:06 -07:00
}
class App extends React.Component {
2016-08-14 18:25:12 -07:00
state = {
2017-10-27 13:39:06 -07:00
value: initialValue,
2016-07-08 14:10:06 -07:00
}
2017-09-06 17:51:40 -07:00
2017-10-27 13:39:06 -07:00
onChange = ({ value }) => {
this.setState({ value })
2016-07-08 14:10:06 -07:00
}
2017-10-16 21:13:07 -07:00
onKeyDown = (event, change) => {
2017-10-15 19:23:07 -07:00
if (event.key != '& ') return
2017-02-16 10:16:19 -08:00
event.preventDefault()
2017-09-06 17:51:40 -07:00
change.insertText('and')
return true
2016-07-08 14:10:06 -07:00
}
2017-08-02 18:36:33 +02:00
render() {
2016-11-17 18:56:09 -08:00
return (
2017-10-25 17:32:29 -07:00
// Pass in the `renderNode` prop...
2016-11-17 18:56:09 -08:00
< Editor
2017-10-27 13:39:06 -07:00
value={this.state.value}
2016-11-17 18:56:09 -08:00
onChange={this.onChange}
onKeyDown={this.onKeyDown}
2017-10-25 17:32:29 -07:00
renderNode={this.renderNode}
2016-11-17 18:56:09 -08:00
/>
)
}
2017-10-25 17:32:29 -07:00
// Add a `renderNode` method to render a `CodeNode` for code blocks.
2018-02-07 15:58:41 +00:00
renderNode = props => {
2017-10-25 17:32:29 -07:00
switch (props.node.type) {
2018-02-07 15:58:41 +00:00
case 'code':
return < CodeNode { . . . props } / >
2017-10-25 17:32:29 -07:00
}
}
2016-07-08 14:10:06 -07:00
}
```
2018-01-08 16:38:46 -05:00
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 `control-\` ` shortcut that does just that:
2016-07-08 14:10:06 -07:00
```js
2016-08-14 18:25:12 -07:00
function CodeNode(props) {
2018-02-07 15:58:41 +00:00
return (
< pre { . . . props . attributes } >
< code > {props.children}< / code >
< / pre >
)
2016-07-08 14:10:06 -07:00
}
class App extends React.Component {
2016-08-14 18:25:12 -07:00
state = {
2017-10-27 13:39:06 -07:00
value: initialValue,
2016-07-08 14:10:06 -07:00
}
2017-09-06 17:51:40 -07:00
2017-10-27 13:39:06 -07:00
onChange = ({ value }) => {
this.setState({ value })
2016-07-08 14:10:06 -07:00
}
2017-10-16 21:13:07 -07:00
onKeyDown = (event, change) => {
2018-01-08 16:38:46 -05:00
// Return with no changes if it's not the "`" key with ctrl pressed.
if (event.key != '`' || !event.ctrlKey) return
2017-09-06 17:51:40 -07:00
2017-02-16 10:16:19 -08:00
// Prevent the "`" from being inserted by default.
event.preventDefault()
2016-07-08 14:10:06 -07:00
// Otherwise, set the currently selected blocks type to "code".
2018-02-21 21:03:30 -05:00
change.setBlocks('code')
2017-09-06 17:51:40 -07:00
return true
2016-11-17 18:56:09 -08:00
}
2017-08-02 18:36:33 +02:00
render() {
2016-11-17 18:56:09 -08:00
return (
< Editor
2017-10-27 13:39:06 -07:00
value={this.state.value}
2016-11-17 18:56:09 -08:00
onChange={this.onChange}
onKeyDown={this.onKeyDown}
2017-10-25 17:32:29 -07:00
renderNode={this.renderNode}
2016-11-17 18:56:09 -08:00
/>
)
2016-07-08 14:10:06 -07:00
}
2018-02-07 15:58:41 +00:00
renderNode = props => {
2017-10-25 17:32:29 -07:00
switch (props.node.type) {
2018-02-07 15:58:41 +00:00
case 'code':
return < CodeNode { . . . props } / >
2017-10-25 17:32:29 -07:00
}
}
2016-07-08 14:10:06 -07:00
}
```
2018-02-07 15:58:41 +00:00
Now, if you press `control-\` ` the block your cursor is in should turn into a code block! Magic!
2016-07-08 14:10:06 -07:00
2018-02-07 15:58:41 +00:00
_Note: The Edge browser does not currently support `control-...` key events (see [issue ](https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/742263/ )), so this example won't work on it._
2018-02-02 11:22:08 +08:00
2018-01-08 16:38:46 -05:00
But we forgot one thing. When you hit `control-\` ` 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:
2016-07-08 14:10:06 -07:00
```js
2016-08-14 18:25:12 -07:00
function CodeNode(props) {
2018-02-07 15:58:41 +00:00
return (
< pre { . . . props . attributes } >
< code > {props.children}< / code >
< / pre >
)
2016-07-08 14:10:06 -07:00
}
class App extends React.Component {
2016-08-14 18:25:12 -07:00
state = {
2017-10-27 13:39:06 -07:00
value: initialValue,
2016-07-08 14:10:06 -07:00
}
2017-09-06 17:51:40 -07:00
2017-10-27 13:39:06 -07:00
onChange = ({ value }) => {
this.setState({ value })
2016-07-08 14:10:06 -07:00
}
2017-10-16 21:13:07 -07:00
onKeyDown = (event, change) => {
2018-01-08 16:38:46 -05:00
if (event.key != '`' || !event.ctrlKey) return
2017-09-06 17:51:40 -07:00
2017-02-16 10:16:19 -08:00
event.preventDefault()
2016-07-08 14:10:06 -07:00
// Determine whether any of the currently selected blocks are code blocks.
2017-10-27 13:39:06 -07:00
const isCode = change.value.blocks.some(block => block.type == 'code')
2016-07-08 14:10:06 -07:00
// Toggle the block type depending on `isCode` .
2018-02-21 21:03:30 -05:00
change.setBlocks(isCode ? 'paragraph' : 'code')
2017-09-06 17:51:40 -07:00
return true
2016-07-08 14:10:06 -07:00
}
2017-08-02 18:36:33 +02:00
render() {
2016-11-17 18:56:09 -08:00
return (
< Editor
2017-10-27 13:39:06 -07:00
value={this.state.value}
2016-11-17 18:56:09 -08:00
onChange={this.onChange}
onKeyDown={this.onKeyDown}
2017-10-25 17:32:29 -07:00
renderNode={this.renderNode}
2016-11-17 18:56:09 -08:00
/>
)
}
2018-01-08 16:38:46 -05:00
2018-02-07 15:58:41 +00:00
renderNode = props => {
2017-10-25 17:32:29 -07:00
switch (props.node.type) {
2018-02-07 15:58:41 +00:00
case 'code':
return < CodeNode { . . . props } / >
2017-10-25 17:32:29 -07:00
}
}
2016-07-08 14:10:06 -07:00
}
```
2018-01-08 16:38:46 -05:00
And there you have it! If you press `control-\` ` while inside a code block, it should turn back into a paragraph!
2016-07-08 14:10:06 -07:00
< br / >
2016-07-08 14:54:52 -07:00
< p align = "center" > < strong > Next:< / strong > < br / > < a href = "./applying-custom-formatting.md" > Applying Custom Formatting< / a > < / p >
2016-07-08 14:10:06 -07:00
< br / >