feat(ui): add right-click context menu to TreeView (R15)

This commit is contained in:
Joseph Doherty
2026-03-23 02:32:57 -04:00
parent f127efe6ea
commit 4e5b5facec
2 changed files with 98 additions and 2 deletions

View File

@@ -19,6 +19,14 @@ else
</ul>
}
@if (_showContextMenu && _contextMenuItem != null && ContextMenu != null)
{
<div class="tv-ctx-overlay" @onclick="DismissContextMenu" style="position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:1049;background:transparent;"></div>
<div class="dropdown-menu show" style="position:fixed;top:@(_contextMenuY)px;left:@(_contextMenuX)px;z-index:1050;">
@ContextMenu(_contextMenuItem)
</div>
}
@{ void RenderNode(TItem item, int depth)
{
var key = KeySelector(item);
@@ -29,7 +37,8 @@ else
<li role="treeitem" @key="key"
aria-expanded="@(isBranch ? (isExpanded ? "true" : "false") : null)"
aria-selected="@(Selectable && SelectedKey != null && SelectedKey.Equals(key) ? "true" : null)">
<div class="tv-row @(Selectable && SelectedKey != null && SelectedKey.Equals(key) ? SelectedCssClass : "")" style="padding-left: @(depth * IndentPx)px">
<div class="tv-row @(Selectable && SelectedKey != null && SelectedKey.Equals(key) ? SelectedCssClass : "")" style="padding-left: @(depth * IndentPx)px"
@oncontextmenu="(e) => OnContextMenu(e, item)" @oncontextmenu:preventDefault="@(ContextMenu != null)">
@if (isBranch)
{
<span class="tv-toggle" @onclick="() => ToggleExpand(key)" @onclick:stopPropagation>@(isExpanded ? "\u2212" : "+")</span>
@@ -60,6 +69,10 @@ else
private HashSet<object> _expandedKeys = new();
private bool _initialExpansionApplied;
private bool _storageLoaded;
private TItem? _contextMenuItem;
private double _contextMenuX;
private double _contextMenuY;
private bool _showContextMenu;
[Parameter, EditorRequired] public IReadOnlyList<TItem> Items { get; set; } = [];
[Parameter, EditorRequired] public Func<TItem, IReadOnlyList<TItem>> ChildrenSelector { get; set; } = default!;
@@ -67,6 +80,7 @@ else
[Parameter, EditorRequired] public Func<TItem, object> KeySelector { get; set; } = default!;
[Parameter, EditorRequired] public RenderFragment<TItem> NodeContent { get; set; } = default!;
[Parameter] public RenderFragment? EmptyContent { get; set; }
[Parameter] public RenderFragment<TItem>? ContextMenu { get; set; }
[Parameter] public int IndentPx { get; set; } = 24;
[Parameter] public bool ShowGuideLines { get; set; } = true;
[Parameter] public Func<TItem, bool>? InitiallyExpanded { get; set; }
@@ -164,6 +178,22 @@ else
}
}
private void OnContextMenu(MouseEventArgs e, TItem item)
{
if (ContextMenu == null) return;
_contextMenuItem = item;
_contextMenuX = e.ClientX;
_contextMenuY = e.ClientY;
_showContextMenu = true;
}
private void DismissContextMenu()
{
_showContextMenu = false;
_contextMenuItem = default;
}
/// <summary>Expand every branch node in the tree.</summary>
public void ExpandAll()
{