File

src/lib/sidenav/sidenav.component.ts

Metadata

Index

Properties
Methods

Methods

Async closeSidenav
closeSidenav()
Returns : any
onOpenedChanged
onOpenedChanged(opened: boolean)

To ensure the open state is in sync with the component. If the sidenav is closed, e.g. clicking outside the sidenav, it is required to manually update the state in the LayoutService.

Parameters :
Name Type Optional
opened boolean No
Returns : void
Async openSidenav
openSidenav()
Returns : any
togglePinned
togglePinned()
Returns : void

Properties

Public Readonly collapsable
Type : Signal<boolean>
Default value : computed(() => this.layoutService.collapsable())
Public Readonly collapsed
Type : Signal<boolean>
Default value : computed(() => this.layoutService.collapsed())
Public Readonly fixedBottomGap
Type : Signal<number>
Default value : computed(() => this.layoutService.fixedBottomGap())
Public Readonly fixedInViewport
Type : Signal<boolean>
Default value : computed(() => this.layoutService.fixedInViewport())
Public Readonly fixedTopGap
Type : Signal<number>
Default value : computed(() => this.layoutService.fixedTopGap())
Protected Readonly layoutService
Default value : inject(LayoutService)
Public Readonly opened
Type : Signal<boolean>
Default value : computed(() => this.layoutService.opened())
Public Readonly pinned
Type : Signal<boolean>
Default value : computed(() => this.layoutService.pinned())
Protected Readonly sidenav
Default value : viewChild(MatSidenav)
Public Readonly sidenavFooterDirective
Default value : contentChild(SidenavFooterDirective)
Public Readonly sidenavHeaderDirective
Default value : contentChild(SidenavHeaderDirective)
Public Readonly sidenavMode
Type : Signal<MatDrawerMode>
Default value : computed(() => this.layoutService.mode())
import {
  NgClass,
  NgIf,
  NgStyle,
  NgTemplateOutlet,
} from '@angular/common';
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  contentChild,
  inject,
  Signal,
  viewChild,
} from '@angular/core';
import { MatIconButton } from '@angular/material/button';
import { MatDivider } from '@angular/material/divider';
import { MatIcon } from '@angular/material/icon';
import {
  MatDrawerMode,
  MatSidenav,
  MatSidenavContainer,
  MatSidenavContent,
} from '@angular/material/sidenav';
import { LayoutService } from '../layout.service';
import { NavigationComponent } from '../navigation/navigation.component';
import { SidenavFooterDirective } from './sidenav-footer.directive';
import { SidenavHeaderDirective } from './sidenav-header.directive';

@Component({
  selector: 'rxap-sidenav',
  templateUrl: './sidenav.component.html',
  styleUrls: ['./sidenav.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    MatIcon,
    MatIconButton,
    MatSidenav,
    MatSidenavContainer,
    MatSidenavContent,
    NavigationComponent,
    NgIf,
    NgClass,
    NgStyle,
    MatDivider,
    NgTemplateOutlet,
  ],
})
export class SidenavComponent {

  protected readonly layoutService = inject(LayoutService);

  protected readonly sidenav = viewChild(MatSidenav);

  public readonly sidenavMode: Signal<MatDrawerMode> = computed(() => this.layoutService.mode());
  public readonly fixedBottomGap: Signal<number> = computed(() => this.layoutService.fixedBottomGap());
  public readonly fixedTopGap: Signal<number> = computed(() => this.layoutService.fixedTopGap());
  public readonly fixedInViewport: Signal<boolean> = computed(() => this.layoutService.fixedInViewport());
  public readonly pinned: Signal<boolean> = computed(() => this.layoutService.pinned());
  public readonly collapsed: Signal<boolean> = computed(() => this.layoutService.collapsed());
  public readonly collapsable: Signal<boolean> = computed(() => this.layoutService.collapsable());
  public readonly opened: Signal<boolean> = computed(() => this.layoutService.opened());
  public readonly sidenavFooterDirective = contentChild(SidenavFooterDirective);
  public readonly sidenavHeaderDirective = contentChild(SidenavHeaderDirective);

  togglePinned() {
    this.layoutService.togglePinned();
  }

  /**
   * To ensure the open state is in sync with the component. If the sidenav is closed, e.g. clicking outside the sidenav,
   * it is required to manually update the state in the LayoutService.
   * @param opened
   */
  onOpenedChanged(opened: boolean) {
    if (!opened) {
      this.layoutService.closeSidenav();
    }
  }

  async openSidenav() {
    await this.sidenav()?.open();
    this.layoutService.openSidenav();
  }

  async closeSidenav() {
    await this.sidenav()?.close();
    this.layoutService.closeSidenav();
  }

}
<mat-sidenav-container [ngStyle]="{
    'padding-top.px': fixedTopGap(),
    'padding-bottom.px': fixedBottomGap(),
  }" class="h-full">
  <mat-sidenav
    [fixedBottomGap]="fixedBottomGap()"
    [fixedInViewport]="fixedInViewport()"
    [fixedTopGap]="fixedTopGap()"
    [mode]="sidenavMode()"
    [ngClass]="{ collapsable: collapsable() }"
    [opened]="opened()"
    (openedChange)="onOpenedChanged($event)"
  >
    <div (mouseleave)="collapsable() && !pinned() && closeSidenav()"
         class="h-full py-2 flex flex-col items-center gap-y-5 justify-items-stretch">

      <div (click)="togglePinned()" *ngIf="collapsable()"
           class="pl-2 self-stretch grow-0 flex flex-row justify-between items-center">
        <span class="text-lg" i18n>Navigation</span>
        <div class="flex flex-row items-center justify-center" style="width: 64px">
          <button mat-icon-button>
            <mat-icon *ngIf="!pinned()">radio_button_unchecked</mat-icon>
            <mat-icon *ngIf="pinned()">radio_button_checked</mat-icon>
          </button>
        </div>
      </div>

      @if (sidenavHeaderDirective()?.template; as template) {
        <div [ngClass]="{ hidden: collapsed() }" class="header grow-0">
          <ng-container *ngTemplateOutlet="template"></ng-container>
        </div>
        <mat-divider [ngClass]="{ hidden: collapsed() }" class="grow-0"></mat-divider>
      }

      <ul
        (mouseenter)="collapsable() && !pinned() && openSidenav()"
        class="grow self-stretch"
        [root]="true"
        rxap-navigation
      >
      </ul>

      @if (sidenavFooterDirective()?.template; as template) {
        <mat-divider [ngClass]="{ hidden: collapsed() }" class="grow-0"></mat-divider>
        <div [ngClass]="{ hidden: collapsed() }" class="footer grow-0">
          <ng-container *ngTemplateOutlet="template"></ng-container>
        </div>
      }
    </div>
  </mat-sidenav>
  <mat-sidenav-content [ngClass]="{ 'ml-16': collapsable() }" class="p-4">
    <ng-content></ng-content>
  </mat-sidenav-content>
</mat-sidenav-container>

./sidenav.component.scss

:host {
  .collapsable.mat-drawer {

    &:not(.mat-drawer-opened) {
      transform: translateX(calc(-100% + 64px)) !important;
      visibility: visible !important;
      box-shadow: inherit !important;
      transition-property: transform;
      transition-delay: 250ms;
      display: flex;
      border-right: solid 1px rgba(0, 0, 0, .12);

      ::ng-deep .mat-drawer-inner-container {
        display: block;
      }

    }

    ::ng-deep .mat-drawer-inner-container::-webkit-scrollbar {
      display: none; /* Chrome, Safari, Opera*/
    }

  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""