Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion docs/docs/core-functionalities/handling-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,56 @@ sidebar_position: 3

# Handling events

<!-- TODO: write content for this page -->
Since the input is [uncontrolled](/fundamentals/core-concepts#the-input-is-uncontrolled),
events are how you observe it. You change content by calling ref methods; you
react to changes by listening to the callbacks below.

Full payload shapes of the available callbacks can be found in the `EnrichedTextInput` reference.

## Content

- **`onChangeText`** - plain-text content changed.
- **`onChangeHtml`** - the HTML changed.

:::tip

The `onChangeHtml` callback has to parse the content into HTML on every keystroke.
This is a heavy computational operation that might slow down your app's performance. Consider using the `getHTML()` ref method instead if it meets your requirements.

:::

## Selection and style state

- **`onChangeSelection`** - the cursor moved or the selection changed. Gives you
`start`, `end`, and the selected `text`. Useful for range-based methods like
[`setLink`](/rich-text-formatting/links).
- **`onChangeState`** - the active styles at the cursor changed. This is the
event that drives a toolbar by using reported `isActive`, `isBlocking`, and
`isConflicting`, plus the current `alignment`. See the
[style state model](/fundamentals/core-concepts#the-style-state-model).

## Focus

- **`onFocus`** / **`onBlur`** - the input gained or lost focus.

## Mentions

- **`onStartMention`** - a mention started being edited.
- **`onChangeMention`** - the query after the indicator changed.
- **`onEndMention`** - editing a mention stopped.
- **`onMentionDetected`** - the cursor entered or left a mention.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be worth mentioning that when the cursor leaves a mention, the event fires with empty values so you can clean up any related state?


## Links

- **`onLinkDetected`** - the cursor entered or left a link.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add some info about onLinkDetected cleanup event, the same as for mentions


## Images

- **`onPasteImages`** - the user pasted one or more images; hands you each
image's data so you can upload and insert them with
[`setImage`](/rich-text-formatting/inline-images).

## Keyboard and submission

- **`onKeyPress`** - a key was pressed.
- **`onSubmitEditing`** - the user presses return/enter key. Fires when `submitBehavior` is set to either `submit` or `blurAndSubmit`.
7 changes: 0 additions & 7 deletions docs/docs/core-functionalities/rendering-rich-text.md

This file was deleted.

81 changes: 81 additions & 0 deletions docs/docs/core-functionalities/rendering-rich-text.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
sidebar_position: 2
---

import InteractiveExample from '@site/src/components/InteractiveExample';
import RenderingEditor from '@site/src/examples/RenderingEditor';
import RenderingEditorSrc from '!!raw-loader!@site/src/examples/RenderingEditor';

# Rendering rich text

`EnrichedTextInput` is for editing. To _display_ rich text without an editor -
a chat message, a comment, an article - use its read-only counterpart,
**`EnrichedText`**.

Both components speak the same [HTML format](/fundamentals/html-format-and-supported-tags),
so the typical flow is: edit in `EnrichedTextInput`, persist the
`getHTML` output, and later feed that
string to `EnrichedText`.

## Passing content

`EnrichedText` takes the HTML string as its `children`:

```tsx
import { EnrichedText } from 'react-native-enriched-html';

<EnrichedText>{'<p>Hello <b>world</b></p>'}</EnrichedText>;
```

## Styling

Styling mirrors the input. `style` controls the container and base typography,
and `htmlStyle` controls per-element appearance. `EnrichedText` extends
`htmlStyle` with **press states** for interactive elements, since links and
mentions are pressable here:

```tsx
<EnrichedText
style={{ fontSize: 16, color: '#232736' }}
htmlStyle={{
a: { pressColor: '#1e40af' },
mention: { pressColor: '#16a34a', pressBackgroundColor: '#dcfce7' },
}}>
{html}
</EnrichedText>
```

The added `pressColor` / `pressBackgroundColor` fields on `a` and `mention` are
the only shape difference from the input's `htmlStyle`. See the
`EnrichedText` reference for the full type.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that EnrichedText should be link to: api-reference/enriched-text ?


## Notable props

- **`selectable`** - allow the user to select and copy the rendered text.
Defaults to `false`.
- **`onLinkPress` / `onMentionPress`** - fire when a link or mention is pressed.
- **`numberOfLines` / `ellipsizeMode`** - truncate long content to a fixed
number of lines with an ellipsis.
- **`useHtmlNormalizer`** - normalize external or messy HTML into the library's canonical
tag subset before rendering. Defaults to `true`. See
[Normalization](/fundamentals/core-concepts#normalization).

:::note

On web, the default behavior of the pressed `<a>` tag is suppressed. To navigate to the link's URL, you need to properly handle the `onLinkPress` event.

:::

## Try it out

Format some text in the editor, then press **Render** - the current HTML is read
with `getHTML()` and handed to an `EnrichedText` below.

<InteractiveExample src={RenderingEditorSrc} component={RenderingEditor} />

:::caution

On iOS and Android, `EnrichedText` does not sanitize HTML for you. Sanitize anything you render that
came from users or other untrusted sources. To know more about the web's built-in sanitization, visit [Web support](/core-functionalities/web-support#sanitization).

:::
78 changes: 77 additions & 1 deletion docs/docs/core-functionalities/styling-the-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,80 @@ sidebar_position: 1

# Styling the input

<!-- TODO: write content for this page -->
`EnrichedTextInput` is styled through two separate props. Together they cover
everything from the container's dimensions down to the color of a bullet point.

- **`style`** - the container's layout behavior and its base typography (`fontSize`, `color`, `fontFamily`, …). It accepts a subset of React Native's `TextStyle`, described by
`EnrichedInputStyle`.
- **`htmlStyle`** - the appearance of individual rich text elements: heading
sizes, blockquote borders, code colors, list markers, mention colors, and so
on.

```tsx
<EnrichedTextInput
style={{
fontSize: 16,
color: '#232736',
padding: 12,
borderRadius: 12,
backgroundColor: '#eef0ff',
}}
htmlStyle={{
h1: { fontSize: 28, bold: true },
blockquote: { borderColor: '#57b495', borderWidth: 3 },
code: { color: '#c026d3' },
}}
/>
```

## `htmlStyle`

`htmlStyle` maps each supported element to a small config object. Anything you
omit falls back to the built-in default. The available keys are:

| Key | Styles | Notable options |
| ------------ | -------------- | ----------------------------------------------------------- |
| `h1`–`h6` | Headings | `fontSize`, `bold` |
| `blockquote` | Blockquote | `borderColor`, `borderWidth`, `gapWidth`, `color` |
| `codeblock` | Code block | `color`, `backgroundColor`, `borderRadius` |
| `code` | Inline code | `color`, `backgroundColor` |
| `a` | Links | `color`, `textDecorationLine` |
| `mention` | Mentions | `color`, `backgroundColor`, `textDecorationLine` |
| `ol` | Ordered list | `markerColor`, `markerFontWeight`, `marginLeft`, `gapWidth` |
| `ul` | Unordered list | `bulletColor`, `bulletSize`, `marginLeft`, `gapWidth` |
| `ulCheckbox` | Checkbox list | `boxColor`, `boxSize`, `marginLeft`, `gapWidth` |

The full list of properties, defaults, and platform notes lives in the
`EnrichedTextInput` reference.

### Styling mentions per indicator

`mention` accepts either a single config applied to every mention, or a record
keyed by [indicator](/rich-text-formatting/mentions) so each mention type gets
its own look:

```tsx
htmlStyle={{
mention: {
'@': { color: '#2563eb', backgroundColor: '#dbeafe' },
'#': { color: '#16a34a', backgroundColor: '#dcfce7' },
},
}}
```

:::tip

You can also create a default `mention` style config, by using the `'default'` key.

```tsx
htmlStyle={{
mention: {
'default': { color: '#2563eb', backgroundColor: '#dbeafe' },
'#': { color: '#16a34a', backgroundColor: '#dcfce7' },
},
}}
```

This way you can create a style for any mention indicator to fallback if it doesn't have one fully defined.

:::
91 changes: 90 additions & 1 deletion docs/docs/core-functionalities/web-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,93 @@ sidebar_position: 4

# Web support

<!-- TODO: write content for this page -->
Both `EnrichedTextInput` and `EnrichedText` run on the web. On native the editor
is backed by the platform's text engine; on the web it is built on
[Tiptap](https://tiptap.dev/) (on top of ProseMirror). That implementation
detail stays behind the same public API.

## One API across platforms

The web build exposes the **same props, ref methods, and events** as native.
Events keep their native shape too - they arrive as
`NativeSyntheticEvent`, read off `e.nativeEvent`, so
[event-handling](/core-functionalities/handling-events) code is portable as-is:

```tsx
<EnrichedTextInput
onChangeHtml={e => setHtml(e.nativeEvent.value)}
onChangeState={e => setState(e.nativeEvent)}
/>
```

The interactive examples throughout these docs are the web build running live.

## Keyboard shortcuts

The web editor ships desktop-style formatting shortcuts out of the box, along
with native browser **undo/redo**. `Mod` is `⌘` on macOS and `Ctrl` on
Windows/Linux.
Comment on lines +30 to +32

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The web editor ships desktop-style formatting shortcuts out of the box, along
with native browser **undo/redo**. `Mod` is `` on macOS and `Ctrl` on
Windows/Linux.
The web editor ships desktop-style formatting shortcuts out of the box, along
with native browser **undo/redo**.

I think that we don't need this info about Mod


| Action | macOS | Windows / Linux |
| ------------------- | -------------- | --------------------------- |
| Bold | `⌘B` | `Ctrl+B` |
| Italic | `⌘I` | `Ctrl+I` |
| Underline | `⌘U` | `Ctrl+U` |
| Strikethrough | `⌘⇧X` | `Ctrl+Shift+X` |
| Inline code | `⌘⇧C` | `Ctrl+Shift+C` |
| Code block | `⌘⌥⇧C` | `Ctrl+Alt+Shift+C` |
| Normal paragraph | `⌘⌥0` | `Ctrl+Alt+0` |
| Heading 1–6 | `⌘⌥1` – `⌘⌥6` | `Ctrl+Alt+1` – `Ctrl+Alt+6` |
| Numbered list | `⌘⇧7` | `Ctrl+Shift+7` |
| Unordered list | `⌘⇧8` | `Ctrl+Shift+8` |
| Checkbox list | `⌘⇧9` | `Ctrl+Shift+9` |
| Paste as plain text | `⌘⇧V` | `Ctrl+Shift+V` |
| Undo | `⌘Z` | `Ctrl+Z` |
| Redo | `⌘⇧Z` | `Ctrl+Shift+Z` |
| Select all | `⌘A` | `Ctrl+A` |

## Platform differences

A few native-only features have no web equivalent and are ignored there:

- **`contextMenuItems`** - the native editing menu isn't available; use your own
UI instead.
- **`returnKeyLabel`** - can't be set inside a browser. `returnKeyType` maps to
the browser's [`enterkeyhint`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint).
- **RN layout ref methods** - `measure`, `measureInWindow`, `measureLayout`, and
`setNativeProps` are no-ops.

The [`EnrichedTextInput`](/api-reference/enriched-text-input) and
[`EnrichedText`](/api-reference/enriched-text) references note per-prop platform
support.

:::note

On web, `onPasteImages` gives each image a `blob:` URL. If you hold onto those
URIs, call `URL.revokeObjectURL(uri)` once you're done with them (e.g. after an
upload) so the browser can release the memory.

:::

## Sanitization

Unlike the native platforms, the web build sanitizes HTML for you. It runs
[DOMPurify](https://github.com/cure53/DOMPurify) at **every entrypoint** - the
`children` of `EnrichedText`, and `defaultValue`, `setValue`, and pasted content
on `EnrichedTextInput`. Sanitization is also run on the input component's **output** - `getHTML()`.
This ensures untrusted markup can't inject scripts or unsafe attributes into the DOM.
:::caution

Sanitization is tied to link detection: the [`linkRegex`](/rich-text-formatting/links)
you provide determines which `href` values are allowed to survive on `<a>`
elements. Anchors whose URLs don't match the pattern have their `href` stripped
during sanitization.

:::

## Server-side rendering

The library does **not** support SSR. Normalization and sanitization both need a
DOM to work against, which isn't available during server rendering. In an SSR
framework (Next.js, Remix, …), make sure both `EnrichedTextInput` and
`EnrichedText` render **client-side only**.
Loading
Loading