diff --git a/docs/concepts/06-commands.md b/docs/concepts/06-commands.md index 0caed05f6..cc23bbd32 100644 --- a/docs/concepts/06-commands.md +++ b/docs/concepts/06-commands.md @@ -44,6 +44,9 @@ Transforms are a specific set of helpers that allow you to perform a wide variet ```javascript // Set a "bold" format on all of the text nodes in a range. +// Normally you would apply a style like bold using the Editor.addMark() command. +// The addMark() command performs a similar setNodes transform, but it uses a more +// complicated match function in order to apply marks within markableVoid elements. Transforms.setNodes( editor, { bold: true }, diff --git a/docs/concepts/09-rendering.md b/docs/concepts/09-rendering.md index f6a79c8a2..61f3b5e64 100644 --- a/docs/concepts/09-rendering.md +++ b/docs/concepts/09-rendering.md @@ -37,7 +37,7 @@ const MyEditor = () => { } ``` -> 🤖 Be sure to mix in `props.attributes` and render `props.children` in your custom components! The attributes must be added to the top-level DOM element inside the component, as they are required for Slate's DOM helper functions to work. And the children are the actual text content of your document which Slate manages for you automatically. +> 🤖 Be sure to mix in `props.attributes` and render `props.children` in your custom components! The attributes must be added to the top-level DOM element inside the component, as they are required for Slate's DOM helper functions to work. And the children are the "leaves" holding text content and inline elements. You don't have to use simple HTML elements, you can use your own custom React components too: @@ -56,7 +56,7 @@ const renderElement = useCallback(props => { ## Leaves -When text-level formatting is rendered, the characters are grouped into "leaves" of text that each contain the same formatting applied to them. +When text-level formatting is rendered, the characters are grouped into "leaves" of text that each contain the same formatting (marks) applied to them. To customize the rendering of each leaf, you use a custom `renderLeaf` prop: @@ -78,6 +78,8 @@ const renderLeaf = useCallback(({ attributes, children, leaf }) => { Notice though how we've handled it slightly differently than `renderElement`. Since text formatting tends to be fairly simple, we've opted to ditch the `switch` statement and just toggle on/off a few styles instead. \(But there's nothing preventing you from using custom components if you'd like!\) +> 🤖 As with the Element renderer, be sure to mix in `props.attributes` and render `props.children` in your leaf renderer! The attributes must be added to the top-level DOM element inside the component, as they are required for Slate's DOM helper functions to work. And the children are the actual text content of your document which Slate manages for you automatically. + One disadvantage of text-level formatting is that you cannot guarantee that any given format is "contiguous"—meaning that it stays as a single leaf. This limitation with respect to leaves is similar to the DOM, where this is invalid: ```markup diff --git a/docs/walkthroughs/04-applying-custom-formatting.md b/docs/walkthroughs/04-applying-custom-formatting.md index bf6fd51c3..5213891fe 100644 --- a/docs/walkthroughs/04-applying-custom-formatting.md +++ b/docs/walkthroughs/04-applying-custom-formatting.md @@ -98,13 +98,7 @@ const App = () => { // When "B" is pressed, bold the text in the selection. case 'b': { event.preventDefault() - Transforms.setNodes( - editor, - { bold: true }, - // Apply it to text nodes, and split the text node up if the - // selection is overlapping only part of it. - { match: n => Text.isText(n), split: true } - ) + Editor.addMark(editor, 'bold', true) break } } @@ -115,9 +109,11 @@ const App = () => { } ``` +Unlike the code format from the previous step, which is a block-level format, bold is a character-level format. Slate manages text contained within blocks (or any other element) using "leaves". Slate's character-level formats/styles are called "marks". Adjacent text with the same marks (styles) applied will be grouped within the same "leaf". When we use `addMark` to add our bold mark to the selected text, Slate will automatically break up the "leaves" using the selection boundaries and produce a new "leaf" with the bold mark added. + Okay, so we've got the hotkey handler setup... but! If you happen to now try selecting text and hitting `Ctrl-B`, you won't notice any change. That's because we haven't told Slate how to render a "bold" mark. -For every format you add, Slate will break up the text content into "leaves", and you need to tell Slate how to read it, just like for elements. So let's define a `Leaf` component: +For every format you add, you need to tell Slate how to render it, just like for elements. So let's define a `Leaf` component: ```jsx // Define a React component to render leaves with bold text. @@ -189,11 +185,7 @@ const App = () => { case 'b': { event.preventDefault() - Transforms.setNodes( - editor, - { bold: true }, - { match: n => Text.isText(n), split: true } - ) + Editor.addMark(editor, 'bold', true) break } } diff --git a/docs/walkthroughs/05-executing-commands.md b/docs/walkthroughs/05-executing-commands.md index 080c39d17..478e592f8 100644 --- a/docs/walkthroughs/05-executing-commands.md +++ b/docs/walkthroughs/05-executing-commands.md @@ -60,11 +60,7 @@ const App = () => { case 'b': { event.preventDefault() - Transforms.setNodes( - editor, - { bold: true }, - { match: n => Text.isText(n), split: true } - ) + Editor.addMark(editor, 'bold', true) break } } @@ -83,12 +79,8 @@ We can instead implement these domain-specific concepts by creating custom helpe // Define our own custom set of helpers. const CustomEditor = { isBoldMarkActive(editor) { - const [match] = Editor.nodes(editor, { - match: n => n.bold === true, - universal: true, - }) - - return !!match + const marks = Editor.marks(editor) + return marks ? marks.bold === true : false }, isCodeBlockActive(editor) { @@ -101,11 +93,11 @@ const CustomEditor = { toggleBoldMark(editor) { const isActive = CustomEditor.isBoldMarkActive(editor) - Transforms.setNodes( - editor, - { bold: isActive ? null : true }, - { match: n => Text.isText(n), split: true } - ) + if (isActive) { + Editor.removeMark(editor, 'bold') + } else { + Editor.addMark(editor, 'bold', true) + } }, toggleCodeBlock(editor) { diff --git a/site/examples/hovering-toolbar.tsx b/site/examples/hovering-toolbar.tsx index 8f471cdb9..ac252ce2b 100644 --- a/site/examples/hovering-toolbar.tsx +++ b/site/examples/hovering-toolbar.tsx @@ -26,13 +26,13 @@ const HoveringMenuExample = () => { switch (event.inputType) { case 'formatBold': event.preventDefault() - return toggleFormat(editor, 'bold') + return toggleMark(editor, 'bold') case 'formatItalic': event.preventDefault() - return toggleFormat(editor, 'italic') + return toggleMark(editor, 'italic') case 'formatUnderline': event.preventDefault() - return toggleFormat(editor, 'underlined') + return toggleMark(editor, 'underlined') } }} /> @@ -40,21 +40,19 @@ const HoveringMenuExample = () => { ) } -const toggleFormat = (editor, format) => { - const isActive = isFormatActive(editor, format) - Transforms.setNodes( - editor, - { [format]: isActive ? null : true }, - { match: Text.isText, split: true } - ) +const toggleMark = (editor, format) => { + const isActive = isMarkActive(editor, format) + + if (isActive) { + Editor.removeMark(editor, format) + } else { + Editor.addMark(editor, format, true) + } } -const isFormatActive = (editor, format) => { - const [match] = Editor.nodes(editor, { - match: n => n[format] === true, - mode: 'all', - }) - return !!match +const isMarkActive = (editor, format) => { + const marks = Editor.marks(editor) + return marks ? marks[format] === true : false } const Leaf = ({ attributes, children, leaf }) => { @@ -141,8 +139,8 @@ const FormatButton = ({ format, icon }) => { return (