Rich text
The rich text plugin's block kinds, marks, Notion hotkeys, placeholders and formatting helpers, including link and color sanitization.
richTextPlugin defines the everyday document: paragraphs, headings, lists, to-dos, toggles, callouts, quotes, dividers, and ten marks, with Notion’s shortcuts. The core defines no block kinds, so <Edytor> includes it by default, after your plugins:
<script lang="ts">
import { Edytor } from 'edytor';
</script>
<!-- Rich text, with no plugins listed. -->
<Edytor />
It takes no options. List it yourself only to place it elsewhere in the order, or with defaultPlugins={false} (see Default plugins). To change one of its kinds, define a kind with the same name in any of your plugins, since they come before it, or override only its snippet (see Blocks).
Block kinds
| Kind | Element | Data | Presets (markdown) |
|---|---|---|---|
paragraph |
div with a p inside |
none | Text |
heading |
h1 to h3 from level |
{ level: 'h1' } |
Heading 1 (# ), Heading 2 (## ), Heading 3 (### ) |
bulleted-list-item |
li |
none | Bulleted list (- , * , + ) |
numbered-list-item |
li |
none | Numbered list (1. , a. , i. ) |
todo-item |
div with a checkbox |
{ checked: boolean } |
To-do list ([ ] , [] ) |
toggle |
details, content in summary |
none | Toggle list (> ) |
callout |
div with an icon |
{ icon: string } |
Callout (icon 💡) |
quote |
blockquote |
none | Quote (" ) |
divider |
hr, void, no content |
none | Divider (---) |
The kinds are listed in catalogue order, which the menus follow. Presets feed the slash menu (all under Basic blocks), markdown shortcuts and block menus. Their command ids are block.paragraph, block.heading1 to block.heading3, block.bulleted-list-item, block.numbered-list-item, block.todo-item, block.toggle, block.callout, block.quote and block.divider.
The plugin also defines kinds without presets, for content that arrives by paste or from stored documents:
ordered-listandunordered-list:olandulcontainers that render only their children. A new child takes thelist-itemkind.list-item: anli.details: the same astoggle.horizontalRule: the same asdivider.
Behavior worth knowing:
- Bulleted and numbered items, to-dos and toggles are continuing kinds: Enter opens another of the same kind (an unchecked to-do), and in an empty one ends the list. Toggles, callouts and quotes are containers: Enter at the end of one with children, or of an open toggle, opens a first child, and in the middle of the header moves the rest of the text into that first child. Backspace at the start of any kind but a paragraph turns it into a paragraph first. Enter and Backspace by role has the full rules.
- Headings have Notion’s three levels. A
heading’s element followsdata.level,h1toh3, and so does its copied HTML. Without a level it ish1; any other level (a storedh5, say) renders and copies ash3, as pastingh4toh6gives a levelh3heading. - A
toggleis a native<details>. The browser owns itsopenattribute, so opening and closing it is not an edit and is never reverted. Arrow keys skip the body of a closed toggle. - The
todo-itemcheckbox (data-edytor-todo-checkbox) showsdata.checked. Clicking it, or Mod + Enter in the to-do, flipscheckedas one undo step. It does not change in a readonly editor. - A
calloutshowsdata.iconin adata-edytor-callout-iconspan,💡when it has none. - The
paragraphelement carries no classes of its own: style it through[data-edytor-type='paragraph']. - Pasted
ol > libecomes a numbered item and otherlia bulleted item.
Marks
| Mark | Element | Value | Toolbar button |
|---|---|---|---|
bold |
strong |
true |
B |
italic |
em |
true |
I |
underline |
u |
true |
U |
strike |
s |
true |
S |
code |
code |
true |
</> |
link |
a with href and target |
{ href, target? } |
Link panel |
superscript |
sup |
true |
|
subscript |
sub |
true |
|
color |
span with color |
a CSS color | A ⌄ (text) |
highlight |
span with background-color |
a CSS color | A ⌄ (background) |
Marks nest in this order, bold innermost. Typing at the end of a link extends it only when the caret is inside the link; typing at the edge of any other mark extends it.
Values are sanitized when rendered and copied. A link href keeps only http:, https:, mailto:, tel: and scheme-less URLs; any other scheme, such as javascript:, drops the href and leaves the text. A color that could inject CSS (it contains ;, braces, quotes, url( and similar) drops the style.
Pasted HTML maps b, i, u, strike and del to their marks, reads Google Docs’ styled spans for bold, italic, underline, strike, color and highlight, and turns mark into a yellow highlight.
Hotkeys
| Keys | Action |
|---|---|
| Mod + B | Toggle bold |
| Mod + I | Toggle italic |
| Mod + U | Toggle underline |
| Mod + E | Toggle inline code |
| Mod + Shift + S, Mod + Shift + X | Toggle strikethrough |
| Mod + Shift + H | Toggle red text color |
| Mod + Enter | Check or uncheck the to-do (in a to-do only) |
With a collapsed caret, a mark hotkey sets the mark for the next characters you type instead.
Notion’s “turn into” chords convert the block holding the caret, or, as in Notion, every block of a block selection or of a text range across blocks, as one undo step that keeps the selection (Code converts only the first):
| Keys | Turns into |
|---|---|
| Mod + Alt + 0 | Text |
| Mod + Alt + 1 | Heading 1 |
| Mod + Alt + 2 | Heading 2 |
| Mod + Alt + 3 | Heading 3 |
| Mod + Alt + 4 | To-do list |
| Mod + Alt + 5 | Bulleted list |
| Mod + Alt + 6 | Numbered list |
| Mod + Alt + 7 | Toggle list |
| Mod + Alt + 8 | Code (with the code plugin); a block with text or children keeps them, and the code block is inserted after it |
Each runs the kind’s command (block.paragraph, block.heading1, …, block.code) and is claimed only when that command is registered.
The plugin also handles the formatting input types browsers send from their own menus and from iOS: bold, italic, underline, strikethrough, superscript, subscript, remove formatting, text and background color, insert link, ordered and unordered list (converts the block), and horizontal rule (inserts a divider at the caret, splitting the block when the caret is inside it).
Formatting from code
richTextOperations(edytor) returns the helpers the hotkeys and the toolbar use. They act on the current selection:
import { richTextOperations } from 'edytor';
const format = richTextOperations(edytor);
format.setMarkAtRange('bold'); // toggle, or stage at a caret
format.setMarkValueAtRange('highlight', '#fff3a3'); // set, never toggle
format.removeMarkAtRange('highlight'); // remove one mark
format.setLinkAtRange({ href: 'https://svelte.dev', target: '_blank' });
format.removeLinkAtRange();
format.removeAllMarksAtRange();
format.insertDividerAtSelection();
setLinkAtRange ignores an unsafe href. setMarkValueAtRange ignores an unsafe color. removeMarkAtRange does nothing at a collapsed caret.
Placeholders
richTextPlaceholder gives the kinds Notion’s placeholders (“Heading 1”, “List”, “To-do”, “Type ‘/’ for commands” in a focused paragraph…). Pass it to <Edytor placeholder={richTextPlaceholder} />. See Placeholder.