From 3cf14527af434a0afd48a415f5288d93447cc0ae Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Fri, 8 Jul 2016 14:10:06 -0700 Subject: [PATCH] update docs --- docs/guides/adding-custom-formatting.md | 128 ---------- docs/guides/adding-event-handlers.md | 21 +- docs/guides/applying-custom-formatting.md | 276 +++++++++++++++++++++ docs/guides/defining-custom-block-nodes.md | 249 +++++++++++++++++++ docs/guides/installing-slate.md | 22 +- 5 files changed, 564 insertions(+), 132 deletions(-) delete mode 100644 docs/guides/adding-custom-formatting.md create mode 100644 docs/guides/applying-custom-formatting.md create mode 100644 docs/guides/defining-custom-block-nodes.md diff --git a/docs/guides/adding-custom-formatting.md b/docs/guides/adding-custom-formatting.md deleted file mode 100644 index 1a205087b..000000000 --- a/docs/guides/adding-custom-formatting.md +++ /dev/null @@ -1,128 +0,0 @@ - -
-

Previous:
Adding Event Handlers

-
- -### 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 ( - 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 ( - 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! - - -
-

Next:
Adding Custom Block Types

-
diff --git a/docs/guides/adding-event-handlers.md b/docs/guides/adding-event-handlers.md index 6b3235eae..82b084e1a 100644 --- a/docs/guides/adding-event-handlers.md +++ b/docs/guides/adding-event-handlers.md @@ -27,11 +27,16 @@ class App extends React.Component { return ( 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 ( 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 ( 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.
-

Next:
Adding Custom Formatting

+

Next:
Adding Custom Block Nodes


diff --git a/docs/guides/applying-custom-formatting.md b/docs/guides/applying-custom-formatting.md new file mode 100644 index 000000000..7c1df477f --- /dev/null +++ b/docs/guides/applying-custom-formatting.md @@ -0,0 +1,276 @@ + +
+

Previous:
Defining Custom Block Nodes

+
+ +### 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
{props.children}
+} + +const ParagraphNode = (props) => { + return

{props.children}

+} + +class App extends React.Component { + + constructor(props) { + super(props) + this.state = { + state: initialState + } + } + + render() { + return ( + 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 ( + 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 ( + 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 ( + 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! + +
+

Next:
Using Plugins

+
diff --git a/docs/guides/defining-custom-block-nodes.md b/docs/guides/defining-custom-block-nodes.md new file mode 100644 index 000000000..68b292011 --- /dev/null +++ b/docs/guides/defining-custom-block-nodes.md @@ -0,0 +1,249 @@ + +
+

Previous:
Adding Event Handlers

+
+ +### 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 ( + 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
{props.children}
+} +``` + +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
{props.children}
+} + +const ParagraphNode = (props) => { + return

{props.children}

+} + +class App extends React.Component { + + constructor(props) { + super(props) + this.state = { + state: initialState + } + } + + render() { + return ( + 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
{props.children}
+} + +const ParagraphNode = (props) => { + return

{props.children}

+} + +class App extends React.Component { + + constructor(props) { + super(props) + this.state = { + state: initialState + } + } + + render() { + return ( + 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
{props.children}
+} + +const ParagraphNode = (props) => { + return

{props.children}

+} + +class App extends React.Component { + + constructor(props) { + super(props) + this.state = { + state: initialState + } + } + + render() { + return ( + 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! + +
+

Next:
Applying Custom Formatting

+
diff --git a/docs/guides/installing-slate.md b/docs/guides/installing-slate.md index c70796618..94846c5ce 100644 --- a/docs/guides/installing-slate.md +++ b/docs/guides/installing-slate.md @@ -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 `

` element. +const ParagraphNode = (props) => { + return

{props.children}

+} +``` + +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

{props.children}

+} + // Define our app... class App extends React.Component { @@ -86,11 +101,16 @@ class App extends React.Component { return ( 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 })