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
8 changes: 3 additions & 5 deletions demo/StaticSample/StaticSample.Client/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@

<MudLayout>
<MudAppBar Elevation="0">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" />
<MudStaticNavDrawerToggle Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" />
<MudText Typo="Typo.h6" Style="white-space:nowrap;">Static Input</MudText>
<MudSpacer />
<RenderStateViewer Parent="this" Class="px-4" />
<MudIconButton Icon="@Icons.Custom.Brands.MudBlazor" Color="Color.Inherit" Href="https://mudblazor.com/" Target="_blank" />
<MudIconButton Icon="@Icons.Custom.Brands.GitHub" Color="Color.Inherit" Href="https://github.com/0phois/MudBlazor.StaticInput" Target="_blank" />
</MudAppBar>

<MudDrawer @bind-Open="_drawerOpen" Elevation="1">
<MudDrawerHeader>
<MudText Typo="Typo.h6">Static Input</MudText>
</MudDrawerHeader>
<MudDrawer @bind-Open="_drawerOpen" Elevation="1" Variant="@DrawerVariant.Responsive" Breakpoint="@Breakpoint.Md" ClipMode="DrawerClipMode.Always">
<NavMenu />
</MudDrawer>

Expand Down
20 changes: 11 additions & 9 deletions demo/StaticSample/StaticSample.Client/Layout/NavMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
<MudNavLink Href="counter" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.Add">Counter</MudNavLink>
<MudNavLink Href="weather" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Weather</MudNavLink>

<AuthorizeView>
<Authorized>
<MudNavLink Href="Account/Manage" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">@context.User.Identity?.Name</MudNavLink>
</Authorized>
<NotAuthorized>
<MudNavLink Href="Account/Register" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Register</MudNavLink>
<MudNavLink Href="Account/Login" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Login</MudNavLink>
</NotAuthorized>
</AuthorizeView>
<MudStaticNavGroup Icon="@Icons.Material.Filled.AdminPanelSettings" Title="Account" Expanded="true">
<AuthorizeView>
<Authorized>
<MudNavLink Href="Account/Manage" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">@context.User.Identity?.Name</MudNavLink>
</Authorized>
<NotAuthorized>
<MudNavLink Href="Account/Register" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Register</MudNavLink>
<MudNavLink Href="Account/Login" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Login</MudNavLink>
</NotAuthorized>
</AuthorizeView>
</MudStaticNavGroup>
</MudNavMenu>
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.10" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\MudBlazor.StaticInput.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions demo/StaticSample/StaticSample.Client/_Imports.razor
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
@using StaticSample.Client
@using MudBlazor
@using MudBlazor.Services
@using MudBlazor.StaticInput
@using Blazr.RenderState
41 changes: 41 additions & 0 deletions src/Components/NavMenu/MudStaticNavDrawerToggle.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@using Microsoft.JSInterop

@namespace MudBlazor.StaticInput

@inherits MudIconButton
@inject IJSRuntime JsRuntime

@{
base.BuildRenderTree(__builder);
}

@code {
/// <summary>
/// The id of the <see cref="MudDrawer"/> to toggle
/// </summary>
/// <remarks>
/// If not provided, the first mud-drawer element located within the DOM is used.
/// </remarks>
[Parameter]
public string? DrawerId { get; set; } = "_no_id_provided_";

private string _elementId = string.Empty;

protected override void OnParametersSet()
{
UserAttributes["data-mud-drawer-toggle"] = DrawerId;
UserAttributes["data-static-component"] = true;
UserAttributes["data-static-id"] = _elementId;

base.OnClick = EventCallback.Factory.Create<MouseEventArgs>(this, async () => await JsRuntime.InvokeVoidAsync("MudDrawerInterop.toggleDrawer", DrawerId));

base.OnParametersSet();
}

protected override void OnInitialized()
{
_elementId = Guid.NewGuid().ToString()[..8];

base.OnInitialized();
}
}
28 changes: 28 additions & 0 deletions src/Components/NavMenu/MudStaticNavDrawerToggle.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components;

namespace MudBlazor.StaticInput;

/// <summary>
/// A <see cref="MudIconButton"/> used to toggle the state of the navigation <see cref="MudDrawer"/>
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>
/// <see cref="DrawerClipMode"/> should be set to <see cref="DrawerClipMode.Always"/>
/// </item>
/// <item>
/// <see cref="DrawerVariant.Temporary"/> is not currently supported since the required <see cref="MudOverlay"/> is not implemented
/// </item>
/// </list>
/// </remarks>
public partial class MudStaticNavDrawerToggle : MudIconButton

{
protected new EventCallback<MouseEventArgs> OnClick { get; set; }
protected new ButtonType ButtonType { get; set; }
protected new string HtmlTag { get; set; } = "button";
protected new string? Href { get; set; }
protected new string? Target { get; set; }
protected new bool ClickPropagation { get; set; }
}
64 changes: 64 additions & 0 deletions src/Components/NavMenu/MudStaticNavGroup.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
@namespace MudBlazor.StaticInput

@inherits MudNavGroup

@{
base.BuildRenderTree(__builder);
}

@code {
private string _elementId = string.Empty;

protected override void OnParametersSet()
{
UserAttributes["data-static-component"] = true;
UserAttributes["data-static-id"] = _elementId;

base.OnParametersSet();
}

protected override void OnInitialized()
{
_elementId = Guid.NewGuid().ToString()[..8];

base.OnInitialized();
}
}

@if (IsStatic()) {
<script>
(function () {
document.addEventListener('DOMContentLoaded', () => {
const button = document.querySelector('[data-static-id="@_elementId"] button');

if (button) {
button.addEventListener('click', (event) => {
const navElement = event.target.closest('nav');
const collapseContainer = navElement.querySelector('.mud-collapse-container');
const expandIcon = navElement.querySelector('.mud-nav-link-expand-icon');

if (!collapseContainer || !expandIcon) {
console.warn("Missing required elements for toggling");
return;
}

// Check current state
const isExpanded = button.getAttribute('aria-expanded') === "true";

// Toggle classes for collapse container
collapseContainer.classList.toggle('mud-collapse-entered', !isExpanded);
collapseContainer.classList.toggle('mud-navgroup-collapse', true);
collapseContainer.classList.remove('mud-collapse-entering');
collapseContainer.setAttribute('aria-hidden', isExpanded);

// Toggle classes for expand icon
expandIcon.classList.toggle('mud-transform', !isExpanded);

// Update aria-expanded on the button
button.setAttribute('aria-expanded', !isExpanded);
});
}
});
})();
</script>
}
20 changes: 20 additions & 0 deletions src/Components/NavMenu/MudStaticNavGroup.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Http;

namespace MudBlazor.StaticInput;

public partial class MudStaticNavGroup : MudNavGroup
{
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;

private bool IsStatic()
{
#if NET9_0_OR_GREATER
return !RendererInfo.IsInteractive;
#else
return HttpContext != null;
#endif
}
}

1 change: 1 addition & 0 deletions src/MudBlazor.StaticInput.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="MudBlazor" Version="8.0.0-preview.5" />
</ItemGroup>

Expand Down
119 changes: 115 additions & 4 deletions src/wwwroot/NavigationObserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,128 @@ let hasInitialized = false;
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
observer.observe(document.body, { childList: true, subtree: true });

initializeScripts();
}
};

window.MudDrawerInterop = {
initialize: function () {
const drawerToggleElements = document.querySelectorAll('[data-mud-drawer-toggle]');

drawerToggleElements.forEach(element => {
element.removeEventListener('click', this.handleToggleDrawer);
element.addEventListener('click', this.handleToggleDrawer);
});

const responsiveDrawer = document.querySelectorAll('.mud-drawer-responsive')[0];

if (responsiveDrawer) {
MudDrawerInterop.monitorResize(responsiveDrawer);
}
},

monitorResize(responsiveDrawer) {
const classSections = Array.from(responsiveDrawer.parentElement.classList).find(className => className.includes('responsive')).split('-');
const breakpoint = classSections[classSections.length - 2];
const position = classSections[classSections.length - 1];
const breakpointValue = MudDrawerInterop.getBreakpointValue(breakpoint);
const resizeQuery = window.matchMedia(`(min-width: ${breakpointValue}px)`);

if (responsiveDrawer.parentElement.offsetWidth <= breakpointValue) {
MudDrawerInterop.autoCollapse(responsiveDrawer, breakpoint, position);
} else {
MudDrawerInterop.autoExpand(responsiveDrawer, breakpoint, position);
}

resizeQuery.addEventListener('change', ev => {
if (ev.matches) {
MudDrawerInterop.autoExpand(responsiveDrawer, breakpoint, position);
}
else {
MudDrawerInterop.autoCollapse(responsiveDrawer, breakpoint, position);
}
})
},

getBreakpointValue(breakpoint) {
switch (breakpoint) {
case 'xs': return 380;
case 'sm': return 600;
case 'md': return 960;
case 'lg': return 1280;
case 'xl': return 1920;
}
},

autoCollapse(responsiveDrawer, breakpoint, position) {
responsiveDrawer.classList.add('mud-drawer--closed');
responsiveDrawer.classList.remove('mud-drawer--open');
responsiveDrawer.parentElement.classList.add(`mud-drawer-closed-responsive-${breakpoint}-${position}`)
responsiveDrawer.parentElement.classList.remove(`mud-drawer-open-responsive-${breakpoint}-${position}`)
},

autoExpand(responsiveDrawer, breakpoint, position) {
responsiveDrawer.classList.add('mud-drawer--open');
responsiveDrawer.classList.remove('mud-drawer--closed');
responsiveDrawer.parentElement.classList.add(`mud-drawer-open-responsive-${breakpoint}-${position}`)
responsiveDrawer.parentElement.classList.remove(`mud-drawer-closed-responsive-${breakpoint}-${position}`)
},

handleToggleDrawer: function (event) {
const element = event.currentTarget;
const targetDrawerId = element.getAttribute('data-mud-drawer-toggle');

MudDrawerInterop.toggleDrawer(targetDrawerId);
},

toggleDrawer: function (drawerId) {
let mudDrawer;

if (drawerId === '_no_id_provided_') {
mudDrawer = document.querySelectorAll('.mud-drawer')[0];
} else {
mudDrawer = document.getElementById(drawerId);
}

if (mudDrawer) {
mudDrawer.classList.toggle('mud-drawer--open');
mudDrawer.classList.toggle('mud-drawer--closed');
mudDrawer.classList.remove('mud-drawer--initial');
}

const header = document.querySelector('.mud-drawer-header');

if (header) {
if (mudDrawer.className.includes('mud-drawer--closed') === true) {
header.classList.add('mud-typography-nowrap');
}
else {
header.classList.remove('mud-typography-nowrap');
}
}

const layout = mudDrawer.parentElement;

if (layout) {
if (layout.className.includes('mud-drawer-open') === true) {
layout.className = layout.className.replace(/\bmud-drawer-open\b/g, 'mud-drawer-closed');
} else {
layout.className = layout.className.replace(/\bmud-drawer-closed\b/g, 'mud-drawer-open');

if (mudDrawer.className.includes('mud-static-responsive')) {
mudDrawer.classList.add('mud-drawer-clipped-always');
}
}
}
},
};

function ensureInitialized() {
if (window.MutationObserverModule && !hasInitialized) {
window.MutationObserverModule.initialize();
window.MudDrawerInterop.initialize();
}
}

Expand Down
Loading
Loading