Skip to content

Commit

Permalink
[FluentNavMenu] Show child items via FluentMenu when collapsed (#1730)
Browse files Browse the repository at this point in the history
* [FluentNavMenu] Start work on showing child items via `FluentMenu` when collapsed

* [FluentNavMenu] Add parameter for showing hierarchy when collapsed, add click behavior

* [FluentNavMenu] Hide links and groups without icon when collapsed

* [FluentNavMenu] Update variable names and documentation

---------

Co-authored-by: Alessio Dell Aquila <aquila@hks-systeme.de>
Co-authored-by: Vincent Baaij <vnbaaij@outlook.com>
  • Loading branch information
3 people committed Apr 8, 2024
1 parent 07d6297 commit 4aedf1e
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,31 @@

<FluentStack Orientation="Orientation.Horizontal" Width="100%">
<div>
<FluentNavMenu @bind-Expanded="@Expanded" Width="150" Collapsible="true" Title="Collapsible demo">
<FluentNavMenu @bind-Expanded="@Expanded" Width="250" Collapsible="true" CollapsedChildNavigation="@EnableCollapsedChildNavigation" Title="Collapsible demo">
<FluentNavLink Icon="@(new Icons.Regular.Size24.Home())" Tooltip="Item 1 tooltip">Item 1</FluentNavLink>
<FluentNavLink Icon="@(new Icons.Regular.Size24.Cloud())" Tooltip="Item 2 tooltip">Item 2</FluentNavLink>
<FluentNavLink Icon="@(new Icons.Regular.Size24.Cloud())" Disabled="true" Tooltip="Item 2 tooltip">Item 2</FluentNavLink>
<FluentNavGroup Title="Item 3" Tooltip="Item 3 tooltip" Icon="@(new Icons.Regular.Size24.EarthLeaf())" >
<FluentNavLink Icon="@(new Icons.Regular.Size24.LeafOne())" Tooltip="Item 3.1 tooltip">Item 3.1</FluentNavLink>
<FluentNavLink Icon="@(new Icons.Regular.Size24.LeafTwo())" Tooltip="Item 3.2 tooltip">Item 3.2</FluentNavLink>
</FluentNavGroup>
<FluentNavLink Icon="@(new Icons.Regular.Size24.CalendarAgenda())" Disabled="true" Href="https://microsoft.com" Tooltip="Item 4 tooltip1">Item 4</FluentNavLink>
<FluentNavLink Icon="@(new Icons.Regular.Size24.LeafTwo())" Disabled="true" Tooltip="Item 3.2 tooltip">Item 3.2</FluentNavLink>
<FluentNavGroup Title="Item 3.3" Tooltip="Item 3.3 tooltip" Icon="@(new Icons.Regular.Size24.LeafThree())">
<FluentNavLink Icon="@(new Icons.Regular.Size24.Earth())" Href="https://microsoft.com" Tooltip="Item 3.3.1 tooltip">https://microsoft.com</FluentNavLink>
</FluentNavGroup>
<FluentNavGroup Title="Item 3.4" Tooltip="Item 3.4 tooltip" Icon="@(new Icons.Regular.Size24.TreeDeciduous())" Disabled="true">
<FluentNavLink Icon="@(new Icons.Regular.Size24.Earth())" Href="https://microsoft.com" Tooltip="Item 3.4.1 tooltip">https://microsoft.com</FluentNavLink>
</FluentNavGroup>
</FluentNavGroup>
<FluentNavLink Tooltip="Item 4 tooltip">Item 4</FluentNavLink>
<FluentNavGroup Title="Item 5" Tooltip="Item 5 tooltip">
<FluentNavLink Icon="@(new Icons.Regular.Size24.LeafOne())" Tooltip="Item 5.1 tooltip">Item 5.1</FluentNavLink>
</FluentNavGroup>
<FluentNavLink Icon="@(new Icons.Regular.Size24.CalendarAgenda())" Disabled="true" Href="https://microsoft.com" Tooltip="Item 6 tooltip">Item 6</FluentNavLink>
</FluentNavMenu>
</div>
<div style="width: 100%;">

<div style="margin-bottom: 12px;">
<FluentSwitch @bind-Value="EnableCollapsedChildNavigation" CheckedMessage="Disable collapsed child navigation" UncheckedMessage="Enable collapsed child navigation"></FluentSwitch>
</div>
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce quis lorem lacus.
Ut id leo non enim feugiat ultrices. Proin vulputate volutpat urna nec iaculis.
Integer dui lacus, fermentum sit amet aliquet in, scelerisque vitae dui.
Expand All @@ -23,11 +36,12 @@
Sed maximus, urna in fringilla posuere, enim urna bibendum justo, vel molestie nibh orci nec lectus.
Etiam a varius justo. Aenean nisl ante, interdum eget vulputate eget, iaculis ut massa.
Suspendisse maximus sed purus id molestie. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

</div>
</div>
</FluentStack>

@code
{
bool Expanded = true;
}
bool EnableCollapsedChildNavigation = true;
}
8 changes: 7 additions & 1 deletion examples/Demo/Shared/Pages/NavMenu/NavMenuPage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@

<DemoSection Component="typeof(NavMenuCollapsible)" Title="Collapsible navigation">
<Description>
More complete example which shows how the menu works together with a text section when it collapses.
<p>
More complete example which shows how the menu works together with a text section when it collapses.
When <code>CollapsedChildNavigation</code> is set to true, clicking on a <code>FluentNavGroup</code> opens a menu containing all links included in that group.
</p>
<p>
Note that menu items without an icon are not visible when the menu is collapsed.
</p>
</Description>
</DemoSection>

Expand Down
6 changes: 3 additions & 3 deletions src/Core/Components/Menu/FluentMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
<FluentOverlay @bind-Visible="@Open" OnClose="@CloseAsync" Transparent="true" FullScreen="true" />
<FluentAnchoredRegion Anchor="@Anchor"
HorizontalDefaultPosition="@HorizontalPosition"
VerticalDefaultPosition="@VerticalPosition.Bottom"
HorizontalInset="true"
VerticalInset="false"
VerticalDefaultPosition="@VerticalPosition"
HorizontalInset="@HorizontalInset"
VerticalInset="@VerticalInset"
HorizontalViewportLock="true"
HorizontalThreshold="@HorizontalThreshold"
VerticalThreshold="@VerticalThreshold"
Expand Down
21 changes: 20 additions & 1 deletion src/Core/Components/Menu/FluentMenu.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,30 @@ public partial class FluentMenu : FluentComponentBase, IDisposable
public RenderFragment? ChildContent { get; set; }

/// <summary>
/// Gets or sets the menu position (left or right).
/// Gets or sets the horizontal menu position.
/// </summary>
[Parameter]
public HorizontalPosition HorizontalPosition { get; set; } = HorizontalPosition.Unset;

/// <summary>
/// Gets or sets a value indicating whether the region overlaps the anchor on the horizontal axis.
/// Default is false which places the region adjacent to the anchor element.
/// </summary>
[Parameter]
public bool HorizontalInset { get; set; } = true;

/// <summary>
/// Gets or sets the vertical menu position.
/// </summary>
[Parameter]
public VerticalPosition VerticalPosition { get; set; } = VerticalPosition.Bottom;

/// <summary>
/// Gets or sets a value indicating whether the region overlaps the anchor on the vertical axis.
/// </summary>
[Parameter]
public bool VerticalInset { get; set; } = false;

/// <summary>
/// Gets or sets the width of this menu.
/// </summary>
Expand Down
7 changes: 4 additions & 3 deletions src/Core/Components/Menu/FluentMenuItem.razor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace Microsoft.FluentUI.AspNetCore.Components;

Expand Down Expand Up @@ -56,7 +57,7 @@ public partial class FluentMenuItem : FluentComponentBase, IDisposable
/// Event raised when the user click on this item.
/// </summary>
[Parameter]
public EventCallback OnClick { get; set; }
public EventCallback<MouseEventArgs> OnClick { get; set; }

public FluentMenuItem()
{
Expand All @@ -69,7 +70,7 @@ protected override void OnInitialized()
}

/// <summary />
protected async Task OnClickHandlerAsync()
protected async Task OnClickHandlerAsync(MouseEventArgs ev)
{
if (Disabled)
{
Expand All @@ -81,7 +82,7 @@ protected async Task OnClickHandlerAsync()
await Owner.CloseAsync();
}

await OnClick.InvokeAsync();
await OnClick.InvokeAsync(ev);
}

protected string? GetRole()
Expand Down
5 changes: 4 additions & 1 deletion src/Core/Components/NavMenu/FluentNavBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public abstract class FluentNavBase : FluentComponentBase
[CascadingParameter]
public FluentNavMenu Owner { get; set; } = default!;

[CascadingParameter]
public FluentMenu? SubMenu { get; set; }

/// <summary>
/// Returns <see langword="true"/> if the item has an <see cref="Icon"/> set.
/// </summary>
Expand Down Expand Up @@ -106,7 +109,7 @@ protected async Task OnClickHandlerAsync(MouseEventArgs ev)
}
else
{
if (!Owner.Expanded)
if (!Owner.Expanded && !Owner.CollapsedChildNavigation && SubMenu == null)
{
await Owner.ExpandedChanged.InvokeAsync(true);
}
Expand Down
38 changes: 36 additions & 2 deletions src/Core/Components/NavMenu/FluentNavGroup.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@using Microsoft.AspNetCore.Components.Rendering
@using Microsoft.AspNetCore.Components.Routing

@if (Owner.Expanded || HasIcon)
@if (Owner.Expanded || (HasIcon && (!Owner.CollapsedChildNavigation || SubMenu == null)))
{
<FluentKeyCode Anchor="@Id" OnKeyDown="@HandleExpanderKeyDownAsync" />
<div id="@Id" @ref="@Element" @attributes="AdditionalAttributes" class="@ClassValue" disabled="@Disabled" style="@StyleValue" role="menuitem">
Expand All @@ -26,7 +26,7 @@
else
{
<div class="fluent-nav-link notactive" tabindex="0">
<div class="positioning-region" @onclick="ToggleExpandedAsync" title="@(Tooltip ?? Title)">
<div class="positioning-region" @onclick="ToggleExpandedAsync" title="@(Tooltip ?? Title)">
<div class="content-region">
@_renderContent
@_renderButton
Expand All @@ -41,7 +41,41 @@
</FluentNavMenu>
</FluentCollapsibleRegion>
</div>

if (Owner.CollapsedChildNavigation && SubMenu == null)
{
<FluentMenu @bind-Open="_open"
Anchor="@Id"
HorizontalPosition="@HorizontalPosition.End"
HorizontalInset="@false"
VerticalPosition="@VerticalPosition.Bottom"
VerticalInset="@true">
@ChildContent
</FluentMenu>
}
}
else if (!Owner.Expanded && Owner.CollapsedChildNavigation && SubMenu != null)
{
<div>
<FluentMenuItem Disabled="@Disabled" OnClick="OnClickHandlerAsync">
<ChildContent>
@Title
@TitleTemplate

@if (Icon is not null)
{
<span slot="start">
<FluentIcon Value="@Icon" Width="20px" Color="@IconColor" Class="fluent-nav-icon" />
</span>
}
</ChildContent>
<MenuItems>
@ChildContent
</MenuItems>
</FluentMenuItem>
</div>
}


@code {
private string _data = "no-pagescript";
Expand Down
10 changes: 9 additions & 1 deletion src/Core/Components/NavMenu/FluentNavGroup.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public partial class FluentNavGroup : FluentNavBase
{
private readonly RenderFragment _renderContent;
private readonly RenderFragment _renderButton;
private bool _open;

protected string? ClassValue =>
new CssBuilder("fluent-nav-group")
Expand Down Expand Up @@ -128,7 +129,14 @@ private async Task SetExpandedAsync(bool value)

if (!Owner.Expanded)
{
await Owner.ExpandedChanged.InvokeAsync(true);
if (Owner.CollapsedChildNavigation)
{
_open = !_open;
}
else
{
await Owner.ExpandedChanged.InvokeAsync(true);
}
}
else
{
Expand Down
73 changes: 48 additions & 25 deletions src/Core/Components/NavMenu/FluentNavLink.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,58 @@
@inherits FluentNavBase
@using Microsoft.AspNetCore.Components.Rendering
@using Microsoft.AspNetCore.Components.Routing
@inject NavigationManager NavigationManager

<div id="@Id" @attributes="AdditionalAttributes" class="@ClassValue" disabled="@Disabled" style="@Style" role="menuitem">
@if (!OnClick.HasDelegate && !string.IsNullOrEmpty(Href))
{
<NavLink class="@LinkClassValue"
@attributes="@Attributes"
Match="@Match"
ActiveClass="@ActiveClass"
title="@Tooltip">
<div class="positioning-region">
<div class="content-region">
@_renderContent
@if (Owner == null || Owner.Expanded || (HasIcon && (!Owner.CollapsedChildNavigation || SubMenu == null)))
{
<div id="@Id" @attributes="AdditionalAttributes" class="@ClassValue" disabled="@Disabled" style="@Style" role="menuitem">
@if (!OnClick.HasDelegate && !string.IsNullOrEmpty(Href))
{
<NavLink class="@LinkClassValue"
@attributes="@Attributes"
Match="@Match"
ActiveClass="@ActiveClass"
title="@Tooltip">
<div class="positioning-region">
<div class="content-region">
@_renderContent
</div>
</div>
</div>
</NavLink>
}
else
{
<div class="positioning-region" title="@Tooltip">
<div class="content-region">
<div class="@LinkClassValue" @onclick="OnClickHandlerAsync">
@_renderContent
</NavLink>
}
else
{
<div class="positioning-region" title="@Tooltip">
<div class="content-region">
<div class="@LinkClassValue" @onclick="OnClickHandlerAsync">
@_renderContent
</div>
</div>
</div>
</div>
}
</div>
}
</div>
}
else if (!Owner.Expanded && Owner.CollapsedChildNavigation && SubMenu != null)
{
<div>
<FluentMenuItem Disabled="@Disabled" OnClick="OnClickHandlerAsync">
<NavLink class="@LinkClassValue"
@attributes="@Attributes"
Match="@Match"
ActiveClass="@ActiveClass"
title="@Tooltip">
@ChildContent
</NavLink>

@if (Icon is not null)
{
<span slot="start">
<FluentIcon Value="@Icon" Color="@IconColor" Class="fluent-nav-icon" />
</span>
}
</FluentMenuItem>
</div>
}

@code {
private void RenderContent(RenderTreeBuilder __builder)
Expand All @@ -44,7 +69,5 @@
<div class="fluent-nav-text">
@ChildContent
</div>


}
}
13 changes: 6 additions & 7 deletions src/Core/Components/NavMenu/FluentNavLink.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@
display: flex;
}

::deep .fluent-nav-icon {

::deep :not(fluent-menu-item) .fluent-nav-icon {
min-width: 20px;
}

::deep .fluent-nav-link.disabled {
::deep :not(fluent-menu-item) .fluent-nav-link.disabled {
color: var(--neutral-fill-secondary-rest) !important;
cursor: not-allowed ;
cursor: not-allowed;
pointer-events: none;
}


::deep .fluent-nav-link.disabled .fluent-nav-icon {
fill: var(--neutral-stroke-rest) !important;
}
::deep :not(fluent-menu-item) .fluent-nav-link.disabled .fluent-nav-icon {
fill: var(--neutral-stroke-rest) !important;
}
6 changes: 6 additions & 0 deletions src/Core/Components/NavMenu/FluentNavMenu.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public partial class FluentNavMenu : FluentComponentBase
[Parameter]
public bool Collapsible { get; set; }

/// <summary>
/// Gets or sets whether a menu with all child links is shown for <see cref="FluentNavGroup"/>s when the navigation menu is collapsed.
/// </summary>
[Parameter]
public bool CollapsedChildNavigation { get; set; } = false;

/// <inheritdoc/>
[Parameter]
public bool Expanded { get; set; } = true;
Expand Down

0 comments on commit 4aedf1e

Please sign in to comment.