95 lines
2.4 KiB
Plaintext
95 lines
2.4 KiB
Plaintext
@implements IAsyncDisposable
|
|
@inject IJSRuntime JS
|
|
|
|
<div class="kebab-menu" @ref="_menuRef">
|
|
<button class="kebab-button" type="button" @onclick="Toggle" aria-label="@AriaLabel" aria-haspopup="menu" aria-expanded="@_isOpen">⋮</button>
|
|
@if (_isOpen)
|
|
{
|
|
<div class="kebab-menu__items" role="menu">
|
|
@ChildContent(_context)
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
@code {
|
|
[Parameter] public string AriaLabel { get; set; } = "Menu";
|
|
[Parameter] public RenderFragment<KebabMenuContext> ChildContent { get; set; } = default!;
|
|
|
|
private bool _isOpen;
|
|
private ElementReference _menuRef;
|
|
private DotNetObjectReference<KebabMenu>? _dotNetRef;
|
|
private IJSObjectReference? _jsRef;
|
|
private readonly KebabMenuContext _context;
|
|
|
|
public KebabMenu()
|
|
{
|
|
_context = new KebabMenuContext(Close);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Toggles the open state of the menu.
|
|
/// </summary>
|
|
private void Toggle()
|
|
{
|
|
_isOpen = !_isOpen;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Closes the menu if it is open.
|
|
/// </summary>
|
|
[JSInvokable]
|
|
public void Close()
|
|
{
|
|
if (!_isOpen)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_isOpen = false;
|
|
_ = InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called after the component has been rendered. Registers the JavaScript event handler on first render.
|
|
/// </summary>
|
|
/// <param name="firstRender">True if this is the first render; otherwise, false.</param>
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (!firstRender)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_dotNetRef = DotNetObjectReference.Create(this);
|
|
_jsRef = await JS.InvokeAsync<IJSObjectReference>("kebabMenu.register", _menuRef, _dotNetRef);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disposes the JavaScript object reference and event handler.
|
|
/// </summary>
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (_jsRef is not null)
|
|
{
|
|
await _jsRef.InvokeVoidAsync("dispose");
|
|
await _jsRef.DisposeAsync();
|
|
}
|
|
|
|
_dotNetRef?.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the KebabMenuContext class.
|
|
/// </summary>
|
|
/// <param name="close">The action to close the menu.</param>
|
|
public sealed class KebabMenuContext
|
|
{
|
|
public KebabMenuContext(Action close)
|
|
{
|
|
Close = close;
|
|
}
|
|
|
|
public Action Close { get; }
|
|
}
|
|
}
|