diff --git a/_contentTemplates/common/pager-settings.md b/_contentTemplates/common/pager-settings.md index 61cbce8daa..e0e1800fba 100644 --- a/_contentTemplates/common/pager-settings.md +++ b/_contentTemplates/common/pager-settings.md @@ -4,7 +4,7 @@ | Attribute | Type and Default Value | Description | |----------|----------|----------| -|`Adaptive` | `bool`
(`true`) | Whether pager elements should change based on the screen size. When enabled, the Pager will hide its `Pager Info` and `PageSize Dropdownlist` if they cannot fit in the available space. In the smallest resolution, the page buttons will be rendered as a select element. +|`Responsive` | `bool`
(`true`) | Whether pager elements should change based on the screen size. When enabled, the Pager will hide its `Pager Info` and `PageSize Dropdownlist` if they cannot fit in the available space. In the smallest resolution, the page buttons will be rendered as a select element. |`ButtonCount` | `int`
(10) | The maximum number of page buttons that will be visible. To take effect, `ButtonCount` must be smaller than the page count (`ButtonCount < Total / PageSize`). | `InputType` | `PagerInputType`
(`Buttons`) | Whether the pager will show numeric buttons to go to a specific page, or a textbox to type the page index. The arrow buttons are always visible. The `PagerInputType` enum accepts values `Buttons` or `Input`. When `Input` is used, the page index will change when the textbox is blurred, or when the user hits Enter. This is to avoid unintentional data requests. | `PageSizes` | `List` | Allows users to change the page size via a DropDownList. The attribute configures the DropDownList options. A `null` item in the `PageSizes` `List` will render an "All" option. By default, the Pager DropDownList is not displayed. You can also set `PageSizes` to `null` programmatically to remove the DropDownList at any time. diff --git a/ai/agentic-ui-generator/getting-started.md b/ai/agentic-ui-generator/getting-started.md index 2d0fe316c1..62ff9fa7e4 100644 --- a/ai/agentic-ui-generator/getting-started.md +++ b/ai/agentic-ui-generator/getting-started.md @@ -13,35 +13,65 @@ tag: new This article describes how to install, configure, and use the Telerik UI for Blazor Agentic UI Generator in Visual Studio and Visual Studio Code. -## Prerequisites +## Quick Start + +Follow these steps to set up the Agentic UI Generator: + +1. Ensure you have a supported license. You need a DevCraft Complete or Ultimate Subscription to access the Agentic UI Generator. If you have a different license type, you can start a [30-day AI Tools trial](https://www.telerik.com/mcp-servers-blazor/thank-you). + + > For more information about access requirements based on the license type, see [Usage Limits](slug:agentic-ui-generator-overview#usage-limits). +1. To add the MCP server to your IDE, create an `.mcp.json` file in your solution (if you are using Visual Studio), or an `mcp.json` file in your workspace (if you are using Visual Studio Code) with the following configuration: + ````JSON.skip-repl + { + "servers": { + "blazor-ui-generator": { + "type": "http", + "url": "https://uiagent.mcp.telerik.com/mcp/blazor", + "headers": { + "x-api-key": "YOUR API KEY" + } + } + } + } + ```` + The server name `blazor-ui-generator` can be customized as desired. The name helps distinguish the MCP server in your configuration and does not affect how you invoke the generator tool in your prompt. + + > For more details on how to configure the MCP server, refer to the instructions for your specific IDE below: + > * [Visual Studio](#visual-studio) + > * [Visual Studio Code](#visual-studio-code) + +1. Log in to your [Telerik account](https://www.telerik.com/account/) and generate an API key from the [API Keys page](https://www.telerik.com/account/downloads/api-keys). Replace `YOUR API KEY` in the `mcp.json` file with the generated key. + + > Refer to the [Obtaining the API Key](#obtaining-the-api-key) section below for detailed instructions. +1. Open the AI chat interface of your IDE and start your prompt with the `#telerik_ui_generator` handle to invoke the main generator tool: + ````TEXT.skip-repl + #telerik_ui_generator Create a dashboard page with a grid showing sales data and a chart visualizing monthly trends. + ```` + + > Step-by-step usage instructions are available in [Using the Agentic UI Generator](#using-the-agentic-ui-generator). + +The video below demonstrates all of the above steps in action and shows the complete Agentic UI Generator setup process: -An AI-powered IDE that supports MCP servers, for example, Visual Studio, Visual Studio Code, Cursor. +

-## Installation +## Detailed Installation Instructions -The Agentic UI Generator is available as an MCP (Model Context Protocol) server that integrates with AI-powered IDEs like Visual Studio Code, Cursor, and other MCP-compatible tools. +Use the documentation of your AI-powered MCP client to add the Agentic UI Generator to a specific workspace or globally. Below you can find installation tips and examples for some popular MCP clients, and instructions for obtaining your API key. -### Configuration Steps +### Visual Studio -This section contains information about how to set up the Agentic UI Generator in Visual Studio and Visual Studio Code. +You can use the Agentic UI Generator in Visual Studio on an application level, and you can also enable global automatic discovery of the tool. -#### Visual Studio +#### Application Configuration -1. In your browser, go to the [API Keys](https://www.telerik.com/account/downloads/api-keys) page in your Telerik account. If you already have an existing API key, you can skip steps 2-6 and continue from step 7, instead of creating a new key. -2. Click **Generate New Key +**. -3. In the **Key Note** field, add a note that describes the API key. -4. Click **Generate Key**. -5. Select **Copy and Close**. Once you close the window, you can no longer copy the generated key. For security reasons, the **API Keys** page displays only a portion of the key. -6. Store the generated NuGet API key as you will need it in the next steps. -7. To enable the Agentic UI Generator in your application, in Visual Studio, add a `.mcp.json` file to the solution folder. -8. In the `.mcp.json` file, set your API Key as `x-api-key` value, as demonstrated in the JSON configuration below. +To enable the Agentic UI Generator in your application, in Visual Studio, add a `.mcp.json` file to the solution folder. >caption .mcp.json ````JSON.skip-repl { "servers": { - "telerik-ui-generator": { + "blazor-ui-generator": { "type": "http", "url": "https://uiagent.mcp.telerik.com/mcp/blazor", "headers": { @@ -52,32 +82,32 @@ This section contains information about how to set up the Agentic UI Generator i } ```` -To enable global automatic discovery of the Agentic UI Generator in Visual Studio, add the above `.mcp.json` file to your user directory (`%USERPROFILE%`), for example, `C:\Users\____\.mcp.json`. +> Replace `YOUR API KEY` with the API key you generate. + +The server name `blazor-ui-generator` can be changed to a custom value based on your preferences. -> Once the MCP server configuration is added, make sure that the `telerik-ui-generator` tool is [enabled (checked) in the Copilot Chat window's tool selection dropdown](https://learn.microsoft.com/en-us/visualstudio/ide/mcp-servers?view=vs-2022#configuration-example-with-github-mcp-server). This dropdown opens when clicking the wrench icon 🔧 at the bottom of the Copilot Window. The Telerik Agentic UI Generator may get disabled when starting a new chat, changing threads, or relaunching Visual Studio. This is a known issue with MCP servers in general. +#### Global Configuration -#### Visual Studio Code +To enable global automatic discovery of the Agentic UI Generator in Visual Studio, add the above `.mcp.json` file to your user directory (`%USERPROFILE%`), for example, `C:\Users\____\.mcp.json`. -1. In your browser, go to the [API Keys](https://www.telerik.com/account/downloads/api-keys) page in your Telerik account. If you already have an existing API key, you can skip steps 2-6 and continue from step 7, instead of creating a new key. -2. Click **Generate New Key +**. -3. In the **Key Note** field, add a note that describes the API key. -4. Click **Generate Key**. -5. Select **Copy and Close**. Once you close the window, you can no longer copy the generated key. For security reasons, the **API Keys** page displays only a portion of the key. -6. Store the generated NuGet API key as you will need it in the next steps. -7. To enable the Agentic UI Generator in a specific [workspace](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-workspace), Blazor app, or [globally](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-user-configuration), add a `.vscode` folder with an `mcp.json` file at the root of the workspace, app, or your user folder, respectively. +> Once the MCP server configuration is added, make sure that the `blazor-ui-generator` tool is [enabled (checked) in the Copilot Chat window's tool selection dropdown](https://learn.microsoft.com/en-us/visualstudio/ide/mcp-servers?view=vs-2022#configuration-example-with-github-mcp-server). This dropdown opens when clicking the wrench icon 🔧 at the bottom of the Copilot Window. The Telerik Agentic UI Generator may get disabled when starting a new chat, changing threads, or relaunching Visual Studio. This is a known issue with MCP servers in general. -> This section applies to VS Code 1.102.1 and newer versions. +### Visual Studio Code + +For more information about using MCP servers in Visual Studio Code, refer to [Use MCP servers in VS Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers). Make sure that [`chat.mcp.enabled`](vscode://settings/chat.mcp.enabled) is enabled in the VS Code settings. -9. In the `mcp.json` file, set your API Key as `x-api-key` value, as demonstrated in the JSON configuration below. +#### Workspace Configuration + +To enable the Agentic UI Generator in a specific [workspace](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-workspace), Blazor app, or [globally](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-user-configuration), add a `.vscode` folder with an `mcp.json` file at the root of the workspace, app, or your user folder, respectively. ->caption .vscode/mcp.json +>caption mcp.json ````JSON.skip-repl { "servers": { - "telerik-ui-generator": { + "blazor-ui-generator": { "type": "http", "url": "https://uiagent.mcp.telerik.com/mcp/blazor", "headers": { @@ -88,6 +118,12 @@ Make sure that [`chat.mcp.enabled`](vscode://settings/chat.mcp.enabled) is enabl } ```` +> Replace `YOUR API KEY` with the API key you generate. + +The server name `blazor-ui-generator` can be changed to a custom value based on your preferences. + +#### Global Configuration + To use the Agentic UI Generator in all workspaces and apps, make sure that [`chat.mcp.discovery.enabled`](vscode://settings/chat.mcp.discovery.enabled) is enabled in [`settings.json`](https://code.visualstudio.com/docs/configure/settings#_settings-json-file). >caption VS Code settings.json @@ -101,33 +137,38 @@ To use the Agentic UI Generator in all workspaces and apps, make sure that [`cha After adding the configuration, restart your IDE to load the Agentic UI Generator. -## Basic Usage +### Obtaining the API Key -The Agentic UI Generator can be used in two primary modes: basic usage through the main tool, or advanced usage by calling specific MCP tools directly. +The following steps provide detailed instructions for obtaining your [Telerik API key](https://www.telerik.com/account/downloads/api-keys), which you need to add as a `headers` parameter in the `mcp.json` file: -### Call the Agentic UI Generator +1. Log in to your [Telerik account](https://www.telerik.com/account/). +2. Navigate to **License Keys > API Keys**, or go directly to the [API Keys page](https://www.telerik.com/account/downloads/api-keys). -To use the Agentic UI Generator, you can call it in two ways: + Telerik Account API Keys Page -#### Method 1: Using the Tool Handle +3. Click **Generate New Key** and provide a name of your choice, or use an existing API key if you already have one. -Prefix your prompt with `#telerik_ui_generator` followed by your request: + Generate New API Key Dialog -````TEXT.skip-repl -#telerik_ui_generator Create a dashboard page with a grid showing sales data and a chart visualizing monthly trends -```` +4. Copy the generated API key for use in the MCP configuration. -#### Method 2: Using Natural Language -Prefixing your prompt with `#telerik_ui_generator` will make it more likely for the Agentic UI Generator to get called. Alternatively, you can use natural language and simply describe what you want. The AI assistant automatically recognizes when to use the Agentic UI Generator: +## Using the Agentic UI Generator -````TEXT.skip-repl -Create a product management page with a Telerik grid for displaying products, a form for adding new items, and filtering options -```` +Once installed, start a new chat session in your IDE to begin interacting with the Agentic UI Generator via natural language prompts. The Agentic UI Generator can be used in two primary modes: basic usage through [the main tool](#call-the-agentic-ui-generator), or advanced usage by [calling specific MCP tools directly](#target-the-tools-advanced). + +### Call the Agentic UI Generator + +1. Open the AI chat interface in your IDE—Start a new chat session to begin interacting with the Agentic UI Generator. +1. In Visual Studio Code, you can start your prompt with the `#telerik_ui_generator` handle—this invokes the main MCP tool that uses an agentic flow to analyze and process your request. + > Using the `#telerik_ui_generator` handle ensures the Agentic UI Generator is called. Alternatively, you can use natural language without the handle. Make sure to mention the "telerik" keyword in your natural language prompt, so that the AI model can automatically recognize when to call the generator. The generator analyzes your prompt and creates the appropriate Blazor components, markup, and styling. +1. Inspect the output and verify that the `blazor-ui-generator` MCP server (or the one with your custom server name) is called. Look for a similar statement in the output: + + MCP Server uses Telerik UI Generator in VS Code -The generator analyzes your prompt and creates the appropriate Blazor components, markup, and styling. +1. If prompted, grant the MCP server permission to run for this session, workspace, or always. -## Advanced Usage +### Target the Tools (Advanced) For more granular control, you can call individual tools that make up the Agentic UI Generator: diff --git a/ai/agentic-ui-generator/overview.md b/ai/agentic-ui-generator/overview.md index c98bfdd83c..11ad6c6502 100644 --- a/ai/agentic-ui-generator/overview.md +++ b/ai/agentic-ui-generator/overview.md @@ -111,7 +111,7 @@ Access to the Agentic UI Generator depends on your [Telerik license type](https: * Best for ongoing and high-volume usage. * All Other Licenses (including Perpetual Licenses and other Subscriptions) * License holders have no access to the Agentic UI Generator by default. - * Require starting a [30-day trial](https://www.telerik.com/try/ui-for-blazor) to access the Agentic UI Generator. + * Require starting a [30-day AI Tools trial](https://www.telerik.com/try/agentic-ui-generator) to access the Agentic UI Generator. * After the trial expires, access is no longer available unless the [Perpetual license is converted to a Subscription license](https://www.telerik.com/account/support-center/contact-us/account-support). * Trial License * A [Telerik UI for Blazor trial](https://www.telerik.com/try/ui-for-blazor) automatically starts a 30-day trial for the AI tools. diff --git a/ai/ai-coding-assistant/copilot-extension.md b/ai/ai-coding-assistant/copilot-extension.md index bd3f8fd185..429d94f4de 100644 --- a/ai/ai-coding-assistant/copilot-extension.md +++ b/ai/ai-coding-assistant/copilot-extension.md @@ -42,7 +42,7 @@ To install the Telerik Blazor Copilot extension: 1. Enter your GitHub password. 1. You will be redirected to telerik.com. Enter your Telerik account credentials if prompted. This will authorize the GitHub Copilot extension to integrate with your Telerik account. 1. Upon successful Telerik authentication, you will be redirected once again to a page that confirms successful Copilot extension installation. -1. Restart your [Copilot-enabled apps](https://docs.github.com/en/copilot/building-copilot-extensions/about-building-copilot-extensions#supported-clients-and-ides) (for example, Visual Studio and VS Code). +1. Restart your Copilot-enabled apps (for example, Visual Studio and VS Code). 1. Start a new chat session in Copilot. You can also start the installation from the Telerik UI for Blazor extensions for [Visual Studio](slug:getting-started-vs-integration-ai-configuration#install-telerik-blazor-copilot-extension) and [VS Code](slug:getting-started-vs-code-integration-ai-configuration). Then, continue the installation from step 2. diff --git a/ai/ai-coding-assistant/overview.md b/ai/ai-coding-assistant/overview.md index 5b78ca45c5..2a7f4322a4 100644 --- a/ai/ai-coding-assistant/overview.md +++ b/ai/ai-coding-assistant/overview.md @@ -64,7 +64,7 @@ The Telerik Blazor AI Conding Assistant allows the following maximum number of r ### Perpetual Licenses * Perpetual license holders have no access to the AI Coding Assistant by default. -* You can start a [30-day trial](https://www.telerik.com/try/ui-for-blazor) to access the AI Coding Assistant. +* You can start a [30-day AI Tools trial](https://www.telerik.com/mcp-servers-blazor/thank-you) to access the AI Coding Assistant. * After the trial expires, access is no longer available unless the [Perpetual license is converted to a Subscription license](https://www.telerik.com/account/support-center/contact-us/account-support). ### Trial Licenses diff --git a/ai/images/api-keys-generate-dialog.png b/ai/images/api-keys-generate-dialog.png new file mode 100644 index 0000000000..cd6bedcfe9 Binary files /dev/null and b/ai/images/api-keys-generate-dialog.png differ diff --git a/ai/images/api-keys-page.png b/ai/images/api-keys-page.png new file mode 100644 index 0000000000..6368d2353d Binary files /dev/null and b/ai/images/api-keys-page.png differ diff --git a/ai/images/generator-confirmation.png b/ai/images/generator-confirmation.png new file mode 100644 index 0000000000..3bde6836ad Binary files /dev/null and b/ai/images/generator-confirmation.png differ diff --git a/components/diagram/events.md b/components/diagram/events.md index 84c010c9e8..cf5f1f6f3e 100644 --- a/components/diagram/events.md +++ b/components/diagram/events.md @@ -51,6 +51,8 @@ The `OnShapeClick` event fires when the user clicks on a shape. The event argume } ```` +Use the [Diagram JSON state](slug:diagram-overview#define-shapes-and-connections-in-json) if you need to [change the Diagram configuration](slug:diagram-kb-change-shape-color-onshapeclick) while persisting other user changes that are not part of the component declaration. + ## Example The following example demonstrates all Diagram events in action. diff --git a/components/diagram/overview.md b/components/diagram/overview.md index aa8a452c81..1f81d98a67 100644 --- a/components/diagram/overview.md +++ b/components/diagram/overview.md @@ -100,7 +100,10 @@ Optionally, you can also define: * Shape `width` and `height` as numbers. * Connection `from.connector` and `to.connector` that determine which shape side the connection touches (`"Top"`, `"Right"`, `"Bottom"`, `"Left"`). -The Diagram provides a `SaveAsJsonAsync()` method that returns the current shape and connection state as a JSON string. This allows you to persist user changes or see how to define more advanced shape and connection settings in JSON format. +The Diagram provides a `SaveAsJsonAsync()` method that returns the current shape and connection state as a JSON string. This allows you to: + +* [Persist user changes and apply them to the component in a future user session](slug:diagram-kb-change-shape-color-onshapeclick). +* Review the JSON structure and see how to define all possible shape and connection settings in JSON format. >caption Loading and saving the Diagram shape and connection state @@ -225,6 +228,38 @@ The Diagram allows users to zoom the graph in and out for better perception. The MinZoom="0" /> ```` +## Pan + +The Diagram content may overflow the component boundaries if there are a lot of shapes or if the user zooms in. By default, the Diagram allows users to pan the content by holding the `Ctrl` key and dragging. The following code snippet shows the relevant parameters together with their default values. + +When both panning and [selection](#select) are enabled, make sure the two features use a different `Key`. + +>caption Pan-related Diagram parameters + +````RAZOR.skip-repl + + + +```` + +## Select + +Users can select a single shape or connection with a click. The Diagram also supports multiple selection through dragging a rectangular marquee. The following code snippet shows the relevant parameters together with their default values. + +Once the user selects some shapes and connections, they can drag them to another position or remove them with the `Del` key. + +When both [panning](#pan) and selection are enabled, make sure the two features use a different `Key`. + +>caption Selection-related Diagram parameters + +````RAZOR.skip-repl + + + + + +```` + ## Events The Telerik Diagram fires events that enable the app to detect and react to user interactions with the component. Find out more about the [Diagram events and event arguments](slug:diagram-events). diff --git a/components/dockmanager/overview.md b/components/dockmanager/overview.md index 64645d8216..46824d1588 100644 --- a/components/dockmanager/overview.md +++ b/components/dockmanager/overview.md @@ -121,7 +121,7 @@ The following table lists the Dock Manager parameters. Also check the [DockManag | --- | --- | --- | | `AllowFloat` | `bool` | Determines whether the pane can be dragged from the dock manager layout to create a new floating pane. | | `Class` | `string` | The custom CSS class of the `
` element. Use it to [override theme styles](slug:themes-override). | -| `Closeable` | `bool` | Determines whether the pane can be closed. | +| `Closeable` | `bool`
(`true`) | If false, the close button is hidden and closing via **Esc** key or code is disabled. | | `Dockable` | `bool` | Specifies whether the pane allows other panes to be docked to or over it. This determines if the end user can drop other panes over it or next to it, creating a DockManagerSplitPane (Splitter) or a DockManagerTabGroupPane (TabStrip). | | `HeaderText` | `string` | The pane title, displayed in the pane header and as the button text in the DockManager toolbar when the pane is unpinned. | | `Id` | `string`
(`Guid`) | The id of the pane. | diff --git a/components/gauges/linear/labels.md b/components/gauges/linear/labels.md index 3b75c1e601..c105ad8800 100644 --- a/components/gauges/linear/labels.md +++ b/components/gauges/linear/labels.md @@ -20,6 +20,8 @@ You can customize the appearance of the labels rendered on the [scale](slug:line * [Additional Customization](#additional-customization) +The LinearGauge scale width decreases when the `Min` and `Max` label length increases. See how to [define the same LinearGauge scale widths with different `Min` and `Max` values in multiple Gauge instances](slug:lineargauge-kb-align-gauge-widths-with-different-min-max). + ## Format The `Format` (`string`) parameter allows you to customize the rendering of the labels by using the standard numeric format strings. You can set the values of the labels to showcase, for example, currency, percentage, and so on. diff --git a/components/gauges/linear/overview.md b/components/gauges/linear/overview.md index a5c111bf54..d18bb5794c 100644 --- a/components/gauges/linear/overview.md +++ b/components/gauges/linear/overview.md @@ -54,9 +54,9 @@ The ranges are used to visually distinguish particular values on the scale. [Rea @[template](/_contentTemplates/common/parameters-table-styles.md#table-layout) | Parameter | Type and Default Value | Description | | --- | --- | --- | -| `Class` | `string` | Renders a custom CSS class to the `
` 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 +
@@ -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 + + + + + + + + +
ProductDiagram 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 + + + + + + + + +
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. 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. +*@ + +
+
+
+
    + @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 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 + + + + + + + + +
ProductGrid 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 MenuItems { get; set; } + private SampleData SelectedPerson { get; set; } + private TelerikContextMenu ContextMenuRef { get; set; } + private TelerikGrid GridRef { get; set; } + + private async Task OnContextMenu(GridRowClickEventArgs args) + { + SelectedPerson = args.Item as SampleData; + + if (args.EventArgs is MouseEventArgs mouseEventArgs) + { + await ContextMenuRef.ShowAsync(mouseEventArgs.ClientX, mouseEventArgs.ClientY); + } + } + + private async Task ContextMenuClickHandler(MenuItem item) + { + if (item.Action != null) + { + item.Action.Invoke(); + } + else if (item.CommandName == "CopyRow" && SelectedPerson != null) + { + string rowText = $"ID: {SelectedPerson.ID}, Name: {SelectedPerson.Name}"; + await CopyToClipboard(rowText); + } + + SelectedPerson = null; + } + + private async Task CopyToClipboard(string text) + { + await JS.InvokeVoidAsync("navigator.clipboard.writeText", text); + } + + protected override void OnInitialized() + { + MenuItems = new List() + { + new MenuItem(){ Text = "Copy Row Text", Icon = SvgIcon.Copy, CommandName="CopyRow" }, + new MenuItem(){ Text = "Delete", Icon = SvgIcon.Trash, Action = DeleteItem } + }; + + GridData = new ObservableCollection(); + for (int i = 1; i <= 20; i++) + { + GridData.Add(new SampleData() + { + ID = i, + Name = $"Employee {i}" + }); + } + } + + private void DeleteItem() + { + if (SelectedPerson != null) + { + GridData.Remove(SelectedPerson); + } + } + + public class MenuItem + { + public string Text { get; set; } + public ISvgIcon Icon { get; set; } + public Action Action { get; set; } + public string CommandName { get; set; } + } + + public class SampleData + { + public int ID { get; set; } + public string Name { get; set; } + } +} +```` + +## See Also + +* [Grid Overview](slug:grid-overview) +* [Context Menu Overview](slug:contextmenu-overview) +* [Grid Context Menu Integration Demo](https://demos.telerik.com/blazor-ui/contextmenu/integration) +* [Grid Context Menu Integration Documentation](slug:contextmenu-integration) diff --git a/knowledge-base/grid-kb-only-one-filtermenu-option.md b/knowledge-base/grid-kb-only-one-filtermenu-option.md index b4032c2032..8fb198d785 100644 --- a/knowledge-base/grid-kb-only-one-filtermenu-option.md +++ b/knowledge-base/grid-kb-only-one-filtermenu-option.md @@ -6,22 +6,24 @@ page_title: Only one filter option in FilterMenu slug: grid-kb-only-one-filtermenu-option position: tags: -ticketid: 1451755, 1551245 +ticketid: 1451755, 1551245, 1705873 res_type: kb --- ## Environment + - +
ProductGrid for BlazorGrid for Blazor,
TreeList for Blazor
## Description + I want simple filtering options in the Grid filter menu - both for my uses and my backend. How do I remove the extra conditions so it behaves like the filter row and does not have extra and/or operators? >caption Before and after results @@ -33,70 +35,50 @@ I want simple filtering options in the Grid filter menu - both for my uses and m There are two options: * Use a [custom filter template](slug:grid-templates-filter). It provides full flexibility over the interface and building the filter descriptor. -* Use custom CSS to [override the theme](slug:themes-override) and hide the elements that provide the and/or secondary conditions. The example below demonstrates this approach. Note that **the required CSS is different for different UI for Blazor versions**: +* Use custom CSS to [override the theme](slug:themes-override) and hide the elements that provide the and/or secondary conditions. The example below demonstrates this approach. -
-````CSS - +````CSS.skip-repl +.k-filter-menu-container > .k-button-group, +.k-filter-menu-container > span:nth-child(n+3) { + display: none; +} ```` ->caption Hide And/Or filter options in the Grid/TreeList FilterMenu with CSS +>caption Hide And/Or ButtonGroup and second filter option in the Grid/TreeList FilterMenu with CSS ````RAZOR -@* Hide the secondary filter interface with CSS *@ - - - - - + + + + @code { - List GridData { get; set; } + private List GridData { get; set; } = new(); protected override void OnInitialized() { - GridData = new List(); - var rnd = new Random(); + var rnd = Random.Shared; - for (int i = 1; i <= 15; i++) + for (int i = 1; i <= 7; i++) { GridData.Add(new Product() { - ID = i, - Name = "Product " + i.ToString(), - Price = (decimal)rnd.Next(1, 100), - ReleaseDate = new DateTime(rnd.Next(2020, 2023), rnd.Next(1, 13), rnd.Next(1, 28)), + Id = i, + Name = $"Product {i}", + Price = rnd.Next(1, 100) * 1.23m, + ReleaseDate = DateTime.Today.AddDays(-rnd.Next(1, 3650)), Discontinued = i % 4 == 0 }); } @@ -104,8 +86,8 @@ There are two options: public class Product { - public int ID { get; set; } - public string Name { get; set; } + public int Id { get; set; } + public string Name { get; set; } = string.Empty; public decimal Price { get; set; } public DateTime ReleaseDate { get; set; } public bool Discontinued { get; set; } diff --git a/knowledge-base/lineargauge-align-gauges-with-different-min-max.md b/knowledge-base/lineargauge-align-gauges-with-different-min-max.md new file mode 100644 index 0000000000..0b92d546ce --- /dev/null +++ b/knowledge-base/lineargauge-align-gauges-with-different-min-max.md @@ -0,0 +1,258 @@ +--- +title: LinearGauge Scale Widths Are Different +description: Learn how to define consistent scale widths for Telerik Linear Gauges for Blazor, when the components are using different Min and Max values. +type: troubleshooting +page_title: How to Fix Different LinearGauge Scale Widths +slug: lineargauge-kb-align-gauge-widths-with-different-min-max +tags: blazor, gauge, lineargauge +ticketid: 1704384 +res_type: kb +--- + +## Environment + + + + + + + + +
ProductLinearGauge for Blazor
+ +## Description + +I am using several `` instances in a Blazor app. They display on multiple lines and need to be aligned. +The `Min` and `Max` values are different and set dynamically at runtime. +Although the Gauge `Width` parameters have the same value, the Gauge scale widths are different. + +## Cause + +The LinearGauge `Width` parameter affects the total width of the component. However, the scale width decreases when the length of the `Min` and `Max` labels increases. + +## Solution + +To achieve consistent width of multiple LinearGauges, set larger `Width` to the instances that have longer `Min` and `Max` labels. + +1. Apply `monospace` font family to the Gauge labels (`div.k-gauge text`). +1. Calculate the Linear Gauge `Width`, based on the string length of the `Min` and `Max` values. Add 3px for each additional `Min` or `Max` digit. +1. Make sure that `MajorUnit` and `MinorUnit` are divisors of the difference between `Min` and `Max`. Otherwise the Gauge scale may not be consistent with the set component `Width`. + +>caption Align linear Gauges with different Min and Max values + +````RAZOR +
+ +Width Correction Multiplier: + + +Refresh Gauges to Apply Changes + +
+ +Gauge Widths: +@($"{GetGaugeWidth(BaseWidth, GaugeMin1, GaugeMax1)}px"), +@($"{GetGaugeWidth(BaseWidth, GaugeMin2, GaugeMax2)}px"), +@($"{GetGaugeWidth(BaseWidth, GaugeMin3, GaugeMax3)}px") +
+For proper alignment, the Gauge widths (yellow) must be smaller than the container widths (green). +

+ +
+ Min: + Max: + MinorUnit: @GetGaugeMinorUnit(GaugeMin1, GaugeMax1) | + MajorUnit: @GetGaugeMajorUnit(GaugeMin1, GaugeMax1) +
+ +
+ + + + + + + + + +
+ +
+ Min: + Max: + MinorUnit: @GetGaugeMinorUnit(GaugeMin2, GaugeMax2) | + MajorUnit: @GetGaugeMajorUnit(GaugeMin2, GaugeMax2) +
+ +
+ + + + + + + + + +
+ +
+ Min: + Max: + MinorUnit: @GetGaugeMinorUnit(GaugeMin3, GaugeMax3) | + MajorUnit: @GetGaugeMajorUnit(GaugeMin3, GaugeMax3) +
+ +
+ + + + + + + + + +
+ +
+ +
+ + + +@code { + #nullable enable + + private int BaseWidth { get; set; } = 400; + private decimal WidthMultiplier { get; set; } = 3m; + + private TelerikLinearGauge? LinearGaugeRef1 { get; set; } + private int GaugeMin1 { get; set; } = 0; + private int GaugeMax1 { get; set; } = 50; + + private TelerikLinearGauge? LinearGaugeRef2 { get; set; } + private int GaugeMin2 { get; set; } = 150; + private int GaugeMax2 { get; set; } = 300; + + private TelerikLinearGauge? LinearGaugeRef3 { get; set; } + private int GaugeMin3 { get; set; } = 10000; + private int GaugeMax3 { get; set; } = 11000; + + private void RefreshGauges() + { + LinearGaugeRef1?.Refresh(); + LinearGaugeRef2?.Refresh(); + LinearGaugeRef3?.Refresh(); + } + + private string GetGaugeWidth(int baseWidth, int min, int max) + { + int correction = string.Concat(min.ToString(), max.ToString()).Length; + decimal width = baseWidth + (WidthMultiplier * correction); + return width.ToString().Split(",")[0]; + } + + private int GetGaugeMajorUnit(int min, int max) + { + if (max <= min) + { + return 1; + } + + int diff = max - min; + int divisor = Convert.ToInt32(Math.Ceiling((decimal)diff / 5)); + while (divisor >= 1) + { + if (diff % divisor == 0) + { + break; + } + else + { + divisor -= (diff.ToString().Length - 1); + } + } + + return divisor; + } + + private int GetGaugeMinorUnit(int min, int max) + { + if (max <= min) + { + return 1; + } + + int majorUnit = GetGaugeMajorUnit(min, max); + int divisor = majorUnit / 10; + while (divisor >= 1) + { + if (majorUnit % divisor == 0) + { + break; + } + else + { + divisor -= 1; + } + } + + return divisor; + } +} +```` + +## See Also + +* [Linear Gauge Overview](slug:linear-gauge-overview) +* [Linear Gauge Scale](slug:linear-gauge-scale) +* [Linear Gauge Labels](slug:linear-gauge-labels) diff --git a/knowledge-base/popover-refresh-callout-position.md b/knowledge-base/popover-refresh-callout-position.md new file mode 100644 index 0000000000..2be60171bc --- /dev/null +++ b/knowledge-base/popover-refresh-callout-position.md @@ -0,0 +1,83 @@ +--- +title: Align Popover with Anchor After Content and Size Change +description: Learn how to align the Telerik Popover for Blazor to its anchor after the component content and size change dynamically at runtime. +type: troubleshooting +page_title: How to Align Popover with Anchor After Content and Size Change +slug: popover-kb-refresh-callout-position +tags: blazor, popover +ticketid: 1703828 +res_type: kb +--- + +## Environment + + + + + + + + +
ProductPopover for Blazor
+ +## Description + +I have a Popover that may change its content and size at runtime. In these cases, the callout does not line up with the anchor anymore. How to dynamically align the Popover with its anchor after the content shrinks or expands? + +## Cause + +The Popover component does not track changes to its content or size for performance reasons. + +## Solution + +Use the [Popover `Refresh` method](slug:popover-overview#popover-reference-and-methods) to recalculate the component and callout position with regard to the anchor. If the Popover content change depends on C# code, you may need to wait for the next Blazor re-render or trigger it immediately with `await Task.Delay(1)`. + +>caption Refresh Popover position and callout when the dimensions change + +```RAZOR + + + Telerik Popover for Blazor +
+ Toggle Content + @if (ContentVisible) + { +
+ Some dynamic content... +
+ } +
+ + Close + +
+ +






+ +Toggle Popover + +@code{ + #nullable enable + + private TelerikPopover? PopoverRef { get; set; } + + private bool ContentVisible { get; set; } = true; + + private async Task ToggleContent() + { + ContentVisible = !ContentVisible; + await Task.Delay(1); + + PopoverRef?.Refresh(); + } +} +``` + +## See Also + +* [Popover Position and Collision](slug:popover-position-collision)