Skip to content

Commit d3884ad

Browse files
georgedautovdimodi
andcommitted
docs(Editor): add editor mentions kb
Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com>
1 parent f1b8fec commit d3884ad

File tree

1 file changed

+252
-0
lines changed

1 file changed

+252
-0
lines changed

knowledge-base/editor-mentions.md

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
---
2+
title: Mentions in Editor
3+
description: Learn how to add support for mentions in the Telerik Editor component for Blazor.
4+
type: how-to
5+
page_title: Mentions in Editor
6+
slug: editor-kb-mentions
7+
position:
8+
tags: telerik, blazor, editor, mentions
9+
ticketid: 1545505
10+
res_type: kb
11+
---
12+
13+
## Environment
14+
15+
<table>
16+
<tbody>
17+
<tr>
18+
<td>Product</td>
19+
<td>Editor for Blazor</td>
20+
</tr>
21+
</tbody>
22+
</table>
23+
24+
## Description
25+
26+
How to enable or implement support for `@mentions` in the TelerikEditor for Blazor, similar to GitHub, Facebook, etc.?
27+
28+
## Solution
29+
30+
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).
31+
32+
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.
33+
34+
````RAZOR.skip-repl
35+
<TelerikEditor EditMode="EditorEditMode.Div" />
36+
````
37+
38+
### Setting up WebPack and Installing proseMirror-mentions
39+
40+
1. Create a directory at the root of your project which will contain your JavaScript module (e.g. `npmjs`)
41+
2. In that directory, run
42+
````SH.skip-repl
43+
npm init -y
44+
````
45+
> The `-y` flag accepts all defaults for simplicity. In a real world application, consider running `npm init` without the flag to configure settings interactively.
46+
3. Install a JavaScript bundler. In this example we will use [webpack](https://webpack.js.org), so run
47+
````SH.skip-repl
48+
npm install webpack webpack-cli --save-dev
49+
````
50+
4. Configure the build script in `package.json`. Add this to the `scripts` section:
51+
````JSON.skip-repl
52+
"scripts": {
53+
"build": "webpack ./src/index.js --output-path ../wwwroot/js --output-filename index.bundle.js"
54+
},
55+
````
56+
> In this example all of our Javascript files will go into `npmjs/src`
57+
5. Update the module type in `package.json`:
58+
````JSON.skip-repl
59+
"type": module"
60+
````
61+
This enables ES6 `import`/`export` syntax instead of the CommonJS require statements which will be useful later on.
62+
6. Install [proseMirror-mentions](https://github.com/joelewis/prosemirror-mentions) by running
63+
````SH.skip-repl
64+
npm install prosemirror-mentions
65+
````
66+
inside the `npmjs` directory
67+
68+
### Integrate the Mentions Plugin
69+
70+
The following code demonstrates how to integrate the `proseMirror-mentions` plugin in the Editor.
71+
72+
<div class="skip-repl"></div>
73+
74+
````razor Component.razor
75+
@using Microsoft.Extensions.Logging.Abstractions
76+
77+
@implements IDisposable
78+
79+
@inject IJSRuntime JSRuntime
80+
@inject IServiceProvider ServiceProvider
81+
82+
<TelerikEditor Plugins="pluginsProvider"
83+
Schema="schemaProvider"
84+
EditMode="EditorEditMode.Div">
85+
</TelerikEditor>
86+
87+
@code {
88+
// Replace Component with your actual component type
89+
private DotNetObjectReference<Component>? dotNetRef;
90+
private List<Mention> Mentions { get; set; } = new List<Mention>()
91+
{
92+
new()
93+
{
94+
Id = "board",
95+
Name = "Jane Simons",
96+
Email = "jane.simons@company.com",
97+
},
98+
new()
99+
{
100+
Id = "engineering",
101+
Name = "Peter Parker",
102+
Email = "peter.parker@company.com"
103+
},
104+
new()
105+
{
106+
Id = "generalManager",
107+
Name = "Liam Turner",
108+
Email = "liam.turner@company.com"
109+
}
110+
};
111+
112+
protected override async Task OnAfterRenderAsync(bool firstRender)
113+
{
114+
if (firstRender)
115+
{
116+
dotNetRef = DotNetObjectReference.Create(this);
117+
await JSRuntime.InvokeVoidAsync("initializeMentions", dotNetRef);
118+
}
119+
}
120+
121+
[JSInvokable]
122+
public async Task<Mention[]> GetMentionSuggestionsAsync(string text)
123+
{
124+
return Mentions.Where(mention => mention.Name.ToLower().Contains(text)).ToArray();
125+
}
126+
127+
[JSInvokable]
128+
public async Task<string> GetMentionSuggestionsHTML(List<Mention> mentions)
129+
{
130+
using var htmlRenderer = new HtmlRenderer(ServiceProvider, NullLoggerFactory.Instance);
131+
var html = await htmlRenderer.Dispatcher.InvokeAsync(async () =>
132+
{
133+
var dictionary = new Dictionary<string, object?>
134+
{
135+
{ "Items", mentions }
136+
};
137+
var parameters = ParameterView.FromDictionary(dictionary);
138+
var output = await htmlRenderer.RenderComponentAsync<MentionSuggestionList>(parameters);
139+
return output.ToHtmlString();
140+
});
141+
142+
return html;
143+
}
144+
145+
public void Dispose()
146+
{
147+
dotNetRef?.Dispose();
148+
}
149+
}
150+
````
151+
````razor MentionSuggestionList.razor
152+
@*
153+
IMPORTANT: outer div's "suggestion-item-list" class is mandatory. The plugin uses this class for querying.
154+
IMPORTANT: inner div's "suggestion-item" class is mandatory too for the same reasons
155+
*@
156+
157+
<div class="suggestion-item-list">
158+
@if (Items == null || Items.Count == 0)
159+
{
160+
<div class="suggestion-item">
161+
No suggestions
162+
</div>
163+
} else
164+
{
165+
@foreach (Mention item in Items) {
166+
<div class="suggestion-item">
167+
<div class="suggestion-item-content">
168+
<div class="suggestion-text">
169+
<div class="suggestion-name">
170+
@item.Name
171+
</div>
172+
<div class="suggestion-title">
173+
@item.Email
174+
</div>
175+
</div>
176+
</div>
177+
</div>
178+
}
179+
}
180+
</div>
181+
182+
@code {
183+
[Parameter]
184+
public IEnumerable<Mention> Items { get; set; }
185+
}
186+
````
187+
````cs Mention.cs
188+
public class Mention
189+
{
190+
public string Id { get; set; } = string.Empty;
191+
public string Name { get; set; } = string.Empty;
192+
public string Email { get; set; } = string.Empty;
193+
}
194+
````
195+
````js index.js
196+
import { addMentionNodes, addTagNodes, getMentionsPlugin } from 'prosemirror-mentions';
197+
198+
let _dotnetRef;
199+
window.initializeMentions = (dotnetRef) => {
200+
_dotnetRef = dotnetRef;
201+
}
202+
203+
let mentionSuggestionsHTML = null;
204+
205+
var mentionPlugin = getMentionsPlugin({
206+
getSuggestions: (type, text, done) => {
207+
setTimeout(async () => {
208+
if (type === 'mention') {
209+
try {
210+
const suggestions = await _dotnetRef.invokeMethodAsync('GetMentionSuggestionsAsync', text);
211+
mentionSuggestionsHTML = await _dotnetRef.invokeMethodAsync('GetMentionSuggestionsHTML', suggestions);
212+
done(suggestions);
213+
} catch (error) {
214+
console.error('Error getting suggestions:', error);
215+
done([]);
216+
}
217+
}
218+
}, 0);
219+
},
220+
getSuggestionsHTML: (items, type) => {
221+
if (type === 'mention') {
222+
return mentionSuggestionsHTML;
223+
}
224+
}
225+
});
226+
227+
window.pluginsProvider = (args) => {
228+
const schema = args.getSchema();
229+
230+
return [mentionPlugin, ...args.getPlugins(schema)];
231+
}
232+
233+
window.schemaProvider = (args) => {
234+
const schema = args.getSchema();
235+
const Schema = args.ProseMirror.Schema;
236+
const nodes = addMentionNodes(schema.spec.nodes);
237+
const mentionsSchema = new Schema({
238+
nodes: nodes,
239+
marks: schema.spec.marks
240+
});
241+
242+
return mentionsSchema;
243+
}
244+
245+
````
246+
247+
## See Also
248+
249+
* [Editor Schema](slug:editor-prosemirror-schema-overview)
250+
* [Editor Plugins](slug:editor-prosemirror-plugins)
251+
* [ProseMirror Documentation](https://prosemirror.net/docs/ref)
252+
* [proseMirror-mentions](https://github.com/joelewis/prosemirror-mentions)

0 commit comments

Comments
 (0)