Skip to content

Commit a5dfe36

Browse files
Merge pull request #51 from DHTMLX/sp-next-v-2-1
Changes before relese v2.1
2 parents 10ef19d + 121407a commit a5dfe36

20 files changed

Lines changed: 1079 additions & 19 deletions

docs/api/config/image-upload-url.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,19 @@ description: You can learn about the imageUploadUrl config in the documentation
88

99
### Description
1010

11-
@short: Optional. Specifies the URL which will be used for image upload
11+
@short: Optional. Specifies the URL which will be used for image upload (from the toolbar, menubar, clipboard paste, or drag-and-drop)
12+
13+
When the property is set, RichText uploads each inserted image to the given endpoint and inserts the URL returned by the server.
14+
15+
When the property is omitted or set to a falsy value (`""`, `null`, `undefined`), RichText switches to **inline mode**: the image file is read on the client and embedded directly into the content as a base64 data URL — no server is required. Inline images larger than 1024×800 are proportionally downscaled to fit within these limits.
16+
17+
:::note
18+
Inline (base64) images are not preserved by the built-in DOCX / PDF [export](api/events/export.md). If you rely on export, supply an `imageUploadUrl` so that images reference an external location.
19+
:::
20+
21+
:::caution
22+
Base64 encoding increases the encoded payload by roughly one third relative to the original file. A document with several large inline images grows accordingly, which affects the size of the value returned by [`getValue()`](api/methods/get-value.md), the memory footprint of the editor, and the cost of persisting or transferring the content. Prefer a server `imageUploadUrl` for documents that contain many or large images.
23+
:::
1224

1325
### Usage
1426

@@ -18,16 +30,27 @@ imageUploadUrl?: string;
1830

1931
### Example
2032

33+
Upload images to a server endpoint:
34+
2135
~~~jsx {3}
2236
// initialize RichText
2337
new richtext.Richtext("#root", {
24-
imageUploadUrl: "some URL"
38+
imageUploadUrl: "https://example.com/upload"
39+
// other configuration properties
40+
});
41+
~~~
42+
43+
Insert images inline as base64 (no server required) — omit the property or pass an empty string:
44+
45+
~~~jsx {2}
46+
new richtext.Richtext("#root", {
47+
// imageUploadUrl is not set, images are inserted as base64 data URLs
2548
// other configuration properties
2649
});
2750
~~~
2851

29-
**Change log:** The property was added in v2.0
52+
**Change log:** The property was added in v2.0. Starting with v2.1, the property is optional: when omitted, images are inserted inline as base64 data URLs.
3053

31-
**Related articles:** [Configuration](guides/configuration.md)
54+
**Related articles:** [Configuration](guides/configuration.md), [Working with the server](guides/working_with_server.md)
3255

3356
**Related sample:** [RichText. Initialization](https://snippet.dhtmlx.com/t55alxiy?tag=richtext)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
sidebar_label: triggerTemplate
3+
title: triggerTemplate Config
4+
description: You can learn about the triggerTemplate config in the documentation of the DHTMLX JavaScript RichText library. Browse developer guides and API reference, try out code examples and live demos, and download a free 30-day evaluation version of DHTMLX RichText.
5+
---
6+
7+
# triggerTemplate
8+
9+
### Description
10+
11+
@short: Optional. Customizes how RichText renders items in the suggestion dropdown opened by a [`triggers`](api/config/triggers.md) entry
12+
13+
By default, the dropdown shows each item's `label` as plain text. Use `triggerTemplate` to render richer rows — for example, an avatar plus a name and an email.
14+
15+
### Usage
16+
17+
~~~jsx {}
18+
function triggerTemplate({ data, trigger }) {
19+
return "HTML template of the suggestion item";
20+
};
21+
~~~
22+
23+
### Parameters
24+
25+
The callback function takes an object with the following parameters:
26+
27+
- `data` - the current suggestion item (`{ id, label, url }`, plus any custom fields you add to the trigger's `data` source)
28+
- `trigger` - the trigger character that opened the dropdown (`"@"`, `"#"`, etc.)
29+
30+
:::tip
31+
The dropdown default width is `160px`. If you need more space for your template, add the `.wx-editor` parent in front of the selector:
32+
33+
~~~css {}
34+
.wx-editor .wx-suggest-anchor {
35+
width: 220px;
36+
}
37+
~~~
38+
:::
39+
40+
### Example
41+
42+
The following code snippet configures two triggers: `@` for mentions and `#` for tags. Use `triggerTemplate` to expand the `trigger` value to render each dropdown differently. For the `@` dropdown the template returns a custom HTML row with an avatar (`data.image`), a nickname (`data.label`), and a full name (`data.name`). For the `#` trigger the template uses the `label`:
43+
44+
~~~jsx {5-6,8-15}
45+
const { template, Richtext } = richtext;
46+
47+
new Richtext("#root", {
48+
triggers: [
49+
{ trigger: "@", data: people },
50+
{ trigger: "#", data: tags }
51+
],
52+
triggerTemplate: template(obj => {
53+
if (obj.trigger === "@") {
54+
return `<div class="user">
55+
<img class="user-avatar" src="${obj.data.image}">
56+
<div class="user-nickname">${obj.data.label}</div>
57+
<div class="user-name">${obj.data.name}</div>
58+
</div>`;
59+
}
60+
// other triggers (for example, "#") use the plain label
61+
return obj.data.label;
62+
})
63+
});
64+
~~~
65+
66+
**Change log:** The property was added in v2.1
67+
68+
**Related articles:** [Mentions and tags](guides/mentions_and_tags.md)

docs/api/config/triggers.md

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
---
2+
sidebar_label: triggers
3+
title: triggers Config
4+
description: You can learn about the triggers config in the documentation of the DHTMLX JavaScript RichText library. Browse developer guides and API reference, try out code examples and live demos, and download a free 30-day evaluation version of DHTMLX RichText.
5+
---
6+
7+
# triggers
8+
9+
### Description
10+
11+
@short: Optional. Defines dropdown triggers for inserting mentions, tags, and other tokens
12+
13+
When a user types a configured character (for example, `@` or `#`), RichText opens a dropdown with predefined items. When the user selects an item, RichText inserts it into the document as a non-editable token (`<a data-token="..." data-token-id="...">`).
14+
15+
### Usage
16+
17+
~~~jsx {}
18+
triggers?: Array<{
19+
trigger: string,
20+
data: Array<{ id?: string | number; label?: string; url?: string }>
21+
| ((query: string) =>
22+
Array<{ id?: string | number; label?: string; url?: string }>
23+
| Promise<Array<{ id?: string | number; label?: string; url?: string }>>),
24+
showTrigger?: boolean,
25+
action?: (item) => void
26+
}>;
27+
~~~
28+
29+
### Parameters
30+
31+
Each entry of the `triggers` array accepts the following fields:
32+
33+
- `trigger` - (required) the character that opens the suggestion dropdown (for example, `"@"`, `"#"`, `"/"`, `"$"`)
34+
- `data` - (required) the data source for the dropdown; can be an array, a sync function, or an async function. See [Data source forms](#data-source-forms)
35+
- `showTrigger` - (optional) when `true` (default), RichText keeps the trigger character in the inserted token (for example, `@Alice`); when `false`, RichText inserts only `label` (for example, `Alice`)
36+
- `action` - (optional) a custom callback called when a user selects an item. When set, RichText removes the typed trigger text (the trigger character plus the query) and calls `action(item)` **instead of** inserting a token. The callback receives the picked item and can insert any content instead of the selected one. The `action` parameter takes priority over `showTrigger`, which has no effect when `action` is set. See [Custom action](#custom-action)
37+
38+
### Data source forms
39+
40+
* **Static array** — RichText filters the array automatically by matching the query against `label` (case-insensitive, `startsWith`):
41+
42+
~~~jsx {3-7}
43+
new richtext.Richtext("#root", {
44+
triggers: [{
45+
trigger: "@",
46+
data: [
47+
{ id: "alice", label: "Alice" },
48+
{ id: "bob", label: "Bob" }
49+
]
50+
}]
51+
});
52+
~~~
53+
54+
* **Sync function** — RichText calls your function with the current `query` string; you do the filtering and return the matching array:
55+
56+
~~~jsx {3-6}
57+
new richtext.Richtext("#root", {
58+
triggers: [{
59+
trigger: "#",
60+
data: query => tags.filter(t =>
61+
t.label.toLowerCase().startsWith(query.toLowerCase())
62+
)
63+
}]
64+
});
65+
~~~
66+
67+
* **Async function** — RichText calls your function with the current `query` string; return a `Promise` that resolves to the matching array. Useful for server-side search:
68+
69+
~~~jsx {3-8}
70+
new richtext.Richtext("#root", {
71+
triggers: [{
72+
trigger: "+",
73+
data: async query => {
74+
const res = await fetch(`/api/users?q=${encodeURIComponent(query)}`);
75+
const users = await res.json();
76+
return users.map(u => ({ id: String(u.id), label: u.name, url: u.website }));
77+
}
78+
}]
79+
});
80+
~~~
81+
82+
### Suggestion item fields
83+
84+
Each item in `data` (or each item returned by a function) has the following fields:
85+
86+
- `id` - (optional) unique identifier saved on the inserted token. If omitted, RichText generates an ID automatically
87+
- `label` - (optional) the text shown in the dropdown and inserted into the document. Required only for the default rendering; with a custom [`triggerTemplate`](api/config/trigger-template.md) you can render items from other fields (for example, `template(({ data }) => data.id)`) and omit `label`
88+
- `url` - (optional) URL associated with the item. RichText stores the URL as the inserted token's `href` attribute. `Ctrl+Click` on the token opens the link
89+
90+
An item may also include any number of custom fields beyond `id`, `label`, and `url` (for example, `code` for an emoji, or `image` and `name` for an avatar). These extra fields are passed through to the [`triggerTemplate`](api/config/trigger-template.md) callback and to the `action` callback.
91+
92+
### Rendered token
93+
94+
When a user selects an item in the dropdown, RichText inserts a non-editable token element into the document:
95+
96+
~~~html {}
97+
<a data-token="@" data-token-id="alice" href="mailto:alice@example.com">@Alice</a>
98+
~~~
99+
100+
- `@` (in `data-token="@"`) - the item's `trigger`
101+
- `alice` (in `data-token-id="alice"`) - the item's `id`
102+
- `mailto:alice@example.com` (in `href="mailto:alice@example.com"`) - the item's `url`
103+
- `@Alice` - the combination of `trigger` and `label`; with `showTrigger: false` it would be just `Alice`
104+
105+
Use the `data-token` and `data-token-id` attributes to target tokens with CSS, for example, to highlight all mentions of a user:
106+
107+
~~~css {}
108+
.wx-editor-content a[data-token="@"][data-token-id="alice"] {
109+
background: #fb8500;
110+
color: #fff;
111+
}
112+
~~~
113+
114+
### Custom action
115+
116+
By default, when a user picks an item, RichText inserts the item into the document as a token. Set the `action` parameter to run your code instead: RichText removes the typed trigger string (the trigger character and the query) and calls the `action(item)` callback with the picked item. No token is inserted, so you can decide what to add to the document (or run your custom code). The `action` parameter takes priority over `showTrigger`. When `action` is set, `showTrigger` is ignored.
117+
118+
#### Add emoji
119+
120+
A common use case is inserting an emoji from a `:` trigger, where each item contains a custom `code` field. Pair `action` with [`triggerTemplate`](api/config/trigger-template.md) so the dropdown shows the emoji itself instead of just its label:
121+
122+
~~~jsx {8,12}
123+
const { template, Richtext } = richtext;
124+
125+
const editor = new Richtext("#root", {
126+
triggers: [
127+
{
128+
trigger: ":",
129+
data: emoji, // [{ id: "apple", label: "apple", code: "1F34E" }, ...]
130+
action: item => editor.insertValue(`<span>${emojiFromCode(item.code)} </span>`)
131+
}
132+
],
133+
// render the emoji itself (not just its label) in the dropdown
134+
triggerTemplate: template(({ data }) => `${emojiFromCode(data.code)} ${data.label}`)
135+
});
136+
137+
function emojiFromCode(code) {
138+
return String.fromCodePoint(parseInt(code, 16));
139+
}
140+
~~~
141+
142+
#### Group emoji by categories
143+
144+
When the `data` parameter is a function, you are not limited to the built-in `label` matching. You can run your own filtering and keep category headers in the dropdown. Add header items that include a `label` field and do not include `code`. The `data` function first finds the emoji that match the query, then returns emoji together with the headers of the categories that still have matches:
145+
146+
~~~jsx {18-26,31-33,41}
147+
const { template, Richtext } = richtext;
148+
149+
// header items carry no `code` field; emoji items include one
150+
const emoji = [
151+
{ id: "$smileys", label: "Smileys", category: 1 }, // category
152+
{ id: "grinning", label: "grinning", code: "1F600", category: 1 },
153+
{ id: "smile", label: "smile", code: "1F604", category: 1 },
154+
{ id: "$animals", label: "Animals", category: 2 }, // category
155+
{ id: "dog", label: "dog", code: "1F436", category: 2 },
156+
{ id: "cat", label: "cat", code: "1F431", category: 2 }
157+
];
158+
159+
const editor = new Richtext("#root", {
160+
triggers: [
161+
{
162+
trigger: ":",
163+
data: query => {
164+
const matched = emoji.filter(item =>
165+
item.code &&
166+
item.label.toLowerCase().startsWith(query.toLowerCase().trim())
167+
);
168+
const categories = new Set(matched.map(item => item.category));
169+
// keep matching emoji plus the headers of categories that still match
170+
return emoji.filter(item =>
171+
item.code ? matched.includes(item) : categories.has(item.category)
172+
);
173+
},
174+
action: item => editor.insertValue(`<span>${emojiFromCode(item.code)} </span>`)
175+
}
176+
],
177+
// render emoji rows normally and category headers in bold
178+
triggerTemplate: template(({ data }) =>
179+
data.code ? `${emojiFromCode(data.code)} ${data.label}` : `<b>${data.label}</b>`
180+
)
181+
});
182+
183+
function emojiFromCode(code) {
184+
return String.fromCodePoint(parseInt(code, 16));
185+
}
186+
187+
// headers have no `code` — ignore picks on them so they are never inserted
188+
editor.api.intercept("insert-token", ({ data }) => !!data.code);
189+
~~~
190+
191+
#### Add slash-style command menu
192+
193+
You can use `action` to build a slash-style command menu (like `/` in Notion or Slack). Store a command name in each item's `id`, its options in a custom `config` field, and let the callback run it with [`api.exec`](api/internal/exec.md):
194+
195+
~~~jsx {13}
196+
// each item stores an api.exec action name in `id` and its parameters in `config`
197+
const commands = [
198+
{ id: "set-text-style", label: "Heading 1", config: { tag: "h1" } },
199+
{ id: "insert-list", label: "Bulleted list", config: { type: "bulleted" } },
200+
{ id: "insert-line", label: "Divider" } // no config → `|| {}` applies
201+
];
202+
203+
const editor = new richtext.Richtext("#root", {
204+
triggers: [
205+
{
206+
trigger: "/",
207+
data: commands,
208+
action: item => editor.api.exec(item.id, item.config || {})
209+
}
210+
]
211+
});
212+
~~~
213+
214+
### Example
215+
216+
The following example sets up two triggers: `@` for mentions (each item carries a `url` that becomes the token's `href`) and `#` for tags (label only):
217+
218+
~~~jsx {4,11}
219+
new richtext.Richtext("#root", {
220+
triggers: [
221+
{
222+
trigger: "@",
223+
data: [
224+
{ id: "alice", label: "Alice", url: "mailto:alice@example.com" },
225+
{ id: "bob", label: "Bob", url: "mailto:bob@example.com" }
226+
]
227+
},
228+
{
229+
trigger: "#",
230+
data: [
231+
{ id: "css", label: "CSS" },
232+
{ id: "html", label: "HTML" }
233+
]
234+
}
235+
]
236+
});
237+
~~~
238+
239+
**Change log:** The property was added in v2.1
240+
241+
**Related articles:** [Mentions and tags](guides/mentions_and_tags.md)

0 commit comments

Comments
 (0)