import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { TemplatePortal } from '@angular/cdk/portal'; import { MatButton } from '@angular/material/button'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; import { Shortcut } from 'app/layout/common/shortcuts/shortcuts.types'; import { ShortcutsService } from 'app/layout/common/shortcuts/shortcuts.service'; @Component({ selector : 'shortcuts', templateUrl : './shortcuts.component.html', encapsulation : ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, exportAs : 'shortcuts' }) export class ShortcutsComponent implements OnChanges, OnInit, OnDestroy { @Input() shortcuts: Shortcut[]; @ViewChild('shortcutsOrigin') private _shortcutsOrigin: MatButton; @ViewChild('shortcutsPanel') private _shortcutsPanel: TemplateRef; mode: 'view' | 'modify' | 'add' | 'edit' = 'view'; shortcutForm: FormGroup; private _overlayRef: OverlayRef; private _unsubscribeAll: Subject = new Subject(); /** * Constructor */ constructor( private _changeDetectorRef: ChangeDetectorRef, private _formBuilder: FormBuilder, private _shortcutsService: ShortcutsService, private _overlay: Overlay, private _viewContainerRef: ViewContainerRef ) { } // ----------------------------------------------------------------------------------------------------- // @ Lifecycle hooks // ----------------------------------------------------------------------------------------------------- /** * On changes * * @param changes */ ngOnChanges(changes: SimpleChanges): void { // Shortcuts if ( 'shortcuts' in changes ) { // Store the shortcuts on the service this._shortcutsService.store(changes.shortcuts.currentValue); } } /** * On init */ ngOnInit(): void { // Initialize the form this.shortcutForm = this._formBuilder.group({ id : [null], label : ['', Validators.required], description: [''], icon : ['', Validators.required], link : ['', Validators.required], useRouter : ['', Validators.required] }); // Get the shortcuts this._shortcutsService.shortcuts$ .pipe(takeUntil(this._unsubscribeAll)) .subscribe((shortcuts: Shortcut[]) => { // Load the shortcuts this.shortcuts = shortcuts; // Mark for check this._changeDetectorRef.markForCheck(); }); } /** * On destroy */ ngOnDestroy(): void { // Unsubscribe from all subscriptions this._unsubscribeAll.next(); this._unsubscribeAll.complete(); // Dispose the overlay if ( this._overlayRef ) { this._overlayRef.dispose(); } } // ----------------------------------------------------------------------------------------------------- // @ Public methods // ----------------------------------------------------------------------------------------------------- /** * Open the shortcuts panel */ openPanel(): void { // Return if the shortcuts panel or its origin is not defined if ( !this._shortcutsPanel || !this._shortcutsOrigin ) { return; } // Make sure to start in 'view' mode this.mode = 'view'; // Create the overlay if it doesn't exist if ( !this._overlayRef ) { this._createOverlay(); } // Attach the portal to the overlay this._overlayRef.attach(new TemplatePortal(this._shortcutsPanel, this._viewContainerRef)); } /** * Close the messages panel */ closePanel(): void { this._overlayRef.detach(); } /** * Change the mode */ changeMode(mode: 'view' | 'modify' | 'add' | 'edit'): void { // Change the mode this.mode = mode; } /** * Prepare for a new shortcut */ newShortcut(): void { // Reset the form this.shortcutForm.reset(); // Enter the add mode this.mode = 'add'; } /** * Edit a shortcut */ editShortcut(shortcut: Shortcut): void { // Reset the form with the shortcut this.shortcutForm.reset(shortcut); // Enter the edit mode this.mode = 'edit'; } /** * Save shortcut */ save(): void { // Get the data from the form const shortcut = this.shortcutForm.value; // If there is an id, update it... if ( shortcut.id ) { this._shortcutsService.update(shortcut.id, shortcut).subscribe(); } // Otherwise, create a new shortcut... else { this._shortcutsService.create(shortcut).subscribe(); } // Go back the modify mode this.mode = 'modify'; } /** * Delete shortcut */ delete(): void { // Get the data from the form const shortcut = this.shortcutForm.value; // Delete this._shortcutsService.delete(shortcut.id).subscribe(); // Go back the modify mode this.mode = 'modify'; } /** * Track by function for ngFor loops * * @param index * @param item */ trackByFn(index: number, item: any): any { return item.id || index; } // ----------------------------------------------------------------------------------------------------- // @ Private methods // ----------------------------------------------------------------------------------------------------- /** * Create the overlay */ private _createOverlay(): void { // Create the overlay this._overlayRef = this._overlay.create({ hasBackdrop : true, backdropClass : 'fuse-backdrop-on-mobile', scrollStrategy : this._overlay.scrollStrategies.block(), positionStrategy: this._overlay.position() .flexibleConnectedTo(this._shortcutsOrigin._elementRef.nativeElement) .withLockedPosition() .withPush(true) .withPositions([ { originX : 'start', originY : 'bottom', overlayX: 'start', overlayY: 'top' }, { originX : 'start', originY : 'top', overlayX: 'start', overlayY: 'bottom' }, { originX : 'end', originY : 'bottom', overlayX: 'end', overlayY: 'top' }, { originX : 'end', originY : 'top', overlayX: 'end', overlayY: 'bottom' } ]) }); // Detach the overlay from the portal on backdrop click this._overlayRef.backdropClick().subscribe(() => { this._overlayRef.detach(); }); } }