mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-07 14:56:02 -04:00
Merge pull request #28 from lanedirt/27-fix-wasm-topbar-menu-open-toggle-css-styles
WASM app tweaks for UI (#27)
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
@page "/user/logout"
|
||||
@using AliasVault.WebApp.Auth.Services
|
||||
@attribute [AllowAnonymous]
|
||||
@layout Auth.Layout.MainLayout
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject AuthService AuthService
|
||||
@inject GlobalNotificationService GlobalNotificationService
|
||||
@using AliasVault.WebApp.Auth.Services
|
||||
|
||||
@code {
|
||||
/// <inheritdoc />
|
||||
@@ -13,6 +14,7 @@
|
||||
await base.OnInitializedAsync();
|
||||
await AuthService.RemoveTokensAsync();
|
||||
await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
GlobalNotificationService.ClearMessages();
|
||||
|
||||
// Redirect to home page
|
||||
NavigationManager.NavigateTo("/");
|
||||
|
||||
@@ -28,7 +28,7 @@ public class AuthStateProvider(AuthService authService) : AuthenticationStatePro
|
||||
var jsonBytes = ParseBase64WithoutPadding(payload);
|
||||
var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);
|
||||
|
||||
if (keyValuePairs == null)
|
||||
if (keyValuePairs is null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to parse JWT token.");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@inherits PageBase
|
||||
@implements IDisposable
|
||||
|
||||
<header>
|
||||
<nav class="fixed z-30 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700 py-3 px-4">
|
||||
@@ -41,7 +42,7 @@
|
||||
|
||||
@if (isMenuOpen)
|
||||
{
|
||||
<div class="z-50 my-4 w-56 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700 dark:divide-gray-600" id="userMenuDropdown" data-popper-placement="bottom" style="position: absolute; inset: 0px auto auto 0px; margin: 0px; margin-left: -200px; transform: translate3d(1537.5px, 58px, 0px);">
|
||||
<div class="absolute top-10 right-0 z-50 my-4 w-56 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700 dark:divide-gray-600" id="userMenuDropdown" data-popper-placement="bottom">
|
||||
<div class="py-3 px-4">
|
||||
<span class="block text-sm font-semibold text-gray-900 dark:text-white">@_username</span>
|
||||
</div>
|
||||
@@ -58,49 +59,35 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
<button type="button" id="toggleMobileMenuButton" data-collapse-toggle="toggleMobileMenu" class="items-center p-2 text-gray-500 rounded-lg md:ml-2 lg:hidden hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600">
|
||||
<button @onclick="ToggleMobileMenu" type="button" id="toggleMobileMenuButton" class="items-center p-2 text-gray-500 rounded-lg md:ml-2 lg:hidden hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600">
|
||||
<span class="sr-only">Open menu</span>
|
||||
<svg class="w-6 h-6" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="bg-white dark:bg-gray-900">
|
||||
|
||||
<ul id="toggleMobileMenu" class="hidden flex-col mt-0 pt-16 w-full text-sm font-medium lg:hidden">
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<a href="#" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0" aria-current="page">Home</a>
|
||||
</li>
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<a href="#" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0">Messages</a>
|
||||
</li>
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<a href="#" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0">Profile</a>
|
||||
</li>
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<a href="#" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0">Settings</a>
|
||||
</li>
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<button type="button" data-collapse-toggle="dropdownMobileNavbar" class="flex justify-between items-center py-3 px-4 w-full text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0">Dropdown <svg class="w-6 h-6 text-gray-500 dark:text-gray-400" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg></button>
|
||||
<ul id="dropdownMobileNavbar" class="hidden">
|
||||
<li class="block border-t border-b dark:border-gray-700">
|
||||
<a href="#" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0">Item 1</a>
|
||||
</li>
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<a href="#" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0">Item 2</a>
|
||||
</li>
|
||||
<li class="block">
|
||||
<a href="#" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0">Item 3</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@if (isMobileMenuOpen)
|
||||
{
|
||||
<nav class="bg-white dark:bg-gray-900">
|
||||
<ul id="mobileMenu" class="flex-col mt-0 pt-16 w-full text-sm font-medium lg:hidden">
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<NavLink href="/" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Home
|
||||
</NavLink>
|
||||
</li>
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<NavLink href="/aliases" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Aliases
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
}
|
||||
</header>
|
||||
|
||||
@code {
|
||||
private bool isMenuOpen = false;
|
||||
private bool isMobileMenuOpen = false;
|
||||
private string _username { get; set; } = "";
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -108,6 +95,7 @@
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_username = await GetUsernameAsync();
|
||||
NavigationManager.LocationChanged += LocationChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -117,16 +105,38 @@
|
||||
if (firstRender)
|
||||
{
|
||||
await Js.InvokeVoidAsync("window.initTopMenu");
|
||||
DotNetObjectReference<TopMenu> objRef = DotNetObjectReference.Create(this);
|
||||
await Js.InvokeVoidAsync("window.registerClickOutsideHandler", objRef);
|
||||
}
|
||||
}
|
||||
|
||||
void LocationChanged(object? sender, LocationChangedEventArgs e)
|
||||
{
|
||||
isMenuOpen = false;
|
||||
isMobileMenuOpen = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void ToggleMenu()
|
||||
{
|
||||
isMenuOpen = !isMenuOpen;
|
||||
}
|
||||
|
||||
private void CloseMenu()
|
||||
private void ToggleMobileMenu()
|
||||
{
|
||||
isMobileMenuOpen = !isMobileMenuOpen;
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public void CloseMenu()
|
||||
{
|
||||
isMenuOpen = false;
|
||||
isMobileMenuOpen = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
NavigationManager.LocationChanged -= LocationChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ else
|
||||
<div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
|
||||
<h3 class="mb-4 text-xl font-semibold dark:text-white">Login credentials</h3>
|
||||
<div class="mb-4">
|
||||
<button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 dark:bg-blue-500 dark:hover:bg-blue-600 dark:focus:ring-blue-800" @onclick="GenerateRandomIdentity">Generate Random Identity</button>
|
||||
<button type="button" class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 dark:bg-blue-500 dark:hover:bg-blue-600 dark:focus:ring-blue-800" @onclick="GenerateRandomIdentity">Generate Random Identity</button>
|
||||
<button type="submit" class="px-4 py-2 text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 dark:bg-green-500 dark:hover:bg-green-600 dark:focus:ring-green-800">Save Alias</button>
|
||||
@if (IsIdentityLoading)
|
||||
{
|
||||
|
||||
@@ -75,5 +75,5 @@ public class EmailApiModel
|
||||
/// <summary>
|
||||
/// Gets or sets the list of attachments in the email.
|
||||
/// </summary>
|
||||
public List<AttachmentApiModel> Attachments { get; set; } = new();
|
||||
public List<AttachmentApiModel> Attachments { get; set; } = [];
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@ public class MailboxApiModel
|
||||
/// <summary>
|
||||
/// Gets or sets the list of mailbox email API models.
|
||||
/// </summary>
|
||||
public List<MailboxEmailApiModel> Mails { get; set; } = new();
|
||||
public List<MailboxEmailApiModel> Mails { get; set; } = [];
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class PageBase : OwningComponentBase
|
||||
/// <summary>
|
||||
/// Gets or sets the breadcrumb items for the page. A default set of breadcrumbs is added in the parent OnInitialized method.
|
||||
/// </summary>
|
||||
protected List<BreadcrumbItem> BreadcrumbItems { get; set; } = new List<BreadcrumbItem>();
|
||||
protected List<BreadcrumbItem> BreadcrumbItems { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component asynchronously.
|
||||
|
||||
@@ -90,5 +90,14 @@ public class GlobalNotificationService
|
||||
return messages;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all messages.
|
||||
/// </summary>
|
||||
public void ClearMessages()
|
||||
{
|
||||
SuccessMessages.Clear();
|
||||
ErrorMessages.Clear();
|
||||
}
|
||||
|
||||
private void NotifyStateChanged() => OnChange?.Invoke();
|
||||
}
|
||||
|
||||
@@ -603,6 +603,14 @@ video {
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.top-12 {
|
||||
top: 3rem;
|
||||
}
|
||||
|
||||
.top-10 {
|
||||
top: 2.5rem;
|
||||
}
|
||||
|
||||
.z-10 {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,22 @@
|
||||
initDarkModeSwitcher();
|
||||
};
|
||||
|
||||
window.registerClickOutsideHandler = (dotNetHelper) => {
|
||||
document.addEventListener('click', (event) => {
|
||||
const menu = document.getElementById('userMenuDropdown');
|
||||
const menuButton = document.getElementById('userMenuDropdownButton');
|
||||
if (menu && !menu.contains(event.target) && !menuButton.contains(event.target)) {
|
||||
dotNetHelper.invokeMethodAsync('CloseMenu');
|
||||
}
|
||||
|
||||
const mobileMenu = document.getElementById('mobileMenu');
|
||||
const mobileMenuButton = document.getElementById('toggleMobileMenuButton');
|
||||
if (mobileMenu && !mobileMenu.contains(event.target) && !mobileMenuButton.contains(event.target)) {
|
||||
dotNetHelper.invokeMethodAsync('CloseMenu');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.clipboardCopy = {
|
||||
copyText: function (text) {
|
||||
navigator.clipboard.writeText(text).then(function () { })
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
function initDarkModeSwitcher() {
|
||||
console.log('initDarkModeSwitcher');
|
||||
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
|
||||
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
|
||||
|
||||
@@ -16,6 +15,11 @@ function initDarkModeSwitcher() {
|
||||
themeToggleDarkIcon.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Default to light mode if not set.
|
||||
document.documentElement.classList.remove('dark');
|
||||
themeToggleLightIcon.classList.remove('hidden');
|
||||
}
|
||||
|
||||
const themeToggleBtn = document.getElementById('theme-toggle');
|
||||
|
||||
|
||||
@@ -106,13 +106,13 @@ public class BlazorWasmAppManager
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await TestContext.Out.WriteLineAsync(e.Message);
|
||||
|
||||
if (_blazorWasmErrors.Count > 0)
|
||||
{
|
||||
Assert.Fail($"WASM failed to start: {string.Join(Environment.NewLine, _blazorWasmErrors)}");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user