Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/Aspire.Dashboard/Components/Controls/AspireMenu.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@namespace Aspire.Dashboard.Components
@using System.Collections.Immutable
@using Aspire.Dashboard.Model
@using Microsoft.FluentUI.AspNetCore.Components.DesignTokens
@inherits FluentComponentBase

<FluentMenu @ref="_menu" Anchor="@Anchor" Anchored="@Anchored" Open="@Open" OpenChanged="OnOpenChanged" VerticalThreshold="200">
@foreach (var item in Items)
{
@if (item.IsDivider)
{
<FluentDivider />
}
else
{
var additionalMenuItemAttributes = new Dictionary<string, object>(item.AdditionalAttributes ?? ImmutableDictionary<string, object>.Empty)
{
{ "title", item.Tooltip ?? item.Text ?? string.Empty }
};

<FluentMenuItem Id="@item.Id" Class="@item.Class" OnClick="() => HandleItemClicked(item)" Disabled="@item.IsDisabled" AdditionalAttributes="@additionalMenuItemAttributes">
@item.Text
@if (item.Icon != null)
{
<span slot="start">
<FluentIcon Value="@item.Icon" Style="vertical-align: text-bottom;" Width="16px" />
</span>
}
</FluentMenuItem>
}
}
</FluentMenu>
65 changes: 65 additions & 0 deletions src/Aspire.Dashboard/Components/Controls/AspireMenu.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Dashboard.Model;
using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components;

namespace Aspire.Dashboard.Components;

public partial class AspireMenu : FluentComponentBase
{
private FluentMenu? _menu;

[Parameter]
public string? Anchor { get; set; }

[Parameter]
public bool Open { get; set; }

[Parameter]
public bool Anchored { get; set; } = true;

/// <summary>
/// Raised when the <see cref="Open"/> property changed.
/// </summary>
[Parameter]
public EventCallback<bool> OpenChanged { get; set; }

[Parameter]
public required IList<MenuButtonItem> Items { get; set; }

public async Task CloseAsync()
{
if (_menu is { } menu)
{
await menu.CloseAsync();
}
}

public async Task OpenAsync(int clientX, int clientY)
{
if (_menu is { } menu)
{
await menu.OpenAsync(clientX, clientY);
}
}

private async Task HandleItemClicked(MenuButtonItem item)
{
if (item.OnClick is {} onClick)
{
await onClick();
}
Open = false;
}

private Task OnOpenChanged(bool open)
{
Open = open;

return OpenChanged.HasDelegate
? OpenChanged.InvokeAsync(open)
: Task.CompletedTask;
}
}
27 changes: 1 addition & 26 deletions src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,4 @@
}
</FluentButton>

<FluentMenu Anchor="@MenuButtonId" aria-labelledby="button" @bind-Open="@_visible" VerticalThreshold="200">
@foreach (var item in Items)
{
@if (item.IsDivider)
{
<FluentDivider />
}
else
{
var additionalMenuItemAttributes = new Dictionary<string, object>(item.AdditionalAttributes ?? ImmutableDictionary<string, object>.Empty)
{
{ "title", item.Tooltip ?? item.Text ?? string.Empty }
};

<FluentMenuItem Id="@item.Id" Class="@item.Class" OnClick="() => HandleItemClicked(item)" Disabled="@item.IsDisabled" AdditionalAttributes="@additionalMenuItemAttributes">
@item.Text
@if (item.Icon != null)
{
<span slot="start">
<FluentIcon Value="@item.Icon" Style="vertical-align: text-bottom;" Width="16px" />
</span>
}
</FluentMenuItem>
}
}
</FluentMenu>
<AspireMenu Anchor="@MenuButtonId" @bind-Open="@_visible" Items="Items" />
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,6 @@ private void ToggleMenu()
_visible = !_visible;
}

private async Task HandleItemClicked(MenuButtonItem item)
{
if (item.OnClick is {} onClick)
{
await onClick();
}
_visible = false;
}

private void OnKeyDown(KeyboardEventArgs args)
{
if (args is not null && args.Key == "Escape")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
{
var highlightedCommand = _highlightedCommands[i];

<FluentButton Appearance="Appearance.Lightweight" Title="@(!string.IsNullOrEmpty(highlightedCommand.DisplayDescription) ? highlightedCommand.DisplayDescription : highlightedCommand.DisplayName)" OnClick="@(() => CommandSelected.InvokeAsync(highlightedCommand))" Disabled="@(highlightedCommand.State == CommandViewModelState.Disabled || IsCommandExecuting(Resource, highlightedCommand))">
<FluentButton Appearance="Appearance.Lightweight" Title="@(!string.IsNullOrEmpty(highlightedCommand.DisplayDescription) ? highlightedCommand.DisplayDescription : highlightedCommand.DisplayName)" OnClick="@(() => CommandSelected(highlightedCommand))" Disabled="@(highlightedCommand.State == CommandViewModelState.Disabled || IsCommandExecuting(Resource, highlightedCommand))">
@if (!string.IsNullOrEmpty(highlightedCommand.IconName) && IconResolver.ResolveIconName(highlightedCommand.IconName, IconSize.Size16, highlightedCommand.IconVariant) is { } icon)
{
<FluentIcon Value="@icon" Width="16px" />
Expand Down
116 changes: 74 additions & 42 deletions src/Aspire.Dashboard/Components/Controls/ResourceActions.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,13 @@ public partial class ResourceActions : ComponentBase
public required TelemetryRepository TelemetryRepository { get; set; }

[Parameter]
public required IList<CommandViewModel> Commands { get; set; }

[Parameter]
public required EventCallback<CommandViewModel> CommandSelected { get; set; }
public required Func<CommandViewModel, Task> CommandSelected { get; set; }

[Parameter]
public required Func<ResourceViewModel, CommandViewModel, bool> IsCommandExecuting { get; set; }

[Parameter]
public required EventCallback<string> OnViewDetails { get; set; }
public required Func<string?, Task> OnViewDetails { get; set; }

[Parameter]
public required ResourceViewModel Resource { get; set; }
Expand All @@ -65,86 +62,121 @@ protected override void OnParametersSet()
_menuItems.Clear();
_highlightedCommands.Clear();

_menuItems.Add(new MenuButtonItem
AddMenuItems(
_menuItems,
_menuButton?.MenuButtonId,
Resource,
NavigationManager,
TelemetryRepository,
GetResourceName,
ControlLoc,
Loc,
OnViewDetails,
CommandSelected,
IsCommandExecuting,
showConsoleLogsItem: true);

// If display is desktop then we display highlighted commands next to the ... button.
if (ViewportInformation.IsDesktop)
{
_highlightedCommands.AddRange(Resource.Commands.Where(c => c.IsHighlighted && c.State != CommandViewModelState.Hidden).Take(MaxHighlightedCount));
}
}

public static void AddMenuItems(
List<MenuButtonItem> menuItems,
string? openingMenuButtonId,
ResourceViewModel resource,
NavigationManager navigationManager,
TelemetryRepository telemetryRepository,
Func<ResourceViewModel, string> getResourceName,
IStringLocalizer<Resources.ControlsStrings> controlLoc,
IStringLocalizer<Resources.Resources> loc,
Func<string?, Task> onViewDetails,
Func<CommandViewModel, Task> commandSelected,
Func<ResourceViewModel, CommandViewModel, bool> isCommandExecuting,
bool showConsoleLogsItem)
{
menuItems.Add(new MenuButtonItem
{
Text = ControlLoc[nameof(Resources.ControlsStrings.ActionViewDetailsText)],
Text = controlLoc[nameof(Resources.ControlsStrings.ActionViewDetailsText)],
Icon = s_viewDetailsIcon,
OnClick = () => OnViewDetails.InvokeAsync(_menuButton?.MenuButtonId)
OnClick = () => onViewDetails(openingMenuButtonId)
});
_menuItems.Add(new MenuButtonItem

if (showConsoleLogsItem)
{
Text = Loc[nameof(Resources.Resources.ResourceActionConsoleLogsText)],
Icon = s_consoleLogsIcon,
OnClick = () =>
menuItems.Add(new MenuButtonItem
{
NavigationManager.NavigateTo(DashboardUrls.ConsoleLogsUrl(resource: Resource.Name));
return Task.CompletedTask;
}
});
Text = loc[nameof(Resources.Resources.ResourceActionConsoleLogsText)],
Icon = s_consoleLogsIcon,
OnClick = () =>
{
navigationManager.NavigateTo(DashboardUrls.ConsoleLogsUrl(resource: resource.Name));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Update after #8470 is merged.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or #8470 will need to react to this PR 😄

return Task.CompletedTask;
}
});
}

// Show telemetry menu items if there is telemetry for the resource.
var hasTelemetryApplication = TelemetryRepository.GetApplicationByCompositeName(Resource.Name) != null;
var hasTelemetryApplication = telemetryRepository.GetApplicationByCompositeName(resource.Name) != null;
if (hasTelemetryApplication)
{
_menuItems.Add(new MenuButtonItem { IsDivider = true });
_menuItems.Add(new MenuButtonItem
menuItems.Add(new MenuButtonItem { IsDivider = true });
menuItems.Add(new MenuButtonItem
{
Text = Loc[nameof(Resources.Resources.ResourceActionStructuredLogsText)],
Tooltip = Loc[nameof(Resources.Resources.ResourceActionStructuredLogsText)],
Text = loc[nameof(Resources.Resources.ResourceActionStructuredLogsText)],
Tooltip = loc[nameof(Resources.Resources.ResourceActionStructuredLogsText)],
Icon = s_structuredLogsIcon,
OnClick = () =>
{
NavigationManager.NavigateTo(DashboardUrls.StructuredLogsUrl(resource: GetResourceName(Resource)));
navigationManager.NavigateTo(DashboardUrls.StructuredLogsUrl(resource: getResourceName(resource)));
return Task.CompletedTask;
}
});
_menuItems.Add(new MenuButtonItem
menuItems.Add(new MenuButtonItem
{
Text = Loc[nameof(Resources.Resources.ResourceActionTracesText)],
Tooltip = Loc[nameof(Resources.Resources.ResourceActionTracesText)],
Text = loc[nameof(Resources.Resources.ResourceActionTracesText)],
Tooltip = loc[nameof(Resources.Resources.ResourceActionTracesText)],
Icon = s_tracesIcon,
OnClick = () =>
{
NavigationManager.NavigateTo(DashboardUrls.TracesUrl(resource: GetResourceName(Resource)));
navigationManager.NavigateTo(DashboardUrls.TracesUrl(resource: getResourceName(resource)));
return Task.CompletedTask;
}
});
_menuItems.Add(new MenuButtonItem
menuItems.Add(new MenuButtonItem
{
Text = Loc[nameof(Resources.Resources.ResourceActionMetricsText)],
Tooltip = Loc[nameof(Resources.Resources.ResourceActionMetricsText)],
Text = loc[nameof(Resources.Resources.ResourceActionMetricsText)],
Tooltip = loc[nameof(Resources.Resources.ResourceActionMetricsText)],
Icon = s_metricsIcon,
OnClick = () =>
{
NavigationManager.NavigateTo(DashboardUrls.MetricsUrl(resource: GetResourceName(Resource)));
navigationManager.NavigateTo(DashboardUrls.MetricsUrl(resource: getResourceName(resource)));
return Task.CompletedTask;
}
});
}

// If display is desktop then we display highlighted commands next to the ... button.
if (ViewportInformation.IsDesktop)
{
_highlightedCommands.AddRange(Commands.Where(c => c.IsHighlighted && c.State != CommandViewModelState.Hidden).Take(MaxHighlightedCount));
}

var menuCommands = Commands.Where(c => !_highlightedCommands.Contains(c) && c.State != CommandViewModelState.Hidden).ToList();
var menuCommands = resource.Commands
.Where(c => c.State != CommandViewModelState.Hidden)
.OrderBy(c => !c.IsHighlighted)
.ToList();
if (menuCommands.Count > 0)
{
_menuItems.Add(new MenuButtonItem { IsDivider = true });
menuItems.Add(new MenuButtonItem { IsDivider = true });

foreach (var command in menuCommands)
{
var icon = (!string.IsNullOrEmpty(command.IconName) && IconResolver.ResolveIconName(command.IconName, IconSize.Size16, command.IconVariant) is { } i) ? i : null;

_menuItems.Add(new MenuButtonItem
menuItems.Add(new MenuButtonItem
{
Text = command.DisplayName,
Tooltip = command.DisplayDescription,
Icon = icon,
OnClick = () => CommandSelected.InvokeAsync(command),
IsDisabled = command.State == CommandViewModelState.Disabled || IsCommandExecuting(Resource, command)
OnClick = () => commandSelected(command),
IsDisabled = command.State == CommandViewModelState.Disabled || isCommandExecuting(resource, command)
});
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
{
if (_highlightedCommands.Count > 0)
{
<span style="margin-left: 10px;">@Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsResourceCommands)]</span>
<span style="margin-left: 10px;">@Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsResourceActions)]</span>

@foreach (var command in _highlightedCommands)
{
Expand All @@ -60,7 +60,7 @@
<AspireMenuButton ButtonAppearance="Appearance.Lightweight"
Icon="@(new Icons.Regular.Size20.MoreHorizontal())"
Items="@_resourceMenuItems"
Title="@Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsResourceCommands)]" />
Title="@Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsResourceActions)]" />
}
}

Expand Down
Loading