fuse-angular/src/app/layout/common/shortcuts/shortcuts.component.ts
2024-12-29 12:23:56 +03:00

280 lines
8.0 KiB
TypeScript

import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { NgClass, NgTemplateOutlet } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
TemplateRef,
ViewChild,
ViewContainerRef,
ViewEncapsulation,
} from '@angular/core';
import {
FormsModule,
ReactiveFormsModule,
UntypedFormBuilder,
UntypedFormGroup,
Validators,
} from '@angular/forms';
import { MatButton, MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterLink } from '@angular/router';
import { ShortcutsService } from 'app/layout/common/shortcuts/shortcuts.service';
import { Shortcut } from 'app/layout/common/shortcuts/shortcuts.types';
import { Subject, takeUntil } from 'rxjs';
@Component({
selector: 'shortcuts',
templateUrl: './shortcuts.component.html',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'shortcuts',
imports: [
MatButtonModule,
MatIconModule,
MatTooltipModule,
NgClass,
NgTemplateOutlet,
RouterLink,
FormsModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatSlideToggleModule,
],
})
export class ShortcutsComponent implements OnInit, OnDestroy {
@ViewChild('shortcutsOrigin') private _shortcutsOrigin: MatButton;
@ViewChild('shortcutsPanel') private _shortcutsPanel: TemplateRef<any>;
mode: 'view' | 'modify' | 'add' | 'edit' = 'view';
shortcutForm: UntypedFormGroup;
shortcuts: Shortcut[];
private _overlayRef: OverlayRef;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _formBuilder: UntypedFormBuilder,
private _shortcutsService: ShortcutsService,
private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef
) {}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* 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(null);
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 shortcuts 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(true)
.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();
});
}
}