mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-20 06:01:24 +02:00
update walkthroughs
This commit is contained in:
@@ -16,8 +16,6 @@ _Note, if you'd rather use a pre-bundled version of Slate, you can `yarn add sla
|
|||||||
|
|
||||||
Once you've installed Slate, you'll need to import it.
|
Once you've installed Slate, you'll need to import it.
|
||||||
|
|
||||||
Slate exposes a set of modules that you'll use to build your editor. The most important of which are the `Editor` class and the `<Editable>` component.
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Import the Slate editor factory.
|
// Import the Slate editor factory.
|
||||||
import { createEditor } from 'slate'
|
import { createEditor } from 'slate'
|
||||||
@@ -47,19 +45,44 @@ const App = () => {
|
|||||||
|
|
||||||
Of course we haven't rendered anything, so you won't see any changes.
|
Of course we haven't rendered anything, so you won't see any changes.
|
||||||
|
|
||||||
Next up is to render a `<Slate>` context provider.
|
Next we want to create state for `value` and `selection`:
|
||||||
|
|
||||||
The provider component keeps track of your Slate editor, its plugins, its default value, and any changes that occur. It **must** be rendered above any `<Editable>` components. But it can also provide the editor state to other components like toolbars, menus, etc. using the `useSlate` hook.
|
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
// Render the Slate editor context.
|
|
||||||
return <Slate editor={editor} />
|
// Keep track of state for the value and selection of the editor.
|
||||||
|
const [value, setValue] = useState([])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can think of the `<Slate>` component as provided an "un-controlled" editor context to every component underneath it.
|
Next up is to render a `<Slate>` context provider.
|
||||||
|
|
||||||
|
The provider component keeps track of your Slate editor, its plugins, its value, its selection, and any changes that occur. It **must** be rendered above any `<Editable>` components. But it can also provide the editor state to other components like toolbars, menus, etc. using the `useSlate` hook.
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
const App = () => {
|
||||||
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [value, setValue] = useState([])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
// Render the Slate context.
|
||||||
|
return (
|
||||||
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can think of the `<Slate>` component as providing a "controlled" context to every component underneath it.
|
||||||
|
|
||||||
This is a slightly different mental model than things like `<input>` or `<textarea>`, because richtext documents are more complex. You'll often want to include toolbars, or live previews, or other complex components next to your editable content.
|
This is a slightly different mental model than things like `<input>` or `<textarea>`, because richtext documents are more complex. You'll often want to include toolbars, or live previews, or other complex components next to your editable content.
|
||||||
|
|
||||||
@@ -70,9 +93,19 @@ Okay, so the next step is to render the `<Editable>` component itself:
|
|||||||
```jsx
|
```jsx
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [value, setValue] = useState([])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
return (
|
return (
|
||||||
// Add the editable component inside the context.
|
// Add the editable component inside the context.
|
||||||
<Slate editor={editor}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable />
|
<Editable />
|
||||||
</Slate>
|
</Slate>
|
||||||
)
|
)
|
||||||
@@ -81,28 +114,32 @@ const App = () => {
|
|||||||
|
|
||||||
The `<Editable>` component acts like `contenteditable`. Anywhere you render it will render an editable richtext document for the nearest editor context.
|
The `<Editable>` component acts like `contenteditable`. Anywhere you render it will render an editable richtext document for the nearest editor context.
|
||||||
|
|
||||||
There's only one last step. So far we haven't defined what the default value of the editor is, so it's empty. Let's fix that by defining an initial value.
|
There's only one last step. So far we've been using an empty `[]` array as the initial value of the editor, so it has no content. Let's fix that by defining an initial value.
|
||||||
|
|
||||||
The value is just plain JSON. Here's one containing a single paragraph block with some text in it:
|
The value is just plain JSON. Here's one containing a single paragraph block with some text in it:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Create our default value...
|
|
||||||
const defaultValue = [
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
text: 'A line of text in a paragraph.',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
// Add the initial value when setting up our state.
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Add the default value as a prop to the editor context.
|
<Slate
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable />
|
<Editable />
|
||||||
</Slate>
|
</Slate>
|
||||||
)
|
)
|
||||||
@@ -112,5 +149,3 @@ const App = () => {
|
|||||||
There you have it!
|
There you have it!
|
||||||
|
|
||||||
That's the most basic example of Slate. If you render that onto the page, you should see a paragraph with the text `A line of text in a paragraph.`. And when you type, you should see the text change!
|
That's the most basic example of Slate. If you render that onto the page, you should see a paragraph with the text `A line of text in a paragraph.`. And when you type, you should see the text change!
|
||||||
|
|
||||||
You'll notice that there is no `onChange` handler defined. That's because the `<Slate>` context acts like an **un-controlled** component, with the changes automatically being propagated to any context consumers. However, just like with un-controlled components you can attach an `onChange` prop to listen for changes. We'll cover that later.
|
|
||||||
|
@@ -11,8 +11,24 @@ Here's our app from earlier:
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable />
|
<Editable />
|
||||||
</Slate>
|
</Slate>
|
||||||
)
|
)
|
||||||
@@ -24,8 +40,24 @@ Now we add an `onKeyDown` handler:
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
// Define a new handler which prints the key that was pressed.
|
// Define a new handler which prints the key that was pressed.
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
@@ -46,8 +78,24 @@ Our `onKeyDown` handler might look like this:
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
if (event.key === '&') {
|
if (event.key === '&') {
|
||||||
|
@@ -9,8 +9,24 @@ We'll show you how. Let's start with our app from earlier:
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
if (event.key === '&') {
|
if (event.key === '&') {
|
||||||
@@ -60,6 +76,13 @@ Now, let's add that renderer to our `Editor`:
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
// Define a rendering function based on the element passed to `props`. We use
|
// Define a rendering function based on the element passed to `props`. We use
|
||||||
// `useCallback` here to memoize the function for subsequent renders.
|
// `useCallback` here to memoize the function for subsequent renders.
|
||||||
@@ -73,7 +96,15 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
// Pass in the `renderElement` function.
|
// Pass in the `renderElement` function.
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
@@ -109,6 +140,14 @@ import { Editor } from 'slate'
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (props.element.type) {
|
switch (props.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -119,7 +158,15 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
@@ -154,8 +201,15 @@ But we forgot one thing. When you hit `` Ctrl-` `` again, it should change the c
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [value, setValue] = useState(initialValue)
|
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (props.element.type) {
|
switch (props.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -166,20 +220,26 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
if (event.key === '`' && event.ctrlKey) {
|
if (event.key === '`' && event.ctrlKey) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
// Determine whether any of the currently selected blocks are code blocks.
|
// Determine whether any of the currently selected blocks are code blocks.
|
||||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||||
const isCodeActive = !!node
|
// Toggle the block type depending on whether there's already a match.
|
||||||
|
|
||||||
// Toggle the block type depending on `isCode`.
|
|
||||||
Editor.setNodes(
|
Editor.setNodes(
|
||||||
editor,
|
editor,
|
||||||
{ type: isCodeActive ? 'paragraph' : 'code' },
|
{ type: match ? 'paragraph' : 'code' },
|
||||||
{ match: 'block' }
|
{ match: 'block' }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,14 @@ So we start with our app from earlier:
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (props.element.type) {
|
switch (props.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -19,18 +27,25 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
if (event.key === '`' && event.ctrlKey) {
|
if (event.key === '`' && event.ctrlKey) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const { selection } = editor
|
const { selection } = editor
|
||||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||||
const isCodeActive = !!node
|
|
||||||
Editor.setNodes(
|
Editor.setNodes(
|
||||||
editor,
|
editor,
|
||||||
{ type: isCodeActive ? 'paragraph' : 'code' },
|
{ type: match ? 'paragraph' : 'code' },
|
||||||
{ match: 'block' }
|
{ match: 'block' }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -46,6 +61,14 @@ And now, we'll edit the `onKeyDown` handler to make it so that when you press `c
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (prop.element.type) {
|
switch (prop.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -56,7 +79,15 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
@@ -68,11 +99,10 @@ const App = () => {
|
|||||||
// When "`" is pressed, keep our existing code block logic.
|
// When "`" is pressed, keep our existing code block logic.
|
||||||
case '`': {
|
case '`': {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||||
const isCodeActive = !!node
|
|
||||||
Editor.setNodes(
|
Editor.setNodes(
|
||||||
editor,
|
editor,
|
||||||
{ type: isCodeActive ? null : 'code' },
|
{ type: match ? 'paragraph' : 'code' },
|
||||||
{ match: 'block' }
|
{ match: 'block' }
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
@@ -123,6 +153,14 @@ And now, let's tell Slate about that leaf. To do that, we'll pass in the `render
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (props.element.type) {
|
switch (props.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -138,7 +176,15 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
// Pass in the `renderLeaf` function.
|
// Pass in the `renderLeaf` function.
|
||||||
@@ -151,11 +197,10 @@ const App = () => {
|
|||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case '`': {
|
case '`': {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||||
const isCodeActive = !!node
|
|
||||||
Editor.setNodes(
|
Editor.setNodes(
|
||||||
editor,
|
editor,
|
||||||
{ type: isCodeActive ? null : 'code' },
|
{ type: match ? null : 'code' },
|
||||||
{ match: 'block' }
|
{ match: 'block' }
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
@@ -13,6 +13,14 @@ We'll start with our app from earlier:
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (props.element.type) {
|
switch (props.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -27,7 +35,15 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
renderLeaf={renderLeaf}
|
renderLeaf={renderLeaf}
|
||||||
@@ -39,11 +55,10 @@ const App = () => {
|
|||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case '`': {
|
case '`': {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const [node] = Editor.nodes(editor, { match: { type: 'code' } })
|
const [match] = Editor.nodes(editor, { match: { type: 'code' } })
|
||||||
const isCodeActive = !!node
|
|
||||||
Editor.setNodes(
|
Editor.setNodes(
|
||||||
editor,
|
editor,
|
||||||
{ type: isCodeActive ? null : 'code' },
|
{ type: match ? null : 'code' },
|
||||||
{ match: 'block' }
|
{ match: 'block' }
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
@@ -79,6 +94,14 @@ const withCustom = editor => {
|
|||||||
const App = () => {
|
const App = () => {
|
||||||
// Wrap the editor with our new `withCustom` plugin.
|
// Wrap the editor with our new `withCustom` plugin.
|
||||||
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (props.element.type) {
|
switch (props.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -93,7 +116,15 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
renderLeaf={renderLeaf}
|
renderLeaf={renderLeaf}
|
||||||
@@ -193,6 +224,14 @@ const CustomEditor = {
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (props.element.type) {
|
switch (props.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -207,16 +246,24 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable
|
<Editable
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
renderLeaf={renderLeaf}
|
renderLeaf={renderLeaf}
|
||||||
// Replace the `onKeyDown` logic with our new commands.
|
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
if (!event.ctrlKey) {
|
if (!event.ctrlKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace the `onKeyDown` logic with our new commands.
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case '`': {
|
case '`': {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -242,6 +289,14 @@ Now our commands are clearly defined and you can invoke them from anywhere we ha
|
|||||||
```js
|
```js
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
const editor = useMemo(() => withCustom(withReact(createEditor())), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const renderElement = useCallback(props => {
|
const renderElement = useCallback(props => {
|
||||||
switch (props.element.type) {
|
switch (props.element.type) {
|
||||||
case 'code':
|
case 'code':
|
||||||
@@ -256,8 +311,16 @@ const App = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Add a toolbar with buttons that call the same methods.
|
// Add a toolbar with buttons that call the same methods.
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
onMouseDown={event => {
|
onMouseDown={event => {
|
||||||
|
@@ -7,24 +7,26 @@ In this guide, we'll show you how to add logic to save your Slate content to a d
|
|||||||
Let's start with a basic editor:
|
Let's start with a basic editor:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import React, { useMemo } from 'react'
|
|
||||||
import { createEditor } from 'slate'
|
|
||||||
import { Slate, Editable, withReact } from 'slate-react'
|
|
||||||
|
|
||||||
const defaultValue = [
|
|
||||||
{
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
text: 'A line of text in a paragraph.',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate editor={editor} defaultValue={defaultValue}>
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
value={value}
|
||||||
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Editable />
|
<Editable />
|
||||||
</Slate>
|
</Slate>
|
||||||
)
|
)
|
||||||
@@ -50,11 +52,23 @@ const defaultValue = [
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
const [value, setValue] = useState([
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate
|
<Slate
|
||||||
editor={editor}
|
editor={editor}
|
||||||
defaultValue={defaultValue}
|
value={value}
|
||||||
onChange={value => {
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
|
|
||||||
// Save the value to Local Storage.
|
// Save the value to Local Storage.
|
||||||
const content = JSON.stringify(value)
|
const content = JSON.stringify(value)
|
||||||
localStorage.setItem('content', content)
|
localStorage.setItem('content', content)
|
||||||
@@ -71,25 +85,27 @@ Now whenever you edit the page, if you look in Local Storage, you should see the
|
|||||||
But... if you refresh the page, everything is still reset. That's because we need to make sure the initial value is pulled from that same Local Storage location, like so:
|
But... if you refresh the page, everything is still reset. That's because we need to make sure the initial value is pulled from that same Local Storage location, like so:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Update the initial content to be pulled from Local Storage if it exists.
|
|
||||||
const existingValue = JSON.parse(localStorage.getItem('content'))
|
|
||||||
const defaultValue = existingValue || [
|
|
||||||
{
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
text: 'A line of text in a paragraph.',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
// Update the initial content to be pulled from Local Storage if it exists.
|
||||||
|
const [value, setValue] = useState(
|
||||||
|
JSON.parse(localStorage.getItem('content')) || [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: 'A line of text in a paragraph.' }],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate
|
<Slate
|
||||||
editor={editor}
|
editor={editor}
|
||||||
defaultValue={defaultValue}
|
value={value}
|
||||||
onChange={value => {
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
const content = JSON.stringify(value)
|
const content = JSON.stringify(value)
|
||||||
localStorage.setItem('content', content)
|
localStorage.setItem('content', content)
|
||||||
}}
|
}}
|
||||||
@@ -131,20 +147,24 @@ const deserialize = string => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use our deserializing function to read the data from Local Storage.
|
|
||||||
const existingValue = localStorage.getItem('content')
|
|
||||||
const initialValue = deserialize(existingValue || '')
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const editor = useMemo(() => withReact(createEditor()), [])
|
const editor = useMemo(() => withReact(createEditor()), [])
|
||||||
|
const [selection, setSelection] = useState(null)
|
||||||
|
// Use our deserializing function to read the data from Local Storage.
|
||||||
|
const [value, setValue] = useState(
|
||||||
|
deserialize(localStorage.getItem('content')) || ''
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slate
|
<Slate
|
||||||
editor={editor}
|
editor={editor}
|
||||||
defaultValue={defaultValue}
|
value={value}
|
||||||
onChange={value => {
|
selection={selection}
|
||||||
|
onChange={(value, selection) => {
|
||||||
|
setValue(value)
|
||||||
|
setSelection(selection)
|
||||||
// Serialize the value and save the string value to Local Storage.
|
// Serialize the value and save the string value to Local Storage.
|
||||||
const content = serialize(value)
|
localStorage.setItem('content', serialize(value))
|
||||||
localStorage.setItem('content', content)
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Editable />
|
<Editable />
|
||||||
|
Reference in New Issue
Block a user