Skip to content

Commit ffe2494

Browse files
committed
docs: guides section
1 parent b2f25f5 commit ffe2494

8 files changed

Lines changed: 621 additions & 22 deletions

File tree

docs/docs/guides/chat-input-with-images.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/docs/guides/custom-context-menu.md

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,135 @@ sidebar_position: 4
44

55
# Custom context menu
66

7-
<!-- TODO: write content for this page -->
7+
The `contextMenuItems` prop lets you add your own actions to the native
8+
text-selection menu - the popover that e.g. shows **Copy / Paste / Cut** when the
9+
user long-presses selected text.
10+
11+
:::info
12+
13+
This is a **native-only** feature (iOS and Android) - which
14+
is why this page has no live preview. To see it in action, run the snippet below in the
15+
[example app](https://github.com/software-mansion/react-native-enriched).
16+
17+
:::
18+
19+
## The shape of an item
20+
21+
```ts
22+
interface ContextMenuItem {
23+
text: string; // the label shown in the menu
24+
visible?: boolean; // whether to show it (defaults to true)
25+
onPress: (args: {
26+
text: string; // the currently selected text
27+
selection: { start: number; end: number }; // its range
28+
styleState: OnChangeStateEvent; // active styles
29+
}) => void;
30+
}
31+
```
32+
33+
Every `onPress` receives the same three-field payload, resolved at the moment
34+
the item is tapped:
35+
36+
- **`text`** - the selected text.
37+
- **`selection`** - the `start` and `end` offsets of the selection.
38+
- **`styleState`** - the latest style state, the same object you get from
39+
`onChangeState`.
40+
41+
`visible` is read when the menu opens, so you can drive it from state to show an
42+
item only in the right context.
43+
44+
## Example
45+
46+
This editor adds three items. The first two read the selection payload and run an
47+
editor command. The third links the selection, so it's only shown via `visible`
48+
when there's actually a ranged selection to link:
49+
50+
```tsx
51+
import { EnrichedTextInput } from 'react-native-enriched-html';
52+
import type {
53+
ContextMenuItem,
54+
EnrichedTextInputInstance,
55+
OnChangeSelectionEvent,
56+
} from 'react-native-enriched-html';
57+
import { useMemo, useRef, useState } from 'react';
58+
import { View, StyleSheet, Alert } from 'react-native';
59+
60+
export default function App() {
61+
const ref = useRef<EnrichedTextInputInstance>(null);
62+
const [selection, setSelection] = useState<OnChangeSelectionEvent | null>(
63+
null
64+
);
65+
66+
const hasRangedSelection = !!selection && selection.start !== selection.end;
67+
68+
const contextMenuItems: ContextMenuItem[] = useMemo(
69+
() => [
70+
{
71+
// `text` and `selection` describe what the user long-pressed.
72+
text: 'Show selection',
73+
onPress: ({ text, selection: range }) => {
74+
Alert.alert(
75+
'Selection',
76+
`"${text}" at [${range.start}, ${range.end}]`
77+
);
78+
},
79+
},
80+
{
81+
// Menu items can call any editor command through the ref.
82+
text: 'Bold',
83+
onPress: () => {
84+
ref.current?.toggleBold();
85+
},
86+
},
87+
{
88+
// Only useful with a ranged selection, so hide it otherwise; when
89+
// shown, `selection` lets you target the exact range you were given.
90+
text: 'Link to Software Mansion',
91+
visible: hasRangedSelection,
92+
onPress: ({ text, selection: range }) => {
93+
ref.current?.setLink(
94+
range.start,
95+
range.end,
96+
text,
97+
'https://swmansion.com'
98+
);
99+
},
100+
},
101+
],
102+
[hasRangedSelection]
103+
);
104+
105+
return (
106+
<View style={styles.container}>
107+
<EnrichedTextInput
108+
ref={ref}
109+
style={styles.input}
110+
placeholder="Select some text, then long-press it..."
111+
contextMenuItems={contextMenuItems}
112+
onChangeSelection={(e) => setSelection(e.nativeEvent)}
113+
/>
114+
</View>
115+
);
116+
}
117+
118+
const styles = StyleSheet.create({
119+
container: { gap: 12 },
120+
input: {
121+
fontSize: 18,
122+
color: '#232736',
123+
padding: 12,
124+
borderRadius: 12,
125+
minHeight: 96,
126+
backgroundColor: '#eef0ff',
127+
},
128+
});
129+
```
130+
131+
:::note
132+
133+
Item placement differs per platform. On **iOS** your items appear in array
134+
order, before the system items (Copy/Paste/Cut). On **Android** there is no
135+
guaranteed order, and depending on the device manufacturer your items may be
136+
tucked into an overflow submenu.
137+
138+
:::

docs/docs/guides/emojis.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/docs/guides/emojis.mdx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
import InteractiveExample from '@site/src/components/InteractiveExample';
6+
import EmojiEditor from '@site/src/examples/EmojiEditor';
7+
import EmojiEditorSrc from '!!raw-loader!@site/src/examples/EmojiEditor';
8+
9+
# Emojis
10+
11+
Mentions are extremely powerful that you can build much more with. An emoji picker is just a mention with where its display text does not match its query. For the indicator, we'll use `:` - the user types `:smile`, picks from a list, and the shortcode is
12+
replaced with the emoji itself. If the mention events are new to you, start with
13+
[Mentions](/rich-text-formatting/mentions) and the
14+
[Mention-only input](/guides/mention-only-input) guide; this page reuses the
15+
exact same flow.
16+
17+
## A shortcode table
18+
19+
Map each shortcode to the glyph it inserts:
20+
21+
```tsx
22+
const EMOJIS = [
23+
{ shortcode: 'smile', char: '😄' },
24+
{ shortcode: 'heart', char: '❤️' },
25+
{ shortcode: 'fire', char: '🔥' },
26+
{ shortcode: 'rocket', char: '🚀' },
27+
];
28+
```
29+
30+
## Wiring it up
31+
32+
Register `:` as the only indicator and filter the table with the text typed
33+
after it. Because people habitually close the shortcode (`:smile:`), strip a
34+
trailing colon before matching:
35+
36+
```tsx
37+
<EnrichedTextInput
38+
mentionIndicators={[':']}
39+
onStartMention={() => setOpen(true)}
40+
onChangeMention={({ text }) => setQuery(text)}
41+
onEndMention={() => setOpen(false)}
42+
// ...
43+
/>;
44+
45+
const q = query.replace(/:$/, '').toLowerCase();
46+
const suggestions = EMOJIS.filter(e => e.shortcode.startsWith(q));
47+
```
48+
49+
When the user picks, `setMention` inserts the glyph as the mention's display
50+
text. The `data-shortcode` makes a handy attribute if you ever need to reconstruct it:
51+
52+
```tsx
53+
const pick = (emoji) => {
54+
ref.current?.setMention(':', emoji.char, {
55+
'data-shortcode': emoji.shortcode,
56+
});
57+
};
58+
```
59+
60+
Since the emoji glyph _is_ the whole mention, drop the usual highlight so it
61+
reads as plain text:
62+
63+
```tsx
64+
const htmlStyle = {
65+
mention: {
66+
':': {
67+
color: '#232736',
68+
backgroundColor: 'transparent',
69+
textDecorationLine: 'none',
70+
},
71+
},
72+
};
73+
```
74+
75+
:::info
76+
77+
If you want to see the whole code used to build this example, you can find it below by switching the tab from `Preview` to `Code`.
78+
79+
:::
80+
81+
## Try it out
82+
83+
Type `:` followed by a name - `:fire`, `:heart` - then tap a suggestion. The
84+
shortcode collapses into a single emoji you can place anywhere in the text.
85+
86+
<InteractiveExample src={EmojiEditorSrc} component={EmojiEditor} />

docs/docs/guides/mention-only-input.md

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
sidebar_position: 2
3+
---
4+
5+
import InteractiveExample from '@site/src/components/InteractiveExample';
6+
import MentionOnlyEditor from '@site/src/examples/MentionOnlyEditor';
7+
import MentionOnlyEditorSrc from '!!raw-loader!@site/src/examples/MentionOnlyEditor';
8+
9+
# User and channel mentions
10+
11+
This guide wires the mention events into a complete picker - the flow behind a
12+
chat composer where typing `@` suggests people and `#` suggests channels. If you
13+
haven't met the mention API yet, read
14+
[Mentions](/rich-text-formatting/mentions) first; here we move quickly and
15+
assume the events and `setMention` are familiar.
16+
17+
## The data behind a mention
18+
19+
Mentions are particularly useful if they point at something. Keep two lists around - one
20+
for users, one for channels - each item carrying an `id` you can attach
21+
to the finished mention:
22+
23+
```tsx
24+
const USERS = [
25+
{ id: 'u1', name: 'John Doe' },
26+
{ id: 'u2', name: 'Jane Smith' },
27+
{ id: 'u3', name: 'Alice Johnson' },
28+
{ id: 'u4', name: 'Bob Brown' },
29+
];
30+
31+
const CHANNELS = [
32+
{ id: 'c1', name: 'general' },
33+
{ id: 'c2', name: 'engineering' },
34+
{ id: 'c3', name: 'random' },
35+
{ id: 'c4', name: 'announcements' },
36+
];
37+
```
38+
39+
## Wiring the picker
40+
41+
Now let's register both indicators and the events callbacks:
42+
43+
```tsx
44+
<EnrichedTextInput
45+
mentionIndicators={['@', '#']}
46+
onStartMention={openPicker} // fired when '@' or '#' is typed
47+
onChangeMention={updateQuery} // fired on every keystroke after it
48+
onEndMention={closePicker} // fired when the mention is left
49+
// ...
50+
/>
51+
```
52+
53+
`onStartMention` hands you the indicator, so you know whether to show people or
54+
channels. `onChangeMention` hands you the `text` typed so far - filter your list
55+
with it. `onEndMention` fires when the cursor leaves the mention, so you dismiss
56+
the list.
57+
58+
When the user taps a suggestion, finish the mention with `setMention`. Pass the
59+
same indicator that started it, the display text, and
60+
the item's data as attributes:
61+
62+
```tsx
63+
const pick = (item) => {
64+
ref.current?.setMention(indicator, `${indicator}${item.name}`, {
65+
id: item.id,
66+
});
67+
};
68+
```
69+
70+
Now let's give each indicator its own look through
71+
`htmlStyle.mention`:
72+
73+
```tsx
74+
const htmlStyle = {
75+
mention: {
76+
'@': { color: '#2b7a4b', backgroundColor: '#d8f3e3' },
77+
'#': { color: '#2b5f9e', backgroundColor: '#d8e6f9' },
78+
},
79+
};
80+
```
81+
82+
:::info
83+
84+
If you want to see the whole code used to build this example, you can find it below by switching the tab from `Preview` to `Code`.
85+
86+
:::
87+
88+
## Try it out
89+
90+
Type `@` to filter people or `#` to filter channels, keep typing to narrow the
91+
list, then tap a suggestion.
92+
93+
<InteractiveExample src={MentionOnlyEditorSrc} component={MentionOnlyEditor} />
94+
95+
:::tip
96+
97+
A mention is only active while the editor is focused - if it blurs,
98+
`onEndMention` fires and `setMention` becomes a no-op. On native, tapping a
99+
suggestion never steals focus, so it just works. On web it does, so the rows
100+
call `preventDefault` on `mousedown` to keep the editor focused. The example
101+
wraps that in a small `keepEditorFocused` helper (see the Code tab).
102+
103+
:::
104+
105+
:::note
106+
107+
The attributes you pass to `setMention` (here `{ id }`) ride along in the HTML
108+
and survive a round-trip through `getHTML` / `setValue`. Prefix custom keys with
109+
`data-` if they need to outlive a sanitizer - see the note in
110+
[Mentions](/rich-text-formatting/mentions).
111+
112+
:::

0 commit comments

Comments
 (0)