From 11015c74dd88702ac67c7105ac0be9cde05053b9 Mon Sep 17 00:00:00 2001 From: georgedautov Date: Tue, 18 Nov 2025 18:21:42 +0200 Subject: [PATCH 1/5] docs(Editor): add editor mentions kb Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> --- knowledge-base/editor-mentions.md | 290 ++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 knowledge-base/editor-mentions.md diff --git a/knowledge-base/editor-mentions.md b/knowledge-base/editor-mentions.md new file mode 100644 index 000000000..22eebeb53 --- /dev/null +++ b/knowledge-base/editor-mentions.md @@ -0,0 +1,290 @@ +--- +title: Mentions in Editor +description: Learn how to add support for mentions in the Telerik Editor component for Blazor. +type: how-to +page_title: Mentions in Editor +slug: editor-kb-mentions +position: +tags: telerik, blazor, editor, mentions +ticketid: 1545505 +res_type: kb +--- + +## Environment + + + + + + + + +
ProductEditor for Blazor
+ +## Description + +How to enable or implement support for `@mentions` in the TelerikEditor for Blazor, similar to GitHub, Facebook, etc.? + +## Solution + +You can use the [proseMirror-mentions](https://github.com/joelewis/prosemirror-mentions) plugin to provide a `@mentions` and `#hashtags` functionality for the Telerik Blazor Editor component. To implement the feature, customize the built-in [ProseMirror schema](slug:editor-prosemirror-schema-overview) and [integrate a ProseMirror plugin](slug:editor-prosemirror-plugins). + +For dynamic positioning of the mentions list, set the [`EditMode`](slug:Telerik.Blazor.Components.TelerikEditor#telerik_blazor_components_telerikeditor_editmode) property of the Editor to `EditorEditorMode.Div`. This ensures that the mentions dropdown position is correct relative to the Editor content. + +````RAZOR.skip-repl + +```` + +### Setting up WebPack and Installing proseMirror-mentions + +1. Create a directory at the root of your project which will contain your JavaScript module (e.g. `npmjs`). All of the next steps will be done inside that directory +2. Setup the Javascript project by running: + ````SH.skip-repl + npm init -y + ```` + > This command creates a `package.json` file containing the project's configuration. The `-y` flag accepts all defaults for simplicity. In a real world application, consider running `npm init` without the flag to configure settings interactively. +3. Install a JavaScript bundler. In this example we will use [webpack](https://webpack.js.org), so run: + ````SH.skip-repl + npm install webpack webpack-cli --save-dev + ```` +4. Configure the build script in the `scripts` section of `package.json`: + ````JSON.skip-repl + "scripts": { + "build": "webpack ./src/index.js --output-path ../wwwroot/js --output-filename index.bundle.js" + }, + ```` + > In this example all of our Javascript files will go into `npmjs/src` +5. Update the module type in `package.json`: + ````JSON.skip-repl + "type": module" + ```` + This enables ES6 `import`/`export` syntax instead of the CommonJS require statements which will be useful later on. +6. Install [proseMirror-mentions](https://github.com/joelewis/prosemirror-mentions) by running: + ````SH.skip-repl + npm install prosemirror-mentions + ```` +7. Build the JavaScript bundle by running: + ````SH.skip-repl + npm run build + ```` + This creates the `index.bundle.js` file in your `wwwroot/js` directory. + +### Include the JavaScript Bundle + +After building the JavaScript bundle, you need to include it in your Blazor application. You can do this at either the global or component level: + +**Global Level (App.razor):** +
+ +````razor App.razor + + + + + + + + + + + +```` + +**Component Level (Component.razor):** +If you prefer to load the script only when the component is used, include it in your component with the `defer` attribute: + +
+ +````razor Component.razor + + + +```` + +> Note: The script tag does not need `type="module"` since webpack bundles all ES6 modules into a single compatible file. + +### Integrate the Mentions Plugin + +The following code demonstrates how to integrate the `proseMirror-mentions` plugin in the Editor. + +
+ +````razor Component.razor +@using Microsoft.Extensions.Logging.Abstractions + +@implements IDisposable + +@inject IJSRuntime JSRuntime +@inject IServiceProvider ServiceProvider + + + + +@code { + // Replace Component with your actual component type + private DotNetObjectReference? dotNetRef; + private List Mentions { get; set; } = new List() + { + new() + { + Id = "board", + Name = "Jane Simons", + Email = "jane.simons@company.com", + }, + new() + { + Id = "engineering", + Name = "Peter Parker", + Email = "peter.parker@company.com" + }, + new() + { + Id = "generalManager", + Name = "Liam Turner", + Email = "liam.turner@company.com" + } + }; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + dotNetRef = DotNetObjectReference.Create(this); + await JSRuntime.InvokeVoidAsync("initializeMentions", dotNetRef); + } + } + + [JSInvokable] + public async Task GetMentionSuggestionsAsync(string text) + { + return Mentions.Where(mention => mention.Name.ToLower().Contains(text)).ToArray(); + } + + [JSInvokable] + public async Task GetMentionSuggestionsHTML(List mentions) + { + using var htmlRenderer = new HtmlRenderer(ServiceProvider, NullLoggerFactory.Instance); + var html = await htmlRenderer.Dispatcher.InvokeAsync(async () => + { + var dictionary = new Dictionary + { + { "Items", mentions } + }; + var parameters = ParameterView.FromDictionary(dictionary); + var output = await htmlRenderer.RenderComponentAsync(parameters); + return output.ToHtmlString(); + }); + + return html; + } + + public void Dispose() + { + dotNetRef?.Dispose(); + } +} +```` +````razor MentionSuggestionList.razor +@* + IMPORTANT: outer div's "suggestion-item-list" class is mandatory. The plugin uses this class for querying. + IMPORTANT: inner div's "suggestion-item" class is mandatory too for the same reasons +*@ + +
+ @if (Items == null || Items.Count() == 0) + { +
+ No suggestions +
+ } else + { + @foreach (Mention item in Items) { +
+
+
+
+ @item.Name +
+
+ @item.Email +
+
+
+
+ } + } +
+ +@code { + [Parameter] + public IEnumerable Items { get; set; } +} +```` +````cs Mention.cs +public class Mention +{ + public string Id { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; +} +```` +````js npmjs/src/index.js +import { addMentionNodes, addTagNodes, getMentionsPlugin } from 'prosemirror-mentions'; + +let _dotnetRef; +window.initializeMentions = (dotnetRef) => { + _dotnetRef = dotnetRef; +} + +let mentionSuggestionsHTML = null; + +var mentionPlugin = getMentionsPlugin({ + getSuggestions: (type, text, done) => { + setTimeout(async () => { + if (type === 'mention') { + try { + const suggestions = await _dotnetRef.invokeMethodAsync('GetMentionSuggestionsAsync', text); + mentionSuggestionsHTML = await _dotnetRef.invokeMethodAsync('GetMentionSuggestionsHTML', suggestions); + done(suggestions); + } catch (error) { + console.error('Error getting suggestions:', error); + done([]); + } + } + }, 0); + }, + getSuggestionsHTML: (items, type) => { + if (type === 'mention') { + return mentionSuggestionsHTML; + } + } +}); + +window.pluginsProvider = (args) => { + const schema = args.getSchema(); + + return [mentionPlugin, ...args.getPlugins(schema)]; +} + +window.schemaProvider = (args) => { + const schema = args.getSchema(); + const Schema = args.ProseMirror.Schema; + const nodes = addMentionNodes(schema.spec.nodes); + const mentionsSchema = new Schema({ + nodes: nodes, + marks: schema.spec.marks + }); + + return mentionsSchema; +} + +```` + +## See Also + +* [Editor Schema](slug:editor-prosemirror-schema-overview) +* [Editor Plugins](slug:editor-prosemirror-plugins) +* [ProseMirror Documentation](https://prosemirror.net/docs/ref) +* [proseMirror-mentions](https://github.com/joelewis/prosemirror-mentions) \ No newline at end of file From e03a7adcef0447bc60d2ecc0ed69b704f20aa1d4 Mon Sep 17 00:00:00 2001 From: georgedautov Date: Mon, 8 Dec 2025 17:58:49 +0200 Subject: [PATCH 2/5] Integrate suggestions from code review --- knowledge-base/editor-mentions.md | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/knowledge-base/editor-mentions.md b/knowledge-base/editor-mentions.md index 22eebeb53..e454308cb 100644 --- a/knowledge-base/editor-mentions.md +++ b/knowledge-base/editor-mentions.md @@ -37,23 +37,21 @@ For dynamic positioning of the mentions list, set the [`EditMode`](slug:Telerik. ### Setting up WebPack and Installing proseMirror-mentions -1. Create a directory at the root of your project which will contain your JavaScript module (e.g. `npmjs`). All of the next steps will be done inside that directory -2. Setup the Javascript project by running: +1. Setup the Javascript project by running at the root of your project: ````SH.skip-repl npm init -y ```` > This command creates a `package.json` file containing the project's configuration. The `-y` flag accepts all defaults for simplicity. In a real world application, consider running `npm init` without the flag to configure settings interactively. -3. Install a JavaScript bundler. In this example we will use [webpack](https://webpack.js.org), so run: +2. Install a JavaScript bundler. In this example we will use [webpack](https://webpack.js.org), so run: ````SH.skip-repl npm install webpack webpack-cli --save-dev ```` -4. Configure the build script in the `scripts` section of `package.json`: +3. Configure the build script in the `scripts` section of `package.json` (in this example all of our Javascript files will go into `wwwroot/js`): ````JSON.skip-repl "scripts": { - "build": "webpack ./src/index.js --output-path ../wwwroot/js --output-filename index.bundle.js" + "build": "webpack ./wwwroot/js/index.js --output-path ./wwwroot/js --output-filename index.bundle.js" }, ```` - > In this example all of our Javascript files will go into `npmjs/src` 5. Update the module type in `package.json`: ````JSON.skip-repl "type": module" @@ -63,7 +61,8 @@ For dynamic positioning of the mentions list, set the [`EditMode`](slug:Telerik. ````SH.skip-repl npm install prosemirror-mentions ```` -7. Build the JavaScript bundle by running: +7. Create a file named `index.js` in your project's `wwwroot/js` directory and paste the contents from the **JavaScript Code** section below. +8. Build the JavaScript bundle by running: ````SH.skip-repl npm run build ```` @@ -90,19 +89,6 @@ After building the JavaScript bundle, you need to include it in your Blazor appl ```` -**Component Level (Component.razor):** -If you prefer to load the script only when the component is used, include it in your component with the `defer` attribute: - -
- -````razor Component.razor - - - -```` - -> Note: The script tag does not need `type="module"` since webpack bundles all ES6 modules into a single compatible file. - ### Integrate the Mentions Plugin The following code demonstrates how to integrate the `proseMirror-mentions` plugin in the Editor. @@ -230,7 +216,7 @@ public class Mention public string Email { get; set; } = string.Empty; } ```` -````js npmjs/src/index.js +````js wwwroot/js/index.js import { addMentionNodes, addTagNodes, getMentionsPlugin } from 'prosemirror-mentions'; let _dotnetRef; From 81877bd69e9513ce98380887f2f39d1beaf0eca4 Mon Sep 17 00:00:00 2001 From: georgedautov Date: Mon, 8 Dec 2025 18:01:09 +0200 Subject: [PATCH 3/5] Integrate improvements from code review --- knowledge-base/editor-mentions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knowledge-base/editor-mentions.md b/knowledge-base/editor-mentions.md index e454308cb..cba2e21f6 100644 --- a/knowledge-base/editor-mentions.md +++ b/knowledge-base/editor-mentions.md @@ -70,7 +70,7 @@ For dynamic positioning of the mentions list, set the [`EditMode`](slug:Telerik. ### Include the JavaScript Bundle -After building the JavaScript bundle, you need to include it in your Blazor application. You can do this at either the global or component level: +After building the JavaScript bundle, you need to include it in your Blazor application: **Global Level (App.razor):**
From abcbc2846c9599a9c24cb7a17db73e40c2ebed6b Mon Sep 17 00:00:00 2001 From: George Dautov Date: Tue, 9 Dec 2025 13:54:08 +0200 Subject: [PATCH 4/5] Update knowledge-base/editor-mentions.md Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> --- knowledge-base/editor-mentions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knowledge-base/editor-mentions.md b/knowledge-base/editor-mentions.md index cba2e21f6..c335023e7 100644 --- a/knowledge-base/editor-mentions.md +++ b/knowledge-base/editor-mentions.md @@ -37,7 +37,7 @@ For dynamic positioning of the mentions list, set the [`EditMode`](slug:Telerik. ### Setting up WebPack and Installing proseMirror-mentions -1. Setup the Javascript project by running at the root of your project: +1. Setup the Javascript project by running the following command in the root folder of your project: ````SH.skip-repl npm init -y ```` From ac62923409d4298979791fb0f72b805e557cd59f Mon Sep 17 00:00:00 2001 From: George Dautov Date: Tue, 9 Dec 2025 13:54:56 +0200 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> --- knowledge-base/editor-mentions.md | 51 ++++++++++++++++--------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/knowledge-base/editor-mentions.md b/knowledge-base/editor-mentions.md index c335023e7..874260e6a 100644 --- a/knowledge-base/editor-mentions.md +++ b/knowledge-base/editor-mentions.md @@ -41,7 +41,7 @@ For dynamic positioning of the mentions list, set the [`EditMode`](slug:Telerik. ````SH.skip-repl npm init -y ```` - > This command creates a `package.json` file containing the project's configuration. The `-y` flag accepts all defaults for simplicity. In a real world application, consider running `npm init` without the flag to configure settings interactively. + > This command creates a `package.json` file with the project's configuration. The `-y` flag accepts all defaults for simplicity. In a real world application, consider running `npm init` without the flag to configure settings interactively. 2. Install a JavaScript bundler. In this example we will use [webpack](https://webpack.js.org), so run: ````SH.skip-repl npm install webpack webpack-cli --save-dev @@ -61,7 +61,7 @@ For dynamic positioning of the mentions list, set the [`EditMode`](slug:Telerik. ````SH.skip-repl npm install prosemirror-mentions ```` -7. Create a file named `index.js` in your project's `wwwroot/js` directory and paste the contents from the **JavaScript Code** section below. +7. Create a file named `index.js` in your project's `wwwroot/js` directory and paste the contents from the respective code tab below. 8. Build the JavaScript bundle by running: ````SH.skip-repl npm run build @@ -175,37 +175,38 @@ The following code demonstrates how to integrate the `proseMirror-mentions` plug ````razor MentionSuggestionList.razor @* IMPORTANT: outer div's "suggestion-item-list" class is mandatory. The plugin uses this class for querying. - IMPORTANT: inner div's "suggestion-item" class is mandatory too for the same reasons + IMPORTANT: inner div's "suggestion-item" class is mandatory too for the same reasons. *@ -
- @if (Items == null || Items.Count() == 0) - { -
- No suggestions -
- } else - { - @foreach (Mention item in Items) { -
-
-
-
+
+
+
+
    + @if (Items == null || Items.Count() == 0) + { +
  • + No Suggestions +
  • + } + else + { + @foreach (Mention item in Items) + { +
  • @item.Name -
-
+
@item.Email -
-
-
-
- } - } + + } + } + +
+
@code { [Parameter] - public IEnumerable Items { get; set; } + public IEnumerable? Items { get; set; } } ```` ````cs Mention.cs