Class Sealed
public sealed class MokaJsonContextMenu : ComponentBase, IAsyncDisposable

Namespace: Moka.Blazor.Json.Components

Context menu component with customizable actions per node. Positioned entirely via C# inline styles to avoid Blazor/JS DOM conflicts.

Inheritance

Inherits from: ComponentBase

Implements: IAsyncDisposable

Properties

NameDescription
Actions The list of available context actions.
IsVisible Whether the context menu is currently visible.
NodeContext The context of the node on which the menu was invoked.
OnDismiss Callback when the context menu should be dismissed.

Actions

IReadOnlyList<MokaJsonContextAction>? MokaJsonContextMenu.Actions { get; set; }

The list of available context actions.

IsVisible

bool MokaJsonContextMenu.IsVisible { get; set; }

Whether the context menu is currently visible.

NodeContext

MokaJsonNodeContext? MokaJsonContextMenu.NodeContext { get; set; }

The context of the node on which the menu was invoked.

OnDismiss

EventCallback MokaJsonContextMenu.OnDismiss { get; set; }

Callback when the context menu should be dismissed.

Methods

NameDescription
DismissContextMenu() Called from JS when a click outside the menu occurs.
DisposeAsync()
ShowAsync(double clientX, double clientY) Shows the context menu at the given viewport coordinates.

DismissContextMenu()

Task MokaJsonContextMenu.DismissContextMenu()

Called from JS when a click outside the menu occurs.

ShowAsync(double clientX, double clientY)

Task MokaJsonContextMenu.ShowAsync(double clientX, double clientY)

Shows the context menu at the given viewport coordinates.

Type Relationships
classDiagram
                    style MokaJsonContextMenu fill:#f9f,stroke:#333,stroke-width:2px
                    MokaJsonContextMenu --|> ComponentBase : inherits
                    MokaJsonContextMenu ..|> IAsyncDisposable : implements
                
View Source
/// <summary>
///     Context menu component with customizable actions per node.
///     Positioned entirely via C# inline styles to avoid Blazor/JS DOM conflicts.
/// </summary>
public sealed partial class MokaJsonContextMenu : ComponentBase, IAsyncDisposable
{
#region Injected Services
    [Inject]
    private MokaJsonInterop Interop { get; set; } = null!;

#endregion
#region IAsyncDisposable
    /// <inheritdoc/>
    public async ValueTask DisposeAsync()
    {
        await RemoveDismissListenerAsync();
        _selfRef?.Dispose();
    }

#endregion
#region Parameters
    /// <summary>The list of available context actions.</summary>
    [Parameter]
    public IReadOnlyList<MokaJsonContextAction>? Actions { get; set; }

    /// <summary>Whether the context menu is currently visible.</summary>
    [Parameter]
    public bool IsVisible { get; set; }

    /// <summary>The context of the node on which the menu was invoked.</summary>
    [Parameter]
    public MokaJsonNodeContext? NodeContext { get; set; }

    /// <summary>Callback when the context menu should be dismissed.</summary>
    [Parameter]
    public EventCallback OnDismiss { get; set; }
#endregion
#region State Fields
    private string MenuId { get; } = $"moka-ctx-{Guid.NewGuid():N}";

    private double _left;
    private double _top;
    private int _dismissListenerId;
    private DotNetObjectReference<MokaJsonContextMenu>? _selfRef;
#endregion
#region Computed Properties
    private string MenuStyle => string.Create(CultureInfo.InvariantCulture, $"left:{_left}px;top:{_top}px;display:block;");
    private IEnumerable<MokaJsonContextAction> VisibleActions => (Actions ?? []).Where(a => a.IsVisible?.Invoke(NodeContext!) ?? true).OrderBy(a => a.Order);

#endregion
#region Public API
    /// <summary>
    ///     Shows the context menu at the given viewport coordinates.
    /// </summary>
    public async Task ShowAsync(double clientX, double clientY)
    {
        // Clean up previous dismiss listener if any
        await RemoveDismissListenerAsync();
        _left = clientX;
        _top = clientY;
        StateHasChanged();
        // Register a dismiss listener for clicks outside
        _selfRef ??= DotNetObjectReference.Create(this);
        _dismissListenerId = await Interop.AddContextMenuDismissListenerAsync(_selfRef, MenuId);
    }

    /// <summary>
    ///     Called from JS when a click outside the menu occurs.
    /// </summary>
    [JSInvokable]
    public async Task DismissContextMenu()
    {
        await RemoveDismissListenerAsync();
        await OnDismiss.InvokeAsync();
    }

#endregion
#region Private Methods
    private static string GetItemClass(bool isEnabled) => isEnabled ? "moka-json-context-item" : "moka-json-context-item moka-json-context-item--disabled";
    private async Task HandleAction(MokaJsonContextAction action, bool isEnabled)
    {
        if (!isEnabled || NodeContext is null)
        {
            return;
        }

        await action.OnExecute(NodeContext);
        await RemoveDismissListenerAsync();
        await OnDismiss.InvokeAsync();
    }

    private async Task RemoveDismissListenerAsync()
    {
        if (_dismissListenerId != 0)
        {
            try
            {
                await Interop.RemoveContextMenuDismissListenerAsync(_dismissListenerId);
            }
            catch (JSDisconnectedException)
            {
            }

            _dismissListenerId = 0;
        }
    }
#endregion
}
Was this page helpful?