feat: add animation for opening and closing OverlayComponent and optimize code

This commit is contained in:
Chneemann 2025-04-11 09:29:06 +02:00
parent a013402e23
commit 19f1a2de5a
3 changed files with 74 additions and 35 deletions

View file

@ -1,5 +1,8 @@
@if (overlayData) { <div
<div class="overlay"> class="overlay"
[class.show]="shouldShowOverlay"
[class.closing]="isClosingAnimation"
>
<div class="overlay-content"> <div class="overlay-content">
@if (overlayType === "taskOverlay") { @if (overlayType === "taskOverlay") {
<app-task-overlay <app-task-overlay
@ -18,7 +21,7 @@
[overlayType]="overlayType" [overlayType]="overlayType"
(closeDialogEmitter)="onCloseOverlay($event)" (closeDialogEmitter)="onCloseOverlay($event)"
></app-task-edit-overlay> ></app-task-edit-overlay>
} @if (overlayType === "dialog") { } @if (overlayType === "dialogOverlay") {
<app-dialog-overlay <app-dialog-overlay
[overlayData]="overlayData" [overlayData]="overlayData"
[overlayType]="overlayType" [overlayType]="overlayType"
@ -33,4 +36,3 @@
} }
</div> </div>
</div> </div>
}

View file

@ -1,4 +1,7 @@
.overlay { .overlay {
display: flex;
justify-content: center;
align-items: center;
position: fixed; position: fixed;
top: 0; top: 0;
bottom: 0; bottom: 0;
@ -7,20 +10,38 @@
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
backdrop-filter: blur(5px); backdrop-filter: blur(5px);
background-color: rgba($color: #000000, $alpha: 0.2); background-color: rgba(0, 0, 0, 0.2);
display: flex; pointer-events: none;
justify-content: center; transition: opacity 0.3s ease, backdrop-filter 0.3s ease;
align-items: center;
z-index: 999; z-index: 999;
opacity: 0;
&.show {
opacity: 1;
pointer-events: auto;
& > .overlay-content {
transform: scale(1) translateY(0);
opacity: 1;
transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55),
opacity 0.4s ease-out;
}
}
&.closing {
opacity: 0;
pointer-events: none;
& > .overlay-content {
transform: scale(0.95) translateY(20px);
opacity: 0;
transition: transform 0.4s ease-in, opacity 0.4s ease-out;
}
}
} }
.overlay-mobile { .overlay-content {
position: fixed; transform: scale(0.95) translateY(20px);
top: 0; opacity: 0;
bottom: 0; transition: transform 0.4s ease-out, opacity 0.4s ease-out;
left: 0;
right: 0;
width: 100vw;
height: 100vh;
z-index: 999;
} }

View file

@ -22,21 +22,31 @@ import { Subject, takeUntil } from 'rxjs';
styleUrl: './overlay.component.scss', styleUrl: './overlay.component.scss',
}) })
export class OverlayComponent implements OnInit, OnDestroy { export class OverlayComponent implements OnInit, OnDestroy {
overlayType: any; overlayType:
overlayData: any; | 'taskOverlay'
| 'taskOverlayEdit'
| 'newTaskOverlay'
| 'dialogOverlay'
| 'contactOverlay'
| null = null;
overlayData: any = null;
shouldShowOverlay = false;
isClosingAnimation = false;
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
constructor(private overlayService: OverlayService, private router: Router) {} constructor(private overlayService: OverlayService, private router: Router) {}
/** /**
* Angular lifecycle hook that is called after the component's view has been fully initialized. * Checks the overlay data observable from the OverlayService and subscribes to it.
* Invokes `checkOverlayData` to subscribe to overlay data changes and update the component's state accordingly.
*/ */
ngOnInit(): void { ngOnInit(): void {
this.checkOverlayData(); this.listenToOverlayChanges();
} }
/**
* Emits an event to stop listening to the overlay data observable and unsubscribes from it.
*/
ngOnDestroy(): void { ngOnDestroy(): void {
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();
@ -44,40 +54,46 @@ export class OverlayComponent implements OnInit, OnDestroy {
/** /**
* Subscribes to the overlay data observable from the OverlayService. * Subscribes to the overlay data observable from the OverlayService.
* Updates the component's `overlayType` and `overlayData` properties * Updates the overlay type and data when new data is received.
* whenever new overlay data is emitted. * Sets the overlay as visible with a slight delay if data is present.
* Marks the overlay as closing and invisible if no data is present.
*/ */
checkOverlayData() { listenToOverlayChanges() {
this.overlayService.overlayData$ this.overlayService.overlayData$
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe((data) => { .subscribe((data) => {
if (data) { if (data) {
this.overlayType = data.overlay; this.overlayType = data.overlay;
this.overlayData = data.data; this.overlayData = data.data;
this.isClosingAnimation = false;
requestAnimationFrame(() => {
this.shouldShowOverlay = true;
});
} else {
this.isClosingAnimation = true;
this.shouldShowOverlay = false;
} }
}); });
} }
/** /**
* Emits an event to close the overlay and set the overlay type/data to the provided string. * Closes the overlay by clearing the overlay data from the service.
* If the overlay type is 'dialog', navigates to the login route. * @param {boolean} close Indicates the close event.
* @param emitter The string to be emitted, which will also be set as the overlay data.
*/ */
onCloseOverlay(emitter: boolean) { onCloseOverlay(close: boolean) {
this.overlayData = emitter; this.overlayData = close;
this.overlayService.clearOverlayData(); this.overlayService.clearOverlayData();
} }
@HostListener('document:click', ['$event']) @HostListener('document:click', ['$event'])
/** /**
* Closes the contact edit overlay if the user clicks on the overlay background. * If the event target is the overlay itself, the overlay is closed.
* * @param {MouseEvent} event The event object passed from the document click event.
* @param event The MouseEvent triggered by a click action.
*/ */
checkOpenContactEdit(event: MouseEvent) { onDocumentClick(event: MouseEvent) {
const targetElement = event.target as HTMLElement; const targetElement = event.target as HTMLElement;
if (targetElement === document.querySelector('.overlay')) { if (targetElement && targetElement.classList.contains('overlay')) {
this.onCloseOverlay(false); this.onCloseOverlay(false);
} }
} }