import { ChangeDetectorRef , Component , ElementRef , EventEmitter , HostBinding , HostListener , Input , OnDestroy , OnInit , Output , Renderer2 , ViewEncapsulation } from '@angular/core' ; import { animate , AnimationBuilder , AnimationPlayer , style } from '@angular/animations' ; import { MediaObserver } from '@angular/flex-layout' ; import { Subject } from 'rxjs' ; import { takeUntil } from 'rxjs/operators' ; import { FuseSidebarService } from './sidebar.service' ; import { FuseMatchMediaService } from '@fuse/services/match-media.service' ; import { FuseConfigService } from '@fuse/services/config.service' ; @ Component ( { selector : 'fuse-sidebar' , templateUrl : './sidebar.component.html' , styleUrls : [ './sidebar.component.scss' ] , encapsulation : ViewEncapsulation . None } ) export class FuseSidebarComponent implements OnInit , OnDestroy { @ Input ( ) name : string ; @ Input ( ) key : string ; @ Input ( ) position : 'left' | 'right' ; @ HostBinding ( 'class.open' ) opened : boolean ; @ Input ( ) lockedOpen : string ; @ HostBinding ( 'class.locked-open' ) isLockedOpen : boolean ; @ Input ( ) foldedWidth : number ; @ Input ( ) foldedAutoTriggerOnHover : boolean ; @ HostBinding ( 'class.unfolded' ) unfolded : boolean ; @ Input ( ) invisibleOverlay : boolean ; @ Output ( ) foldedChanged : EventEmitter < boolean > ; @ Output ( ) openedChanged : EventEmitter < boolean > ; private _folded : boolean ; private _fuseConfig : any ; private _wasActive : boolean ; private _wasFolded : boolean ; private _backdrop : HTMLElement | null = null ; private _player : AnimationPlayer ; private _unsubscribeAll : Subject < any > ; @ HostBinding ( 'class.animations-enabled' ) private _animationsEnabled : boolean ; param { AnimationBuilder } _animationBuilder param { ChangeDetectorRef } _changeDetectorRef param { ElementRef } _elementRef param { FuseConfigService } _fuseConfigService param { FuseMatchMediaService } _fuseMatchMediaService param { FuseSidebarService } _fuseSidebarService param { ObservableMedia } _observableMedia param { Renderer2 } _renderer constructor ( private _animationBuilder , private _changeDetectorRef , private _elementRef , private _fuseConfigService , private _fuseMatchMediaService , private _fuseSidebarService , private _observableMedia , private _renderer ) { this . foldedAutoTriggerOnHover = true ; this . foldedWidth = 64 ; this . foldedChanged = new EventEmitter ( ) ; this . openedChanged = new EventEmitter ( ) ; this . opened = false ; this . position = 'left' ; this . invisibleOverlay = false ; this . _animationsEnabled = false ; this . _folded = false ; this . _unsubscribeAll = new Subject ( ) ; } param { boolean } value @ Input ( ) set folded ( value ) { this . _folded = value ; if ( ! this . opened ) { return ; } let sibling , styleRule ; const styleValue = this . foldedWidth + 'px' ; if ( this . position === 'left' ) { sibling = this . _elementRef . nativeElement . nextElementSibling ; styleRule = 'padding-left' ; } else { sibling = this . _elementRef . nativeElement . previousElementSibling ; styleRule = 'padding-right' ; } if ( ! sibling ) { return ; } if ( value ) { this . fold ( ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'width' , styleValue ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'min-width' , styleValue ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'max-width' , styleValue ) ; this . _renderer . setStyle ( sibling , styleRule , styleValue ) ; this . _renderer . addClass ( this . _elementRef . nativeElement , 'folded' ) ; } else { this . unfold ( ) ; this . _renderer . removeStyle ( this . _elementRef . nativeElement , 'width' ) ; this . _renderer . removeStyle ( this . _elementRef . nativeElement , 'min-width' ) ; this . _renderer . removeStyle ( this . _elementRef . nativeElement , 'max-width' ) ; this . _renderer . removeStyle ( sibling , styleRule ) ; this . _renderer . removeClass ( this . _elementRef . nativeElement , 'folded' ) ; } this . foldedChanged . emit ( this . folded ) ; } get folded ( ) : boolean { return this . _folded ; } /**
     * On init
     */ ngOnInit ( ) { this . _fuseConfigService . config . pipe ( takeUntil ( this . _unsubscribeAll ) ) . subscribe ( ( config ) => { this . _fuseConfig = config ; } ) ; this . _fuseSidebarService . register ( this . name , this ) ; this . _setupVisibility ( ) ; this . _setupPosition ( ) ; this . _setupLockedOpen ( ) ; this . _setupFolded ( ) ; } /**
     * On destroy
     */ ngOnDestroy ( ) { if ( this . folded ) { this . unfold ( ) ; } this . _fuseSidebarService . unregister ( this . name ) ; this . _unsubscribeAll . next ( ) ; this . _unsubscribeAll . complete ( ) ; } private private _setupVisibility ( ) { this . _renderer . setStyle ( this . _elementRef . nativeElement , 'box-shadow' , 'none' ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'visibility' , 'hidden' ) ; } private private _setupPosition ( ) { if ( this . position === 'right' ) { this . _renderer . addClass ( this . _elementRef . nativeElement , 'right-positioned' ) ; } else { this . _renderer . addClass ( this . _elementRef . nativeElement , 'left-positioned' ) ; } } private private _setupLockedOpen ( ) { if ( ! this . lockedOpen ) { return ; } this . _wasActive = false ; this . _wasFolded = this . folded ; this . _showSidebar ( ) ; this . _fuseMatchMediaService . onMediaChange . pipe ( takeUntil ( this . _unsubscribeAll ) ) . subscribe ( ( ) => { const isActive = this . _observableMedia . isActive ( this . lockedOpen ) ; if ( this . _wasActive === isActive ) { return ; } if ( isActive ) { this . isLockedOpen = true ; this . _showSidebar ( ) ; this . opened = true ; this . openedChanged . emit ( this . opened ) ; if ( this . _wasFolded ) { this . _enableAnimations ( ) ; this . folded = true ; this . _changeDetectorRef . markForCheck ( ) ; } this . _hideBackdrop ( ) ; } else { this . isLockedOpen = false ; this . unfold ( ) ; this . opened = false ; this . openedChanged . emit ( this . opened ) ; this . _hideSidebar ( ) ; } this . _wasActive = isActive ; } ) ; } private private _setupFolded ( ) { if ( ! this . folded ) { return ; } if ( ! this . opened ) { return ; } let sibling , styleRule ; const styleValue = this . foldedWidth + 'px' ; if ( this . position === 'left' ) { sibling = this . _elementRef . nativeElement . nextElementSibling ; styleRule = 'padding-left' ; } else { sibling = this . _elementRef . nativeElement . previousElementSibling ; styleRule = 'padding-right' ; } if ( ! sibling ) { return ; } this . fold ( ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'width' , styleValue ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'min-width' , styleValue ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'max-width' , styleValue ) ; this . _renderer . setStyle ( sibling , styleRule , styleValue ) ; this . _renderer . addClass ( this . _elementRef . nativeElement , 'folded' ) ; } private private _showBackdrop ( ) { this . _backdrop = this . _renderer . createElement ( 'div' ) ; this . _backdrop . classList . add ( 'fuse-sidebar-overlay' ) ; if ( this . invisibleOverlay ) { this . _backdrop . classList . add ( 'fuse-sidebar-overlay-invisible' ) ; } this . _renderer . appendChild ( this . _elementRef . nativeElement . parentElement , this . _backdrop ) ; this . _player = this . _animationBuilder . build ( [ animate ( '300ms ease' , style ( { opacity : 1 } ) ) ] ) . create ( this . _backdrop ) ; this . _player . play ( ) ; this . _backdrop . addEventListener ( 'click' , ( ) => { this . close ( ) ; } ) ; this . _changeDetectorRef . markForCheck ( ) ; } private private _hideBackdrop ( ) { if ( ! this . _backdrop ) { return ; } this . _player = this . _animationBuilder . build ( [ animate ( '300ms ease' , style ( { opacity : 0 } ) ) ] ) . create ( this . _backdrop ) ; this . _player . play ( ) ; this . _player . onDone ( ( ) => { if ( this . _backdrop ) { this . _backdrop . parentNode . removeChild ( this . _backdrop ) ; this . _backdrop = null ; } } ) ; this . _changeDetectorRef . markForCheck ( ) ; } private private _showSidebar ( ) { this . _renderer . removeStyle ( this . _elementRef . nativeElement , 'box-shadow' ) ; this . _renderer . removeStyle ( this . _elementRef . nativeElement , 'visibility' ) ; this . _changeDetectorRef . markForCheck ( ) ; } private private _hideSidebar ( delay = true ) { const delayAmount = delay ? 300 : 0 ; setTimeout ( ( ) => { this . _renderer . setStyle ( this . _elementRef . nativeElement , 'box-shadow' , 'none' ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'visibility' , 'hidden' ) ; } , delayAmount ) ; this . _changeDetectorRef . markForCheck ( ) ; } private private _enableAnimations ( ) { if ( this . _animationsEnabled ) { return ; } this . _animationsEnabled = true ; this . _changeDetectorRef . markForCheck ( ) ; } /**
     * Open the sidebar
     */ open ( ) { if ( this . opened || this . isLockedOpen ) { return ; } this . _enableAnimations ( ) ; this . _showSidebar ( ) ; this . _showBackdrop ( ) ; this . opened = true ; this . openedChanged . emit ( this . opened ) ; this . _changeDetectorRef . markForCheck ( ) ; } /**
     * Close the sidebar
     */ close ( ) { if ( ! this . opened || this . isLockedOpen ) { return ; } this . _enableAnimations ( ) ; this . _hideBackdrop ( ) ; this . opened = false ; this . openedChanged . emit ( this . opened ) ; this . _hideSidebar ( ) ; this . _changeDetectorRef . markForCheck ( ) ; } /**
     * Toggle open/close the sidebar
     */ toggleOpen ( ) { if ( this . opened ) { this . close ( ) ; } else { this . open ( ) ; } } /**
     * Mouseenter
     */ @ HostListener ( 'mouseenter' ) onMouseEnter ( ) { if ( ! this . foldedAutoTriggerOnHover ) { return ; } this . unfoldTemporarily ( ) ; } /**
     * Mouseleave
     */ @ HostListener ( 'mouseleave' ) onMouseLeave ( ) { if ( ! this . foldedAutoTriggerOnHover ) { return ; } this . foldTemporarily ( ) ; } /**
     * Fold the sidebar permanently
     */ fold ( ) { if ( this . folded ) { return ; } this . _enableAnimations ( ) ; this . folded = true ; this . _changeDetectorRef . markForCheck ( ) ; } /**
     * Unfold the sidebar permanently
     */ unfold ( ) { if ( ! this . folded ) { return ; } this . _enableAnimations ( ) ; this . folded = false ; this . _changeDetectorRef . markForCheck ( ) ; } /**
     * Toggle the sidebar fold/unfold permanently
     */ toggleFold ( ) { if ( this . folded ) { this . unfold ( ) ; } else { this . fold ( ) ; } } /**
     * Fold the temporarily unfolded sidebar back
     */ foldTemporarily ( ) { if ( ! this . folded ) { return ; } this . _enableAnimations ( ) ; this . unfolded = false ; const styleValue = this . foldedWidth + 'px' ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'width' , styleValue ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'min-width' , styleValue ) ; this . _renderer . setStyle ( this . _elementRef . nativeElement , 'max-width' , styleValue ) ; this . _changeDetectorRef . markForCheck ( ) ; } /**
     * Unfold the sidebar temporarily
     */ unfoldTemporarily ( ) { if ( ! this . folded ) { return ; } this . _enableAnimations ( ) ; this . unfolded = true ; this . _renderer . removeStyle ( this . _elementRef . nativeElement , 'width' ) ; this . _renderer . removeStyle ( this . _elementRef . nativeElement , 'min-width' ) ; this . _renderer . removeStyle ( this . _elementRef . nativeElement , 'max-width' ) ; this . _changeDetectorRef . markForCheck ( ) ; } }