From 3d4da9d6969c1443dccaca9a8afc094815b508cb Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:16:34 +0300 Subject: [PATCH 01/14] [add] hide-suggest event docs - new API reference page for the hide-suggest event - documents when the dropdown closes (selection, Escape, cursor leaves trigger, empty result) --- docs/api/events/hide-suggest.md | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/api/events/hide-suggest.md diff --git a/docs/api/events/hide-suggest.md b/docs/api/events/hide-suggest.md new file mode 100644 index 0000000..e458359 --- /dev/null +++ b/docs/api/events/hide-suggest.md @@ -0,0 +1,47 @@ +--- +sidebar_label: hide-suggest +title: hide-suggest Event +description: You can learn about the hide-suggest event 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. +--- + +# hide-suggest + +### Description + +@short: Fires when the suggestion dropdown closes + +The event fires when any of these happen: + +- the user picks an item from the dropdown +- the user presses `Escape` +- the cursor leaves the trigger context (for example, on `Backspace` past the trigger character) +- the current query returns no matches + +### Usage + +~~~jsx {} +"hide-suggest": () => boolean | void; +~~~ + +### Parameters + +The `hide-suggest` event callback does not receive any parameters. + +:::info +To handle internal events, use [**Event Bus methods**](api/overview/event_bus_methods_overview.md). +::: + +### Example + +~~~jsx {5-7} +// initialize RichText +const editor = new richtext.Richtext("#root", { + // configuration properties +}); +// subscribe to the "hide-suggest" event +editor.api.on("hide-suggest", () => { + console.log("Suggestion dropdown closed"); +}); +~~~ + +**Change log:** The event was added in v2.1 From 6eac5771c560c997cd762ce55e6ad82fcb4b27e9 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:16:54 +0300 Subject: [PATCH 02/14] [add] insert-token event docs - new API reference page for the insert-token event - documents callback fields (id, label, url, trigger, showTrigger) --- docs/api/events/insert-token.md | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/api/events/insert-token.md diff --git a/docs/api/events/insert-token.md b/docs/api/events/insert-token.md new file mode 100644 index 0000000..1632a65 --- /dev/null +++ b/docs/api/events/insert-token.md @@ -0,0 +1,55 @@ +--- +sidebar_label: insert-token +title: insert-token Event +description: You can learn about the insert-token event 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. +--- + +# insert-token + +### Description + +@short: Fires after the user picks a suggestion item and RichText inserts it as a token + +The event fires after the user picks an item from a trigger dropdown (mentions, tags, or any custom trigger you set up through the [`triggers`](api/config/triggers.md) property). + +### Usage + +~~~jsx {} +"insert-token": ({ + id: string, + label: string, + url: string, + trigger: string, + showTrigger?: boolean +}) => boolean | void; +~~~ + +### Parameters + +The callback of the `insert-token` event receives an object with the following fields: + +- `id` - the id of the picked suggestion item +- `label` - the label of the picked item (the text inserted into the document) +- `url` - the url associated with the item (becomes the `href` attribute of the inserted token) +- `trigger` - the trigger character that opened the dropdown (for example, `"@"` or `"#"`) +- `showTrigger` - when `false`, RichText inserts only `label`; otherwise it also shows the trigger character (default) + +:::info +To handle internal events, use [**Event Bus methods**](api/overview/event_bus_methods_overview.md). +::: + +### Example + +~~~jsx {5-9} +// initialize RichText +const editor = new richtext.Richtext("#root", { + // configuration properties +}); +// subscribe to the "insert-token" event +editor.api.on("insert-token", (obj) => { + console.log(obj); + console.log(`Inserted ${obj.trigger}${obj.label} (id: ${obj.id})`); +}); +~~~ + +**Change log:** The event was added in v2.1 From 34f4c798550533247fba21584dfcfb00af4ea242 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:17:06 +0300 Subject: [PATCH 03/14] [add] show-suggest event docs - new API reference page for the show-suggest event - documents callback fields (trigger, query, items, pos) and intercept example --- docs/api/events/show-suggest.md | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 docs/api/events/show-suggest.md diff --git a/docs/api/events/show-suggest.md b/docs/api/events/show-suggest.md new file mode 100644 index 0000000..f191ba0 --- /dev/null +++ b/docs/api/events/show-suggest.md @@ -0,0 +1,54 @@ +--- +sidebar_label: show-suggest +title: show-suggest Event +description: You can learn about the show-suggest event 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. +--- + +# show-suggest + +### Description + +@short: Fires when the suggestion dropdown opens for a configured trigger + +The event fires after RichText resolves a non-empty list of items for the current trigger, just before the dropdown opens. Intercept the event to adjust the items, move the dropdown, or cancel it. + +### Usage + +~~~jsx {} +"show-suggest": ({ + trigger: string, + query: string, + items: Array<{ id: string, label: string, url: string }>, + pos: DOMRect +}) => boolean | void; +~~~ + +### Parameters + +The callback of the `show-suggest` event receives an object with the following fields: + +- `trigger` - the trigger character that opened the dropdown +- `query` - the text typed after the trigger character (used to filter `items`) +- `items` - the resolved (and already filtered) list of suggestion items +- `pos` - a `DOMRect` describing the cursor position; used to place the dropdown on screen + +:::info +To handle internal events, use [**Event Bus methods**](api/overview/event_bus_methods_overview.md). +::: + +### Example + +~~~jsx {5-10} +// initialize RichText +const editor = new richtext.Richtext("#root", { + // configuration properties +}); +// override the suggestion list before the dropdown opens +editor.api.intercept("show-suggest", (state) => { + if (state.trigger === "@" && state.query === "") { + return { ...state, items: state.items.slice(0, 5) }; + } +}); +~~~ + +**Change log:** The event was added in v2.1 From 44b66d8f35e474f64ffc981b02afc087ae1be2b5 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:17:20 +0300 Subject: [PATCH 04/14] [add] triggers config docs - new API reference page for the triggers property - covers static array, sync function, async function data sources - documents suggestion item fields and rendered token CSS targeting --- docs/api/config/triggers.md | 130 ++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 docs/api/config/triggers.md diff --git a/docs/api/config/triggers.md b/docs/api/config/triggers.md new file mode 100644 index 0000000..0ef565e --- /dev/null +++ b/docs/api/config/triggers.md @@ -0,0 +1,130 @@ +--- +sidebar_label: triggers +title: triggers Config +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. +--- + +# triggers + +### Description + +@short: Optional. Defines dropdown triggers for inserting mentions, tags, and other tokens + +When the user types a configured character (for example, `@` or `#`), RichText opens a suggestion dropdown. When the user selects an item, RichText inserts it into the document as a non-editable token (``). + +### Usage + +~~~jsx {} +triggers?: Array<{ + trigger: string, + data: Array<{ id?: string; label?: string; url?: string }> + | ((query: string) => + Array<{ id?: string; label?: string; url?: string }> + | Promise>), + showTrigger?: boolean +}>; +~~~ + +### Parameters + +Each entry of the `triggers` array accepts the following fields: + +- `trigger` - (required) the character that opens the suggestion dropdown (for example, `"@"`, `"#"`, `"/"`, `"$"`) +- `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) +- `showTrigger` - (optional) when `true` (default), RichText keeps the trigger character in the inserted token (`@Alice`); when `false`, RichText inserts only `label` (`Alice`) + +### Data source forms + +**Static array** — RichText filters the array automatically by matching the query against `label` (case-insensitive, `startsWith`): + +~~~jsx {3-7} +new richtext.Richtext("#root", { + triggers: [{ + trigger: "@", + data: [ + { id: "alice", label: "Alice" }, + { id: "bob", label: "Bob" } + ] + }] +}); +~~~ + +**Sync function** — RichText calls your function with the current `query` string; you do the filtering and return the matching array: + +~~~jsx {3-6} +new richtext.Richtext("#root", { + triggers: [{ + trigger: "#", + data: query => tags.filter(t => + t.label.toLowerCase().startsWith(query.toLowerCase()) + ) + }] +}); +~~~ + +**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: + +~~~jsx {3-8} +new richtext.Richtext("#root", { + triggers: [{ + trigger: "+", + data: async query => { + const res = await fetch(`/api/users?q=${encodeURIComponent(query)}`); + const users = await res.json(); + return users.map(u => ({ id: String(u.id), label: u.name, url: u.website })); + } + }] +}); +~~~ + +### Suggestion item fields + +Each item in `data` (or each item returned by a function) has the following fields: + +- `id` - (optional) unique identifier saved on the inserted token. If omitted, RichText generates an ID automatically. +- `label` - (required) the text shown in the dropdown and inserted into the document +- `url` - (optional) URL associated with the item. RichText stores it on the inserted token as the `href` attribute. + +### Rendered token + +When a user selects an item, RichText inserts a non-editable token element into the document: + +~~~html {} +@Alice +~~~ + +Use the `data-token` and `data-token-id` attributes to target tokens with CSS, for example, to highlight all mentions of a user: + +~~~css {} +.wx-editor-content a[data-token="@"][data-token-id="alice"] { + background: #fb8500; + color: #fff; +} +~~~ + +### Example + +~~~jsx {2-13} +new richtext.Richtext("#root", { + triggers: [ + { + trigger: "@", + data: [ + { id: "alice", label: "Alice", url: "mailto:alice@example.com" }, + { id: "bob", label: "Bob", url: "mailto:bob@example.com" } + ] + }, + { + trigger: "#", + data: [ + { id: "css", label: "CSS" }, + { id: "html", label: "HTML" } + ] + } + ] +}); +~~~ + +**Change log:** The property was added in v2.1 + +**Related articles:** [Mentions and tags](guides/mentions_and_tags.md) From a49929befabc5d8c8bb1b0a0992f98f5e9e6ea69 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:17:44 +0300 Subject: [PATCH 05/14] [add] triggerTemplate config docs - new API reference page for the triggerTemplate property - documents callback parameters (data, trigger) and template usage - includes tip on overriding the dropdown anchor width --- docs/api/config/trigger-template.md | 62 +++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/api/config/trigger-template.md diff --git a/docs/api/config/trigger-template.md b/docs/api/config/trigger-template.md new file mode 100644 index 0000000..0ade570 --- /dev/null +++ b/docs/api/config/trigger-template.md @@ -0,0 +1,62 @@ +--- +sidebar_label: triggerTemplate +title: triggerTemplate Config +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. +--- + +# triggerTemplate + +### Description + +@short: Optional. Customizes how RichText renders items in the suggestion dropdown opened by a [`triggers`](api/config/triggers.md) entry + +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. + +### Usage + +~~~jsx {} +function triggerTemplate({ data, trigger }) { + return "HTML template of the suggestion item"; +}; +~~~ + +### Parameters + +The callback function takes an object with the following parameters: + +- `data` - the current suggestion item (`{ id, label, url }`, plus any custom fields you add to the trigger's `data` source) +- `trigger` - the trigger character that opened the dropdown (`"@"`, `"#"`, etc.) + +:::tip +The dropdown anchor has a fixed default width of `160px`. To make room for a wider template, override the width from a parent stylesheet (`!important` is needed because the widget sets the default via its own scoped CSS): + +~~~css {} +.wx-suggest-anchor { + width: 220px !important; +} +~~~ +::: + +### Example + +~~~jsx {1,4-9} +const { template } = richtext; + +new richtext.Richtext("#root", { + triggers: [{ trigger: "@", data: people }], + triggerTemplate: template(obj => { + if (obj.trigger === "@") { + return `
+ +
${obj.data.label}
+
${obj.data.name}
+
`; + } + return obj.data.label; + }) +}); +~~~ + +**Change log:** The property was added in v2.1 + +**Related articles:** [Mentions and tags](guides/mentions_and_tags.md) From d1adb47a0760744d0b41cf7bb969ffad4e2ae417 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:18:02 +0300 Subject: [PATCH 06/14] [add] Mentions and tags guide - new guide covering trigger characters, data source forms, token rendering, and event handling - examples for static array, sync function, async function, and custom dropdown templates --- docs/guides/mentions_and_tags.md | 144 +++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 docs/guides/mentions_and_tags.md diff --git a/docs/guides/mentions_and_tags.md b/docs/guides/mentions_and_tags.md new file mode 100644 index 0000000..c2494e0 --- /dev/null +++ b/docs/guides/mentions_and_tags.md @@ -0,0 +1,144 @@ +--- +sidebar_label: Mentions and tags +title: Mentions and tags +description: 'Learn how to configure @mentions, #tags, and custom dropdown triggers in DHTMLX RichText. Browse developer guides and API reference, try out code examples and live demos, and download a free 30-day evaluation version of DHTMLX RichText.' +--- + +# Mentions and tags + +RichText supports user-defined trigger characters that open a suggestion dropdown inside the editor. When the user picks an item, RichText inserts a non-editable token into the document. Typical use cases: + +- `@` — mention a person +- `#` — apply a tag +- `/` — insert a command or template +- `$` — insert a financial ticker or variable + +Configure the behavior through the [`triggers`](api/config/triggers.md) array. Each entry binds one character to a data source. + +## Configure triggers + +Each trigger is an object `{ trigger, data, showTrigger? }` within the [`triggers`](api/config/triggers.md) array. The [`data`](api/config/triggers.md/#data-source-forms) field can take three forms: + +- A static array — RichText filters it automatically by `label` (case-insensitive, `startsWith`): + +~~~jsx {} +{ trigger: "@", data: people } +~~~ + +- A sync function — use it to filter results yourself: + +~~~jsx {} +{ + trigger: "#", + data: query => tags.filter(t => + t.label.toLowerCase().startsWith(query.toLowerCase()) + ) +} +~~~ + +- An async function — use it for server-side search: + +~~~jsx {} +{ + trigger: "+", + data: async query => { + const res = await fetch(`/api/users?q=${encodeURIComponent(query)}`); + const users = await res.json(); + return users.map(u => ({ + id: String(u.id), + label: u.name, + url: u.website + })); + } +} +~~~ + +## Token rendering + +When the user picks an item, RichText inserts it as an `` element with two data attributes: + +~~~html {2-3} +@Alice +~~~ + +The token is a single non-editable node. `Backspace` deletes it in one step. RichText stores the `url` field in `href`, so `Ctrl+Click` on the token follows the link. + +You can style tokens with the `data-token` selector: + +~~~css {} +.wx-editor-content a[data-token="@"][data-token-id="alice"] { + background: #fb8500; + color: #fff; + border-radius: 3px; + padding: 0 2px; +} +~~~ + +## Hide the trigger character + +Set `showTrigger: false` on a trigger to insert only the item label, without the trigger symbol: + +~~~jsx {4} +{ + trigger: "/", + data: commands, + showTrigger: false +} +~~~ + +## Keyboard interaction + +Inside the suggestion dropdown you can use the following shortcuts: + +- `↑` / `↓` — move between items +- `Enter` — insert the active item +- `Escape` — close the dropdown without inserting + +## Listen to suggestion events + +Three events expose the dropdown lifecycle through the Event Bus: + +- [`insert-token`](api/events/insert-token.md) — fires when a user picks an item +- [`show-suggest`](api/events/show-suggest.md) — fires when the dropdown opens +- [`hide-suggest`](api/events/hide-suggest.md) — fires when the dropdown closes + +~~~jsx {5-7} +const editor = new richtext.Richtext("#root", { + triggers: [{ trigger: "@", data: people }] +}); + +editor.api.on("insert-token", ({ trigger, label, id }) => { + console.log(`Inserted ${trigger}${label} (id: ${id})`); +}); +~~~ + +## Customize the dropdown item + +By default the dropdown shows the `label` of each item. To render custom suggestions (for example, an avatar plus a name and an email) pass a template via the [`triggerTemplate`](api/config/trigger-template.md) property. + +### Example + +~~~jsx {1,4-9} +const { template } = richtext; + +new richtext.Richtext("#root", { + triggers: [{ trigger: "@", data: people }], + triggerTemplate: template(({ data, trigger }) => ` +
+
${trigger}${data.label}
+
${data.url || ""}
+
+ `) +}); +~~~ + +## Related API + +- [`triggers`](api/config/triggers.md) +- [`triggerTemplate`](api/config/trigger-template.md) +- [`insert-token`](api/events/insert-token.md) +- [`show-suggest`](api/events/show-suggest.md) +- [`hide-suggest`](api/events/hide-suggest.md) From 6948c9cccdd81b9fa983511b49672ee516c62d59 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:18:15 +0300 Subject: [PATCH 07/14] [update] add suggestion events to events overview - list hide-suggest, insert-token, and show-suggest in the events table --- docs/api/overview/events_overview.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api/overview/events_overview.md b/docs/api/overview/events_overview.md index 957e228..449c945 100644 --- a/docs/api/overview/events_overview.md +++ b/docs/api/overview/events_overview.md @@ -18,12 +18,14 @@ You can use these events to extend functionality, track user interaction, or tri | [](api/events/cut.md) | @getshort(api/events/cut.md) | | [](api/events/delete-link.md) | @getshort(api/events/delete-link.md) | | [](api/events/export.md) | @getshort(api/events/export.md) | +| [](api/events/hide-suggest.md) | @getshort(api/events/hide-suggest.md) | | [](api/events/import.md) | @getshort(api/events/import.md) | | [](api/events/indent.md) | @getshort(api/events/indent.md) | | [](api/events/insert-image.md) | @getshort(api/events/insert-image.md) | | [](api/events/insert-line.md) | @getshort(api/events/insert-line.md) | | [](api/events/insert-link.md) | @getshort(api/events/insert-link.md) | | [](api/events/insert-list.md) | @getshort(api/events/insert-list.md) | +| [](api/events/insert-token.md) | @getshort(api/events/insert-token.md) | | [](api/events/outdent.md) | @getshort(api/events/outdent.md) | | [](api/events/paste.md) | @getshort(api/events/paste.md) | | [](api/events/print.md) | @getshort(api/events/print.md) | @@ -36,6 +38,7 @@ You can use these events to extend functionality, track user interaction, or tri | [](api/events/set-text-format.md) | @getshort(api/events/set-text-format.md) | | [](api/events/set-text-style.md) | @getshort(api/events/set-text-style.md) | | [](api/events/show-popup.md) | @getshort(api/events/show-popup.md) | +| [](api/events/show-suggest.md) | @getshort(api/events/show-suggest.md) | | [](api/events/subscript.md) | @getshort(api/events/subscript.md) | | [](api/events/superscript.md) | @getshort(api/events/superscript.md) | | [](api/events/toggle-fullscreen-mode.md) | @getshort(api/events/toggle-fullscreen-mode.md)| From ce748b791f2a9236c6140850a986fa3e0f039847 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:18:26 +0300 Subject: [PATCH 08/14] [update] add trigger configs to properties overview - list triggers and triggerTemplate in the properties table --- docs/api/overview/properties_overview.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api/overview/properties_overview.md b/docs/api/overview/properties_overview.md index 255a158..e545112 100644 --- a/docs/api/overview/properties_overview.md +++ b/docs/api/overview/properties_overview.md @@ -18,4 +18,6 @@ They help you control layout, toolbar, value, localization, and other aspects of | [](api/config/locale.md) | @getshort(api/config/locale.md) | | [](api/config/menubar.md) | @getshort(api/config/menubar.md) | | [](api/config/toolbar.md) | @getshort(api/config/toolbar.md) | +| [](api/config/trigger-template.md) | @getshort(api/config/trigger-template.md) | +| [](api/config/triggers.md) | @getshort(api/config/triggers.md) | | [](api/config/value.md) | @getshort(api/config/value.md) | From 90c074d73a4f44e3f917bb5430e455bed33e72e7 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:18:45 +0300 Subject: [PATCH 09/14] [update] mention triggers in configuration guide - list triggers in the property overview with a link to the Mentions and tags guide --- docs/guides/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md index 855ae27..d98eb9f 100644 --- a/docs/guides/configuration.md +++ b/docs/guides/configuration.md @@ -16,6 +16,7 @@ You can configure the RichText appearance and behavior with the following proper - [`locale`](api/config/locale.md) — apply a localization object on initialization - [`defaultStyles`](api/config/default-styles.md) — set default styles for specific block types - [`imageUploadUrl`](api/config/image-upload-url.md) — set the endpoint for image uploads +- [`triggers`](api/config/triggers.md) — enable @mentions, #tags, and custom dropdown triggers (see the [Mentions and tags](guides/mentions_and_tags.md) guide) ## Layout modes From d7646923231d91cbefb9b5d3581e01293a94de97 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:19:02 +0300 Subject: [PATCH 10/14] [fix] correct anchor for custom toolbar controls link - update anchor in whats_new from #custom-toolbar-controls to #add-custom-toolbar-controls to match the heading in configuration.md --- docs/news/whats_new.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/news/whats_new.md b/docs/news/whats_new.md index 27dc4f3..2e96ab1 100644 --- a/docs/news/whats_new.md +++ b/docs/news/whats_new.md @@ -83,7 +83,7 @@ API of v1.2 is not compatible with v2.0. Refer to the [**migration guide**](news - **Granular toolbar configuration** Take full control of the toolbar: - Define [individual toolbar controls](guides/configuration.md/#default-toolbar-controls) and their order - - Add [custom controls](guides/configuration.md/#custom-toolbar-controls) + - Add [custom controls](guides/configuration.md/#add-custom-toolbar-controls) - **Optional [menubar](api/config/menubar.md)** Enable a classic menu-style interface on the top of the editor From f888bf746732d328ae42c607452253ff2d36a479 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 27 May 2026 09:19:19 +0300 Subject: [PATCH 11/14] [update] register trigger pages in sidebar - add hide-suggest, insert-token, show-suggest under events - add triggers and trigger-template under properties - add mentions_and_tags under guides --- sidebars.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sidebars.js b/sidebars.js index 4be1cb8..683ed3d 100644 --- a/sidebars.js +++ b/sidebars.js @@ -117,12 +117,14 @@ module.exports = { "api/events/cut", "api/events/delete-link", "api/events/export", + "api/events/hide-suggest", "api/events/import", "api/events/indent", "api/events/insert-image", "api/events/insert-line", "api/events/insert-link", "api/events/insert-list", + "api/events/insert-token", "api/events/outdent", "api/events/paste", "api/events/print", @@ -135,6 +137,7 @@ module.exports = { "api/events/set-text-format", "api/events/set-text-style", "api/events/show-popup", + "api/events/show-suggest", "api/events/subscript", "api/events/superscript", "api/events/toggle-fullscreen-mode", @@ -162,7 +165,9 @@ module.exports = { "api/config/locale", "api/config/menubar", "api/config/toolbar", - "api/config/value" + "api/config/trigger-template", + "api/config/triggers", + "api/config/value" ] } ] @@ -198,6 +203,7 @@ module.exports = { items: [ "guides/initialization", "guides/configuration", + "guides/mentions_and_tags", "guides/localization", "guides/stylization", "guides/typescript_support" From b77eef15b55a7da8750111b77188950195c285c1 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 3 Jun 2026 18:27:15 +0300 Subject: [PATCH 12/14] [update] document trigger action callback - add `action` field to triggers config with Custom action section (emoji insertion and slash-style command menu examples) - update insert-token event payload: id/label/url replaced by `data` object carrying the picked item plus custom fields; add `action` - note that `action` takes priority over `showTrigger` - show-suggest items now documented as optional fields + custom keys - extend mentions guide with custom action examples - fix typos, a broken anchor, and align wording across pages --- docs/api/config/triggers.md | 79 +++++++++++++++++++++++---- docs/api/events/insert-token.md | 30 ++++++----- docs/api/events/show-suggest.md | 18 +++++-- docs/guides/mentions_and_tags.md | 93 +++++++++++++++++++++++++++++--- 4 files changed, 184 insertions(+), 36 deletions(-) diff --git a/docs/api/config/triggers.md b/docs/api/config/triggers.md index 0ef565e..69fdcf0 100644 --- a/docs/api/config/triggers.md +++ b/docs/api/config/triggers.md @@ -10,7 +10,7 @@ description: You can learn about the triggers config in the documentation of the @short: Optional. Defines dropdown triggers for inserting mentions, tags, and other tokens -When the user types a configured character (for example, `@` or `#`), RichText opens a suggestion dropdown. When the user selects an item, RichText inserts it into the document as a non-editable token (``). +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 (``). ### Usage @@ -21,7 +21,8 @@ triggers?: Array<{ | ((query: string) => Array<{ id?: string; label?: string; url?: string }> | Promise>), - showTrigger?: boolean + showTrigger?: boolean, + action?: (item) => void }>; ~~~ @@ -31,11 +32,12 @@ Each entry of the `triggers` array accepts the following fields: - `trigger` - (required) the character that opens the suggestion dropdown (for example, `"@"`, `"#"`, `"/"`, `"$"`) - `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) -- `showTrigger` - (optional) when `true` (default), RichText keeps the trigger character in the inserted token (`@Alice`); when `false`, RichText inserts only `label` (`Alice`) +- `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`) +- `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 selected one. The `action` parameter takes priority over `showTrigger`, which has no effect when `action` is set. See [Custom action](#custom-action) ### Data source forms -**Static array** — RichText filters the array automatically by matching the query against `label` (case-insensitive, `startsWith`): +* **Static array** — RichText filters the array automatically by matching the query against `label` (case-insensitive, `startsWith`): ~~~jsx {3-7} new richtext.Richtext("#root", { @@ -49,7 +51,7 @@ new richtext.Richtext("#root", { }); ~~~ -**Sync function** — RichText calls your function with the current `query` string; you do the filtering and return the matching array: +* **Sync function** — RichText calls your function with the current `query` string; you do the filtering and return the matching array: ~~~jsx {3-6} new richtext.Richtext("#root", { @@ -62,7 +64,7 @@ new richtext.Richtext("#root", { }); ~~~ -**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: +* **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: ~~~jsx {3-8} new richtext.Richtext("#root", { @@ -79,15 +81,17 @@ new richtext.Richtext("#root", { ### Suggestion item fields -Each item in `data` (or each item returned by a function) has the following fields: +Each item in the `data` object (or each item returned by a function) has the following fields: -- `id` - (optional) unique identifier saved on the inserted token. If omitted, RichText generates an ID automatically. +- `id` - (optional) unique identifier saved on the inserted token. If omitted, RichText generates an ID automatically - `label` - (required) the text shown in the dropdown and inserted into the document -- `url` - (optional) URL associated with the item. RichText stores it on the inserted token as the `href` attribute. +- `url` - (optional) URL associated with the item. RichText stores the inserted token URL as the `href` attribute. + +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. ### Rendered token -When a user selects an item, RichText inserts a non-editable token element into the document: +When a user selects an item in the dropdown, RichText inserts a non-editable token element into the document: ~~~html {} @Alice @@ -102,9 +106,62 @@ Use the `data-token` and `data-token-id` attributes to target tokens with CSS, f } ~~~ +### Custom action + +By default, when a user picks an item, RichText inserts it into the document as a token. Set the `action` parameter to run your own 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. The `action` parameter takes priority over `showTrigger`. When `action` is set, `showTrigger` is ignored. + +#### Add emoji + +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: + +~~~jsx {8,12} +const { template } = richtext; + +const editor = new richtext.Richtext("#root", { + triggers: [ + { + trigger: ":", + data: emoji, // [{ id: "apple", label: "apple", code: "1F34E" }, ...] + action: item => editor.insertValue(`${emojiFromCode(item.code)} `) + } + ], + // render the emoji itself (not just its label) in the dropdown + triggerTemplate: template(({ data }) => `${emojiFromCode(data.code)} ${data.label}`) +}); + +function emojiFromCode(code) { + return String.fromCodePoint(parseInt(code, 16)); +} +~~~ + +#### Add slash-style command menu + +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): + +~~~jsx {13} +// each item stores an api.exec action name in `id` and its parameters in `config` +const commands = [ + { id: "set-text-style", label: "Heading 1", config: { tag: "h1" } }, + { id: "insert-list", label: "Bulleted list", config: { type: "bulleted" } }, + { id: "insert-line", label: "Divider" } // no config → `|| {}` applies +]; + +const editor = new richtext.Richtext("#root", { + triggers: [ + { + trigger: "/", + data: commands, + action: item => editor.api.exec(item.id, item.config || {}) + } + ] +}); +~~~ + ### Example -~~~jsx {2-13} +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): + +~~~jsx {4,11} new richtext.Richtext("#root", { triggers: [ { diff --git a/docs/api/events/insert-token.md b/docs/api/events/insert-token.md index 1632a65..dea71c5 100644 --- a/docs/api/events/insert-token.md +++ b/docs/api/events/insert-token.md @@ -10,17 +10,21 @@ description: You can learn about the insert-token event in the documentation of @short: Fires after the user picks a suggestion item and RichText inserts it as a token -The event fires after the user picks an item from a trigger dropdown (mentions, tags, or any custom trigger you set up through the [`triggers`](api/config/triggers.md) property). +The `insert-token` event fires after the user picks an item from a trigger dropdown (mentions, tags, or any custom trigger you set up through the [`triggers`](api/config/triggers.md) property). ### Usage ~~~jsx {} "insert-token": ({ - id: string, - label: string, - url: string, + data: { + id?: string | number, + label?: string, + url?: string, + // ...any custom fields from the trigger's data source + }, trigger: string, - showTrigger?: boolean + showTrigger?: boolean, + action?: (item) => void }) => boolean | void; ~~~ @@ -28,11 +32,10 @@ The event fires after the user picks an item from a trigger dropdown (mentions, The callback of the `insert-token` event receives an object with the following fields: -- `id` - the id of the picked suggestion item -- `label` - the label of the picked item (the text inserted into the document) -- `url` - the url associated with the item (becomes the `href` attribute of the inserted token) +- `data` - the picked suggestion item. Contains the `id`, `label`, and `url` of the item, as well as any custom fields you added to the trigger's `data` source - `trigger` - the trigger character that opened the dropdown (for example, `"@"` or `"#"`) -- `showTrigger` - when `false`, RichText inserts only `label`; otherwise it also shows the trigger character (default) +- `showTrigger` - when `false`, RichText inserts only `label`; otherwise the widget also shows the trigger character (default) +- `action` - a custom action defined for the matching [trigger](api/config/triggers.md). When set, the parameter takes priority over token insertion: RichText removes the typed text (the trigger character and the query) and calls `action(data)` instead of inserting a token. The `showTrigger` parameter has no effect in this case :::info To handle internal events, use [**Event Bus methods**](api/overview/event_bus_methods_overview.md). @@ -40,16 +43,17 @@ To handle internal events, use [**Event Bus methods**](api/overview/event_bus_me ### Example -~~~jsx {5-9} +~~~jsx {5-8} // initialize RichText const editor = new richtext.Richtext("#root", { // configuration properties }); // subscribe to the "insert-token" event -editor.api.on("insert-token", (obj) => { - console.log(obj); - console.log(`Inserted ${obj.trigger}${obj.label} (id: ${obj.id})`); +editor.api.on("insert-token", ({ data, trigger, showTrigger }) => { + console.log(`Inserted ${trigger}${data.label} (id: ${data.id})`); }); ~~~ **Change log:** The event was added in v2.1 + +**Related articles:** [Mentions and tags](guides/mentions_and_tags.md) diff --git a/docs/api/events/show-suggest.md b/docs/api/events/show-suggest.md index f191ba0..666bc8b 100644 --- a/docs/api/events/show-suggest.md +++ b/docs/api/events/show-suggest.md @@ -10,7 +10,7 @@ description: You can learn about the show-suggest event in the documentation of @short: Fires when the suggestion dropdown opens for a configured trigger -The event fires after RichText resolves a non-empty list of items for the current trigger, just before the dropdown opens. Intercept the event to adjust the items, move the dropdown, or cancel it. +The `show-suggest` event fires after RichText resolves a non-empty list of items for the current trigger, just before the dropdown opens. Intercept the event to adjust the items, move the dropdown, or cancel it. ### Usage @@ -18,7 +18,12 @@ The event fires after RichText resolves a non-empty list of items for the curren "show-suggest": ({ trigger: string, query: string, - items: Array<{ id: string, label: string, url: string }>, + items: Array<{ + id?: string | number, + label?: string, + url?: string, + // ...any custom fields from the trigger's data source + }>, pos: DOMRect }) => boolean | void; ~~~ @@ -29,7 +34,7 @@ The callback of the `show-suggest` event receives an object with the following f - `trigger` - the trigger character that opened the dropdown - `query` - the text typed after the trigger character (used to filter `items`) -- `items` - the resolved (and already filtered) list of suggestion items +- `items` - the resolved (and already filtered) list of suggestion items. Each item follows the [suggestion item shape](api/config/triggers.md#suggestion-item-fields): optional `id`, `label`, and `url`, plus any custom fields (such as `image` or `name`) used by [`triggerTemplate`](api/config/trigger-template.md) - `pos` - a `DOMRect` describing the cursor position; used to place the dropdown on screen :::info @@ -38,10 +43,11 @@ To handle internal events, use [**Event Bus methods**](api/overview/event_bus_me ### Example -~~~jsx {5-10} +~~~jsx {6-11} // initialize RichText const editor = new richtext.Richtext("#root", { - // configuration properties + triggers: [{ trigger: "@", data: people }] + // other configuration properties }); // override the suggestion list before the dropdown opens editor.api.intercept("show-suggest", (state) => { @@ -52,3 +58,5 @@ editor.api.intercept("show-suggest", (state) => { ~~~ **Change log:** The event was added in v2.1 + +**Related articles:** [Mentions and tags](guides/mentions_and_tags.md) diff --git a/docs/guides/mentions_and_tags.md b/docs/guides/mentions_and_tags.md index c2494e0..8611fd1 100644 --- a/docs/guides/mentions_and_tags.md +++ b/docs/guides/mentions_and_tags.md @@ -6,18 +6,19 @@ description: 'Learn how to configure @mentions, #tags, and custom dropdown trigg # Mentions and tags -RichText supports user-defined trigger characters that open a suggestion dropdown inside the editor. When the user picks an item, RichText inserts a non-editable token into the document. Typical use cases: +RichText supports user-defined trigger characters that open a suggestion dropdown inside the document. When the user picks an item, RichText inserts a non-editable token into the document. Typical use cases: - `@` — mention a person - `#` — apply a tag - `/` — insert a command or template - `$` — insert a financial ticker or variable +- `:` - insert an emoji -Configure the behavior through the [`triggers`](api/config/triggers.md) array. Each entry binds one character to a data source. +Configure the behavior through the [`triggers`](api/config/triggers.md) property. Each entry binds one character to a data source. ## Configure triggers -Each trigger is an object `{ trigger, data, showTrigger? }` within the [`triggers`](api/config/triggers.md) array. The [`data`](api/config/triggers.md/#data-source-forms) field can take three forms: +Each trigger is an object `{ trigger, data, showTrigger?, action? }` within the [`triggers`](api/config/triggers.md) array. The [`data`](api/config/triggers.md#data-source-forms) field can take three forms: - A static array — RichText filters it automatically by `label` (case-insensitive, `startsWith`): @@ -55,7 +56,7 @@ Each trigger is an object `{ trigger, data, showTrigger? }` within the [`trigger ## Token rendering -When the user picks an item, RichText inserts it as an `` element with two data attributes: +When the user picks an item from the dropdown, RichText inserts it as an `` element with two data attributes: ~~~html {2-3} { - console.log(`Inserted ${trigger}${label} (id: ${id})`); +editor.api.on("insert-token", ({ trigger, data }) => { + console.log(`Inserted ${trigger}${data.label} (id: ${data.id})`); }); ~~~ ## Customize the dropdown item -By default the dropdown shows the `label` of each item. To render custom suggestions (for example, an avatar plus a name and an email) pass a template via the [`triggerTemplate`](api/config/trigger-template.md) property. +By default the dropdown shows the `label` of each item. To render custom suggestions (for example, avatar, name and email) pass a template via the [`triggerTemplate`](api/config/trigger-template.md) property. ### Example @@ -135,6 +136,84 @@ new richtext.Richtext("#root", { }); ~~~ +## Custom action on select + +By default, picking an item inserts it into the document as a token. To run your own code instead, add an `action` callback to the trigger. RichText removes the typed trigger text and calls `action(item)` with the picked item — no token is inserted, so you can decide what to add. + +:::note + `action` takes priority over `showTrigger`. When `action` is set, `showTrigger` is ignored. +::: + +### Add emoji + +A `:` trigger can insert an emoji, where each item includes a custom `code` field. Pair `action` with [`triggerTemplate`](api/config/trigger-template.md) so the dropdown shows the emoji instead of just its label: + +~~~jsx {8,12} +const { template } = richtext; + +const editor = new richtext.Richtext("#root", { + triggers: [ + { + trigger: ":", + data: emoji, // [{ id: "apple", label: "apple", code: "1F34E" }, ...] + action: item => editor.insertValue(`${emojiFromCode(item.code)} `) + } + ], + // render the emoji itself (not just its label) in the dropdown + triggerTemplate: template(({ data }) => `${emojiFromCode(data.code)} ${data.label}`) +}); + +function emojiFromCode(code) { + return String.fromCodePoint(parseInt(code, 16)); +} + +const emoji = [ + { + id: "apple", + label: "apple", + code: "1F34E", + }, + { + id: "blue_car", + label: "blue_car", + code: "1F699", + }, + { + id: "computer", + label: "computer", + code: "1F4BB", + }, + { + id: "dvd", + label: "dvd", + code: "1F4C0", + }, +]; +~~~ + +### Add slash-style command menu + +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): + +~~~jsx {13} +// each item stores an api.exec action name in `id` and its parameters in `config` +const commands = [ + { id: "set-text-style", label: "Heading 1", config: { tag: "h1" } }, + { id: "insert-list", label: "Bulleted list", config: { type: "bulleted" } }, + { id: "insert-line", label: "Divider" } // no config → `|| {}` applies +]; + +const editor = new richtext.Richtext("#root", { + triggers: [ + { + trigger: "/", + data: commands, + action: item => editor.api.exec(item.id, item.config || {}) + } + ] +}); +~~~ + ## Related API - [`triggers`](api/config/triggers.md) From 2f2a6b74354e701ea9dea6423dc2408a0afe06bb Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 3 Jun 2026 18:41:14 +0300 Subject: [PATCH 13/14] [update] align insert-token handler in guide - destructure ({ data, trigger, showTrigger }) to match the insert-token event docs and the PR review suggestion --- docs/guides/mentions_and_tags.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/mentions_and_tags.md b/docs/guides/mentions_and_tags.md index 8611fd1..f2b0b52 100644 --- a/docs/guides/mentions_and_tags.md +++ b/docs/guides/mentions_and_tags.md @@ -111,7 +111,7 @@ const editor = new richtext.Richtext("#root", { triggers: [{ trigger: "@", data: people }] }); -editor.api.on("insert-token", ({ trigger, data }) => { +editor.api.on("insert-token", ({ data, trigger, showTrigger }) => { console.log(`Inserted ${trigger}${data.label} (id: ${data.id})`); }); ~~~ From 5ae4c900053805cda505ce3313e259d28e9cc7c5 Mon Sep 17 00:00:00 2001 From: Serhii Pylypchuk Date: Wed, 3 Jun 2026 22:34:01 +0300 Subject: [PATCH 14/14] [update] address review on triggers docs - mark suggestion `label` as optional (custom triggerTemplate case) - note Ctrl+Click opens token url, explain mailto behavior - break down rendered token attributes (trigger/id/url/label) - clarify custom action can run code without inserting content - add "Group emoji by category" example with custom filtering, category headers and api.intercept guard - triggerTemplate: add second trigger so trigger check is meaningful, replace !important width tip with more specific selector --- docs/api/config/trigger-template.md | 20 ++++--- docs/api/config/triggers.md | 66 ++++++++++++++++++++--- docs/guides/mentions_and_tags.md | 84 +++++++++++++++++++++-------- 3 files changed, 134 insertions(+), 36 deletions(-) diff --git a/docs/api/config/trigger-template.md b/docs/api/config/trigger-template.md index 0ade570..574a437 100644 --- a/docs/api/config/trigger-template.md +++ b/docs/api/config/trigger-template.md @@ -28,22 +28,27 @@ The callback function takes an object with the following parameters: - `trigger` - the trigger character that opened the dropdown (`"@"`, `"#"`, etc.) :::tip -The dropdown anchor has a fixed default width of `160px`. To make room for a wider template, override the width from a parent stylesheet (`!important` is needed because the widget sets the default via its own scoped CSS): +The dropdown default width is `160px`. If you need more space for your template, add the `.wx-editor` parent in front of the selector: ~~~css {} -.wx-suggest-anchor { - width: 220px !important; +.wx-editor .wx-suggest-anchor { + width: 220px; } ~~~ ::: ### Example -~~~jsx {1,4-9} -const { template } = richtext; +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 use the `label`: -new richtext.Richtext("#root", { - triggers: [{ trigger: "@", data: people }], +~~~jsx {5-6,8-15} +const { template, Richtext } = richtext; + +new Richtext("#root", { + triggers: [ + { trigger: "@", data: people }, + { trigger: "#", data: tags } + ], triggerTemplate: template(obj => { if (obj.trigger === "@") { return `
@@ -52,6 +57,7 @@ new richtext.Richtext("#root", {
${obj.data.name}
`; } + // other triggers (for example, "#") use the plain label return obj.data.label; }) }); diff --git a/docs/api/config/triggers.md b/docs/api/config/triggers.md index 69fdcf0..3f916d5 100644 --- a/docs/api/config/triggers.md +++ b/docs/api/config/triggers.md @@ -81,11 +81,11 @@ new richtext.Richtext("#root", { ### Suggestion item fields -Each item in the `data` object (or each item returned by a function) has the following fields: +Each item in `data` (or each item returned by a function) has the following fields: - `id` - (optional) unique identifier saved on the inserted token. If omitted, RichText generates an ID automatically -- `label` - (required) the text shown in the dropdown and inserted into the document -- `url` - (optional) URL associated with the item. RichText stores the inserted token URL as the `href` attribute. +- `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` +- `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 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. @@ -97,6 +97,11 @@ When a user selects an item in the dropdown, RichText inserts a non-editable tok
@Alice ~~~ +- `@` (in `data-token="@"`) - the item's `trigger` +- `alice` (in `data-token-id="alice"`) - the item's `id` +- `mailto:alice@example.com` (in `href="mailto:alice@example.com"`) - the item's `url` +- `@Alice` - the combination of `trigger` and `label`; with `showTrigger: false` it would be just `Alice` + Use the `data-token` and `data-token-id` attributes to target tokens with CSS, for example, to highlight all mentions of a user: ~~~css {} @@ -108,16 +113,16 @@ Use the `data-token` and `data-token-id` attributes to target tokens with CSS, f ### Custom action -By default, when a user picks an item, RichText inserts it into the document as a token. Set the `action` parameter to run your own 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. The `action` parameter takes priority over `showTrigger`. When `action` is set, `showTrigger` is ignored. +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. #### Add emoji 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: ~~~jsx {8,12} -const { template } = richtext; +const { template, Richtext } = richtext; -const editor = new richtext.Richtext("#root", { +const editor = new Richtext("#root", { triggers: [ { trigger: ":", @@ -134,6 +139,55 @@ function emojiFromCode(code) { } ~~~ +#### Group emoji by categories + +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: + +~~~jsx {18-26,31-33,41} +const { template, Richtext } = richtext; + +// header items carry no `code` field; emoji items include one +const emoji = [ + { id: "$smileys", label: "Smileys", category: 1 }, // category + { id: "grinning", label: "grinning", code: "1F600", category: 1 }, + { id: "smile", label: "smile", code: "1F604", category: 1 }, + { id: "$animals", label: "Animals", category: 2 }, // category + { id: "dog", label: "dog", code: "1F436", category: 2 }, + { id: "cat", label: "cat", code: "1F431", category: 2 } +]; + +const editor = new Richtext("#root", { + triggers: [ + { + trigger: ":", + data: query => { + const matched = emoji.filter(item => + item.code && + item.label.toLowerCase().startsWith(query.toLowerCase().trim()) + ); + const categories = new Set(matched.map(item => item.category)); + // keep matching emoji plus the headers of categories that still match + return emoji.filter(item => + item.code ? matched.includes(item) : categories.has(item.category) + ); + }, + action: item => editor.insertValue(`${emojiFromCode(item.code)} `) + } + ], + // render emoji rows normally and category headers in bold + triggerTemplate: template(({ data }) => + data.code ? `${emojiFromCode(data.code)} ${data.label}` : `${data.label}` + ) +}); + +function emojiFromCode(code) { + return String.fromCodePoint(parseInt(code, 16)); +} + +// headers have no `code` — ignore picks on them so they are never inserted +editor.api.intercept("insert-token", ({ data }) => !!data.code); +~~~ + #### Add slash-style command menu 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): diff --git a/docs/guides/mentions_and_tags.md b/docs/guides/mentions_and_tags.md index f2b0b52..d6a449b 100644 --- a/docs/guides/mentions_and_tags.md +++ b/docs/guides/mentions_and_tags.md @@ -148,10 +148,22 @@ By default, picking an item inserts it into the document as a token. To run your A `:` trigger can insert an emoji, where each item includes a custom `code` field. Pair `action` with [`triggerTemplate`](api/config/trigger-template.md) so the dropdown shows the emoji instead of just its label: -~~~jsx {8,12} -const { template } = richtext; +~~~jsx {18-20,24} +const { template, Richtext } = richtext; -const editor = new richtext.Richtext("#root", { +const emoji = [ + { + id: "apple", label: "apple", code: "1F34E" + }, + { + id: "blue_car", label: "blue_car", code: "1F699" + }, + { + id: "computer", label: "computer", code: "1F4BB" + } +]; + +const editor = new Richtext("#root", { triggers: [ { trigger: ":", @@ -166,29 +178,55 @@ const editor = new richtext.Richtext("#root", { function emojiFromCode(code) { return String.fromCodePoint(parseInt(code, 16)); } +~~~ + +### Group emoji by categories + +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: + +~~~jsx {17-26,31-33} +const { template, Richtext } = richtext; +// header items carry no `code` field; emoji items include one const emoji = [ - { - id: "apple", - label: "apple", - code: "1F34E", - }, - { - id: "blue_car", - label: "blue_car", - code: "1F699", - }, - { - id: "computer", - label: "computer", - code: "1F4BB", - }, - { - id: "dvd", - label: "dvd", - code: "1F4C0", - }, + { id: "$smileys", label: "Smileys", category: 1 }, // category + { id: "grinning", label: "grinning", code: "1F600", category: 1 }, + { id: "smile", label: "smile", code: "1F604", category: 1 }, + { id: "$animals", label: "Animals", category: 2 }, // category + { id: "dog", label: "dog", code: "1F436", category: 2 }, + { id: "cat", label: "cat", code: "1F431", category: 2 } ]; + +const editor = new Richtext("#root", { + triggers: [ + { + trigger: ":", + data: query => { + const matched = emoji.filter(item => + item.code && + item.label.toLowerCase().startsWith(query.toLowerCase().trim()) + ); + const categories = new Set(matched.map(item => item.category)); + // keep matching emoji plus the headers of categories that still match + return emoji.filter(item => + item.code ? matched.includes(item) : categories.has(item.category) + ); + }, + action: item => editor.insertValue(`${emojiFromCode(item.code)} `) + } + ], + // render emoji rows normally and category headers in bold + triggerTemplate: template(({ data }) => + data.code ? `${emojiFromCode(data.code)} ${data.label}` : `${data.label}` + ) +}); + +function emojiFromCode(code) { + return String.fromCodePoint(parseInt(code, 16)); +} + +// headers have no `code` — ignore picks on them so they are never inserted +editor.api.intercept("insert-token", ({ data }) => !!data.code); ~~~ ### Add slash-style command menu