mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-04-21 22:02:05 +02:00
Use mark functions for docs and examples (#5441)
When documenting how to apply character-level styling, use `addMark` and `removeMark` instead of `setNodes`. This avoids new users creating code that only works in the simplest cases. Similarly, update the Hovering Toolbar example to apply marks instead of setting nodes. This change was prompted by a discussion on Slack where the developer was disappointed that `markableVoid` did not appear to be working. The problem was they were using `setNodes` to apply Marks, and did not use the same `match` function that `addMark` uses.
This commit is contained in:
parent
a94d64ec09
commit
5eb589dbbb
@ -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 },
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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 (
|
||||
<Button
|
||||
reversed
|
||||
active={isFormatActive(editor, format)}
|
||||
onClick={() => toggleFormat(editor, format)}
|
||||
active={isMarkActive(editor, format)}
|
||||
onClick={() => toggleMark(editor, format)}
|
||||
>
|
||||
<Icon>{icon}</Icon>
|
||||
</Button>
|
||||
|
Loading…
x
Reference in New Issue
Block a user