finish menu animations

This commit is contained in:
Christian Werner 2025-11-15 03:59:34 +01:00
parent 7fab8cfd8f
commit a1ba6b35f1
7 changed files with 96 additions and 40 deletions

View File

@ -26,13 +26,13 @@ export const routes: Routes = [
title: 'Home', title: 'Home',
path: '', path: '',
component: HomeComponent, component: HomeComponent,
data: { showInNav: true } data: { showInNav: true, icon: 'home' }
}, },
{ {
title: 'Dashboard', title: 'Dashboard',
path: 'dashboard', path: 'dashboard',
component: DashboardComponent, component: DashboardComponent,
data: { showInNav: true }, data: { showInNav: true, icon: 'dashboard' },
children: [ children: [
{ {
title: 'Test', title: 'Test',
@ -74,13 +74,13 @@ export const routes: Routes = [
title: 'Users', title: 'Users',
path: 'users', path: 'users',
component: HomeComponent, component: HomeComponent,
data: { showInNav: true } data: { showInNav: true, icon: 'groups' }
}, },
{ {
title: 'Tenants', title: 'Tenants',
path: 'tenants', path: 'tenants',
component: OutletComponent, component: OutletComponent,
data: { showInNav: true }, data: { showInNav: true, icon: 'domain' },
children: [ children: [
{ {
title: 'Grid', title: 'Grid',

View File

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

View File

@ -6,19 +6,14 @@
--transition-duration: .2s; --transition-duration: .2s;
--menu-trasition-duration: calc(var(--transition-duration) / 2); --menu-trasition-duration: calc(var(--transition-duration) / 2);
.material-symbols-outlined.collapsed-icon { .material-symbols-outlined.menu-icon {
display: none; transform: scaleX(1);
} transition: transform var(--menu-trasition-duration) linear;
.material-symbols-outlined.open-icon {
display: inline-block;
} }
&:has(.drawer-wrapper.collapsed) { &:has(.drawer-wrapper.collapsed) {
.material-symbols-outlined.collapsed-icon { .material-symbols-outlined.menu-icon {
display: inline-block; transform: scaleX(-1);
}
.material-symbols-outlined.open-icon {
display: none;
} }
} }
@ -31,17 +26,7 @@
flex-shrink: 0; flex-shrink: 0;
//overflow: hidden; //overflow: hidden;
transition: transition: width var(--transition-duration) linear;
width var(--transition-duration) linear;
transition-behavior: allow-discrete;
//&.ready {
// @starting-style {
// width: 0;
// display: block;
// }
//}
.drawer-container { .drawer-container {
@ -62,7 +47,7 @@
box-sizing: border-box; box-sizing: border-box;
transition: left var(--transition-duration) linear; transition: left var(--transition-duration) linear, width var(--transition-duration) linear, min-width var(--transition-duration) linear;
.menu-button { .menu-button {
position: absolute; position: absolute;
@ -89,13 +74,40 @@
} }
@media (min-width: 768px) {
&.collapsed {
width: 78px;
.drawer-container {
width: 78px;
min-width: 78px;
}
}
}
@media (max-width: 768px) { @media (max-width: 768px) {
width: 0; width: 0;
.drawer-container {
@starting-style {
left: -78px;
width: 78px;
min-width: 78px;
.menu-button {
right: -3rem;
}
}
}
&.collapsed { &.collapsed {
.drawer-container { .drawer-container {
left: -250px; left: -78px;
width: 78px;
min-width: 78px;
.menu-button { .menu-button {
right: -3rem; right: -3rem;
@ -103,6 +115,8 @@
} }
} }
} }
} }
.main-content-container { .main-content-container {

View File

@ -13,7 +13,6 @@ import {NgClass} from '@angular/common';
}) })
export class DrawerComponent implements AfterViewInit, OnInit { export class DrawerComponent implements AfterViewInit, OnInit {
protected viewInitialized = false;
protected isCollapsed = signal<boolean>(true); protected isCollapsed = signal<boolean>(true);
private router = inject(Router); private router = inject(Router);
@ -32,9 +31,9 @@ export class DrawerComponent implements AfterViewInit, OnInit {
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
requestAnimationFrame(() => { // requestAnimationFrame(() => {
this.viewInitialized = true; // this.viewInitialized = true;
}) // })
} }
handleMenuButton(_: PointerEvent) { handleMenuButton(_: PointerEvent) {

View File

@ -5,8 +5,11 @@
<div class="nav-links"> <div class="nav-links">
@for (link of navLinks(); track link.label) { @for (link of navLinks(); track link.label) {
<app-link [href]="link.url"> <app-link [href]="link.url" title="{{link.label}}">
<span [ngClass]="{'active': activeTopLevelUrl() === link.url}" class="nav-link">{{link.label}}</span> <span [ngClass]="{'active': activeTopLevelUrl() === link.url}" class="nav-link">
<span class="material-symbols-outlined">{{ link.iconName ?? 'bolt' }}</span>
<span class="label">{{link.label}}</span>
</span>
</app-link> </app-link>
} }
</div> </div>

View File

@ -35,10 +35,20 @@
width: 100%; width: 100%;
padding-bottom: 1rem; padding-bottom: 1rem;
border-bottom: 1px solid var(--neutral-60); border-bottom: 1px solid var(--neutral-60);
overflow: clip;
transition:
width var(--transition-duration) linear,
height var(--transition-duration) linear,
padding-bottom var(--transition-duration) linear;
& ::ng-deep .icon { & ::ng-deep .icon {
height: $height; height: $height;
width: #{$height * 3.356876171875}; width: #{$height * 3.356876171875};
transition:
width var(--transition-duration) linear,
height var(--transition-duration) linear;
} }
} }
@ -48,10 +58,14 @@
flex-direction: column; flex-direction: column;
.nav-link { .nav-link {
width: 100%;
display: flex;
align-items: center;
width: 100%;
padding: 0.5rem; padding: 0.5rem;
border-radius: 0.25rem; border-radius: 0.25rem;
gap: 0.5ch;
overflow: clip;
&:hover, &.active { &:hover, &.active {
background-color: var(--neutral-60); background-color: var(--neutral-60);
@ -77,3 +91,30 @@
margin-left: auto; margin-left: auto;
} }
} }
::ng-deep app-drawer {
.drawer-wrapper.collapsed {
::ng-deep app-icon.logo {
width: 44px !important;
height: 44px !important;
padding-bottom: 28px !important;
.icon {
height: 44px !important;
width: calc(44px * 3.356876171875) !important;
}
}
.nav-links {
.nav-link {
.label {
display: none;
}
}
}
}
}

View File

@ -72,7 +72,8 @@ export class ShellComponent implements OnInit {
.map((r, id) => ({ .map((r, id) => ({
id, id,
label: r.data?.['title'] || r.title || '', label: r.data?.['title'] || r.title || '',
url: '/' + (r.path ?? '') url: '/' + (r.path ?? ''),
iconName: r.data?.['icon']
})); }));
} }
@ -118,4 +119,5 @@ export class Breadcrumb {
id: number = 0; id: number = 0;
label: string = 'undefined'; label: string = 'undefined';
url: string = ''; url: string = '';
iconName?: string;
} }