Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
</FluentStack>

<div style="resize: horizontal; overflow: hidden;">
<FluentLayout GlobalScrollbar="@GlobalScrollbar" Height="400px" Style="@CommonStyles.NeutralBorder1" OnBreakpointEnter="@(e => MobileView = e)" MenuDeferredLoading="@MenuDeferredLoading">
<FluentLayout GlobalScrollbar="@GlobalScrollbar" Height="400px" Style="@CommonStyles.NeutralBorder1" OnBreakpointEnter="@(e => MobileView = e)" NavigationDeferredLoading="@NavigationDeferredLoading">

@if (Header.Visible)
{
<!-- Header -->
<FluentLayoutItem Area="@LayoutArea.Header" Sticky="@Header.Sticky">

<!-- Hamburger menu -->
<!-- Hamburger -->
<FluentLayoutHamburger OnOpened="@(e => Opened = e.Opened)"
ChildContent="@(string.IsNullOrWhiteSpace(HamburgerContent) ? null : builder => { builder.AddMarkupContent(0, HamburgerContent); })" />

Expand All @@ -35,17 +35,17 @@
Sign in
</FluentButton>

<!-- Second Hamburger menu -->
<!-- Second Hamburger -->
<FluentLayoutHamburger OnOpened="@(e => Opened = e.Opened)"
ChildContent="@(string.IsNullOrWhiteSpace(HamburgerContent) ? null : builder => { builder.AddMarkupContent(0, HamburgerContent); })" />

</FluentLayoutItem>
}

@if (Menu.Visible)
@if (Navigation.Visible)
{
<!-- Menu -->
<FluentLayoutItem Area="@LayoutArea.Menu" Sticky="@Menu.Sticky"
<!-- Navigation -->
<FluentLayoutItem Area="@LayoutArea.Navigation" Sticky="@Navigation.Sticky"
Width="150px" Padding="@Padding.All2"
Style="border-right: var(--strokeWidthThin) solid var(--colorNeutralStroke1);">

Expand Down Expand Up @@ -110,18 +110,18 @@
<td><InputCheckbox @bind-Value="@(GlobalScrollbar)" /></td>
</tr>
<tr>
<td>Menu Deferred Loading</td>
<td><InputCheckbox @bind-Value="@(MenuDeferredLoading)" /></td>
<td>Navigation Deferred Loading</td>
<td><InputCheckbox @bind-Value="@(NavigationDeferredLoading)" /></td>
</tr>
<tr>
<td>Header</td>
<td><InputCheckbox @bind-Value="@(Header.Visible)" /></td>
<td><InputCheckbox @bind-Value="@(Header.Sticky)" /></td>
</tr>
<tr>
<td>Menu</td>
<td><InputCheckbox @bind-Value="@(Menu.Visible)" /></td>
<td><InputCheckbox @bind-Value="@(Menu.Sticky)" /></td>
<td>Navigation</td>
<td><InputCheckbox @bind-Value="@(Navigation.Visible)" /></td>
<td><InputCheckbox @bind-Value="@(Navigation.Sticky)" /></td>
</tr>
<tr>
<td>Content</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ namespace FluentUI.Demo.Client.Documentation.Components.Layout.DebugPages;
public partial class DebugLayout
{
private bool GlobalScrollbar = true;
private bool MenuDeferredLoading;
private bool NavigationDeferredLoading;
private string HamburgerContent = string.Empty;
private readonly RenderFragment _renderOptions;
private readonly Option Header = new() { Visible = true, Sticky = false };
private readonly Option Menu = new() { Visible = true, Sticky = false };
private readonly Option Navigation = new() { Visible = true, Sticky = false };
private readonly Option Content = new() { Visible = true, Sticky = false };
private readonly Option Aside = new() { Visible = true, Sticky = false };
private readonly Option Footer = new() { Visible = true, Sticky = false };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<FluentSwitch @bind-Value="@UseGlobalScrollbar" Label=" GlobalScrollbar" Margin="@Margin.All2" />
<FluentSwitch @bind-Value="@UseHeaderSticky" Label="Header Sticky" Margin="@Margin.All2" Disabled="@(!UseGlobalScrollbar)" />
<FluentSwitch @bind-Value="@UseMenuSticky" Label="Menu Sticky" Margin="@Margin.All2" Disabled="@(!UseGlobalScrollbar)" />
<FluentSwitch @bind-Value="@UseNavigationSticky" Label="Navigation Sticky" Margin="@Margin.All2" Disabled="@(!UseGlobalScrollbar)" />
<FluentSwitch @bind-Value="@UseFooterSticky" Label="Footer Sticky" Margin="@Margin.All2" Disabled="@(!UseGlobalScrollbar)" />

<div style="border: var(--strokeWidthThin) solid var(--colorNeutralStroke1);">
Expand All @@ -17,9 +17,9 @@
</FluentStack>
</FluentLayoutItem>

<!-- Menu -->
<FluentLayoutItem Area="@LayoutArea.Menu" Sticky="@UseMenuSticky"
Width="150px" Padding="@Padding.All2"
<!-- Navigation -->
<FluentLayoutItem Area="@LayoutArea.Navigation" Sticky="@UseNavigationSticky"
Width="150px" Padding="@Padding.All2"
Style="border-right: var(--strokeWidthThin) solid var(--colorNeutralStroke1);">

<FluentText Weight="TextWeight.Bold">Navigation</FluentText>
Expand Down Expand Up @@ -51,7 +51,7 @@
{
bool UseGlobalScrollbar = false;
bool UseHeaderSticky = false;
bool UseMenuSticky = false;
bool UseNavigationSticky = false;
bool UseFooterSticky = false;

static MarkupString NavigationContent = SampleData.Text.Titles.Take(20).ToMarkupList("li");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ route: /Layout
# Layout

`FluentLayout` is a component that defines a layout for a page, using a grid composed of a **Header**, a **Footer**
and 3 columns: **Menu**, **Content** and **Aside** panes.
and 3 columns: **Navigation**, **Content** and **Aside** panes.

<table class="layout-schema">
<tr>
<td colspan="3">Header</td>
</tr>
<tr>
<td>Menu</td>
<td>Navigation</td>
<td style="width: 100%; height: 60px;">Content</td>
<td>Aside</td>
<tr>
<td colspan="3">Footer</td>
</tr>
</table>

For mobile devices (< 768px), the layout is a single column with the **Menu**, the **Content** and the **Footer** panes stacked vertically.
For mobile devices (< 768px), the layout is a single column with the **Navigation**, the **Content** and the **Footer** panes stacked vertically.

<table class="layout-schema">
<tr>
Expand All @@ -43,23 +43,23 @@ using the [@container](https://developer.mozilla.org/en-US/docs/Web/CSS/@contain
Each time the breakpoint is reached, the layout will be updated to reflect the new layout, and the event `OnBreakpointEnter` will be triggered.

[!TIP] The `FluentLayout` component can be used with a Blazor static web app or a Blazor interactive app.
The hamburger menu is available in all modes, but the event `OnBreakpointEnter` and the `MenuDeferredLoading` parameter are only available in "interactive mode".
The hamburger Navigation is available in all modes, but the event `OnBreakpointEnter` and the `NavigationDeferredLoading` parameter are only available in "interactive mode".

## Sticky Panels

All panels (except `Content`) can be fixed using the `Sticky` parameter.
In this case, the panel remains fixed when the page is scrolled.

## Hamburger Menu
## Hamburger Navigation

**On mobile device only** (<768 px) the **Menu** pane will be collapsed into a hamburger menu.
**On mobile device only** (<768 px) the **Navigation** pane will be collapsed into a hamburger Navigation.
The hamburger button is displayed when the screen width is less than 768px.

💡 You can "force" the visibility of the hamburger button using
the `FluentLayoutHamburger.Visible="true"` parameter.

By default, on mobile, the menu is hidden and a hamburger button is displayed to make it appear or disappear.
or make it disappear. Once displayed, this menu takes a large part of the screen width.
By default, on mobile, the Navigation is hidden and a hamburger button is displayed to make it appear or disappear.
or make it disappear. Once displayed, this Navigation takes a large part of the screen width.
This is configurable using the `FluentLayoutHamburger.PanelSize` parameter.

To use this Hamburger icon, you need to add the `FluentLayoutHamburger` component to the **Header**.
Expand All @@ -72,7 +72,7 @@ The hamburger menu is available in all modes, but the event `OnBreakpointEnter`
```

When you have navigation elements that can be used from the **FluentLayoutHamburger**, you must intercept
these elements to close/hide the **Hamburger** menu when you want to. For example
these elements to close/hide the **Hamburger** Navigation when you want to. For example

```razor
@inject NavigationManager NavigationManager
Expand All @@ -98,17 +98,17 @@ these elements to close/hide the **Hamburger** menu when you want to. For exampl
}
```

## Customized Hamburger Menu
## Customized Hamburger Navigation

By default, the hamburger menu contains the **Menu** FluentLayoutItem.
This hamburger menu can be customized using some parameters like the `ChildContent` for the panel content,
By default, the hamburger Navigation contains the **Navigation** FluentLayoutItem.
This hamburger Navigation can be customized using some parameters like the `ChildContent` for the panel content,
the `PanelHeader` for the header/title content, the `PanelSize` for the panel width and the `PanelPosition` for the panel position (left or right).

If `ChildContent` is not defined, the menu content will be used.
It is then generated **twice** in the HTML code, once for the menu and once for the hamburger panel.
If your menu is very large, it is best to set the `FluentLayout.MenuDeferredLoading` parameter to `true`.
In this case, Blazor will generate the content in the menu area in Desktop mode and then remove it from the DOM to place it in the hamburger panel in mobile mode.
This parameter `MenuDeferredLoading` is only available in Blazor "interactive mode".
If `ChildContent` is not defined, the Navigation content will be used.
It is then generated **twice** in the HTML code, once for the Navigation and once for the hamburger panel.
If your Navigation is very large, it is best to set the `FluentLayout.NavigationDeferredLoading` parameter to `true`.
In this case, Blazor will generate the content in the Navigation area in Desktop mode and then remove it from the DOM to place it in the hamburger panel in mobile mode.
This parameter `NavigationDeferredLoading` is only available in Blazor "interactive mode".

## Example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PageTitle>@App.PageTitle("Demo")</PageTitle>

<FluentLayout @key="@GetLayoutKey()" Style="@LayoutStyleHeight" MenuDeferredLoading="true">
<FluentLayout @key="@GetLayoutKey()" Style="@LayoutStyleHeight" NavigationDeferredLoading="true">

@* ------------------- *@
@* Header *@
Expand Down Expand Up @@ -45,7 +45,7 @@
@* Left menu *@
@* ------------------- *@

<FluentLayoutItem Area="@LayoutArea.Menu" Width="300px" Sticky="true" Height="var(--layout-body-height)"
<FluentLayoutItem Area="@LayoutArea.Navigation" Width="300px" Sticky="true" Height="var(--layout-body-height)"
Style="overflow-y: auto; padding: 8px; border-right: var(--strokeWidthThin) solid var(--colorNeutralStroke1);">
<DemoNavMenu Hamburger="@_hamburger" />
</FluentLayoutItem>
Expand Down
8 changes: 4 additions & 4 deletions src/Core/Components/Layout/FluentLayout.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ public FluentLayout(LibraryConfiguration configuration) : base(configuration)
public EventCallback<bool> OnBreakpointEnter { get; set; }

/// <summary>
/// Gets or sets whether the <see cref="LayoutArea.Menu"/> content is rendered only when this area is visible.
/// Gets or sets whether the <see cref="LayoutArea.Navigation"/> content is rendered only when this area is visible.
/// To reduce the HTML page size, the <see cref="FluentLayoutHamburger" /> is not rendered when the layout is in desktop mode.
/// To use this feature, make sure to enable the Blazor interactive mode.
/// </summary>
[Parameter]
public bool MenuDeferredLoading { get; set; } = false;
public bool NavigationDeferredLoading { get; set; } = false;

/// <summary />
protected override async Task OnAfterRenderAsync(bool firstRender)
Expand Down Expand Up @@ -127,14 +127,14 @@ public async Task FluentLayout_MediaChangedAsync(string size)
}

// Update the layout (Menu and Hamburger)
if (MenuDeferredLoading)
if (NavigationDeferredLoading)
{
foreach (var item in Hamburgers)
{
await item.RefreshAsync();
}

foreach (var item in Areas.Where(i => i.Area == LayoutArea.Menu))
foreach (var item in Areas.Where(i => i.Area == LayoutArea.Navigation))
{
await item.RefreshAsync();
}
Expand Down
4 changes: 2 additions & 2 deletions src/Core/Components/Layout/FluentLayout.razor.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
.fluent-layout {
--layout-height: 100dvh;
--zindex-layout-aside: 80;
--zindex-layout-menu: 80;
--zindex-layout-nav: 80;
--zindex-layout-header: 85;
--zindex-layout-footer: 85;
display: grid;
grid-template-areas:
"header header header"
"menu content aside"
"nav content aside"
"footer footer footer";
grid-template-columns: auto 1fr auto;
grid-template-rows: auto 1fr auto;
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Components/Layout/FluentLayoutHamburger.razor
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</span>

<div>
@MenuContent
@NavigationContent
</div>
}
</fluent-drawer-body>
Expand Down
4 changes: 2 additions & 2 deletions src/Core/Components/Layout/FluentLayoutHamburger.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public FluentLayoutHamburger(LibraryConfiguration configuration) : base(configur
public bool? Visible { get; set; }

/// <summary />
private RenderFragment? MenuContent => ChildContent ?? LayoutContainer?.Areas.Find(i => i.Area == LayoutArea.Menu)?.ChildContent;
private RenderFragment? NavigationContent => ChildContent ?? LayoutContainer?.Areas.Find(i => i.Area == LayoutArea.Navigation)?.ChildContent;

/// <summary />
protected override void OnInitialized()
Expand Down Expand Up @@ -166,7 +166,7 @@ private bool RenderDrawer()
}

// If the Desktop view is active
if (LayoutContainer.MenuDeferredLoading && !LayoutContainer.IsMobile)
if (LayoutContainer.NavigationDeferredLoading && !LayoutContainer.IsMobile)
{
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Core/Components/Layout/FluentLayoutItem.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ private void AddWidthHeightStyles(StyleBuilder styles)
/// <param name="styles"></param>
private void AddStickyStyle(StyleBuilder styles)
{
var isMiddleArea = Area == LayoutArea.Aside || Area == LayoutArea.Menu || Area == LayoutArea.Content;
var isMiddleArea = Area == LayoutArea.Aside || Area == LayoutArea.Navigation || Area == LayoutArea.Content;
if (isMiddleArea && LayoutContainer != null && LayoutContainer.HasHeader && LayoutContainer.HeaderSticky)
{
styles.AddStyle("top", LayoutContainer?.HeaderHeight ?? "0");
Expand All @@ -189,7 +189,7 @@ private bool RenderThisArea()
}

// For the Menu area, if the Native view is active
if (Area == LayoutArea.Menu && LayoutContainer.MenuDeferredLoading && LayoutContainer.IsMobile)
if (Area == LayoutArea.Navigation && LayoutContainer.NavigationDeferredLoading && LayoutContainer.IsMobile)
{
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Core/Components/Layout/FluentLayoutItem.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@
z-index: var(--zindex-layout-footer);
}

.fluent-layout-item[area="menu"] {
.fluent-layout-item[area="nav"] {
background-color: var(--colorNeutralBackground2);
left: 0;
overflow-y: auto;
top: 0;
z-index: var(--zindex-layout-menu);
z-index: var(--zindex-layout-nav);
}

.fluent-layout-item[area="menu"][sticky] {
.fluent-layout-item[area="nav"][sticky] {
height: fit-content;
}

Expand Down
8 changes: 7 additions & 1 deletion src/Core/Enums/LayoutArea.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ------------------------------------------------------------------------
// This file is licensed to you under the MIT License.
// ------------------------------------------------------------------------
using System.ComponentModel;

namespace Microsoft.FluentUI.AspNetCore.Components;

Expand All @@ -9,17 +10,22 @@ namespace Microsoft.FluentUI.AspNetCore.Components;
public enum LayoutArea
{
/// <summary />
[Description("header")]
Header,

/// <summary />
[Description("footer")]
Footer,

/// <summary />
Menu,
[Description("nav")]
Navigation,

/// <summary />
[Description("content")]
Content,

/// <summary />
[Description("aside")]
Aside,
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div id="xxx">
<div id="xxx" class="fluent-layout" style="height: var(--layout-height); --layout-height: 100dvh; --layout-header-height: 44px; --layout-footer-height: 36px; --layout-body-height: calc(var(--layout-height) - var(--layout-header-height) - var(--layout-footer-height));">
<div class="fluent-layout-item" style="grid-area: header; height: var(--layout-header-height);" area="header" inside-layout="">Header</div>
<div class="fluent-layout-item" style="grid-area: menu;" area="menu" inside-layout="">Menu</div>
<div class="fluent-layout-item" style="grid-area: nav;" area="nav" inside-layout="">Navigation</div>
<div class="fluent-layout-item" style="grid-area: content;" area="content" inside-layout="">Content</div>
<div class="fluent-layout-item" style="grid-area: aside; margin-right: 0;" area="aside" inside-layout="">Aside</div>
<div class="fluent-layout-item" style="grid-area: footer; height: var(--layout-footer-height);" area="footer" inside-layout="">Footer</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<style>#my-id-container{container-type:inline-size;container-name:layout-my-id}@container layout-my-id (max-width:768px){.fluent-layout{grid-template-areas:"header" "content" "footer";grid-template-columns:1fr;grid-template-rows:auto 1fr auto;overflow-x:auto}.fluent-layout .fluent-layout-item[area="menu"],.fluent-layout .fluent-layout-item[area="aside"]{display:none}.fluent-layout .fluent-layout-item[area="header"] .fluent-layout-hamburger{display:flex}}</style>
<div id="xxx">
<div id="xxx" class="fluent-layout" style="height: var(--layout-height); --layout-height: 100dvh; --layout-header-height: 44px; --layout-footer-height: 36px; --layout-body-height: calc(var(--layout-height) - var(--layout-header-height) - var(--layout-footer-height));">
<div class="fluent-layout-item" style="grid-area: menu;" area="menu" inside-layout="">Menu</div>
<div class="fluent-layout-item" style="grid-area: nav;" area="nav" inside-layout="">Navigation</div>
<div class="fluent-layout-item" style="grid-area: content;" area="content" inside-layout="">Content</div>
<div class="fluent-layout-item" style="grid-area: aside; margin-right: 0;" area="aside" inside-layout="">Aside</div>
</div>
Expand Down
Loading
Loading