Browse Source

UI: add desktop sidenav collapse/expand with width animation

Sidenav width is now driven by signals — 250px expanded, 50px collapsed — with a matching margin-left animation on mat-sidenav-content. The toolbar button toggles between sidebar-close and sidebar-open icons, and the logo is hidden when the rail is collapsed. Desktop-only; mobile overlay sidenav keeps its existing behaviour.
pull/15888/head
Igor Kulikov 4 months ago
parent
commit
df83c3acae
  1. 22
      ui-ngx/src/app/modules/home/home.component.html
  2. 12
      ui-ngx/src/app/modules/home/home.component.scss
  3. 29
      ui-ngx/src/app/modules/home/home.component.ts

22
ui-ngx/src/app/modules/home/home.component.html

@ -17,16 +17,26 @@
-->
<mat-sidenav-container>
<mat-sidenav #sidenav class="tb-site-sidenav mat-elevation-z2"
[class.tb-desktop]="sidenavDesktop()"
[disableClose]="sidenavOpened"
(click)="sidenavClicked()"
[mode]="sidenavMode"
[opened]="sidenavOpened && !forceFullscreen">
[opened]="sidenavOpened && !forceFullscreen"
[style.width]="sidenavWidth()">
<header class="tb-nav-header">
<mat-toolbar class="tb-nav-header-toolbar">
<div class="flex flex-1 flex-row justify-between">
<tb-logo [src]="logo" class="tb-logo-title"></tb-logo>
@if (sidenavDesktop) {
<button mat-icon-button class="tb-mat-32"><tb-icon class="tb-mat-20">sidebar-close</tb-icon></button>
@if (showLogo()) {
<tb-logo [src]="logo" class="tb-logo-title"></tb-logo>
}
@if (sidenavDesktop()) {
<button mat-icon-button class="tb-mat-32" (click)="toggleSidenav()">
@if (sidenavCollapsed()) {
<tb-icon class="tb-mat-20">sidebar-open</tb-icon>
} @else {
<tb-icon class="tb-mat-20">sidebar-close</tb-icon>
}
</button>
}
</div>
</mat-toolbar>
@ -38,7 +48,9 @@
<tb-user-menu></tb-user-menu>
</mat-toolbar>
</mat-sidenav>
<mat-sidenav-content>
<mat-sidenav-content class="tb-site-content"
[class.tb-desktop]="sidenavDesktop()"
[style.margin-left]="sidenavWidth()">
<div class="flex flex-col" role="main" style="height: 100%;">
<mat-toolbar class="tb-primary-toolbar flex flex-row">
<button mat-icon-button id="main"

12
ui-ngx/src/app/modules/home/home.component.scss

@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
$sidenav-anim-timing: 400ms cubic-bezier(0.25, 0.8, 0.25, 1);
:host {
display: flex;
width: 100%;
@ -26,6 +29,9 @@
@media (max-width:456px) {
width: calc(100% - 56px);
}
&.tb-desktop {
transition: width $sidenav-anim-timing;
}
.tb-nav-header {
z-index: 2;
flex-shrink: 0;
@ -60,6 +66,12 @@
}
}
}
mat-sidenav-content.tb-site-content {
&.tb-desktop {
margin-left: 250px;
transition: margin-left $sidenav-anim-timing;
}
}
.tb-primary-toolbar {
z-index: 2;
height: 56px;

29
ui-ngx/src/app/modules/home/home.component.ts

@ -14,7 +14,17 @@
/// limitations under the License.
///
import { AfterViewInit, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import {
AfterViewInit,
Component,
computed,
ElementRef,
Inject,
OnDestroy,
OnInit,
signal,
ViewChild
} from '@angular/core';
import { skip, startWith, Subject } from 'rxjs';
import { Store } from '@ngrx/store';
import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
@ -52,10 +62,15 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
sidenavMode: 'over' | 'push' | 'side' = 'side';
sidenavOpened = true;
sidenavDesktop = true;
sidenavDesktop = signal(true);
sidenavCollapsed = signal(false);
sidenavWidth = computed(() => this.sidenavDesktop() ? (this.sidenavCollapsed() ? '50px' : '250px') : '');
logo = 'assets/logo_title_black.svg';
showLogo= computed(() => !this.sidenavDesktop() || !this.sidenavCollapsed());
@ViewChild('sidenav')
sidenav: MatSidenav;
@ -84,7 +99,7 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
const isGtSm = this.breakpointObserver.isMatched(MediaBreakpoints['gt-sm']);
this.sidenavMode = isGtSm ? 'side' : 'over';
this.sidenavOpened = isGtSm;
this.sidenavDesktop = isGtSm;
this.sidenavDesktop.set(isGtSm);
this.breakpointObserver
.observe(MediaBreakpoints['gt-sm'])
@ -93,11 +108,11 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
if (state.matches) {
this.sidenavMode = 'side';
this.sidenavOpened = true;
this.sidenavDesktop = true;
this.sidenavDesktop.set(true);
} else {
this.sidenavMode = 'over';
this.sidenavOpened = false;
this.sidenavDesktop = false;
this.sidenavDesktop.set(false);
}
}
);
@ -124,6 +139,10 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
}
}
toggleSidenav() {
this.sidenavCollapsed.update(state => !state);
}
toggleFullscreen() {
if (screenfull.isEnabled) {
screenfull.toggle();

Loading…
Cancel
Save