Skip to content

Commit 1589fa1

Browse files
committed
Enable Urls to display using FluentIcon
Added IconName, IconVarient to ResourceUrlAnnotaion and plumbed through.
1 parent d9c3266 commit 1589fa1

File tree

12 files changed

+79
-12
lines changed

12 files changed

+79
-12
lines changed

playground/TestShop/TestShop.AppHost/Program.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
.WithDataVolume()
99
.WithPgAdmin(resource =>
1010
{
11-
resource.WithUrlForEndpoint("http", u => u.DisplayText = "PG Admin");
11+
resource.WithUrlForEndpoint("http", u =>
12+
{
13+
u.DisplayText = "PG Admin";
14+
u.IconName = "DatabaseStack";
15+
u.IconVariant = IconVariant.Filled;
16+
});
1217
})
1318
.AddDatabase("catalogdb");
1419

src/Aspire.Dashboard/Components/ResourcesGridColumns/UrlsColumnDisplay.razor

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ else if (DisplayedUrls.Count > 1)
4545
{
4646
var d = (DisplayedUrl)item.Data!;
4747
<div class="url-link">
48-
@WriteUrl(d)
48+
@WriteUrl(d, displayLabel: true)
4949
</div>
5050
}
5151
</div>
@@ -62,10 +62,18 @@ else if (DisplayedUrls.Count > 1)
6262
}
6363

6464
@code {
65-
RenderFragment WriteUrl(DisplayedUrl displayedUrl)
65+
RenderFragment WriteUrl(DisplayedUrl displayedUrl, bool displayLabel = false)
6666
{
6767
if (displayedUrl.Url != null)
6868
{
69+
if (string.IsNullOrWhiteSpace(displayedUrl.IconName) == false && IconResolver.ResolveIconName(displayedUrl.IconName, IconSize.Size16, iconVariant: displayedUrl.IconVariant) is { } icon)
70+
{
71+
return @<FluentButton Appearance="Appearance.Lightweight" Title="@displayedUrl.Text" StopPropagation="true" OnClick="@(() => JSRuntime.InvokeAsync<object>("open", displayedUrl.Url, "_blank"))" >
72+
<FluentIcon Value="@icon" Width="16px" Title="@displayedUrl.Text" />
73+
</FluentButton>;
74+
}
75+
//<a href="@displayedUrl.Url" target="_blank" @onclick:stopPropagation="true">
76+
6977
return @<a href="@displayedUrl.Url" target="_blank" @onclick:stopPropagation="true">@displayedUrl.Text</a>;
7078
}
7179
else

src/Aspire.Dashboard/Components/ResourcesGridColumns/UrlsColumnDisplay.razor.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Aspire.Dashboard.Resources;
66
using Microsoft.AspNetCore.Components;
77
using Microsoft.Extensions.Localization;
8+
using Microsoft.JSInterop;
89

910
namespace Aspire.Dashboard.Components;
1011

@@ -25,5 +26,14 @@ public partial class UrlsColumnDisplay
2526
[Inject]
2627
public required IStringLocalizer<Columns> Loc { get; init; }
2728

29+
[Inject]
30+
public required IconResolver IconResolver { get; init; }
31+
32+
[Inject]
33+
public required NavigationManager NavigationManager { get; init; }
34+
35+
[Inject]
36+
public required IJSRuntime JSRuntime { get; init; }
37+
2838
private bool _popoverVisible;
2939
}

src/Aspire.Dashboard/Model/ResourceUrlHelpers.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Diagnostics;
55
using Aspire.Dashboard.Components.Controls;
6+
using Microsoft.FluentUI.AspNetCore.Components;
67

78
namespace Aspire.Dashboard.Model;
89

@@ -51,7 +52,9 @@ public static List<DisplayedUrl> GetUrls(ResourceViewModel resource, bool includ
5152
SortOrder = url.DisplayProperties.SortOrder,
5253
DisplayName = string.IsNullOrEmpty(url.DisplayProperties.DisplayName) ? null : url.DisplayProperties.DisplayName,
5354
OriginalUrlString = url.Url.OriginalString,
54-
Text = string.IsNullOrEmpty(url.DisplayProperties.DisplayName) ? url.Url.OriginalString : url.DisplayProperties.DisplayName
55+
Text = string.IsNullOrEmpty(url.DisplayProperties.DisplayName) ? url.Url.OriginalString : url.DisplayProperties.DisplayName,
56+
IconName = url.DisplayProperties.IconName,
57+
IconVariant = url.DisplayProperties.IconVariant,
5558
});
5659
index++;
5760
}
@@ -86,6 +89,9 @@ public sealed class DisplayedUrl : IPropertyGridItem
8689
public string? DisplayName { get; set; }
8790
public required string OriginalUrlString { get; set; }
8891

92+
public string? IconName { get; set; }
93+
public IconVariant? IconVariant { get; set; }
94+
8995
/// <summary>
9096
/// Don't display a plain string value here. The URL will be displayed as a hyperlink
9197
/// in <see cref="ResourceDetails.RenderAddressValue(DisplayedUrl, string)"/> instead.

src/Aspire.Dashboard/Model/ResourceViewModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,9 @@ public UrlViewModel(string? endpointName, Uri url, bool isInternal, bool isInact
428428
}
429429
}
430430

431-
public record UrlDisplayPropertiesViewModel(string DisplayName, int SortOrder)
431+
public record UrlDisplayPropertiesViewModel(string DisplayName, int SortOrder, string? IconName, IconVariant? IconVariant)
432432
{
433-
public static readonly UrlDisplayPropertiesViewModel Empty = new(string.Empty, 0);
433+
public static readonly UrlDisplayPropertiesViewModel Empty = new(string.Empty, 0, string.Empty, (IconVariant)(-1));
434434
}
435435

436436
public sealed record class VolumeViewModel(int index, string Source, string Target, string MountType, bool IsReadOnly) : IPropertyGridItem

src/Aspire.Dashboard/ServiceClient/Partials.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,21 @@ static string TranslateKnownUrlName(Url url)
9898
};
9999
}
100100

101+
static FluentUIIconVariant MapIconVariant(IconVariant iconVariant)
102+
{
103+
return iconVariant switch
104+
{
105+
IconVariant.Regular => FluentUIIconVariant.Regular,
106+
IconVariant.Filled => FluentUIIconVariant.Filled,
107+
_ => throw new InvalidOperationException("Unknown icon variant: " + iconVariant),
108+
};
109+
}
110+
101111
// Filter out bad urls
102112
return (from u in Urls
103113
let parsedUri = Uri.TryCreate(u.FullUrl, UriKind.Absolute, out var uri) ? uri : null
104114
where parsedUri != null
105-
select new UrlViewModel(u.EndpointName, parsedUri, u.IsInternal, u.IsInactive, new UrlDisplayPropertiesViewModel(TranslateKnownUrlName(u), u.DisplayProperties.SortOrder)))
115+
select new UrlViewModel(u.EndpointName, parsedUri, u.IsInternal, u.IsInactive, new UrlDisplayPropertiesViewModel(TranslateKnownUrlName(u), u.DisplayProperties.SortOrder, u.DisplayProperties.IconName, MapIconVariant(u.DisplayProperties.IconVariant))))
106116
.ToImmutableArray();
107117
}
108118

src/Aspire.Hosting/ApplicationModel/CustomResourceSnapshot.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,10 @@ internal void Deconstruct(out string? name, out string url, out bool isInternal,
228228
/// </summary>
229229
/// <param name="DisplayName">The display name of the url.</param>
230230
/// <param name="SortOrder">The order of the url in UI. Higher numbers are displayed first in the UI.</param>
231-
public sealed record UrlDisplayPropertiesSnapshot(string DisplayName = "", int SortOrder = 0);
231+
/// <param name="IconName">The icon name for the Url. The name should be a valid FluentUI icon name. https://aka.ms/fluentui-system-icons</param>
232+
/// <param name="IconVariant">The icon variant.</param>
233+
///
234+
public sealed record UrlDisplayPropertiesSnapshot(string DisplayName = "", int SortOrder = 0, string? IconName = null, IconVariant? IconVariant = null);
232235

233236
/// <summary>
234237
/// A snapshot of a volume, mounted to a container.

src/Aspire.Hosting/ApplicationModel/ResourceUrlAnnotation.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ public sealed class ResourceUrlAnnotation : IResourceAnnotation
3636
/// </summary>
3737
public UrlDisplayLocation DisplayLocation { get; set; } = UrlDisplayLocation.SummaryAndDetails;
3838

39+
/// <summary>
40+
/// The name of the icon to display with this URL.
41+
/// </summary>
42+
public string IconName { get; set; } = string.Empty;
43+
44+
/// <summary>
45+
/// The variant of the icon to display with this URL.
46+
/// </summary>
47+
public IconVariant IconVariant { get; set; } = IconVariant.Regular;
48+
3949
internal bool IsInternal => DisplayLocation == UrlDisplayLocation.DetailsOnly;
4050

4151
internal ResourceUrlAnnotation WithEndpoint(EndpointReference endpoint)
@@ -46,7 +56,7 @@ internal ResourceUrlAnnotation WithEndpoint(EndpointReference endpoint)
4656
DisplayText = DisplayText,
4757
Endpoint = endpoint,
4858
DisplayOrder = DisplayOrder,
49-
DisplayLocation = DisplayLocation
59+
DisplayLocation = DisplayLocation,
5060
};
5161
}
5262
}

src/Aspire.Hosting/Dashboard/proto/Partials.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ public static Resource FromSnapshot(ResourceSnapshot snapshot)
6666
displayProperties.SortOrder = urlSnapshot.DisplayProperties.SortOrder;
6767
}
6868

69+
if (urlSnapshot.DisplayProperties?.IconName is not null)
70+
{
71+
displayProperties.IconName = urlSnapshot.DisplayProperties.IconName;
72+
}
73+
74+
if (urlSnapshot.DisplayProperties?.IconVariant is not null)
75+
{
76+
displayProperties.IconVariant = MapIconVariant(urlSnapshot.DisplayProperties.IconVariant);
77+
}
78+
6979
url.DisplayProperties = displayProperties;
7080
resource.Urls.Add(url);
7181
}

src/Aspire.Hosting/Dashboard/proto/dashboard_service.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ message UrlDisplayProperties {
143143
int32 sort_order = 1;
144144
// The display name of the url, to appear in the UI.
145145
string display_name = 2;
146+
// Optional icon name. This name should be a valid FluentUI icon name.
147+
// https://aka.ms/fluentui-system-icons
148+
optional string icon_name = 3;
149+
// Optional icon variant.
150+
optional IconVariant icon_variant = 4;
146151
}
147152

148153
// Data about a volume mounted to a container.

0 commit comments

Comments
 (0)