Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -3488,12 +3488,6 @@
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentIcon`1.OnParametersSet">
<summary />
</member>
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentIcon`1.ContainsSVG">
<summary>
Returns true if the icon contains a SVG content.
</summary>
<returns></returns>
</member>
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentIcon`1.GetIconColor">
<summary>
Returns FluentIcon.CustomColor, or FluentIcon.Color, or Icon.Color.
Expand Down Expand Up @@ -3562,6 +3556,12 @@
Gets the width of the icon.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.Icon.ContainsSVG">
<summary>
Returns true if the icon contains a SVG content.
</summary>
<returns></returns>
</member>
<member name="M:Microsoft.FluentUI.AspNetCore.Components.Icon.FromType``1">
<summary>
Returns an icon instance.
Expand Down Expand Up @@ -11008,6 +11008,11 @@
FluentUI System Icon size 48x48
</summary>
</member>
<member name="F:Microsoft.FluentUI.AspNetCore.Components.IconSize.Custom">
<summary>
Custom size included in the SVG content.
</summary>
</member>
<member name="T:Microsoft.FluentUI.AspNetCore.Components.IconVariant">
<summary>
The icon variant.
Expand Down
15 changes: 15 additions & 0 deletions examples/Demo/Shared/Pages/Icon/Examples/MyCircle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.FluentUI.AspNetCore.Components;

namespace FluentUI.Demo.Shared.Pages.Icon.Examples
{
internal class MyCircle : Microsoft.FluentUI.AspNetCore.Components.Icon
{
private const string SVG_CONTENT = "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 320'><circle cx='160' cy='160' r='140'/></svg>";

public MyCircle() : base("MyCircle", IconVariant.Regular, IconSize.Custom, SVG_CONTENT)
{
// Default Color (`fill` style)
WithColor("#F97316");
}
}
}
62 changes: 55 additions & 7 deletions examples/Demo/Shared/Pages/Icon/IconPage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,7 @@
<CodeSnippet Language="language-xml">@("<FluentIcon Value=\"@(new Icons.Regular.Size24.Bookmark())\" />")</CodeSnippet>
</blockquote>
</p>
<p>
You can create your own icons by adding a class like this one:

<CodeSnippet Language="language-csharp">public static class MyIcons
{
public class SettingsEmail : Icon { public SettingsEmail() : base("SettingsEmail", IconVariant.Regular, IconSize.Size20, "&lt;svg width=\"20\" height=\"19\" viewBox=\"0 0 20 19\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">&lt;path d=\"M15.6251 2.5H4.37508L4.2214 2.50428C2.79712 2.58396 1.66675 3.76414 1.66675 5.20833V13.125L1.67103 13.2787C1.75071 14.7029 2.93089 15.8333 4.37508 15.8333H9.76425C9.91725 15.4818 10.1354 15.1606 10.4087 14.8873L10.7126 14.5833H4.37508L4.25547 14.5785C3.50601 14.5177 2.91675 13.8902 2.91675 13.125V6.97833L9.709 10.5531L9.78908 10.5883C9.95267 10.647 10.135 10.6353 10.2912 10.5531L17.0834 6.9775V9.17258C17.5072 9.14483 17.9362 9.21517 18.3334 9.38358V5.20833L18.3292 5.05465C18.2494 3.63038 17.0693 2.5 15.6251 2.5ZM4.37508 3.75H15.6251L15.7447 3.75483C16.4942 3.81568 17.0834 4.44319 17.0834 5.20833V5.565L10.0001 9.29375L2.91675 5.56583V5.20833L2.92158 5.08873C2.98242 4.33926 3.60994 3.75 4.37508 3.75ZM15.9167 10.5579L10.9979 15.4766C10.7112 15.7633 10.5077 16.1227 10.4093 16.5162L10.0279 18.0418C9.86208 18.7052 10.4631 19.3062 11.1265 19.1403L12.6521 18.7588C13.0455 18.6605 13.4048 18.4571 13.6917 18.1703L18.6103 13.2516C19.3542 12.5078 19.3542 11.3018 18.6103 10.5579C17.8665 9.814 16.6605 9.814 15.9167 10.5579Z\" fill=\"#212121\" />&lt;/svg>") { } }
} </CodeSnippet>
</p>
<p>
You can use any of these icons by levaraging the <code>&lt;FluentIcon&gt;</code>&nbsp;component. See below for the parameters and examples.
There is also a search capability available on this page wich allows you to browse to all the different icons. At the moment the icons
Expand Down Expand Up @@ -65,6 +58,56 @@
</Description>
</DemoSection>

<h2>Customization</h2>

<p>
You can create your own icons by adding a class like this one:

<CodeSnippet Language="language-csharp">public static class MyIcons
{
public class SettingsEmail : Icon { public SettingsEmail() : base("SettingsEmail", IconVariant.Regular, IconSize.Size20, "&lt;svg width=\"20\" height=\"19\" viewBox=\"0 0 20 19\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">&lt;path d=\"M15.6251 2.5H4.37508L4.2214 2.50428C2.79712 2.58396 1.66675 3.76414 1.66675 5.20833V13.125L1.67103 13.2787C1.75071 14.7029 2.93089 15.8333 4.37508 15.8333H9.76425C9.91725 15.4818 10.1354 15.1606 10.4087 14.8873L10.7126 14.5833H4.37508L4.25547 14.5785C3.50601 14.5177 2.91675 13.8902 2.91675 13.125V6.97833L9.709 10.5531L9.78908 10.5883C9.95267 10.647 10.135 10.6353 10.2912 10.5531L17.0834 6.9775V9.17258C17.5072 9.14483 17.9362 9.21517 18.3334 9.38358V5.20833L18.3292 5.05465C18.2494 3.63038 17.0693 2.5 15.6251 2.5ZM4.37508 3.75H15.6251L15.7447 3.75483C16.4942 3.81568 17.0834 4.44319 17.0834 5.20833V5.565L10.0001 9.29375L2.91675 5.56583V5.20833L2.92158 5.08873C2.98242 4.33926 3.60994 3.75 4.37508 3.75ZM15.9167 10.5579L10.9979 15.4766C10.7112 15.7633 10.5077 16.1227 10.4093 16.5162L10.0279 18.0418C9.86208 18.7052 10.4631 19.3062 11.1265 19.1403L12.6521 18.7588C13.0455 18.6605 13.4048 18.4571 13.6917 18.1703L18.6103 13.2516C19.3542 12.5078 19.3542 11.3018 18.6103 10.5579C17.8665 9.814 16.6605 9.814 15.9167 10.5579Z\" fill=\"#212121\" />&lt;/svg>") { } }
} </CodeSnippet>
</p>


<p>
If the size of your customized icon (Viewbox) is not one of the standard IconSize sizes,
you can use a <b>IconSize.Custom</b> size.
<br />
<ul>
<li>
<FluentIcon Value="@(new MyCircle())" Width="16px" />
<code>&lt;FluentIcon Value="@@(new MyCircle())" Width="16px" /></code>
</li>
<li>
<FluentIcon Value="@(new MyCircle().WithColor("red"))" Width="16px" />
<code>&lt;FluentIcon Value="@@(new MyCircle().WithColor("red"))" Width="16px" /></code>
</li>
<li>
@(new MyCircle().ToMarkup("16px", "blue"))
<code>@@new MyCircle().ToMarkup("16px", "blue")</code>
</li>
<li>
<FluentButton IconStart="@(new MyCircle())" Loading="@ButtonIsLoading" OnClick="@(e => ButtonIsLoading = !ButtonIsLoading)">Refresh</FluentButton>
<code>&lt;FluentButton IconStart="@@(new MyCircle())">Refresh&lt;/FluentButton></code>
</li>
</ul>
Comment on lines +74 to +94
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are you showing IconSize.Custom being used here because of using the Width parameter?

<br /><br />
<CodeSnippet Language="language-csharp">public class MyCircle : Microsoft.FluentUI.AspNetCore.Components.Icon
{
private const string SVG_CONTENT = "&lt;svg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 320'>&lt;circle cx='160' cy='160' r='140'/>&lt;/svg>";

public MyCircle() : base("MyCircle", IconVariant.Regular, IconSize.Custom, SVG_CONTENT)
{
// Default Color (`fill` style)
WithColor("#F97316");
}
}</CodeSnippet>
</p>
<p>
Using the <b>IconSize.Custom</b> size, certain restrictions will apply: <code>FluentButton.Loading</code> or <code>Icon.ToMarkup</code> could be affected.
</p>

<h2 id="documentation">Documentation</h2>

<ApiDocumentation Component="typeof(FluentIcon<>)" InstanceTypes="@(new[] {typeof(Icon)})" GenericLabel="Icon">
Expand All @@ -73,3 +116,8 @@
<code>Icon</code> Type, while the <code>Value</code> parameter takes a icon object.
</Description>
</ApiDocumentation>

@code
{
bool ButtonIsLoading = false;
}
2 changes: 1 addition & 1 deletion src/Core/Components/Button/FluentButton.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ public void SetDisabled(bool disabled)

private string RingStyle(Icon icon)
{
int size = Convert.ToInt32(icon.Size);
int size = icon.Width;
string inverse = Appearance == AspNetCore.Components.Appearance.Accent ? " filter: invert(1);" : string.Empty;

return $"width: {size}px; height: {size}px;{inverse}";
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Components/Icons/FluentIcon.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@typeparam Icon
@inherits FluentComponentBase

@if (ContainsSVG())
@if (_icon.Size != IconSize.Custom && _icon.ContainsSVG)
{
<svg id="@Id" slot="@Slot" class="@ClassValue" style="@StyleValue" focusable="false" viewBox="@($"0 0 {((int)_icon.Size)} {((int)_icon.Size)}")" aria-hidden="true" @onclick="@OnClickHandlerAsync" @attributes="@AdditionalAttributes">
@if (!string.IsNullOrEmpty(Title))
Expand Down
16 changes: 1 addition & 15 deletions src/Core/Components/Icons/FluentIcon.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public partial class FluentIcon<Icon> : FluentComponentBase
.AddStyle("width", Width ?? $"{_icon.Width}px")
.AddStyle("fill", GetIconColor())
.AddStyle("cursor", "pointer", OnClick.HasDelegate)
.AddStyle("display", "inline-block", !ContainsSVG())
.AddStyle("display", "inline-block", !_icon.ContainsSVG)
.Build();

/// <summary>
Expand Down Expand Up @@ -99,20 +99,6 @@ protected override void OnParametersSet()
}
}

/// <summary>
/// Returns true if the icon contains a SVG content.
/// </summary>
/// <returns></returns>
private bool ContainsSVG()
{
return !string.IsNullOrEmpty(_icon.Content) &&
(_icon.Content.StartsWith("<path ") ||
_icon.Content.StartsWith("<rect ") ||
_icon.Content.StartsWith("<g ") ||
_icon.Content.StartsWith("<circle ") ||
_icon.Content.StartsWith("<mark "));
}

/// <summary>
/// Returns FluentIcon.CustomColor, or FluentIcon.Color, or Icon.Color.
/// </summary>
Expand Down
46 changes: 42 additions & 4 deletions src/Core/Components/Icons/Icon.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components.Utilities;

namespace Microsoft.FluentUI.AspNetCore.Components;

Expand Down Expand Up @@ -87,15 +88,52 @@ internal Icon InverseColor(bool accentContainer)
/// </summary>
public virtual MarkupString ToMarkup(string? size = null, string? color = null)
{
var styleWidth = size ?? $"{(int)Size}px";
var styleColor = color ?? Color ?? "var(--accent-fill-rest)";
return new MarkupString($"<svg viewBox=\"0 0 {(int)Size} {(int)Size}\" fill=\"{styleColor}\" style=\"background-color: var(--neutral-layer-1); width: {styleWidth};\" aria-hidden=\"true\">{Content}</svg>");
if (Size != IconSize.Custom && ContainsSVG)
{
var styleWidth = size ?? $"{(int)Size}px";
var styleColor = color ?? Color ?? "var(--accent-fill-rest)";
return new MarkupString($"<svg viewBox=\"0 0 {(int)Size} {(int)Size}\" fill=\"{styleColor}\" style=\"background-color: var(--neutral-layer-1); width: {styleWidth};\" aria-hidden=\"true\">{Content}</svg>");
}
else
{
if (string.IsNullOrEmpty(size) && string.IsNullOrEmpty(color))
{
return new MarkupString(Content);
}
else
{
var attributes = new StyleBuilder()
.AddStyle("display", "inline-block")
.AddStyle("fill", color, when: !string.IsNullOrEmpty(color))
.AddStyle("width", size, when: !string.IsNullOrEmpty(size))
.Build();

return new MarkupString($"<div style=\"{attributes}\">{Content}</div>");
}
}
}

/// <summary>
/// Gets the width of the icon.
/// </summary>
protected internal virtual int Width => (int)Size;
protected internal virtual int Width => Size == IconSize.Custom ? 20 : (int)Size;

/// <summary>
/// Returns true if the icon contains a SVG content.
/// </summary>
/// <returns></returns>
protected internal bool ContainsSVG
{
get
{
return !string.IsNullOrEmpty(Content) &&
(Content.StartsWith("<path ") ||
Content.StartsWith("<rect ") ||
Content.StartsWith("<g ") ||
Content.StartsWith("<circle ") ||
Content.StartsWith("<mark "));
}
}

/// <summary>
/// Returns an icon instance.
Expand Down
8 changes: 6 additions & 2 deletions src/Core/Enums/IconSize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// The size of the icon.
/// </summary>
public enum IconSize
{
{
/// <summary>
/// FluentUI System Icon size 10x10
/// </summary>
Expand Down Expand Up @@ -36,6 +36,10 @@ public enum IconSize
/// <summary>
/// FluentUI System Icon size 48x48
/// </summary>
Size48 = 48
Size48 = 48,

/// <summary>
/// Custom size included in the SVG content.
/// </summary>
Custom = 0
}
8 changes: 8 additions & 0 deletions tests/Core/Extensions/SampleIcons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,13 @@ internal class Info : Icon { public Info() : base("Info", IconVariant.Filled, Ic
internal class Warning : Icon { public Warning() : base("Warning", IconVariant.Filled, IconSize.Size24, "<path d=\"M10.03 3.66a2.25 2.25 0 0 1 3.94 0l7.74 14A2.25 2.25 0 0 1 19.74 21H4.25a2.25 2.25 0 0 1-1.97-3.34l7.75-14ZM13 17a1 1 0 1 0-2 0 1 1 0 0 0 2 0Zm-.26-7.85a.75.75 0 0 0-1.5.1v4.5l.02.1a.75.75 0 0 0 1.49-.1v-4.5l-.01-.1Z\"/>") { } }

internal class PresenceAvailable : Icon { public PresenceAvailable() : base("PresenceAvailable", IconVariant.Filled, IconSize.Size24, "<path d=\"M12 24a12 12 0 1 0 0-24 12 12 0 0 0 0 24Zm5.06-13.44-5.5 5.5a1.5 1.5 0 0 1-2.12 0l-2-2a1.5 1.5 0 0 1 2.12-2.12l.94.94 4.44-4.44a1.5 1.5 0 0 1 2.12 2.12Z\"/>") { } }

internal class MyCircle : Icon
{
public MyCircle() : base("MyCircle", IconVariant.Regular, IconSize.Custom, "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 320'><circle cx='160' cy='160' r='140'/></svg>")
{
WithColor("#F97316");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

<div>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 320">
<circle cx="160" cy="160" r="140"></circle>
</svg>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

<div>
<div style="display: inline-block; fill: blue; width: 20px;">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 320">
<circle cx="160" cy="160" r="140"></circle>
</svg>
</div>
</div>
22 changes: 22 additions & 0 deletions tests/Core/Icons/FluentIconTests.razor
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,26 @@
// Assert
cut.Verify();
}

[Fact]
public void FluentIcon_Icon_ToMarkupCustomIcon()
{
// Arrange && Act
var icon = new SampleIcons.Samples.MyCircle();
var cut = Render(@<div>@icon.ToMarkup()</div>);

// Assert
cut.Verify();
}

[Fact]
public void FluentIcon_Icon_ToMarkupCustomIcon_SizeColor()
{
// Arrange && Act
var icon = new SampleIcons.Samples.MyCircle();
var cut = Render(@<div>@icon.ToMarkup(size: "20px", color: "blue")</div>);

// Assert
cut.Verify();
}
}