Skip to content

Commit

Permalink
docs: add getting started docs (remirror#1134)
Browse files Browse the repository at this point in the history
docs: Add a "Getting started" section, and update README
  • Loading branch information
whawker authored Aug 23, 2021
1 parent 770f4d8 commit 5e0e70c
Show file tree
Hide file tree
Showing 11 changed files with 399 additions and 94 deletions.
34 changes: 34 additions & 0 deletions docs/extensions/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
hide_title: true
title: Overview
---

# All extensions

**[AnnotationExtension](./annotation-extension.md)**<br /> Allows the annotation (or highlighting) of the content in your editor.

**[BlockquoteExtension](./blockquote-extension.md)**<br /> Makes regions of text appear as an indented quote to ensure it stands out.

**[BoldExtension](./bold-extension.md)**<br /> Makes the text under the cursor / or at the provided position range **bold**.

**[CalloutExtension](./callout-extension.md)**<br /> Makes regions of text stand out on a colour background with optional emoji prefix.

**[CodeExtension](./code-extension.md)**<br /> Makes the text under the cursor / or at the provided position range `code`.

**[EmbedExtension](./embed-extension.md)**<br /> Adds iframe-based embeds to your text editor.

**[ItalicExtension](./italic-extension.md)**<br /> Makes the text under the cursor / or at the provided position range _italic_.

**[LinkExtension](./link-extension.md)**<br /> Makes the text under the cursor / or at the provided position range link to another resource.

**[MarkdownExtension](./markdown-extension.md)**<br /> Transforms the **ProseMirror** content of your editor to markdown syntax.

**[NodeFormattingExtension](./node-formatting-extension.md)**<br /> Enables you to align, index and set the line-height of content in your editor.

**[PlaceholderExtension](./placeholder-extension.md)**<br /> Shows a configurable placeholder when the **ProseMirror** content of your editor is empty

**[StrikeExtension](./strike-extension.md)**<br /> Makes the text under the cursor / or at the provided position range ~~strike~~.

**[TextColorExtension](./text-color-extension.md)**<br /> Makes the text under the cursor / or at the provided position range have the specified color.

**[UnderlineExtension](./underline-extension.md)**<br /> Makes the text under the cursor / or at the provided position range <u>underline</u>.
176 changes: 176 additions & 0 deletions docs/getting-started/commands-and-helpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
---
hide_title: true
title: Commands and helpers
---

# Commands and helpers

So now we have an editor, but how do we make changes to the document, and observe changes to save them to a backend?

Remirror providers **commands** and **helpers** to enable this. You can think of commands as _write_ actions, and helpers as _read_ actions.

## Commands

The only way to modify a Remirror document is via _commands_ - even when you are typing in a Remirror editor, you are creating `insertText` commands behind the scenes.

Commands are provided by extensions, and they may be triggered by input rules (i.e. `**bold**`), or keyboard shortcuts (i.e. `Ctrl/Cmd + B`).

They can also be triggered manually, this is useful when creating custom components that trigger behaviour. `@remirror/react` exposes the hooks `useCommands` or `useChainedCommands` for exactly this purpose.

### `useCommands`

```tsx
import React from 'react';
import { useActive, useCommands } from '@remirror/react';

export const BoldButton = () => {
// Access the commands individually
const { toggleBold, focus } = useCommands();
const active = useActive();

return (
<button
onClick={() => {
toggleBold();
focus();
}}
style={{ fontWeight: active.bold() ? 'bold' : undefined }}
>
B
</button>
);
};
```

### `useChainedCommands`

```tsx
import React from 'react';
import { useActive, useChainedCommands } from '@remirror/react';

export const BoldButton = () => {
// Using command chaining
const chain = useChainedCommands();
const active = useActive();

return (
<button
onClick={() => {
chain // Begin a chain
.toggleBold()
.focus()
.run(); // A chain must always be terminated with `.run()`
}}
style={{ fontWeight: active.bold() ? 'bold' : undefined }}
>
B
</button>
);
};
```

However be aware a command might not make sense in the current selection context - for instance, you cannot bold or italicise text within a code block, or you cannot change callout attributes, if the selected node is not a callout.

It is good UX practice to hide (or disable) actions that are not available at the moment. With Remirror you can check if a command is enabled using its `.enabled()` property.

In the example below we check if `toggleBold` is enabled, by checking the `toggleBold.enabled()` property. If not enabled, we disable the button.

```tsx
import React from 'react';
import { useActive, useCommands } from '@remirror/react';

export const BoldButton = () => {
// Access the commands and the activity status of the editor.
const { toggleBold } = useCommands();
const active = useActive();

return (
<button
onClick={() => toggleBold()}
disabled={toggleBold.enabled() === false}
style={{ fontWeight: active.bold() ? 'bold' : undefined }}
>
B
</button>
);
};
```

`toggleBold` is a very simple command with no arguments, others are more complex and expect arguments, like `insertImage`

```ts
insertImage({ src: 'https://web.site/image.png' });
```

In these cases it is good practice to pass the same arguments to `.enabled`, to ensure accurate results.

```ts
insertImage.enabled({ src: 'https://web.site/image.png' });
```

:::note

#### How this works (advanced)

Commands and there `.enabled` property execute the **exact same code** - the difference is whether they are _dispatched_ to the document or not.

Commands must do 2 things

1. Return `true` or `false`, to indicate whether they are enabled right now.
2. Check whether the `dispatch` property is present.
- If `dispatch` **is** present, the command will modify the document.
- If `dispatch` is **not** present, the command will **not** modify the document (this is the `.enabled()` case, checking if we _could_ execute the command).

[Example here](https://github.com/remirror/remirror/blob/2666698102afd15d118a1e0bdf5c983fec0ba103/packages/remirror__extension-embed/src/iframe-extension.ts#L154-L170)

:::note

## Helpers

Helpers allow you to read state from the Remirror document, they are provided by extensions, just as commands are.

`@remirror/react` exposes the hook `useHelpers` to enable you to create custom components that use document state.

For instance if you want to save your Remirror document when the user presses `Ctrl/Cmd + S`, you could use a helper to extract the JSON representation of the document state.

```tsx
import React, { useCallback } from 'react';
import { BoldExtension } from 'remirror/extensions';
import { Remirror, useHelpers, useKeymap, useRemirror } from '@remirror/react';

// Hooks can be added to the context without the need for creating custom components
const hooks = [
() => {
const { getJSON } = useHelpers();

const handleSaveShortcut = useCallback(
(props) => {
const { state } = props;
saveToBackend(getJSON(state));

return true; // Prevents any further key handlers from being run.
},
[getJSON],
);

// "Mod" means platform agnostic modifier key - i.e. Ctrl on Windows, or Cmd on MacOS
useKeymap('Mod-s', handleSaveShortcut);
},
];

const Editor = () => {
const { manager, state } = useRemirror({ extensions: () => [new BoldExtension()] });

// Using the hooks prop to inject functionality that doesn't require rendered elements
return <Remirror manager={manager} initialContent={state} hooks={hooks} />;
};
```

Here are a few useful helpers, but there are many more!

| Helper | Extension | Description |
| ------------- | -------------------- | ------------------------------------------------- |
| `getJSON` | (Built in) | JSON representation (not stringified) |
| `getText` | (Built in) | Plain text representation (maintains line breaks) |
| `getHTML` | (Built in) | HTML representation (using schema `toDOM`) |
| `getMarkdown` | `markdown-extension` | Markdown representation (where possible) |
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ title: Create an editor

Creating an editor consists of two steps.

1. Creating the wrapper with `Remirror` which sets up the editor functionality by passing in the extension manager.
1. Creating a manager, with extensions for your desired functionality (see previous guide).
2. Rendering the `Remirror` component, by passing in the extension manager.

```tsx
import 'remirror/styles/all.css';
Expand All @@ -18,7 +19,7 @@ import { Remirror, useRemirror } from '@remirror/react';

const extensions = () => [new BoldExtension(), new ItalicExtension(), new UnderlineExtension()];

const Editor = () => {
export const MyEditor = () => {
const { manager } = useRemirror({ extensions });

return (
Expand All @@ -30,21 +31,24 @@ const Editor = () => {
};
```

The above editor created automatically renders the editor into the dom within a div. Everything you type will be passed through with keybindings for the provided extensions.
The above editor automatically renders an editor into the DOM within a div. Everything you type will be passed through with keybindings for the provided extensions.

### Overriding defaults

It's likely that you want a bit more control. This is why the `Remirror` components provides the `RemirrorContext` to all children components. When there are no children provided it automatically renders as above. But once we provide children we can take full control.
It's likely that you want a bit more control, you likely want a menu for instance. When there are no children provided to the `Remirror` component it automatically renders as above. But once we provide children we can take full control.

This is why the `Remirror` component provides the `RemirrorContext` to all children components.

Let's create a menu and custom div to render the editor into.

```tsx
import React from 'react';
import { useActive, useCommands } from '@remirror/react';

const Menu = () => {
export const Menu = () => {
// Access the commands and the activity status of the editor.
const commands = useCommands();
// Whether bold/italic etc is active at the current selection.
const active = useActive();

return (
Expand Down Expand Up @@ -81,11 +85,11 @@ import React from 'react';
import { BoldExtension, ItalicExtension, UnderlineExtension } from 'remirror/extensions';
import { EditorComponent, Remirror, useRemirror } from '@remirror/react';

import { Menu } from './editor';
import { Menu } from './my-menu';

const extensions = () => [new BoldExtension(), new ItalicExtension(), new UnderlineExtension()];

const Editor = () => {
export const MyEditor = () => {
const { manager } = useRemirror({ extensions });

return (
Expand All @@ -98,38 +102,6 @@ const Editor = () => {
};
```

### Setting initial content

To set the initial content for the editor you can pass additional properties to the `useRemirror` hook. Initial content is only supported in uncontrolled components. You can find the docs for creating controlled components [here](./controlled.md).

```tsx
import React from 'react';
import { BoldExtension, ItalicExtension, UnderlineExtension } from 'remirror/extensions';
import { Remirror, useRemirror } from '@remirror/react';

import { Menu } from './editor';

const extensions = () => [new BoldExtension(), new ItalicExtension(), new UnderlineExtension()];

const Editor = () => {
const { manager, state } = useRemirror({
extensions,

// Set the initial content.
content: '<p>Initial content</p>',

// Place the cursor at the start of the document. This an also be set to
// `end`, `all` or a numbered position.
selection: 'start',

// Set the string handler which means the content provided will be
// automatically handled as html. `markdown` is also available when the
// `MarkdownExtension` is added to the editor.
stringHandler: 'html',
});

return <Remirror manager={manager} initialContent={state} />;
};
```
Notice the `EditorComponent` above, this component was automatically rendered in the first example, but now we are rendering it ourselves to have full control of the DOM structure.

The initial content can also be set to a `RemirrorJSON` object which matches this shape of the data provided by remirror. It's up to you how you store the data.
Rendering components as children of the `Remirror` component, that utilise `RemirrorContext` via hooks allows you to create very customised and powerful experiences.
Loading

0 comments on commit 5e0e70c

Please sign in to comment.