` element. |
-| `Width` | `string` | Controls the width of the Linear Gauge. |
-| `Height` | `string` | Controls the height of the Linear Gauge. |
+| `Class` | `string` | A custom CSS class for the `
` element. |
+| `Width` | `string` | The width of the Linear Gauge. The [scale](slug:linear-gauge-scale) width is smaller and [depends on the length of its `Min` and `Max` values](slug:lineargauge-kb-align-gauge-widths-with-different-min-max). |
+| `Height` | `string` | The height of the Linear Gauge. |
| `Transitions` | `bool?` | Controls if the Linear Gauge uses animations for its value changes. |
| `RenderAs` | `RenderingMode?`
(`SVG`) | Controls if the gauge renders as `SVG` or `Canvas`. |
diff --git a/components/grid/columns/stacked.md b/components/grid/columns/stacked.md
index 98bf967b0a..9b6caaca9b 100644
--- a/components/grid/columns/stacked.md
+++ b/components/grid/columns/stacked.md
@@ -79,9 +79,11 @@ The code snippet below uses 3 stacked columns. The first one is twice as wide as
## Integration with Other Features
-When the Grid is in `Stacked` data layout mode, it does not render column headers. As a result, column features like sorting, filtering, grouping, locking are not available through the classic Grid UI. Instead, use [ToolBar command tools](slug:components/grid/features/toolbar#command-tools) to enable the same functionality through different UI.
+In `Stacked` data layout mode the Grid rendering is different and some features use different UI and UX:
-Hierarchy relies on an expand/collapse button, which is below the stacked table row content.
+* The Grid does not render column headers. Column features like sorting, filtering, grouping, and locking require [ToolBar command tools](slug:components/grid/features/toolbar#command-tools).
+* The Grid does not render a command column. Combine [Grid row selection](slug:grid-selection-row) with [Toolbar command tools for the **Delete**, **Edit**, **Save**, and **Cancel** buttons](slug:components/grid/features/toolbar#command-tools).
+* Hierarchy relies on an expand/collapse button, which renders below the stacked table row content.
## Example
@@ -89,7 +91,7 @@ The following sample shows how to:
* Enable and disable column stacking, depending on the viewport width.
* Display 1 or 2 stacked columns, depending on the viewport width.
-* Render ToolBar tools for column operations only when the Grid is in `Stacked` data layout mode.
+* Render ToolBar tools for column and edit operations only when the Grid is in `Stacked` data layout mode.
>caption Using stacked data layout mode in the Blazor Grid
@@ -125,6 +127,9 @@ The following sample shows how to:
Filter
Columns
Group
+
Edit
+
Save
+
Cancel
}
diff --git a/components/grid/overview.md b/components/grid/overview.md
index 6b828f8a49..d055cb6f01 100644
--- a/components/grid/overview.md
+++ b/components/grid/overview.md
@@ -19,6 +19,7 @@ The Telerik Blazor Data Grid provides a comprehensive set of ready-to-use featur
The Telerik Blazor grid is built on native Blazor from the ground up, by a company with a long history of making enterprise-ready Grids. This results in a highly customizable Grid that delivers lighting fast performance.
+> tip **Jumpstart Your Grid**
With the Agentic UI Generator, you can build components and layouts using natural language prompts — directly inside AI-powered IDEs like VS Code and Cursor. Get intelligent assistance with component implementation, styling, layout design, and iconography powered by our documentation and APIs.
[Try the Agentic UI Generator](slug:agentic-ui-generator-getting-started)
## Creating Blazor Grid
diff --git a/components/grid/paging.md b/components/grid/paging.md
index d81eeee690..56d263156e 100644
--- a/components/grid/paging.md
+++ b/components/grid/paging.md
@@ -109,7 +109,7 @@ In addition to `Page` and `PageSize`, the Grid provides advanced pager configura
diff --git a/components/grid/smart-ai-features/overview.md b/components/grid/smart-ai-features/overview.md
index d682e7feb8..4d93b329b4 100644
--- a/components/grid/smart-ai-features/overview.md
+++ b/components/grid/smart-ai-features/overview.md
@@ -49,7 +49,6 @@ The following Grid methods work with the above types.
| --- | --- | --- |
| `GetAIRequest()` | `string` | Returns a `GridAIRequestDescriptor` that includes the user prompt if you pass it as a method argument. When using the `GridToolBarAIAssistantTool`, the app can receive the `GridAIRequestDescriptor` automatically from the `Request` property of the `OnPromptRequest` event argument, which is an `AIPromptPromptRequestEventArgs` object. |
| `ProcessAIResponseAsync()` | `string` | Processes a serialized `GridAIResponse` object that is received as a string method argument. Then, the Grid applies all defined data operations from the `GridAIResponse` to its state, for example, filtering, grouping, highlighting, and sorting. When using the `GridToolBarAIAssistantTool`, you can set the serialized `GridAIResponse` object from the endpoint directly to `Response` property of the `OnPromptRequest` event argument, which is an `AIPromptPromptRequestEventArgs` object. |
-| `GetAIResult()` | `string` | Returns a `GridAIResult` that you can use to [update properties from the Grid state](slug:grid-state#methods). Processes a serialized `GridAIResponse` object that is received as a string method argument. Then, the Grid applies all defined data operations from the `GridAIResponse` to its state, for example, filtering, grouping, highlighting, and sorting. When using the `GridToolBarAIAssistantTool`, you can set the serialized `GridAIResponse` object from the endpoint directly to `Response` property of the `OnPromptRequest` event argument, which is an `AIPromptPromptRequestEventArgs` object. |
## Tutorial
diff --git a/components/grid/templates/command-column-header.md b/components/grid/templates/command-column-header.md
index 5e9033eacf..57487cfd76 100644
--- a/components/grid/templates/command-column-header.md
+++ b/components/grid/templates/command-column-header.md
@@ -31,10 +31,12 @@ The `HeaderTemplate` of the Grid command column enables you to customize the hea
Actions
- Edit
- Update
- Cancel
- Delete
+
+ Edit
+ Update
+ Cancel
+ Delete
+
@@ -75,4 +77,4 @@ The `HeaderTemplate` of the Grid command column enables you to customize the hea
## See Also
-* [Grid Command Column](slug:components/grid/columns/command)
\ No newline at end of file
+* [Grid Command Column](slug:components/grid/columns/command)
diff --git a/components/grid/toolbar.md b/components/grid/toolbar.md
index bf00e22b37..74c938b3d0 100644
--- a/components/grid/toolbar.md
+++ b/components/grid/toolbar.md
@@ -36,6 +36,8 @@ The [Blazor Grid](https://demos.telerik.com/blazor-ui/grid/overview) provides se
| Sort | `GridToolBarSortTool` | A toggle button that opens a list of the sortable columns. Click a column to sort by it. On mobile devices, the popup renders as an `ActionSheet`. The tool also exposes an `Icon` parameter that allows you to override the default icon. |
| SearchBox | `GridToolBarSearchBoxTool` | A [searchbox that filters multiple string columns](slug:grid-searchbox) simultaneously. |
+The **Edit** command button is disabled if there is no [selected Grid row](slug:grid-selection-row). The **Save** and **Cancel** buttons are disabled when there is no row in edit mode.
+
### Layout Tools
| Tool Name | Tool Tag | Description |
@@ -84,45 +86,16 @@ Add a `
` tag inside `` to configure a toolbar, for exa
Custom Grid Tool
-
- Add a product
-
-
-
- Export to CSV
-
-
-
- Export to Excel
-
-
-
- Filter
-
-
-
- Sort
-
-
-
- Group
-
-
-
- Edit
-
-
-
- Save
-
-
-
- Cancel
-
-
-
- Delete
-
+ Add a product
+ Export to CSV
+ Export to Excel
+ Filter
+ Sort
+ Group
+ Edit
+ Save
+ Cancel
+ Delete
@@ -141,7 +114,7 @@ Add a `` tag inside `` to configure a toolbar, for exa
@code {
- private List GridData { get; set; }
+ private List GridData { get; set; } = new();
private IEnumerable SelectedPeople { get; set; } = Enumerable.Empty();
private void OnToolbarCustomClick()
@@ -151,12 +124,11 @@ Add a `` tag inside `` to configure a toolbar, for exa
protected override void OnInitialized()
{
- var data = new List();
var rand = new Random();
for (int i = 0; i < 50; i++)
{
- data.Add(new Person()
+ GridData.Add(new Person()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
@@ -164,38 +136,37 @@ Add a `` tag inside `` to configure a toolbar, for exa
AgeInYears = 20
});
}
- GridData = new List(data);
}
private void CreateItem(GridCommandEventArgs args)
{
- var argsItem = args.Item as Person;
+ var createdItem = (Person)args.Item;
- argsItem.EmployeeId = GridData.Count + 1;
+ createdItem.EmployeeId = GridData.Count + 1;
- GridData.Insert(0, argsItem);
+ GridData.Insert(0, createdItem);
}
private void UpdateItem(GridCommandEventArgs args)
{
- var argsItem = args.Item as Person;
- var itemForEdit = GridData.FirstOrDefault(i => i.EmployeeId == argsItem.EmployeeId);
+ var updatedItem = (Person)args.Item;
+ var itemForEdit = GridData.FirstOrDefault(i => i.EmployeeId == updatedItem.EmployeeId);
if (itemForEdit != null)
{
- itemForEdit.AgeInYears = argsItem.AgeInYears;
- itemForEdit.HireDate = argsItem.HireDate;
- itemForEdit.Name = argsItem.Name;
+ itemForEdit.AgeInYears = updatedItem.AgeInYears;
+ itemForEdit.HireDate = updatedItem.HireDate;
+ itemForEdit.Name = updatedItem.Name;
}
}
private void DeleteItem(GridCommandEventArgs args)
{
- var argsItem = args.Item as Person;
+ var deletedItem = (Person)args.Item;
- if (GridData.Contains(argsItem))
+ if (GridData.Contains(deletedItem))
{
- GridData.Remove(argsItem);
+ GridData.Remove(deletedItem);
}
}
@@ -203,7 +174,7 @@ Add a `` tag inside `` to configure a toolbar, for exa
{
public int? EmployeeId { get; set; }
- public string Name { get; set; }
+ public string Name { get; set; } = string.Empty;
public int? AgeInYears { get; set; }
diff --git a/components/popover/position-collision.md b/components/popover/position-collision.md
index b7257bb4a3..1d8ad4a2b8 100644
--- a/components/popover/position-collision.md
+++ b/components/popover/position-collision.md
@@ -105,3 +105,7 @@ The following example lets you experiment with the available settings that contr
}
````
+
+## See Also
+
+* [How to Align Popover with Anchor After Content and Size Change](slug:popover-kb-refresh-callout-position)
diff --git a/docs-builder.yml b/docs-builder.yml
index 3e0f0dbce4..830e5beb87 100644
--- a/docs-builder.yml
+++ b/docs-builder.yml
@@ -652,3 +652,15 @@ redirects:
-
from: "^/?$"
to: "/introduction"
+
+-
+ from: ^/ai/overview$
+ to: "/ai/ai-coding-assistant/overview"
+
+-
+ from: ^/ai/ai-coding-assistant/coding-extension$
+ to: "/ai/ai-coding-assistant/overview"
+
+-
+ from: ^/ai/coding-extension$
+ to: "/ai/ai-coding-assistant/overview"
\ No newline at end of file
diff --git a/knowledge-base/diagram-change-shape-color-onshapeclick.md b/knowledge-base/diagram-change-shape-color-onshapeclick.md
new file mode 100644
index 0000000000..24048f7728
--- /dev/null
+++ b/knowledge-base/diagram-change-shape-color-onshapeclick.md
@@ -0,0 +1,259 @@
+---
+title: Change Diagram Shape Color on Shape Click
+description: Learn how to change the Diagram shape background color when using the Diagram OnShapeClick event.
+type: troubleshooting
+page_title: How to Change Diagram Shape Color on Shape Click
+slug: diagram-kb-change-shape-color-onshapeclick
+tags: blazor, diagram
+ticketid: 1703700
+res_type: kb
+---
+
+## Environment
+
+
+
+
+ | Product |
+ Diagram for Blazor |
+
+
+
+
+## Description
+
+I am using the Diagram `OnShapeClick` event to update the `DiagramShapeFill` `Color` of the selected Diagram shape. However, this resets the positions of the shapes that the user has dragged. How to update the shape background colors while maintaining the current shape positions? In addition, I want to persist the custom shape colors when the Diagram `Layout` changes.
+
+## Cause
+
+The Diagram `OnShapeClick` event handler is an `EventCallback` and triggers component re-render. If the [Diagram shapes are defined](slug:diagram-shapes#basics) without their `X` and `Y` properties and [shape dragging](slug:diagram-shapes#editability) is enabled, the Razor component definition does not include the current shape positions. As a result, a re-render resets the shapes to their original places.
+
+Similarly, if the Diagram Shape background colors are not part of the Razor component declaration, they will be reset to the default value if the Diagram `Layout` changes.
+
+## Solution
+
+Use the Diagram `SaveAsJsonAsync` and `LoadFromJsonAsync` methods to [define and persist the Diagram state through JSON](slug:diagram-overview#define-shapes-and-connections-in-json) on each render:
+
+1. Implement classes with property names that correspond to the ones in the [Diagram JSON state](slug:diagram-overview#define-shapes-and-connections-in-json).
+1. Subscribe to the [Diagram `OnShapeClick` event](slug:diagram-events#onshapeclick).
+1. Use the Diagram [`SaveAsJsonAsync` method](slug:telerik.blazor.components.telerikdiagram#methods) to get the current component state and deserialize it.
+1. Update the desired shape background.
+1. Serialize the updated Diagram state object and use the [`LoadFromJsonAsync` method](slug:telerik.blazor.components.telerikdiagram#methods) to apply it to the Diagram.
+1. (optional) If the Diagram is using different [shape types](slug:diagram-shapes#shape-types), then [implement a custom JSON converter](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to) for them.
+
+>caption Change Diagram Shape background on click and persist current shape position
+
+```RAZOR
+@using System.Text.Json
+@using System.Text.Json.Serialization
+
+Drag a shape and then click on a shape to change its background color. The shape positions will persist.
+
+Click on a shape and
+
+ Toggle Diagram Layout Type
+
+The shape background colors will persist.
+
+
+
+
+
+
+
+@code {
+ #nullable enable
+
+ private TelerikDiagram? DiagramRef { get; set; }
+
+ private DiagramLayoutType DiagramLayoutType { get; set; } = DiagramLayoutType.Tree;
+
+ private void OnButtonClick()
+ {
+ DiagramLayoutType = DiagramLayoutType == DiagramLayoutType.Tree ? DiagramLayoutType.Force : DiagramLayoutType.Tree;
+ }
+
+ private async Task OnDiagramShapeClick(DiagramShapeClickEventArgs args)
+ {
+ DiagramJson = await DiagramRef!.SaveAsJsonAsync();
+ JsonSerializerOptions jsonOptions = new JsonSerializerOptions()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ };
+
+ DiagramState? diagramStateObject = JsonSerializer.Deserialize(DiagramJson, jsonOptions);
+
+ if (diagramStateObject is not null && diagramStateObject.Shapes is not null)
+ {
+ var rnd = Random.Shared;
+ string newFillColor = $"rgb({rnd.Next(48, 128)},{rnd.Next(48, 128)},{rnd.Next(48, 128)})";
+
+ diagramStateObject.Shapes.First(x => x.Id == args.Id).Fill.Color = newFillColor;
+
+ DiagramJson = JsonSerializer.Serialize(diagramStateObject, jsonOptions);
+ await DiagramRef!.LoadFromJsonAsync(DiagramJson);
+ }
+ }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ if (firstRender && DiagramRef is not null)
+ {
+ // Wait for HTML and client-side Diagram instance
+ await Task.Delay(1);
+
+ await DiagramRef.LoadFromJsonAsync(DiagramJson);
+ StateHasChanged();
+ }
+
+ await base.OnAfterRenderAsync(firstRender);
+ }
+
+ private string DiagramJson { get; set; } = @"
+ {
+ ""shapes"": [
+ {
+ ""id"": ""shape1"",
+ ""type"": ""Circle"",
+ ""content"": {
+ ""text"": ""Shape 1""
+ },
+ ""x"": 200,
+ ""y"": 50
+ },
+ {
+ ""id"": ""shape2"",
+ ""content"": {
+ ""text"": ""Shape 2""
+ },
+ ""height"": 100,
+ ""width"": 160,
+ ""x"": 50,
+ ""y"": 200
+ },
+ {
+ ""id"": ""shape3"",
+ ""type"": ""Display"",
+ ""content"": {
+ ""text"": ""Shape 3""
+ },
+ ""x"": 300,
+ ""y"": 200
+ }
+ ],
+ ""connections"": [
+ {
+ ""from"": {
+ ""shapeId"": ""shape1""
+ },
+ ""to"": {
+ ""shapeId"": ""shape2""
+ }
+ },
+ {
+ ""from"": {
+ ""shapeId"": ""shape1"",
+ ""connector"":""Right""
+ },
+ ""to"": {
+ ""shapeId"": ""shape3"",
+ ""connector"":""Top""
+ }
+ }
+ ]
+ }";
+
+ #region Diagram State Classes
+
+ public class DiagramState
+ {
+ public IEnumerable? Shapes { get; set; }
+ public IEnumerable? Connections { get; set; }
+ }
+
+ public class ShapeDescriptor
+ {
+ public string Id { get; set; } = Guid.NewGuid().ToString();
+
+ [JsonConverter(typeof(DiagramShapeTypeJsonConverter))]
+ public DiagramShapeType Type { get; set; } = DiagramShapeType.Rectangle;
+ public double? X { get; set; }
+ public double? Y { get; set; }
+
+ public double? Height { get; set; } = 100;
+ public double? Width { get; set; } = 100;
+
+ public ShapeDescriptorContentFill Fill { get; set; } = new();
+
+ public ShapeDescriptorContent Content { get; set; } = new();
+ }
+
+ public class ShapeDescriptorContent
+ {
+ public string Text { get; set; } = string.Empty;
+ }
+
+ public class ShapeDescriptorContentFill
+ {
+ public string? Color { get; set; }
+ }
+
+ public class ConnectionDescriptor
+ {
+ public string Id { get; set; } = Guid.NewGuid().ToString();
+
+ public ConnectionDescriptorFromTo From { get; set; } = new();
+ public ConnectionDescriptorFromTo To { get; set; } = new();
+ }
+
+ public class ConnectionDescriptorFromTo
+ {
+ public string ShapeId { get; set; } = string.Empty;
+ }
+
+ #endregion Diagram State Classes
+
+ public class DiagramModel
+ {
+ public string Id { get; set; } = string.Empty;
+ public int? ParentId { get; set; }
+ public string Text { get; set; } = string.Empty;
+ public DiagramShapeType Type { get; set; } = DiagramShapeType.Rectangle;
+ public string BackgroundColor { get; set; } = string.Empty;
+ }
+
+ public class DiagramShapeTypeJsonConverter : JsonConverter
+ {
+ public override DiagramShapeType Read(
+ ref Utf8JsonReader reader,
+ Type typeToConvert,
+ JsonSerializerOptions options)
+ {
+ string shapeTypeString = reader.GetString()!;
+
+ if (Enum.TryParse(shapeTypeString, true, out DiagramShapeType shapeType)) {
+ return shapeType;
+ }
+ else
+ {
+ return DiagramShapeType.Rectangle;
+ }
+ }
+
+ public override void Write(
+ Utf8JsonWriter writer,
+ DiagramShapeType shapeType,
+ JsonSerializerOptions options) =>
+ writer.WriteStringValue(shapeType.ToString());
+ }
+}
+```
+
+## See Also
+
+* [Diagram `OnShapeClick` Event](slug:diagram-events#onshapeclick)
+* [Save and Load Diagram State through JSON](slug:diagram-overview#define-shapes-and-connections-in-json)
+* [Diagram Shapes](slug:diagram-shapes)
+* [Diagram Methods](slug:telerik.blazor.components.telerikdiagram#methods)
diff --git a/knowledge-base/editor-mentions.md b/knowledge-base/editor-mentions.md
new file mode 100644
index 0000000000..874260e6ad
--- /dev/null
+++ b/knowledge-base/editor-mentions.md
@@ -0,0 +1,277 @@
+---
+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
+
+
+
+
+ | Product |
+ Editor 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. Setup the Javascript project by running the following command in the root folder of your project:
+ ````SH.skip-repl
+ npm init -y
+ ````
+ > 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
+ ````
+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 ./wwwroot/js/index.js --output-path ./wwwroot/js --output-filename index.bundle.js"
+ },
+ ````
+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. 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
+ ````
+ 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:
+
+**Global Level (App.razor):**
+
+
+````razor App.razor
+
+
+
+
+
+
+
+
+
+
+
+````
+
+### 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.
+*@
+
+
+
+@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 wwwroot/js/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
diff --git a/knowledge-base/grid-copy-text-clipboard.md b/knowledge-base/grid-copy-text-clipboard.md
new file mode 100644
index 0000000000..23b12d523e
--- /dev/null
+++ b/knowledge-base/grid-copy-text-clipboard.md
@@ -0,0 +1,143 @@
+---
+title: Copy Text from Grid Row to Clipboard
+description: Learn how to add a right-click context menu option to copy text from a Grid to the clipboard and paste it into the Editor.
+type: how-to
+page_title: How to Copy Text from Grid to Clipboard and Paste into UI for Blazor Editor
+meta_title: How to Copy Text from Grid to Clipboard and Paste into UI for Blazor Editor
+slug: grid-kb-copy-text-clipboard
+tags: blazor, clipboard, context-menu, copy-text, grid
+res_type: kb
+ticketid: 1695036
+---
+
+## Environment
+
+
+
+
+ | Product |
+ Grid for Blazor |
+
+
+
+
+## Description
+
+I want to add a right-click context menu to a Grid, which allows copying text from the Grid to the clipboard. The copied text can then be pasted into an Editor or anywhere else.
+
+## Solution
+
+To achieve this, implement a context menu for the Grid and include a "Copy to Clipboard" option. Follow these steps:
+
+1. Define the context menu and its items.
+2. Handle the right-click event to show the context menu.
+3. Implement the logic to copy the selected row's text to the clipboard by using the [navigator clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard).
+
+````Razor
+@using System.Collections.Generic
+@using System.Collections.ObjectModel
+@inject IJSRuntime JS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ private string TheEditorValue { get; set; }
+ private ObservableCollection GridData { get; set; }
+ private List