src/modules/app-sidebar/components/app-sidebar.component.ts
Component to display the application's sidebar Only 2 sidebars are allowed: https://github.com/angular/material2/issues/1514
OnDestroy
OnInit
encapsulation | ViewEncapsulation.None |
host | { |
selector | stark-app-sidebar |
templateUrl | ./app-sidebar.component.html |
Properties |
|
Methods |
|
Inputs |
Public
constructor(logger: StarkLoggingService, sidebarService: StarkAppSidebarService, routingService: StarkRoutingService, breakpointObserver: BreakpointObserver, renderer: Renderer2, elementRef: ElementRef)
|
||||||||||||||||||||||||||||
Class constructor
Parameters :
|
closeOnNavigate | |
Type : boolean
|
|
Default value : true
|
|
When on smaller devices ( Default: |
color | |
Type : string
|
|
Inherited from
AbstractStarkUiComponent
|
|
Defined in
AbstractStarkUiComponent:16
|
|
Color theme |
Public closeSidenav | ||||||||||||
closeSidenav(sidenav: MatSidenav, successHandler: (value: MatDrawerToggleResult) => void)
|
||||||||||||
Close one of the sidenavs
Parameters :
Returns :
void
|
Public ngOnDestroy |
ngOnDestroy()
|
Component lifecycle OnDestroy hook
Returns :
void
|
Public ngOnInit |
ngOnInit()
|
Inherited from
AbstractStarkUiComponent
|
Defined in
AbstractStarkUiComponent:137
|
Component lifecycle OnInit hook
Returns :
void
|
Public onCloseSidenavs |
onCloseSidenavs()
|
Close sidenav handler
Returns :
void
|
Public onObserveBreakpoints | ||||||||
onObserveBreakpoints(state: BreakpointState)
|
||||||||
Breakpoints change handler
Parameters :
Returns :
void
|
Public onOpenSidenav | ||||||||
onOpenSidenav(event: StarkAppSidebarOpenEvent)
|
||||||||
Open sidenav handler
Parameters :
Returns :
void
|
Public onSuccessfulTransition |
onSuccessfulTransition()
|
Navigation handler
Returns :
void
|
Public onToggleSidenav | ||||||||
onToggleSidenav(event: StarkAppSidebarOpenEvent)
|
||||||||
Toggle sidenav handler
Parameters :
Returns :
void
|
Public openSidenav | |||||||||||||||
openSidenav(sidenav: MatSidenav, successHandler: (value: MatDrawerToggleResult) => void)
|
|||||||||||||||
Open one of the sidenavs
Parameters :
Returns :
void
|
Public setClassOnCloseStart |
setClassOnCloseStart()
|
Set the corresponding class when the sidenav starts to close
Returns :
void
|
Public setClassOnOpenStart |
setClassOnOpenStart()
|
Set the corresponding class when the sidenav starts to open
Returns :
void
|
Public setComponentBehaviour |
setComponentBehaviour()
|
Set the component with the right settings before opening
Returns :
void
|
Public toggleClassesOnOpen | ||||||||
toggleClassesOnOpen(isOpen: boolean)
|
||||||||
Toggle the different classes when the sidenav is open or closed
Parameters :
Returns :
void
|
Public appSidenavContainer |
Type : MatSidenavContainer
|
Decorators :
@ViewChild('appSidenavContainer', {static: false})
|
Reference to the MatSidenavContainer embedded in this component |
Public appSidenavLeft |
Type : MatSidenav
|
Decorators :
@ViewChild('appSidenavLeft', {static: true})
|
Reference to the left MatSidenav embedded in this component |
Public appSidenavRight |
Type : MatSidenav
|
Decorators :
@ViewChild('appSidenavRight', {static: true})
|
Reference to the right MatSidenav embedded in this component |
Public breakpointObserver |
Type : BreakpointObserver
|
- Utility for checking the matching state of
|
Public closeSidebarSubscription |
Type : Subscription
|
Subscription to the close sidebar Observable |
Public deregisterTransitionHook |
Type : Function
|
Function to deregister the routing transition hook |
Private displayErrorCallback | ||||
Type : function
|
||||
Default value : () => {...}
|
||||
Callback function to be called when the sidenav failed to open |
||||
Parameters :
|
Private displaySuccessCallback | ||||
Type : function
|
||||
Default value : () => {...}
|
||||
Callback function to be called when the sidenav opens |
||||
Parameters :
|
Public isiOSDevice |
Default value : false
|
Either the browser is running on iOS or not |
Public logger |
Type : StarkLoggingService
|
Decorators :
@Inject(STARK_LOGGING_SERVICE)
|
- The `StarkLoggingService` instance of the application.
|
Public mediaQueryGtLg |
Type : string
|
Default value : "(min-width: 1280px)"
|
Media query for big screens |
Public openSidebarSubscription |
Type : Subscription
|
Subscription to the open sidebar Observable |
Public routingService |
Type : StarkRoutingService
|
Decorators :
@Inject(STARK_ROUTING_SERVICE)
|
- The `StarkRoutingService` instance of the application.
|
Public shiftLeftSidenavCallback | ||||
Type : function
|
||||
Default value : () => {...}
|
||||
Callback function when the left sidenav needs to shift type |
||||
Parameters :
|
Public sidebarService |
Type : StarkAppSidebarService
|
Decorators :
@Inject(STARK_APP_SIDEBAR_SERVICE)
|
- The `StarkAppSidebarService` instance of the application
|
Public sidenavLeftMode |
Type : MatDrawerMode
|
Default value : "over"
|
Dynamic mode for the left sidebar |
Public sidenavLeftOpened |
Default value : false
|
Either the left sidebar is opened or not |
Public Optional sidenavLeftType |
Type : "menu" | "regular"
|
Default value : "menu"
|
Dynamic mode for the menu, should always show on large desktop screen |
Public toggleSidebarSubscription |
Type : Subscription
|
Subscription to the close sidebar Observable |
import { Component, ElementRef, Inject, Input, OnDestroy, OnInit, Renderer2, ViewChild, ViewEncapsulation } from "@angular/core";
import { BreakpointObserver, BreakpointState } from "@angular/cdk/layout";
import { MatDrawerMode, MatDrawerToggleResult, MatSidenav, MatSidenavContainer } from "@angular/material/sidenav";
import { from, Subscription } from "rxjs";
import {
STARK_LOGGING_SERVICE,
STARK_ROUTING_SERVICE,
StarkLoggingService,
StarkRoutingService,
StarkRoutingTransitionHook
} from "@nationalbankbelgium/stark-core";
import { STARK_APP_SIDEBAR_SERVICE, StarkAppSidebarOpenEvent, StarkAppSidebarService } from "../services";
import { AbstractStarkUiComponent } from "@nationalbankbelgium/stark-ui/src/internal-common";
export type StarkAppSidebarLeftMode = "regular" | "menu" | undefined;
/**
* @ignore
*/
const componentName = "stark-app-sidebar";
/**
* Component to display the application's sidebar
* Only 2 sidebars are allowed: https://github.com/angular/material2/issues/1514
*/
@Component({
selector: "stark-app-sidebar",
templateUrl: "./app-sidebar.component.html",
encapsulation: ViewEncapsulation.None,
host: {
class: componentName
}
})
export class StarkAppSidebarComponent extends AbstractStarkUiComponent implements OnDestroy, OnInit {
/**
* When on smaller devices (`width < 1280px`) the sidebar is automatically closed after navigating.
* Can be set to `false` to prevent this behaviour.
*
* Default: `true`
*/
@Input()
public closeOnNavigate = true;
/**
* Reference to the MatSidenavContainer embedded in this component
*/
@ViewChild("appSidenavContainer", { static: false })
public appSidenavContainer!: MatSidenavContainer;
/**
* Reference to the left MatSidenav embedded in this component
*/
@ViewChild("appSidenavLeft", { static: true })
public appSidenavLeft!: MatSidenav;
/**
* Reference to the right MatSidenav embedded in this component
*/
@ViewChild("appSidenavRight", { static: true })
public appSidenavRight!: MatSidenav;
/**
* Subscription to the close sidebar Observable
*/
public closeSidebarSubscription!: Subscription;
/**
* Boolean that indicates if the left sidebar menu is currently shifting from larger to smaller desktop
* In this case, it should wait that the transition is finished before applying the smaller screen styles
*/
public isShiftingToSmaller = false;
// TODO: move this media query to global variable that can be used through stark-ui
/**
* Media query for big screens
*/
public mediaQueryGtLg = "(min-width: 1280px)";
/**
* Subscription to the open sidebar Observable
*/
public openSidebarSubscription!: Subscription;
/**
* Dynamic mode for the menu, should always show on large desktop screen
*/
public sidenavLeftType?: "menu" | "regular" = "menu";
/**
* Dynamic mode for the left sidebar
*/
public sidenavLeftMode: MatDrawerMode = "over";
/**
* Either the left sidebar is opened or not
*/
public sidenavLeftOpened = false;
/**
* Subscription to the close sidebar Observable
*/
public toggleSidebarSubscription!: Subscription;
/**
* Either the browser is running on iOS or not
*/
public isiOSDevice = false;
/**
* Function to deregister the routing transition hook
*/
public deregisterTransitionHook!: Function;
/**
* Class constructor
* @param logger - The `StarkLoggingService` instance of the application.
* @param sidebarService - The `StarkAppSidebarService` instance of the application
* @param routingService - The `StarkRoutingService` instance of the application.
* @param breakpointObserver - Utility for checking the matching state of @media queries
* @param renderer - Angular `Renderer2` wrapper for DOM manipulations.
* @param elementRef - Reference to the DOM element where this component is attached to.
*/
public constructor(
@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService,
@Inject(STARK_APP_SIDEBAR_SERVICE) public sidebarService: StarkAppSidebarService,
@Inject(STARK_ROUTING_SERVICE) public routingService: StarkRoutingService,
public breakpointObserver: BreakpointObserver,
renderer: Renderer2,
elementRef: ElementRef
) {
super(renderer, elementRef);
}
/**
* Component lifecycle OnInit hook
*/
public override ngOnInit(): void {
super.ngOnInit();
this.logger.debug(componentName + ": component initialized");
this.sidenavLeftOpened = this.breakpointObserver.isMatched([this.mediaQueryGtLg]);
this.openSidebarSubscription = this.sidebarService.openSidebar$.subscribe((event: StarkAppSidebarOpenEvent) => {
this.onOpenSidenav(event);
});
this.closeSidebarSubscription = this.sidebarService.closeSidebar$.subscribe(() => {
this.onCloseSidenavs();
});
this.toggleSidebarSubscription = this.sidebarService.toggleSidebar$.subscribe((event: StarkAppSidebarOpenEvent) => {
this.onToggleSidenav(event);
});
this.breakpointObserver.observe([this.mediaQueryGtLg]).subscribe((state: BreakpointState) => {
this.onObserveBreakpoints(state);
});
this.deregisterTransitionHook = this.routingService.addTransitionHook(StarkRoutingTransitionHook.ON_SUCCESS, {}, () => {
this.onSuccessfulTransition();
});
// Detection method documented here: https://stackoverflow.com/questions/9038625/detect-if-device-is-ios#answer-9039885
this.isiOSDevice = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
}
/**
* Component lifecycle OnDestroy hook
*/
public ngOnDestroy(): void {
this.openSidebarSubscription.unsubscribe();
this.closeSidebarSubscription.unsubscribe();
this.toggleSidebarSubscription.unsubscribe();
/* eslint-disable-next-line @angular-eslint/no-lifecycle-call */
this.breakpointObserver.ngOnDestroy();
this.deregisterTransitionHook();
}
/**
* Close one of the sidenavs
* @param sidenav - The sidebar to close
* @param successHandler - Handler function to be called when the sidenav closes
*/
public closeSidenav(sidenav: MatSidenav, successHandler: (value: MatDrawerToggleResult) => void): void {
from(sidenav.close()).subscribe(successHandler, this.displayErrorCallback);
}
/**
* Callback function to be called when the sidenav opens
* @param result - MatDrawerToggleResult
*/
private displaySuccessCallback: (value: MatDrawerToggleResult) => void = (result: MatDrawerToggleResult) => {
this.logger.debug(componentName + ": " + result);
};
/**
* Callback function to be called when the sidenav failed to open
* @param error - Error
*/
private displayErrorCallback: (error: Error) => void = (error: Error) => {
this.logger.warn(componentName + ": ", error);
};
/**
* Close sidenav handler
*/
public onCloseSidenavs(): void {
this.appSidenavContainer.close();
}
/**
* Open sidenav handler
* @param event - The `StarkAppSidebarOpenEvent` object
*/
public onOpenSidenav(event: StarkAppSidebarOpenEvent): void {
switch (event.sidebar) {
case "left":
if (this.sidenavLeftType !== event.type && this.appSidenavLeft.opened) {
this.closeSidenav(this.appSidenavLeft, this.shiftLeftSidenavCallback);
} else if (!this.appSidenavLeft.opened) {
this.sidenavLeftType = event.type;
this.setComponentBehaviour();
this.openSidenav(this.appSidenavLeft);
}
break;
case "right":
if (!this.appSidenavRight.opened) {
this.openSidenav(this.appSidenavRight);
}
break;
default:
break;
}
}
/**
* Breakpoints change handler
* @param state - The current state of a layout breakpoint.
*/
public onObserveBreakpoints(state: BreakpointState): void {
// Enter large desktop screen
if (state.matches) {
if (this.sidenavLeftType === "menu") {
this.sidenavLeftMode = "side";
if (!this.appSidenavLeft.opened) {
this.openSidenav(this.appSidenavLeft);
}
}
}
// Enter smaller screens
else {
if (this.sidenavLeftType === "menu") {
if (this.appSidenavLeft.opened) {
this.isShiftingToSmaller = true;
this.closeSidenav(this.appSidenavLeft, (result: MatDrawerToggleResult) => {
this.logger.debug(componentName + ": sidenav " + result);
this.isShiftingToSmaller = false;
});
} else {
this.sidenavLeftMode = "over";
}
}
}
}
/**
* Navigation handler
*/
public onSuccessfulTransition(): void {
if (this.closeOnNavigate && !this.breakpointObserver.isMatched(this.mediaQueryGtLg)) {
this.sidebarService.close();
}
}
/**
* Toggle sidenav handler
* @param event - The `StarkAppSidebarOpenEvent` object
*/
public onToggleSidenav(event: StarkAppSidebarOpenEvent): void {
switch (event.sidebar) {
case "left":
this.sidenavLeftType = event.type;
if (this.appSidenavLeft.opened) {
this.closeSidenav(this.appSidenavLeft, this.displaySuccessCallback);
} else {
this.setComponentBehaviour();
this.openSidenav(this.appSidenavLeft);
}
break;
case "right":
from(this.appSidenavRight.toggle()).subscribe(this.displaySuccessCallback, this.displayErrorCallback);
break;
default:
break;
}
}
/**
* Open one of the sidenavs
* @param sidenav - The sidebar to open
* @param successHandler - Handler function to be called when the sidenav closes
*/
public openSidenav(sidenav: MatSidenav, successHandler: (value: MatDrawerToggleResult) => void = this.displaySuccessCallback): void {
from(sidenav.open()).subscribe(successHandler, this.displayErrorCallback);
}
/**
* Toggle the different classes when the sidenav is open or closed
* @param isOpen - Whether the sidebar is open
*/
public toggleClassesOnOpen(isOpen: boolean): void {
if (isOpen) {
this.renderer.addClass(this.elementRef.nativeElement, "sidebar-open");
this.renderer.removeClass(this.elementRef.nativeElement, "sidebar-open-start");
this.renderer.removeClass(this.elementRef.nativeElement, "sidebar-close");
} else {
this.renderer.addClass(this.elementRef.nativeElement, "sidebar-close");
this.renderer.removeClass(this.elementRef.nativeElement, "sidebar-close-start");
this.renderer.removeClass(this.elementRef.nativeElement, "sidebar-open");
}
}
/**
* Set the corresponding class when the sidenav starts to open
*/
public setClassOnOpenStart(): void {
this.renderer.addClass(this.elementRef.nativeElement, "sidebar-open-start");
}
/**
* Set the corresponding class when the sidenav starts to close
*/
public setClassOnCloseStart(): void {
this.renderer.addClass(this.elementRef.nativeElement, "sidebar-close-start");
}
/**
* Set the component with the right settings before opening
*/
public setComponentBehaviour(): void {
if (this.sidenavLeftType === "regular") {
this.sidenavLeftMode = "over";
} else {
if (this.breakpointObserver.isMatched([this.mediaQueryGtLg])) {
this.sidenavLeftMode = "side";
} else {
this.sidenavLeftMode = "over";
}
}
}
/**
* Callback function when the left sidenav needs to shift type
* @param result - MatDrawerToggleResult
*/
public shiftLeftSidenavCallback: (result: MatDrawerToggleResult) => void = (result: MatDrawerToggleResult): void => {
this.logger.debug(componentName + ": sidenav " + result);
this.sidenavLeftType = this.sidenavLeftType === "menu" ? "regular" : "menu";
this.setComponentBehaviour();
this.openSidenav(this.appSidenavLeft);
};
}
<mat-sidenav-container #appSidenavContainer>
<mat-sidenav
#appSidenavLeft
class="stark-app-sidenav-left"
[ngClass]="{ 'stark-app-sidenav-menu': sidenavLeftType === 'menu', 'stark-app-sidenav-menu-shifting': isShiftingToSmaller }"
[mode]="sidenavLeftMode"
[fixedInViewport]="true"
[fixedBottomGap]="0"
[opened]="sidenavLeftOpened"
(openedChange)="toggleClassesOnOpen($event)"
(openedStart)="setClassOnOpenStart()"
(closedStart)="setClassOnCloseStart()"
[autoFocus]="false"
>
<ng-container *ngIf="sidenavLeftType === 'regular'">
<ng-content class="regular" select="[stark-app-sidenav-left]"></ng-content>
</ng-container>
<ng-container class="menu" *ngIf="sidenavLeftType === 'menu'">
<ng-content select="[stark-app-sidenav-menu]"></ng-content>
</ng-container>
</mat-sidenav>
<mat-sidenav #appSidenavRight class="stark-app-sidenav-right" mode="over" position="end" [fixedInViewport]="true" [fixedBottomGap]="0">
<ng-content select="[stark-app-sidenav-right]"></ng-content>
</mat-sidenav>
<mat-sidenav-content [class.stark-ios-device]="isiOSDevice">
<ng-content select="[stark-app-sidenav-content]"></ng-content>
</mat-sidenav-content>
</mat-sidenav-container>