Skip to content

Commit b3d8766

Browse files
committed
docs(Editor): add editor mentions kb
1 parent f1b8fec commit b3d8766

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed

knowledge-base/editor-mentions.md

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
---
2+
title: Mentions in Editor
3+
description: How to support mentions in the Editor?
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+
## Solution
25+
26+
<div class="skip-repl"></div>
27+
28+
````razor Component
29+
@using Microsoft.Extensions.Logging.Abstractions
30+
31+
@implements IDisposable
32+
33+
<TelerikEditor Plugins="pluginsProvider"
34+
Schema="schemaProvider"
35+
EditMode="EditorEditMode.Div">
36+
</TelerikEditor>
37+
38+
@code {
39+
private DotNetObjectReference<Editor>? _dotNetRef;
40+
private List<Mention> Mentions { get; set; } = new List<Mention>()
41+
{
42+
new()
43+
{
44+
Id = "board",
45+
Name = "Jane Simons",
46+
Email = "jane.simons@company.com",
47+
},
48+
new()
49+
{
50+
Id = "engineering",
51+
Name = "Peter Parker",
52+
Email = "peter.parker@company.com"
53+
},
54+
new()
55+
{
56+
Id = "generalManager",
57+
Name = "Liam Turner",
58+
Email = "liam.turner@company.com"
59+
}
60+
};
61+
62+
[Inject]
63+
private IJSRuntime JSRuntime { get; set; }
64+
65+
[Inject]
66+
private IServiceProvider ServiceProvider { get; set; }
67+
68+
protected override async Task OnAfterRenderAsync(bool firstRender)
69+
{
70+
if (firstRender)
71+
{
72+
_dotNetRef = DotNetObjectReference.Create(this);
73+
await JSRuntime.InvokeVoidAsync("initializeMentions", _dotNetRef);
74+
}
75+
}
76+
77+
[JSInvokable]
78+
public async Task<Mention[]> GetMentionSuggestionsAsync(string text)
79+
{
80+
return Mentions.Where(mention => mention.Name.ToLower().Contains(text)).ToArray();
81+
}
82+
83+
[JSInvokable]
84+
public async Task<string> GetMentionSuggestionsHTML(List<Mention> mentions)
85+
{
86+
using var htmlRenderer = new HtmlRenderer(ServiceProvider, NullLoggerFactory.Instance);
87+
var html = await htmlRenderer.Dispatcher.InvokeAsync(async () =>
88+
{
89+
var dictionary = new Dictionary<string, object?>
90+
{
91+
{ "Items", mentions }
92+
};
93+
var parameters = ParameterView.FromDictionary(dictionary);
94+
var output = await htmlRenderer.RenderComponentAsync<MentionSuggestionList>(parameters);
95+
return output.ToHtmlString();
96+
});
97+
98+
return html;
99+
}
100+
101+
public void Dispose()
102+
{
103+
_dotNetRef?.Dispose();
104+
}
105+
}
106+
````
107+
````razor MentionSuggestionList
108+
<div class="suggestion-item-list">
109+
@if(Items == null || !Items.Any())
110+
{
111+
<div class="suggestion-item">
112+
No suggestions
113+
</div>
114+
} else
115+
{
116+
@foreach(var item in Items) {
117+
<div class="suggestion-item">
118+
<div class="suggestion-item-content">
119+
<div class="suggestion-text">
120+
<div class="suggestion-name">
121+
@item.Name
122+
</div>
123+
<div class="suggestion-title">
124+
@item.Email
125+
</div>
126+
</div>
127+
</div>
128+
</div>
129+
}
130+
}
131+
</div>
132+
133+
@code {
134+
[Parameter]
135+
public IEnumerable<Mention> Items { get; set; }
136+
}
137+
````
138+
````cs Mention.cs
139+
public class Mention
140+
{
141+
public string Id { get; set; }
142+
public string Name { get; set; }
143+
public string Email { get; set; }
144+
}
145+
````
146+
````js Javascript
147+
import { addMentionNodes, addTagNodes, getMentionsPlugin } from 'prosemirror-mentions';
148+
149+
let _dotnetRef;
150+
window.initializeMentions = (dotnetRef) => {
151+
_dotnetRef = dotnetRef;
152+
}
153+
154+
/**
155+
* IMPORTANT: outer div's "suggestion-item-list" class is mandatory. The plugin uses this class for querying.
156+
* IMPORTANT: inner div's "suggestion-item" class is mandatory too for the same reasons
157+
*/
158+
var getMentionSuggestionsHTML = items => '<div class="suggestion-item-list">' +
159+
items.map(i => '<div class="suggestion-item">' + i.name + '</div>').join('') +
160+
'</div>';
161+
162+
/**
163+
* IMPORTANT: outer div's "suggestion-item-list" class is mandatory. The plugin uses this class for querying.
164+
* IMPORTANT: inner div's "suggestion-item" class is mandatory too for the same reasons
165+
*/
166+
var getTagSuggestionsHTML = items => '<div class="suggestion-item-list">' +
167+
items.map(i => '<div class="suggestion-item">' + i.tag + '</div>').join('') +
168+
'</div>';
169+
170+
let mentionSuggestionsHTML = null;
171+
172+
var mentionPlugin = getMentionsPlugin({
173+
getSuggestions: (type, text, done) => {
174+
setTimeout(async () => {
175+
if (type === 'mention') {
176+
try {
177+
const suggestions = await _dotnetRef.invokeMethodAsync('GetMentionSuggestionsAsync', text);
178+
mentionSuggestionsHTML = await _dotnetRef.invokeMethodAsync('GetMentionSuggestionsHTML', suggestions);
179+
done(suggestions);
180+
} catch (error) {
181+
console.error('Error getting suggestions:', error);
182+
done([]);
183+
}
184+
} else {
185+
// pass dummy tag suggestions
186+
done([{ tag: 'WikiLeaks' }, { tag: 'NetNeutrality' }])
187+
}
188+
}, 0);
189+
},
190+
getSuggestionsHTML: (items, type) => {
191+
if (type === 'mention') {
192+
return mentionSuggestionsHTML;
193+
} else if (type === 'tag') {
194+
return getTagSuggestionsHTML(items)
195+
}
196+
}
197+
});
198+
199+
window.pluginsProvider = (args) => {
200+
const schema = args.getSchema();
201+
202+
return [mentionPlugin, ...args.getPlugins(schema)];
203+
}
204+
205+
window.schemaProvider = (args) => {
206+
const schema = args.getSchema();
207+
const Schema = args.ProseMirror.Schema;
208+
const nodes = addTagNodes(addMentionNodes(schema.spec.nodes));
209+
const mentionsSchema = new Schema({
210+
nodes: nodes,
211+
marks: schema.spec.marks
212+
});
213+
214+
return mentionsSchema;
215+
}
216+
217+
````

0 commit comments

Comments
 (0)