sort of working drawer menu (wip)

This commit is contained in:
Christian Werner 2025-11-11 22:50:54 +01:00
parent 6e520b0cd7
commit bb5f51d8a8
4 changed files with 197 additions and 18 deletions

View File

@ -1,7 +1,17 @@
<div class="drawer-container"> <div class="drawer-wrapper" [ngClass]="{'ready': viewInitialized, 'collapsed': isCollapsed()}">
<ng-content select="drawer-content"></ng-content> <div class="drawer-container">
<ng-content select="drawer-content"></ng-content>
</div>
</div> </div>
<div class="main-content-container"> <div class="main-content-container">
<button class="menu-button secondary" (click)="handleMenuButton($event)">
<span class="material-symbols-outlined collapsed-icon" [ngClass]="{'active': isCollapsed()}">
&#xE5D2;
</span>
<span class="material-symbols-outlined open-icon" [ngClass]="{'active': !isCollapsed()}">
&#xE9BD;
</span>
</button>
<div class="top-nav-container"> <div class="top-nav-container">
<ng-content select="top-nav-container"></ng-content> <ng-content select="top-nav-container"></ng-content>
</div> </div>

View File

@ -1,28 +1,148 @@
:host { :host {
display: flex; display: flex;
height: 100%; height: 100%;
overflow: auto; overflow: auto;
--transition-duration: .3s;
--menu-trasition-duration: calc(var(--transition-duration) / 2);
.material-symbols-outlined.collapsed-icon {
display: none;
}
.material-symbols-outlined.open-icon {
display: inline-block;
}
&:has(.drawer-wrapper.collapsed) {
.material-symbols-outlined.collapsed-icon {
display: inline-block;
}
.material-symbols-outlined.open-icon {
display: none;
}
}
&:has(.open-icon.active) {
// todo fix when closing drawer animation is missing because all these styles are gone
--menu-trasition-duration: .3s;
@media (max-width: 768px) {
.drawer-wrapper {
display: block;
&.ready {
.drawer-container {
@starting-style {
width: 0;
display: block;
padding-inline: 0;
}
}
}
}
.drawer-container {
display: block;
z-index: 1000;
position: absolute;
left: 0;
top: 0;
width: 250px;
height: 100dvh;
overflow: hidden;
min-width: 0;
transition:
width var(--transition-duration) ease-in-out,
padding-inline var(--transition-duration) ease-in-out,
display var(--transition-duration) ease-in-out;
transition-behavior: allow-discrete;
}
.main-content-container {
.menu-button {
top: calc(5rem - 1rem);
left: calc(250px - 1rem);
}
}
}
}
.drawer-wrapper {
height: 100%;
flex-shrink: 0;
width: 250px;
display: block;
overflow: hidden;
transition:
width var(--transition-duration) ease-in-out,
display var(--transition-duration) ease-in-out;
transition-behavior: allow-discrete;
&.ready {
@starting-style {
width: 0;
display: block;
}
}
@media (max-width: 768px) {
width: 0;
display: none;
}
}
.drawer-container { .drawer-container {
height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 1rem; padding: 1rem;
}
.main-content-container{
display: flex;
flex-direction: column;
padding: 1rem 0 0 1rem;
}
.drawer-container {
background-color: var(--neutral-70); background-color: var(--neutral-70);
box-sizing: border-box;
min-width: fit-content;
} }
.main-content-container { .main-content-container {
display: flex;
flex-direction: column;
padding: 1rem 0 0 1rem;
position: relative;
flex-grow: 1; flex-grow: 1;
.menu-button {
position: absolute;
padding: .3333333rem;
border-radius: 50%;
z-index: 9999;
top: calc(5rem - 1rem);
left: -1rem;
width: 2rem;
height: 2rem;
transition: top var(--menu-trasition-duration) ease-out, left var(--menu-trasition-duration) ease-out;
@media (max-width: 768px) {
top: calc(2.5rem - 1rem);
left: 1rem;
}
.material-symbols-outlined {
font-size: 1.3333333rem;
font-weight: 700;
}
}
.top-nav-breadcrumb-container, .top-nav-container { .top-nav-breadcrumb-container, .top-nav-container {
margin-right: 1rem; margin-right: 1rem;
} }
@ -39,7 +159,6 @@
padding-right: 1rem; padding-right: 1rem;
flex-grow: 1; flex-grow: 1;
overflow: auto; overflow: auto;
} }
} }
} }

View File

@ -1,14 +1,48 @@
import { Component } from '@angular/core'; import {AfterViewInit, Component, inject, OnInit, signal} from '@angular/core';
import {RouterOutlet} from '@angular/router'; import {NavigationEnd, Router, RouterOutlet} from '@angular/router';
import {NgClass} from '@angular/common';
@Component({ @Component({
selector: 'app-drawer', selector: 'app-drawer',
imports: [ imports: [
RouterOutlet RouterOutlet,
NgClass
], ],
templateUrl: './drawer.component.html', templateUrl: './drawer.component.html',
styleUrl: './drawer.component.scss' styleUrl: './drawer.component.scss'
}) })
export class DrawerComponent { export class DrawerComponent implements AfterViewInit, OnInit {
protected viewInitialized = false;
protected isCollapsed = signal<boolean>(true);
private router = inject(Router);
constructor() {
this.router.events.subscribe((event) => {
if (!this.isDesktop() && !this.isCollapsed() && event instanceof NavigationEnd) {
this.isCollapsed.set(true);
}
});
}
ngOnInit(): void {
if (this.isDesktop()) {
this.isCollapsed.set(false);
}
}
ngAfterViewInit(): void {
requestAnimationFrame(() => {
this.viewInitialized = true;
})
}
handleMenuButton(_: PointerEvent) {
this.isCollapsed.update(x => !x);
}
private isDesktop(): boolean {
return window.innerWidth > 768
}
} }

View File

@ -9,6 +9,17 @@
.title { .title {
font-size: 2rem; font-size: 2rem;
font-weight: bold; font-weight: bold;
display: flex;
align-items: center;
margin-left: 0;
transition: margin-left var(--menu-trasition-duration) ease-out;
@media (max-width: 768px) {
margin-left: 3rem;
}
} }
app-nav-profile { app-nav-profile {
margin-left: auto; margin-left: auto;
@ -21,9 +32,14 @@
.logo { .logo {
$height: 3rem; $height: 3rem;
height: $height; height: $height;
width: #{$height * 3.356876171875}; width: 100%;
padding-bottom: 1rem; padding-bottom: 1rem;
border-bottom: 1px solid var(--neutral-60); border-bottom: 1px solid var(--neutral-60);
& ::ng-deep .icon {
height: $height;
width: #{$height * 3.356876171875};
}
} }
.nav-links { .nav-links {