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
7 changes: 0 additions & 7 deletions docs/docs/core-functionalities/handling-events.md

This file was deleted.

67 changes: 67 additions & 0 deletions docs/docs/core-functionalities/handling-input-events.md
Comment thread
hejsztynx marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
sidebar_position: 3
---

# Handling input events

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.

:::note

This page covers `EnrichedTextInput` events. The read-only
[`EnrichedText`](/core-functionalities/rendering-rich-text) component only
exposes `onLinkPress` and `onMentionPress` callbacks.

:::

## 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.
Comment thread
kacperzolkiewski marked this conversation as resolved.

## Links

- **`onLinkDetected`** - the cursor entered or left a link.
Comment thread
kacperzolkiewski marked this conversation as resolved.

## 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 pressed return/enter key. Fired 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.

82 changes: 82 additions & 0 deletions docs/docs/core-functionalities/rendering-rich-text.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
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`](/api-reference/enriched-text) reference for the full type.

## Notable props

- **`selectable`** - allow the user to select and copy the rendered text.
Comment thread
hejsztynx marked this conversation as resolved.
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).
- **`allowFontScaling`** - whether to respect the system's accessibility font scaling settings.

:::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).

:::
95 changes: 94 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,97 @@ 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
Comment thread
hejsztynx marked this conversation as resolved.
`EnrichedInputStyle`.
Comment thread
szydlovsky marked this conversation as resolved.
- **`htmlStyle`** - the appearance of individual rich text elements: heading
sizes, blockquote borders, code colors, list markers, mention colors, and so
on.
- **`placeholderTextColor`** - the color of the placeholder text.
- **`selectionColor`** - the color of the text selection highlight.
- **`cursorColor`** - the color of the text cursor.
Comment thread
hejsztynx marked this conversation as resolved.

:::note

`cursorColor` is not supported on iOS. For more platform differences, see [Compatibility](/misc/compatibility).

:::

```tsx

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.

Can it be interactive so users can change props in code tab and immediately observe results?

<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' },
}}
/>
```

## `style`

`style` accepts a subset of React Native's `TextStyle` - layout, appearance,
and base typography - described by `EnrichedInputStyle`. Most of these map directly
to their React Native `TextStyle` counterparts. Some are platform-limited
(e.g. `shadowColor` is iOS-only, `elevation` is Android-only) - see the
`EnrichedTextInput` reference for the full property list.

## `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.

:::
89 changes: 88 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,91 @@ 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/). That implementation
detail stays behind the same public API.

## One API across platforms

The web build exposes the same core API as native - **props, ref methods, and events** -
with one web-only addition - `sanitizationConfig` prop.
Events keep their native shape too - they arrive as
`NativeSyntheticEvent`, read off `e.nativeEvent`, so
[event-handling](/core-functionalities/handling-input-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**.

| 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`, `defaultValue`, `setValue` and content pasted into `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.
Comment thread
hejsztynx marked this conversation as resolved.

:::note

Sanitization can be customized with the `sanitizationConfig` prop. It allows you to set a `linkRegex` that
will define, which URI-containing attributes will actually be preserved by the sanitizer.

:::

## Server-side rendering

The library does **not** support SSR yet. 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