Context Menu

The Arcadia DataGrid supports custom right-click context menus on rows. Use the ContextMenuTemplate render fragment to define menu items, and the OnContextMenu callback to handle actions.

Basic Usage

Provide a ContextMenuTemplate with a RenderFragment<TItem>. The template receives the row item that was right-clicked. The menu is automatically positioned at the click coordinates and dismissed when the user clicks elsewhere.

<ArcadiaDataGrid TItem="Employee" Data="@employees">
    <ArcadiaColumn TItem="Employee" Title="Name" Field="@(e => e.Name)" />
    <ArcadiaColumn TItem="Employee" Title="Department" Field="@(e => e.Department)" />
    <ArcadiaColumn TItem="Employee" Title="Salary" Field="@(e => e.Salary)" Format="C0" />

    <ContextMenuTemplate Context="employee">
        <div class="context-menu">
            <button @onclick="@(() => EditEmployee(employee))">Edit</button>
            <button @onclick="@(() => CopyName(employee))">Copy Name</button>
            <hr />
            <button @onclick="@(() => DeleteEmployee(employee))" class="danger">Delete</button>
        </div>
    </ContextMenuTemplate>
</ArcadiaDataGrid>

@code {
    private List<Employee> employees = new();

    private void EditEmployee(Employee emp)
    {
        Navigation.NavigateTo($"/employees/{emp.Id}/edit");
    }

    private async Task CopyName(Employee emp)
    {
        await Clipboard.SetTextAsync(emp.Name);
    }

    private async Task DeleteEmployee(Employee emp)
    {
        await EmployeeService.DeleteAsync(emp.Id);
        employees.Remove(emp);
    }
}

OnContextMenu Callback

The OnContextMenu callback fires when the user right-clicks a row, before the menu is displayed. Use it for logging, dynamic menu configuration, or to conditionally suppress the menu.

<ArcadiaDataGrid TItem="Employee" Data="@employees"
                 OnContextMenu="@HandleContextMenu">
    <ArcadiaColumn TItem="Employee" Title="Name" Field="@(e => e.Name)" />
    <ArcadiaColumn TItem="Employee" Title="Department" Field="@(e => e.Department)" />

    <ContextMenuTemplate Context="employee">
        <div class="context-menu">
            <button @onclick="@(() => ViewDetails(employee))">View Details</button>
            @if (canEdit)
            {
                <button @onclick="@(() => EditEmployee(employee))">Edit</button>
            }
        </div>
    </ContextMenuTemplate>
</ArcadiaDataGrid>

@code {
    private bool canEdit;

    private void HandleContextMenu(DataGridContextMenuEventArgs<Employee> args)
    {
        // Check permissions for the clicked row
        canEdit = args.Item.Department != "Executive";

        // Optionally cancel the menu entirely
        if (args.Item.IsArchived)
        {
            args.Cancel = true;
        }
    }
}

DataGridContextMenuEventArgs

public class DataGridContextMenuEventArgs<TItem>
{
    public TItem Item { get; set; }        // The right-clicked row item
    public double ClientX { get; set; }    // Mouse X coordinate (viewport)
    public double ClientY { get; set; }    // Mouse Y coordinate (viewport)
    public bool Cancel { get; set; }       // Set true to suppress the menu
}

Positioning

The context menu is rendered in a portal (appended to <body>) and positioned absolutely at the click coordinates. It automatically adjusts if the menu would overflow the viewport edge. The grid sets aria-haspopup="true" on rows when a ContextMenuTemplate is provided.

Styling the Menu

The menu container has the CSS class arcadia-context-menu. Style it to match your theme:

.arcadia-context-menu {
    background: #1a1030;
    border: 1px solid rgba(139, 92, 246, 0.2);
    border-radius: 8px;
    padding: 4px;
    min-width: 160px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}

.arcadia-context-menu button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 12px;
    border: none;
    background: transparent;
    color: #d4d0ec;
    font-size: 0.875rem;
    border-radius: 6px;
    cursor: pointer;
}

.arcadia-context-menu button:hover {
    background: rgba(139, 92, 246, 0.12);
    color: #f1f0f9;
}

.arcadia-context-menu button.danger {
    color: #f87171;
}

.arcadia-context-menu button.danger:hover {
    background: rgba(248, 113, 113, 0.1);
}

.arcadia-context-menu hr {
    border: none;
    border-top: 1px solid rgba(139, 92, 246, 0.1);
    margin: 4px 0;
}

Full Example with Multiple Actions

<ArcadiaDataGrid TItem="Employee" Data="@employees"
                 OnContextMenu="@HandleContextMenu">
    <ArcadiaColumn TItem="Employee" Title="Name" Field="@(e => e.Name)" />
    <ArcadiaColumn TItem="Employee" Title="Department" Field="@(e => e.Department)" />
    <ArcadiaColumn TItem="Employee" Title="Email" Field="@(e => e.Email)" />
    <ArcadiaColumn TItem="Employee" Title="Salary" Field="@(e => e.Salary)" Format="C0" />

    <ContextMenuTemplate Context="employee">
        <div class="context-menu">
            <button @onclick="@(() => ViewDetails(employee))">
                View Details
            </button>
            <button @onclick="@(() => EditEmployee(employee))">
                Edit
            </button>
            <button @onclick="@(() => CopyEmail(employee))">
                Copy Email
            </button>
            <hr />
            <button @onclick="@(() => ExportRow(employee))">
                Export as CSV
            </button>
            <hr />
            <button @onclick="@(() => DeleteEmployee(employee))" class="danger">
                Delete
            </button>
        </div>
    </ContextMenuTemplate>
</ArcadiaDataGrid>

@code {
    private List<Employee> employees = new();

    protected override async Task OnInitializedAsync()
    {
        employees = await EmployeeService.GetAllAsync();
    }

    private void HandleContextMenu(DataGridContextMenuEventArgs<Employee> args)
    {
        // Log for audit trail
        Logger.LogInformation("Context menu opened for {Name}", args.Item.Name);
    }

    private void ViewDetails(Employee emp)
    {
        Navigation.NavigateTo($"/employees/{emp.Id}");
    }

    private void EditEmployee(Employee emp)
    {
        Navigation.NavigateTo($"/employees/{emp.Id}/edit");
    }

    private async Task CopyEmail(Employee emp)
    {
        await Clipboard.SetTextAsync(emp.Email);
    }

    private async Task ExportRow(Employee emp)
    {
        var csv = $"{emp.Name},{emp.Department},{emp.Email},{emp.Salary}";
        await FileExport.DownloadAsync($"{emp.Name}.csv", csv);
    }

    private async Task DeleteEmployee(Employee emp)
    {
        await EmployeeService.DeleteAsync(emp.Id);
        employees.Remove(emp);
    }
}

Accessibility: The context menu is keyboard-navigable. When open, arrow keys move focus between items, Enter activates the focused item, and Escape closes the menu. Focus is returned to the originating row on close.