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
@@ -0,0 +1,10 @@
<FluentMessageBar Animation="MessageBarAnimation.FadeIn">
<ChildContent>
Message providing information to the user with actionable insights.
<FluentLink Href="https://blazor.net" Target="LinkTarget.Blank">Learn more</FluentLink>
</ChildContent>
<ActionsTemplate>
<FluentButton Size="ButtonSize.Small">Action 1</FluentButton>
<FluentButton Size="ButtonSize.Small">Action 2</FluentButton>
</ActionsTemplate>
</FluentMessageBar>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<FluentStack Orientation="Orientation.Vertical" VerticalGap="12px">
<FluentMessageBar Intent="MessageBarIntent.Info" Title="Info">
This is a message bar that provides information to the user.
</FluentMessageBar>
<FluentMessageBar Intent="MessageBarIntent.Warning" Title="Warning">
This is a message bar that provides information to the user.
</FluentMessageBar>
<FluentMessageBar Intent="MessageBarIntent.Success" Title="Success">
This is a message bar that provides information to the user.
</FluentMessageBar>
<FluentMessageBar Intent="MessageBarIntent.Error" Title="Error">
This is a message bar that provides information to the user.
</FluentMessageBar>

<FluentMessageBar Icon="@(new Icons.Regular.Size20.LeafTwo().WithColor("currentColor"))" Title="Custom">
This is a message bar with a customized icon.
</FluentMessageBar>
</FluentStack>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<FluentStack>
<FluentSelect Label="Layout"
Items="@(Enum.GetValues<MessageBarLayout>())"
@bind-Value="@Layout" />

<FluentSwitch Label="Display an Action"
Margin="42px 0 0 24px"
@bind-Value="@DisplayAction" />
</FluentStack>

@if (DisplayAction)
{
<FluentMessageBar Layout="@Layout"
Intent="MessageBarIntent.Success"
Title="Delete operation">
<ChildContent>
Successfully deleted 'XYZ-blazor.pdf'
</ChildContent>
<ActionsTemplate>
<FluentButton Size="ButtonSize.Small">Go to the resource</FluentButton>
</ActionsTemplate>
</FluentMessageBar>
}
else
{
<FluentMessageBar Layout="@Layout"
Intent="MessageBarIntent.Success"
Title="Delete operation"
TimeStamp="@(DateTime.Now.AddHours(-1))">
Successfully deleted 'XYZ-blazor.pdf'
</FluentMessageBar>
}

@code
{
MessageBarLayout Layout = MessageBarLayout.SingleLine;
bool DisplayAction = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: MessageBar
route: /MessageBar
---

# MessageBar

Communicates important information about the state of the entire application or surface.
For example, the status of a page, panel, dialog or card.
The information shouldn't require someone to take immediate action, but should persist until
the user performs one of the required actions.

## Appearance

**FluentMessageBar** components come built-in with preset intents that determine the design and aria live announcement.

{{ MessageBarAppearance }}

You can also use the `Shape` parameter to change the shape of the corners of the message bar: square or rounded.

## Actions

The **FluentMessageBar** can have links and different actions.

Add your buttons actions using the `ActionsTemplate` parameter.
To keep a coherent design, use the `FluentButton` component with the `Size` parameter set to `ButtonSize.Small`.

{{ MessageBarActions }}

## Layout

The `Layout` parameter allows you to choose the position of the actions:
- **SingleLine**: Next to the message content, allowing for a compact layout.
- **MultiLine**: On a new line, allowing for more space for the message content.
- **Notification**: The title, message, and actions are displayed on separate lines, providing a clear and structured layout.

When no action is defined, you can set the `TimeStamp` parameter to display the time when the message was created.
This parameter is ignored if at least one action is defined.
If you want to display an Action and a TimeStamp, you can use the `ActionsTemplate` parameter and customize the content.

{{ MessageBarLayouts }}

## Message Service

TODO in the next PR.

> [!WARNING]
> `FluentMessageBars` are rendered by the `<FluentProviders />`.
> This component needs to be added to the layout of your application.
> See the [Installation page](/installation) for more information.

## API FluentMessageBar

{{ API Type=FluentMessageBar }}

## Migrating to v5

{{ INCLUDE File=MigrationFluentMessageBar }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: Migration FluentMessageBar
route: /Migration/MessageBar
hidden: true
---

### New properties
`Layout`, `Size`, `Shape`, `AriaLive` are new properties.

### Renamed properties 🔃
The `FadeIn` property has been renamed to `Animation`.

### Removed properties💥
The `IconColor` property has been removed. Use `Icon.WithColor()` method instead.

The `Intent.Custom` property has been removed. Don't use the `Intent` property and set `Icon` and `ChildContent` instead to customize the message bar.

The `Type` property has been removed. Use the `Layout` property instead to choose the position of the actions and to display the `TimeStamp`.
10 changes: 10 additions & 0 deletions src/Core/Components/Base/FluentSlot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,14 @@ public static class FluentSlot
/// Slot for the right-side element of a fluent-tree-item.
/// </summary>
internal const string Aside = "aside";

/// <summary>
/// Slot for the Dismiss element of a fluent-message-bar
/// </summary>
internal const string Dismiss = "dismiss";

/// <summary>
/// Slot for the Action element of a fluent-message-bar
/// </summary>
internal const string Actions = "actions";
}
2 changes: 2 additions & 0 deletions src/Core/Components/Icons/CoreIcons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public class QuestionCircle : Icon { public QuestionCircle() : base("QuestionCir
public class Subtract : Icon { public Subtract() : base("Subtract", IconVariant.Regular, IconSize.Size20, "<path d=\"M3 10c0-.28.22-.5.5-.5h13a.5.5 0 0 1 0 1h-13A.5.5 0 0 1 3 10Z\"/>") { } }

public class TableResizeColumn : Icon { public TableResizeColumn() : base("TableResizeColumn", IconVariant.Regular, IconSize.Size20, "<path d=\"M7.35 8.15c.2.2.2.5 0 .7l-.64.65h6.58l-.64-.65a.5.5 0 0 1 .7-.7l1.5 1.5c.2.2.2.5 0 .7l-1.5 1.5a.5.5 0 0 1-.7-.7l.64-.65H6.71l.64.65a.5.5 0 0 1-.7.7l-1.5-1.5a.5.5 0 0 1 0-.7l1.5-1.5c.2-.2.5-.2.7 0ZM17 6a3 3 0 0 0-3-3H6a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V6Zm-4-2v3c.36 0 .72.13 1 .38V4a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2v-3.38a1.5 1.5 0 0 1-1 .38v3H7v-3a1.5 1.5 0 0 1-1-.38V16a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2v3.38A1.5 1.5 0 0 1 7 7V4h6Z\"/>") { } }

public class Info : Icon { public Info() : base("Info", IconVariant.Regular, IconSize.Size20, "<path d=\"M10.5 8.91a.5.5 0 0 0-1 .09v4.6a.5.5 0 0 0 1-.1V8.91Zm.3-2.16a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0ZM18 10a8 8 0 1 0-16 0 8 8 0 0 0 16 0ZM3 10a7 7 0 1 1 14 0 7 7 0 0 1-14 0Z\"></path>") { } };
}
}

Expand Down
56 changes: 56 additions & 0 deletions src/Core/Components/MessageBar/FluentMessageBar.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
@namespace Microsoft.FluentUI.AspNetCore.Components
@using Microsoft.FluentUI.AspNetCore.Components.Extensions
@inherits FluentComponentBase

@if (Visible)
{
<fluent-message-bar id="@Id"
class="@ClassValue"
style="@StyleValue"
shape="@Shape.ToAttributeValue()"
layout="@Layout.ToAttributeValue()"
intent="@GetIntentString()"
animation="@GetAnimation()"
aria-live="@AriaLive.ToAttributeValue()"
@attributes="@AdditionalAttributes">

<AddTag Name="span" slot="@FluentSlot.Icon" TagWhen="@(() => Layout == MessageBarLayout.Notification)" class="notification-title">
<FluentIcon Value="@GetIcon()" Slot="@FluentSlot.Icon" />

@if (!string.IsNullOrEmpty(Title))
{
<span class="title">@((MarkupString)Title)</span>
}
</AddTag>

@if (@ChildContent is not null)
{
<span class="content">@ChildContent</span>
}

@if (AllowDismiss)
{
<FluentButton Appearance="ButtonAppearance.Transparent"
IconStart="@(new CoreIcons.Regular.Size20.Dismiss().WithColor("currentColor"))"
IconOnly="true"
Title="@Localizer[Localization.LanguageResource.MessageBar_Dismiss]"
Size="ButtonSize.Small"
slot="@FluentSlot.Dismiss"
OnClick="DismissClickAsync" />
}

@if (ActionsTemplate is not null)
{
<span class="actions" slot="@FluentSlot.Actions">
@ActionsTemplate
</span>
}
else if (TimeStamp is not null)
{
<span class="actions" slot="@FluentSlot.Actions">
@GetTimeStamp()
</span>
}

</fluent-message-bar>
}
165 changes: 165 additions & 0 deletions src/Core/Components/MessageBar/FluentMessageBar.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// ------------------------------------------------------------------------
// This file is licensed to you under the MIT License.
// ------------------------------------------------------------------------

using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components.Extensions;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// Component to communicate important information about the state of the entire application or surface
/// </summary>
public partial class FluentMessageBar : FluentComponentBase
{
private static readonly Icon IconInfo = new CoreIcons.Regular.Size20.Info().WithColor("var(--info)");
private static readonly Icon IconWarning= new CoreIcons.Filled.Size20.Warning().WithColor("var(--warning)");
private static readonly Icon IconSuccess = new CoreIcons.Filled.Size20.CheckmarkCircle().WithColor("var(--success)");
private static readonly Icon IconError = new CoreIcons.Filled.Size20.DismissCircle().WithColor("var(--error)");

/// <summary />
public FluentMessageBar(LibraryConfiguration configuration) : base(configuration) { }

/// <summary />
protected virtual string? ClassValue => DefaultClassBuilder
.Build();

/// <summary />
protected virtual string? StyleValue => DefaultStyleBuilder
.Build();

/// <summary>
/// Gets or sets the intent of the message bar.
/// Default is <see cref="MessageBarIntent.Info"/>.
/// </summary>
[Parameter]
public MessageBarIntent? Intent { get; set; }

/// <summary>
/// Gets or sets the layout of the message bar.
/// Default is <see cref="MessageBarLayout.SingleLine"/>.
/// </summary>
[Parameter]
public MessageBarLayout? Layout { get; set; }

/// <summary>
/// Gets or sets the shape of the message bar.
/// Default is <see cref="MessageBarShape.Rounded"/>.
/// </summary>
[Parameter]
public MessageBarShape? Shape { get; set; }

/// <summary>
/// Gets or sets the fade in animation when the message bar is shown.
/// Default is none.
/// </summary>
[Parameter]
public MessageBarAnimation? Animation { get; set; }

/// <summary>
/// Gets or sets the `aria-live` attribute, to inform assistive technologies (like screen readers) about updates to dynamic content.
/// </summary>
[Parameter]
public AriaLive? AriaLive { get; set; }

/// <summary>
/// Gets or sets the icon to show in the message bar based on the intent of the message.
/// </summary>
[Parameter]
public Icon? Icon { get; set; }

/// <summary>
/// Gets or sets the most important info to be shown in the message bar.
/// </summary>
[Parameter]
public string? Title { get; set; }

/// <summary>
/// Gets or sets the visibility of the message bar. Default is true.
/// </summary>
[Parameter]
public bool Visible { get; set; } = true;

/// <summary>
/// Gets or sets the ability to dismiss the message bar. Default is true.
/// </summary>
[Parameter]
public bool AllowDismiss { get; set; } = true;

/// <summary>
/// Gets or sets the message to be shown when not using the MessageService methods.
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }

/// <summary>
/// Gets or sets the content to be displayed inline after the main content.
/// </summary>
[Parameter]
public RenderFragment? ActionsTemplate { get; set; }

/// <summary>
/// Gets or sets the time on which the message was created.
/// Only used when <see cref="ActionsTemplate"/> is not used: `null` (if not, this parameter is ignored).
/// </summary>
[Parameter]
public DateTime? TimeStamp { get; set; }

/// <summary />
protected virtual Task DismissClickAsync()
{
Visible = false;
return Task.CompletedTask;
}

/// <summary />
private string? GetIntentString()
{
if (Intent == null || Intent == MessageBarIntent.Custom)
{
return null;
}

return Intent.ToAttributeValue();
}

/// <summary />
private Icon GetIcon()
{
if (Icon is null)
{
return Intent switch
{
MessageBarIntent.Error => IconError,
MessageBarIntent.Warning => IconWarning,
MessageBarIntent.Success => IconSuccess,
MessageBarIntent.Info => IconInfo,
_ => IconInfo,
};
}

return Icon;
}

/// <summary />
private string? GetAnimation()
{
return Animation switch
{
MessageBarAnimation.FadeIn => "fade-in",
_ => null,
};
}

/// <summary />
internal string? GetTimeStamp()
{
if (TimeStamp is null)
{
return null;
}

var delay = DateTimeProvider.Now - TimeStamp.Value;
return delay.ToTimeAgo(Localizer);
}
}
Loading
Loading