Unplanned
Last Updated: 13 Feb 2026 08:38 by ADMIN
Created by: David
Comments: 1
Category: UI for Blazor
Type: Feature Request
1

Draft feature request (copy/paste)

Title: Add per-column value converters (WPF-style) to Telerik Blazor Grid columns

Product: UI for Blazor → Grid
Type: Feature Request

Description:
In Telerik UI for WPF, grid column definitions can reference a converter by name (e.g., IValueConverter) to transform values for display without writing a custom cell template for each column.

In Telerik UI for Blazor Grid, templates are currently the primary way to change how values render in a column (which works, but becomes repetitive across many columns/grids).

Request:
Add a column-level conversion API that allows specifying a converter/formatter function for display (and optionally editing). Example concepts:

  • Converter="nameof(MyConverters.StatusToText)"

  • or DisplayConverter="(item) => …" / ValueFormatter="Func<TItem, object, string>"

  • optionally ConvertBack-like support for editing scenarios (or separate EditConverter)

Why this is needed:

  • Reduces repeated <Template> markup for simple formatting/transformations

  • Centralizes formatting/conversion logic in reusable code

  • Improves maintainability and consistency across grids

  • Eases migration for teams coming from Telerik WPF where converters are a common pattern

Use cases:

  • Enum/int → user-friendly text

  • Boolean → “Yes/No”, icons, badges

  • Null/empty → placeholder text

  • Code → display name (via lookup)

  • Domain-specific formatting shared across many grids

Workarounds today:

  • Column <Template> or computed properties (both valid, but not as concise/reusable for large grids)

  • Extracting RenderFragments can reduce repetition, but still requires templating infrastructure for what is logically a simple conversion step

Expected behavior:

  • Converter applies consistently anywhere the column renders (cell, export if applicable, etc.)

  • Works with sorting/filtering in a predictable way (ideally sorting/filtering still uses raw field value unless explicitly configured)


 

Unplanned
Last Updated: 10 Feb 2026 17:54 by Dave
Created by: Gary
Comments: 2
Category: DockManager
Type: Feature Request
8

Goal

Our application needs to allow end users to dynamically customize the Telerik Blazor DockManager at runtime by:

  • Adding new panels
  • Removing existing panels
  • Rearranging and resizing panels

 

These changes should be fully user-driven and persisted so that:

  • The layout is restored when the user returns
  • The layout is consistent across browsers and devices

 

In short, we want the DockManager to behave as a customizable dashboard whose state can be reliably stored and reloaded from our database.

 

 

Problem

The current Telerik Blazor DockManager implementation requires panels to be declared in Razor markup (markup driven) and managed through an external data source.

 

This creates several challenges:

  • The component state (DockState) is tightly coupled to the initial panel definitions.
  • Dynamically adding or removing panels from the data source conflicts with the internal DockState.
  • To synchronize changes, we must manually manipulate the DockState object using custom code.
  • This manipulation relies on internal behavior that is not formally supported and may break in future Telerik releases.

 

As a result, implementing a truly dynamic and persistent DockManager layout requires complex workarounds that are fragile and difficult to maintain.

 

 

Feature Request

We propose enhancing the DockManager with first-class support for dynamic panel synchronization by introducing:

Two coordinated parameters:

  • Data – the collection of panels
  • DockState – the persisted layout information

 

Expected behavior:

  • If the Data collection contains a panel that is not present in the DockState, the component should automatically:
    • Add the new panel to the layout
    • Place it at the end of the current structure (bottom or right, depending on layout)
    • Update the DockState accordingly
  • If the DockState contains a panel that is no longer present in the Data collection, the component should automatically:
    • Remove that panel from the layout
    • Update the DockState accordingly

 

This would allow developers to treat the DockManager as a true data-driven component, similar to other Telerik Blazor controls, without needing to manually modify internal state structures.

Unplanned
Last Updated: 06 Feb 2026 10:22 by ADMIN
Created by: Alexander
Comments: 1
Category: Diagram
Type: Feature Request
3

Current limitation: The Diagram component does not provide native context menu integration. There are no events like OnShapeContextMenu or OnConnectionContextMenu that fire on right-click. Developers cannot show contextual actions (edit, delete, copy, connect) when user right-clicks on diagram elements.

Requested feature: Add context menu support for Diagram elements:

  1. Context menu events:
    • OnShapeContextMenu — fires on right-click on a shape
    • OnConnectionContextMenu — fires on right-click on a connection
    • OnCanvasContextMenu — fires on right-click on empty canvas area (for "paste", "add shape" actions)
  2. Event arguments:
    • DiagramShapeContextMenuEventArgs:
      • ShapeId — the clicked shape identifier
      • ClientX, ClientY — mouse position for menu placement
      • PageX, PageY — page coordinates
    • DiagramConnectionContextMenuEventArgs:
      • ConnectionId — the clicked connection identifier
      • FromShapeId, ToShapeId — connected shape identifiers
      • ClientX, ClientY, PageX, PageY
    • DiagramCanvasContextMenuEventArgs:
      • ClientX, ClientY, PageX, PageY
      • DiagramX, DiagramY — coordinates in diagram space (for placing new shapes)
  3. Integration with TelerikContextMenu:
    • Ability to bind TelerikContextMenu to diagram and show it on right-click events
    • Automatic prevention of browser default context menu

Use case:
In workflow/process diagram editors, context menu is essential for:

  • Shape actions: Edit, Delete, Duplicate, Copy/Paste, Change type, View details
  • Connection actions: Edit transition, Delete, Change routing
  • Canvas actions: Paste, Add new shape at cursor position, Zoom controls
  • Conditional actions: Show different menu items based on shape type or state

Currently, there is no way to detect right-click on specific diagram elements. The only workaround is complex JavaScript interop to attach event listeners to SVG elements, which is fragile and breaks on re-render.

Example of desired API:

<TelerikDiagram @ref="@DiagramRef"
                OnShapeContextMenu="@OnShapeRightClick"
                OnConnectionContextMenu="@OnConnectionRightClick"
                OnCanvasContextMenu="@OnCanvasRightClick">
    <DiagramShapes>
        @foreach (var shape in Shapes)
        {
            <DiagramShape Id="@shape.Id">
                <DiagramShapeContent Text="@shape.Name" />
            </DiagramShape>
        }
    </DiagramShapes>
</TelerikDiagram>

<TelerikContextMenu @ref="@ShapeContextMenu" 
                    Data="@ShapeMenuItems"
                    OnClick="@OnShapeMenuClick">
    <ItemTemplate Context="item">
        <TelerikFontIcon Icon="@item.Icon" />
        <span>@item.Text</span>
    </ItemTemplate>
</TelerikContextMenu>

@code {
    private TelerikContextMenu<MenuItem>? ShapeContextMenu;
    private string? ClickedShapeId;
    
    private async Task OnShapeRightClick(DiagramShapeContextMenuEventArgs args)
    {
        ClickedShapeId = args.ShapeId;
        await ShapeContextMenu.ShowAsync(args.ClientX, args.ClientY);
    }
    
    private async Task OnShapeMenuClick(MenuItem item)
    {
        switch (item.Action)
        {
            case "edit":
                await OpenShapeEditor(ClickedShapeId);
                break;
            case "delete":
                await DeleteShape(ClickedShapeId);
                break;
            case "duplicate":
                await DuplicateShape(ClickedShapeId);
                break;
        }
    }
}

 

Alternative minimal implementation:
If full context menu integration is complex, at minimum provide the events with coordinates, so developers can manually show TelerikContextMenu or any custom popup.
Unplanned
Last Updated: 06 Feb 2026 08:53 by ADMIN
Created by: Alexander
Comments: 1
Category: Diagram
Type: Feature Request
2

Current limitation: The Diagram component only allows zoom through mouse interactions (scroll wheel). The Zoom parameter sets only the initial zoom level and cannot be changed at runtime. There are no methods to programmatically control the viewport.

Requested feature: Add programmatic viewport control API:

  1. Two-way Zoom binding:
    @bind-Zoom support with ZoomChanged event
    Event args containing OldZoom and NewZoom values
  2. Viewport control methods on TelerikDiagram reference:
    SetZoomAsync(double level) — set zoom to specific level
    ZoomInAsync(double? step) — zoom in by step (default: ZoomRate)
    ZoomOutAsync(double? step) — zoom out by step
    ResetZoomAsync() — reset to initial Zoom value (100%)
    BringIntoViewAsync(IEnumerable<string> shapeIds) — pan/zoom to show specific shapes
    FitToScreenAsync() — auto-zoom to fit all shapes in viewport

Use case:
In enterprise workflow/process editors, users need navigation controls beyond mouse wheel:
Accessibility: Users with trackpads, touch screens, or motor impairments cannot easily use scroll wheel zoom
Toolbar buttons: Standard diagram tools (Visio, Draw.io, Lucidchart) provide +/−/100%/Fit buttons
Programmatic navigation: After adding a new shape, auto-scroll to show it; on search result, navigate to found shape
Keyboard shortcuts: Implement Ctrl+Plus/Minus for zoom without native support

Example of desired API:
<TelerikDiagram @ref="@DiagramRef"
                @bind-Zoom="@CurrentZoom"
                ZoomChanged="@OnZoomChanged">
    ...
</TelerikDiagram>

<TelerikButton OnClick="@(() => DiagramRef.ZoomInAsync())">+</TelerikButton>
<TelerikButton OnClick="@(() => DiagramRef.ZoomOutAsync())">−</TelerikButton>
<TelerikButton OnClick="@(() => DiagramRef.SetZoomAsync(1.0))">100%</TelerikButton>
<TelerikButton OnClick="@(() => DiagramRef.FitToScreenAsync())">Fit</TelerikButton>

@code {
    private TelerikDiagram? DiagramRef;
    private double CurrentZoom = 1.0;
    
    private async Task NavigateToShape(string shapeId)
    {
        await DiagramRef.BringIntoViewAsync(new[] { shapeId });
    }
}


 

Unplanned
Last Updated: 06 Feb 2026 08:47 by Alexander
Created by: Alexander
Comments: 0
Category: Diagram
Type: Feature Request
1

Current limitation: The Diagram component only allows pan through mouse interactions (Ctrl+drag). There are no methods to programmatically control the viewport.

Requested feature: Add programmatic viewport control API:

  1. PanToAsync(double x, double y) — pan viewport to specific coordinates
    PanByAsync(double deltaX, double deltaY) — pan viewport by offset
    BringIntoViewAsync(IEnumerable<string> shapeIds) — pan/zoom to show specific shapes
    FitToScreenAsync() — auto-zoom to fit all shapes in viewport
  2. Pan position binding (optional):
    @bind-PanX / @bind-PanY or PanChanged event

Use case:
Programmatic navigation: After adding a new shape, auto-scroll to show it; on search result, navigate to found shape

===

Forked from https://feedback.telerik.com/blazor/1708938

Unplanned
Last Updated: 05 Feb 2026 13:49 by Ed
Created by: Ed
Comments: 0
Category: Grid
Type: Feature Request
1

The Grid's CheckBoxColumn doesn't support the TextAlign property, making it impossible to center the checkbox with just Grid markup, you have to resort to a workaround.

The TextAlign property in other columns causes style="text-align: center" to be added to the underlying table cell.  This would also work for the checkbox column.

See this test:

<table class="table">
    <tr>
        <td class="table-active" style="width: 40px; text-align: center"><input type="checkbox" /> </td>
        <td>test</td>
    </tr>
</table>

Please add the TextAlign property to the CheckboxColumn.

Unplanned
Last Updated: 05 Feb 2026 11:27 by Daniel

We use QueryableExtensions.ToDataSourceResultmethod to load some data in our component. And at some moment we need to cancel data loading. But ToDataSourceResult method doesn’t support CancellationToken. So we are forced to use a workaround and just ignore the task's result. But task is still executing and causing the performance hit

It would be great if you implemented support for this feature!

Unplanned
Last Updated: 04 Feb 2026 14:32 by Fabio
Created by: BENAISSA
Comments: 6
Category: UI for Blazor
Type: Feature Request
85
I would like to be able to customize the keyboard shortcuts in all applicable components in the Telerik UI for Blazor
Unplanned
Last Updated: 04 Feb 2026 10:29 by ADMIN
Created by: Andrzej
Comments: 9
Category: TreeList
Type: Feature Request
31

Please add TreeList Export to Excel

Regards

Andrzej

Unplanned
Last Updated: 03 Feb 2026 13:00 by Bram
Created by: Bram
Comments: 0
Category: PivotGrid
Type: Feature Request
1
Please add the ability to click on a Pivot Grid column header and sort by the measure values. For multiple row dimensions, the sorting would first have to apply to the highest dimension and than further down the nested dimensions.
Unplanned
Last Updated: 28 Jan 2026 14:59 by Eric
Created by: Eric
Comments: 0
Category: Diagram
Type: Feature Request
2

Expose an OnShapeDrop event for the Diagram. It should provide information about the shape's current X and Y coordinates.

This would allow the user to save the diagram (e.g., as json) and load it later. 

Unplanned
Last Updated: 28 Jan 2026 08:29 by Alex
Created by: Alex
Comments: 0
Category: Grid
Type: Feature Request
3

I want to reorder the Grid rows the same way I can reorder the columns - via the keyboard.

For reference: Telerik jQuery Grid drag and drop

Unplanned
Last Updated: 28 Jan 2026 06:51 by ADMIN
Created by: Kelly
Comments: 12
Category: UI for Blazor
Type: Feature Request
67
Once Drag and Drop is available, it would be nice to have Kanban board component with fully configurable cards.
Unplanned
Last Updated: 27 Jan 2026 00:08 by Adam
Created by: Adam
Comments: 4
Category: Filter
Type: Feature Request
6
We have a large number of fields and it will be useful if we have a search box in the field dropdown to filter them - in a similar fashion to how filtering works in the DropDownList component.
Unplanned
Last Updated: 26 Jan 2026 09:31 by Alexander
Created by: Alexander
Comments: 0
Category: Diagram
Type: Feature Request
3

Current limitation: When DiagramShapeDefaultsEditable.Connect="true", users can draw connections between shapes, but there is no event fired when a new connection is created by the user.
Requested feature: Add OnConnectionCreated event that:

  • Fires when user creates a new connection by dragging from one shape to another
  • Returns event arguments containing:
    • FromShapeId — the source shape identifier
    • ToShapeId — the target shape identifier
    • Connection — reference to the newly created connection object (or its properties)
  • Allows developers to intercept, validate, or customize the connection before it's finalized

Use case: We need to capture user-created connections to persist them to database, validate business rules (e.g., prevent circular dependencies), and set connection properties (type, label, color) based on context.

Unplanned
Last Updated: 26 Jan 2026 09:28 by Alexander
Created by: Alexander
Comments: 0
Category: Diagram
Type: Feature Request
1

Current limitation: DiagramConnectionType only supports Cascading (orthogonal) and Polyline types. There is no curved or Bezier option.
Requested feature: Add a new connection type such as DiagramConnectionType.Bezier or DiagramConnectionType.Curved that:

  • Renders smooth curved lines between shapes
  • Provides natural-looking connections for complex diagrams
  • Optionally allows control point customization

Use case: For workflow diagrams with many crossing connections, curved lines improve visual clarity and reduce cognitive load compared to sharp 90° angles.

Unplanned
Last Updated: 26 Jan 2026 09:17 by Alexander
Created by: Alexander
Comments: 0
Category: Diagram
Type: Feature Request
2

Current limitation: The Diagram component only provides OnShapeClick event. There is no native double-click event for shapes.
Requested feature: Add OnShapeDoubleClick event that:

  • Fires when user double-clicks on a shape
  • Returns DiagramShapeDoubleClickEventArgs containing the shape Id (similar to DiagramShapeClickEventArgs)
  • Allows developers to differentiate between selection (single-click) and action (double-click)

Use case: In workflow editors, single-click should select a shape, while double-click should open an editor dialog. Currently, we have to implement manual timing-based detection which is unreliable and doesn't match native OS double-click behavior.

Unplanned
Last Updated: 23 Jan 2026 06:35 by Niraj
Created by: Niraj
Comments: 0
Category: PivotGrid
Type: Feature Request
2

Please add support for the Display(Name) DataAnnotations attribute for the autogenerated fields in the PivotGrid.

(Related to Title parameter for the rows and columns)

public class PivotModel
{
    [Display(Name = "Net Revenue")]
    public decimal Field1 { get; set; }
}

 

Unplanned
Last Updated: 12 Jan 2026 08:58 by ADMIN
Created by: ranga
Comments: 1
Category: Spreadsheet
Type: Feature Request
1

Hi

I am loading a excel with column A1 as shown below. The text is in A1 and is a long text spanning several columns.

In the telerik spreadsheet component, it displays as shown below- inside A1 but not  with the same width as in the spreadsheet - i have to expand the column manually.

Is there a way to display it exactly as it is in the spreadsheet?

 

thanks
ranga raghuram

 

 

 

 

Unplanned
Last Updated: 07 Jan 2026 14:02 by ADMIN
Created by: Isaac
Comments: 3
Category: UI for Blazor
Type: Feature Request
1

Magnifiable Image Component

A Blazor component designed to provide an interactive image magnification experience, similar to popular eCommerce websites.

Features

  • Interactive Magnifier: On mouse hover, a magnifier appears beside the image, following the cursor and displaying a zoomed-in portion of the image. At the same time an overlay appears on the image indicating the zoomed-in portion of the image.
  • Screen Space Awareness: The magnifier dynamically stretches to fill the available space to the right or left of the image, ensuring optimal use of the viewport and consistent margins.
  • Popover Integration: Utilizes Telerik's Popover for the magnifier, ensuring it appears above all other UI elements and avoids clipping or stacking issues.
  • Configurable Magnification: The magnification scale is configurable via the MagnifyScale parameter.
  • Accessibility: The image is wrapped in a button for keyboard accessibility, and all images support alt text.
  • Full-Size View: Clicking the image opens a modal window displaying the image at its actual size.

Sample Code

MagnifiableImage.razor

@inject IJSRuntime JS
@using Microsoft.JSInterop

@* Container *@
<div @ref="_containerRef" class="@($"magnifiable-image-container {Class}")" style="height: @Height;  width: @Width;">
    
    @* Image *@
    <button @onclick="@OnClick" class="magnifiable-image-button">
        <img src="@Image.Src" alt="@Image.Alt" style="height: 100%; width: 100%;"
             @onmousemove="@OnMouseMove" @onmouseenter="@OnMouseEnterAsync" @onmouseleave="@OnMouseLeave"/>
    </button>
    
    @* Magnifier *@
    <TelerikPopover @ref="_popoverRef" AnchorSelector=".magnifiable-image-container" Position="@(_showOnRight ? PopoverPosition.Right : PopoverPosition.Left)" Offset="@MagnifierMargin"
                    Width="@($"{_magnifierWidth}px")" Height="@($"{_magnifierHeight}px")" Class="popover-magnifier" Collision="PopoverCollision.Fit">
        <PopoverContent>
            
            @* Magnified Image *@
            <img src="@Image.Src" alt="@Image.Alt" class="magnified-image"
                 style="@($"width: {_magnifiedImageWidth}px; height: {_magnifiedImageHeight}px; transform: translateX({_magnifiedImageTransformX}px) translateY({_magnifiedImageTransformY}px); left: {_magnifiedImageLeft}px; top: {_magnifiedImageTop}px;")"/>
        
        </PopoverContent>
    </TelerikPopover>
    
    @* Magnifier Overlay *@
    @if (_isMouseOver)
    {
        <div class="magnifier-overlay" 
             style="@($"width: {_magnifierOverlayWidth}px; height: {_magnifierOverlayHeight}px; transform: translateX({_magnifierOverlayTransformX}px) translateY({_magnifierOverlayTransformY}px);  left: {_magnifierOverlayLeft}px; top: {_magnifierOverlayTop}px;")))">
        </div>
    }
    
    @* Actual Image *@
    <TelerikWindow @bind-Visible="@_isClicked" Modal="true" CloseOnOverlayClick="true" Draggable="false" Resizable="false" Class="window-rounded">
        <WindowActions>
            <WindowAction Name="Close"/>
        </WindowActions>
        <WindowContent>
            <img src="@Image.Src" alt="@Image.Alt"/>
        </WindowContent>
    </TelerikWindow>
    
</div>

<style>
    .magnifiable-image-container {
        position: relative;
        display: inline-block;
        cursor: zoom-in;
    }
    
    .magnifiable-image-button {
        background: none;
        border: none;
        padding: 0;
        margin: 0;
        font: inherit;
        color: inherit;
        cursor: inherit !important;
        outline: none;
        box-shadow: none;
        appearance: none;
        -webkit-appearance: none;
        -moz-appearance: none;
        display: block;
        width: 100%;
        height: 100%;
    }
        .magnifiable-image-button:focus-visible {
            outline: none;
            box-shadow: 0 0 0 2px color-mix(in srgb, var(--kendo-color-on-app-surface, #424242) 50%, transparent);
        }
    
    .popover-magnifier {
        overflow: hidden;
        border-radius: 0;
        position: relative;
        top: @(_adjustForTelerikFit ? $"{(_showOnBottom ? "" : "-")}{MagnifierMargin/4}px" : $"{(_showOnBottom ? "" : "-")}{MagnifierMargin}px");
    }
    
    .magnified-image {
        position: absolute;
    }
    
    .magnifier-overlay {
        position: absolute;
        background: color-mix(in srgb, var(--kendo-color-primary, #1274AC) 15%, transparent);
        pointer-events: none;
        z-index: 5;
        box-sizing: border-box;
        display: block;
    }
</style>

@code 
{
    [Parameter] public required ImageInfo Image { get; set; }

    /// <summary>
    /// The height of the image (e.g., "200px", "100%", or "auto"). Default is "auto".
    /// </summary>
    [Parameter]
    public string Height { get; set; } = "auto";

    /// <summary>
    /// The width of the image (e.g., "200px", "100%", or "auto"). Default is "auto".
    /// </summary>
    [Parameter]
    public string Width { get; set; } = "auto";

    /// <summary>
    /// The magnification scale for the magnifier. Default is 3 (3x magnification).
    /// </summary>
    [Parameter] 
    public double MagnifyScale { get; set; } = 3;

    /// <summary>
    /// Applies additional CSS classes to the MagnifiableImage's root element for custom styling and visual modifications.
    /// </summary>
    [Parameter]
    public string Class { get; set; } = string.Empty;

    private const int MagnifierMargin = 24;
    
    // State for magnifier visibility and container reference
    private bool _isMouseOver;
    private bool _isClicked;
    private ElementReference _containerRef;
    private TelerikPopover? _popoverRef;

    // Image and magnified image dimensions
    private double _imageWidth;
    private double _imageHeight;
    private double _magnifiedImageWidth;
    private double _magnifiedImageHeight;
    
    // Magnifier position and size
    private bool _showOnRight = true;
    private bool _showOnBottom = true;
    private bool _adjustForTelerikFit;
    private double _magnifierWidth;
    private double _magnifierHeight;

    // Magnified image offset within the magnifier
    private double _magnifiedImageLeft;
    private double _magnifiedImageTop;
    
    // Mouse position clamping bounds
    private double _minMouseX;
    private double _minMouseY;
    private double _maxMouseX;
    private double _maxMouseY;
    
    // Mouse position and transform for magnified image
    private double _mouseX;
    private double _mouseY;
    private double _magnifiedImageTransformX;
    private double _magnifiedImageTransformY;

    // Magnifier overlay size and transform
    private double _magnifierOverlayWidth;
    private double _magnifierOverlayHeight;
    private double _magnifierOverlayLeft;
    private double _magnifierOverlayTop;
    private double _magnifierOverlayTransformX;
    private double _magnifierOverlayTransformY;

    private void OnClick()
    {
        _isClicked = true;
    }

    private async Task OnMouseEnterAsync()
    {
        _isMouseOver = true;
        _popoverRef?.Show();
        
        // Get layout info about the image container using getElementLayoutInfo from wwwroot/js/magnifiable-image.js
        var containerLayoutInfo = await JS.InvokeAsync<ElementLayoutInfo>("getElementLayoutInfo", _containerRef);
        
        // Store image size
        _imageWidth = containerLayoutInfo.Width;
        _imageHeight = containerLayoutInfo.Height;
        _magnifiedImageWidth = _imageWidth * MagnifyScale;
        _magnifiedImageHeight = _imageHeight * MagnifyScale;
        
        // Determine magnifier position based on available space
        _showOnRight = containerLayoutInfo.DistanceFromViewportRight >= containerLayoutInfo.DistanceFromViewportLeft;
        _adjustForTelerikFit = Math.Abs(containerLayoutInfo.DistanceFromViewportBottom - containerLayoutInfo.DistanceFromViewportTop) < 20;
        _showOnBottom = containerLayoutInfo.DistanceFromViewportBottom >= containerLayoutInfo.DistanceFromViewportTop;
        
        // Calculate magnifier size based on available space
        _magnifierWidth = _showOnRight 
            ? containerLayoutInfo.DistanceFromViewportRight - MagnifierMargin*2
            : containerLayoutInfo.DistanceFromViewportLeft - MagnifierMargin*2;
        _magnifierHeight = containerLayoutInfo.ViewportHeight - MagnifierMargin*2;
        
        // Center the magnified image in the magnifier
        _magnifiedImageLeft = (_magnifierWidth / 2) - (_imageWidth / 2); 
        _magnifiedImageTop = (_magnifierHeight / 2) - (_imageHeight / 2);
        
        // Calculate min and max mouse X/Y to prevent showing empty space in the magnifier
        _minMouseX = Math.Floor((_magnifierWidth / MagnifyScale) / 2);
        _minMouseY = Math.Floor((_magnifierHeight / MagnifyScale) / 2); 
        _maxMouseX = Math.Ceiling(_imageWidth - ((_magnifierWidth / MagnifyScale) / 2));
        _maxMouseY = Math.Ceiling(_imageHeight - ((_magnifierHeight / MagnifyScale) / 2));

        // Calculate magnifier overlay size and position
        _magnifierOverlayWidth = Math.Floor(Math.Clamp(_magnifierWidth / MagnifyScale, 0, _imageWidth)) - 1;
        _magnifierOverlayHeight = Math.Floor(Math.Clamp(_magnifierHeight / MagnifyScale, 0, _imageHeight)) - 1;
        _magnifierOverlayLeft = -((_magnifierOverlayWidth / 2) + 1);
        _magnifierOverlayTop = -((_magnifierOverlayHeight / 2) + 1);
    }

    private void OnMouseLeave()
    {
        _isMouseOver = false;
        _popoverRef?.Hide();
    }

    private void OnMouseMove(MouseEventArgs e)
    {
        // Clamp mouse X/Y to prevent showing empty space in the magnifier
        if (_minMouseX > _maxMouseX) _mouseX = _imageWidth / 2;
        else _mouseX = Math.Clamp(e.OffsetX, _minMouseX, _maxMouseX);
        if (_minMouseY > _maxMouseY) _mouseY = _imageHeight / 2;
        else _mouseY = Math.Clamp(e.OffsetY, _minMouseY, _maxMouseY);
        
        // Calculate the transform for the magnified image
        _magnifiedImageTransformX = -Math.Round((_mouseX * MagnifyScale) - (_imageWidth / 2));
        _magnifiedImageTransformY = -Math.Round((_mouseY * MagnifyScale) - (_imageHeight / 2));

        // Calculate the transform for the magnifier overlay
        _magnifierOverlayTransformX = Math.Round(_mouseX);
        _magnifierOverlayTransformY = Math.Round(_mouseY);
        
        _popoverRef?.Refresh();
    }

    private record ElementLayoutInfo(
        double Width, 
        double Height, 
        double ViewportHeight,
        double DistanceFromViewportLeft, // distance from viewport's left edge to element's left edge
        double DistanceFromViewportRight, // distance from element's right edge to viewport's right edge
        double DistanceFromViewportTop, // distance from viewport's top edge to element's top edge
        double DistanceFromViewportBottom); // distance from element's bottom edge to viewport's bottom edge
}

magnifiable-image.js

// Returns width, height, and the space to the left and right of the element relative to the viewport
window.getElementLayoutInfo = (element) => {
    if (!element) return null;
    const elementRect = element.getBoundingClientRect();
    return {
        width: elementRect.width,
        height: elementRect.height,
        viewportHeight: window.innerHeight,
        distanceFromViewportLeft: elementRect.left, // distance from viewport's left edge to element's left edge
        distanceFromViewportRight: window.innerWidth - elementRect.right, // distance from element's right edge to viewport's right edge
        distanceFromViewportTop: elementRect.top, // distance from viewport's top edge to element's top edge
        distanceFromViewportBottom: window.innerHeight - elementRect.bottom // distance from element's bottom edge to viewport's bottom edge
    };
};

Note

Only tested in Blazor WebAssembly. The component may see performance issues in Blazor Server.

1 2 3 4 5 6