Forum Discussion
Gavin-Williams
Feb 26, 2023Brass Contributor
Convert the standard Blazor navigation menu to a collapsible icon menu
While I admittedly love Blazor I’ve always changed the out-of-the-box navigation menu that comes with it. It’s the first manoeuvre I pull when spinning up a new Blazor app, stripping out the purpl...
MMaybe990
Apr 03, 2024Copper Contributor
jassen777 Thank you so much for this! I was tearing my hair out trying to figure out why none of my ideas for collapsing menus worked. I also added the option to have sub menu items with those icons available in the Menu Icon section. Here in my code.
@rendermode InteractiveServer
<div class="navbar-wrapper">
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a href="/dashboard"><img src="images/NewLogo-transparent.png" alt="Brand Logo" width="30" height="30"></a>
@if (!@IconMenuActive)
{
<a class="navbar-brand">Brand option</a>
}
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column">
<div class="nav-item px-3" @onclick="ToggleSubMenu">
<NavLink class="nav-link flex-container" Match="NavLinkMatch.All">
<span class="oi oi-dollar" aria-hidden="true" title="Home"></span>
@if (!@IconMenuActive)
{
<label>Accounting</label>
<span class="oi chevron-icon @(showSubMenu ? "oi-chevron-top" : "oi-chevron-bottom") ps-3" style="font-size: 1rem;" aria-hidden="true"></span>
}
</NavLink>
<div class="sub-menu" style="display: @(showSubMenu ? "block" : "none")">
<NavLink class="nav-link" href="subitem1">
<span class="oi oi-folder" aria-hidden="true" title="Sub Item 1"></span>
@if (!@IconMenuActive)
{
<label>Home</label>
}
</NavLink>
<NavLink class="nav-link" href="subitem2">
<span class="oi oi-document" aria-hidden="true" title="Sub Item 2"></span>
@if (!@IconMenuActive)
{
<label>Sub Menu Item 1</label>
}
</NavLink>
</div>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-calculator" aria-hidden="true"></span>
@if (!@IconMenuActive)
{
<label>Sub Menu Item 2</label>
}
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span>
@if (!@IconMenuActive)
{
<label>Fetch data</label>
}
</NavLink>
</div>
</nav>
</div>
<div class="bottom-row">
<div class="icon-menu-arrow">
@if (!@IconMenuActive)
{
<span class="oi oi-arrow-left" style="color: black;" @onclick="ToggleIconMenu"></span>
}
else
{
<span class="oi oi-arrow-right" style="color: black;" @onclick="ToggleIconMenu"></span>
}
</div>
</div>
</div>
@code {
private bool showSubMenu = false;
private void ToggleSubMenu()
{
showSubMenu = !showSubMenu;
}
//bool to send to MainLayout for shrinking sidebar and showing/hide menu text
private bool IconMenuActive { get; set; } = false;
//EventCallback for sending bool to MainLayout
[Parameter]
public EventCallback<bool> ShowIconMenu { get; set; }
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
//Method to toggle IconMenuActive bool and send bool via EventCallback
private async Task ToggleIconMenu()
{
IconMenuActive = !IconMenuActive;
await ShowIconMenu.InvokeAsync(IconMenuActive);
}
}
I'm still working on the styling but here's that as well.
.navbar-wrapper {
border-right: 1px solid #545454;
min-height: 100vh;
overflow: hidden;
}
.a:hover,a:active{
color: black;
}
.navbar-toggler {
background-color: rgba(255, 255, 255, 0.1);
margin-bottom: 20px;
}
.top-row {
height: 3.5rem;
background-color: white;
border-bottom: 1px solid #d6d5d5;
}
.top-row ::deep a.active, .top-row ::deep a:hover {
color: black;
background-color: transparent;
}
.bottom-row {
position: absolute;
bottom: 3rem;
padding-bottom: 10px;
text-align: right;
width: 100%;
padding-right: 28px;
}
.icon-menu-arrow {
text-align: right;
}
.navbar-brand {
font-size: 1.1rem;
color: black;
}
.oi {
width: 2rem;
font-size: 1.1rem;
vertical-align: text-top;
top: -2px;
transition: ease-in-out 0.3s;
transform-origin: center;
}
.chevron-icon{
transition: transform 0.3s ease-in-out;
}
oi-chevron-top{
transform: rotate(180deg);
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep a {
color: #000000;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}
.nav-item ::deep a.active {
background-color: #545454;
color: white;
}
.nav-item ::deep a:hover {
background-color: #545454;
color: white;
}
.sub-menu {
display: none;
flex-direction: column;
padding-left: 0.5rem;
}
.nav-item:hover .sub-menu,
.sub-menu:hover {
display: flex;
}
.sub-menu .nav-link {
font-size: 0.8rem;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.collapse {
/* Never collapse the sidebar for wide screens */
display: block;
transition: width 1s ease;
min-width: 300px;
}
}
@media (max-width: 640px) {
.bottom-row {
display: block;
}
}