From 19f1a2de5a491b7c4a2212a554db53586d507e02 Mon Sep 17 00:00:00 2001 From: Chneemann Date: Fri, 11 Apr 2025 09:29:06 +0200 Subject: [PATCH] feat: add animation for opening and closing OverlayComponent and optimize code --- .../components/overlay/overlay.component.html | 10 ++-- .../components/overlay/overlay.component.scss | 47 ++++++++++++----- .../components/overlay/overlay.component.ts | 52 ++++++++++++------- 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/src/app/shared/components/overlay/overlay.component.html b/src/app/shared/components/overlay/overlay.component.html index d10cabd..df63794 100644 --- a/src/app/shared/components/overlay/overlay.component.html +++ b/src/app/shared/components/overlay/overlay.component.html @@ -1,5 +1,8 @@ -@if (overlayData) { -
+
@if (overlayType === "taskOverlay") { - } @if (overlayType === "dialog") { + } @if (overlayType === "dialogOverlay") {
-} diff --git a/src/app/shared/components/overlay/overlay.component.scss b/src/app/shared/components/overlay/overlay.component.scss index 7306b35..8867cdd 100644 --- a/src/app/shared/components/overlay/overlay.component.scss +++ b/src/app/shared/components/overlay/overlay.component.scss @@ -1,4 +1,7 @@ .overlay { + display: flex; + justify-content: center; + align-items: center; position: fixed; top: 0; bottom: 0; @@ -7,20 +10,38 @@ width: 100vw; height: 100vh; backdrop-filter: blur(5px); - background-color: rgba($color: #000000, $alpha: 0.2); - display: flex; - justify-content: center; - align-items: center; + background-color: rgba(0, 0, 0, 0.2); + pointer-events: none; + transition: opacity 0.3s ease, backdrop-filter 0.3s ease; 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 { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - width: 100vw; - height: 100vh; - z-index: 999; +.overlay-content { + transform: scale(0.95) translateY(20px); + opacity: 0; + transition: transform 0.4s ease-out, opacity 0.4s ease-out; } diff --git a/src/app/shared/components/overlay/overlay.component.ts b/src/app/shared/components/overlay/overlay.component.ts index 645cedb..8a26529 100644 --- a/src/app/shared/components/overlay/overlay.component.ts +++ b/src/app/shared/components/overlay/overlay.component.ts @@ -22,21 +22,31 @@ import { Subject, takeUntil } from 'rxjs'; styleUrl: './overlay.component.scss', }) export class OverlayComponent implements OnInit, OnDestroy { - overlayType: any; - overlayData: any; + overlayType: + | 'taskOverlay' + | 'taskOverlayEdit' + | 'newTaskOverlay' + | 'dialogOverlay' + | 'contactOverlay' + | null = null; + overlayData: any = null; + shouldShowOverlay = false; + isClosingAnimation = false; private destroy$ = new Subject(); constructor(private overlayService: OverlayService, private router: Router) {} /** - * Angular lifecycle hook that is called after the component's view has been fully initialized. - * Invokes `checkOverlayData` to subscribe to overlay data changes and update the component's state accordingly. + * Checks the overlay data observable from the OverlayService and subscribes to it. */ ngOnInit(): void { - this.checkOverlayData(); + this.listenToOverlayChanges(); } + /** + * Emits an event to stop listening to the overlay data observable and unsubscribes from it. + */ ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); @@ -44,40 +54,46 @@ export class OverlayComponent implements OnInit, OnDestroy { /** * Subscribes to the overlay data observable from the OverlayService. - * Updates the component's `overlayType` and `overlayData` properties - * whenever new overlay data is emitted. + * Updates the overlay type and data when new data is received. + * 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$ .pipe(takeUntil(this.destroy$)) .subscribe((data) => { if (data) { this.overlayType = data.overlay; 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. - * If the overlay type is 'dialog', navigates to the login route. - * @param emitter The string to be emitted, which will also be set as the overlay data. + * Closes the overlay by clearing the overlay data from the service. + * @param {boolean} close Indicates the close event. */ - onCloseOverlay(emitter: boolean) { - this.overlayData = emitter; + onCloseOverlay(close: boolean) { + this.overlayData = close; this.overlayService.clearOverlayData(); } @HostListener('document:click', ['$event']) /** - * Closes the contact edit overlay if the user clicks on the overlay background. - * - * @param event The MouseEvent triggered by a click action. + * If the event target is the overlay itself, the overlay is closed. + * @param {MouseEvent} event The event object passed from the document click event. */ - checkOpenContactEdit(event: MouseEvent) { + onDocumentClick(event: MouseEvent) { const targetElement = event.target as HTMLElement; - if (targetElement === document.querySelector('.overlay')) { + if (targetElement && targetElement.classList.contains('overlay')) { this.onCloseOverlay(false); } }