diff --git a/packages/components/core/pop-up/pop-up-trigger.ts b/packages/components/core/pop-up/pop-up-trigger.ts index 1a0eccf05..ef2f37949 100644 --- a/packages/components/core/pop-up/pop-up-trigger.ts +++ b/packages/components/core/pop-up/pop-up-trigger.ts @@ -1,6 +1,7 @@ import { Directionality } from '@angular/cdk/bidi'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { + CdkScrollable, ConnectedOverlayPositionChange, ConnectionPositionPair, FlexibleConnectedPositionStrategy, @@ -102,6 +103,7 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { protected readonly hostView: ViewContainerRef = inject(ViewContainerRef); protected readonly direction = inject(Directionality, { optional: true }); protected readonly destroyRef = inject(DestroyRef); + protected readonly scrollable = inject(CdkScrollable, { optional: true }); protected abstract scrollStrategy: () => ScrollStrategy; diff --git a/packages/components/popover/popover.component.ts b/packages/components/popover/popover.component.ts index 37739795b..20d869f04 100644 --- a/packages/components/popover/popover.component.ts +++ b/packages/components/popover/popover.component.ts @@ -18,6 +18,7 @@ import { EventEmitter, InjectionToken, Input, + OnInit, Output, TemplateRef, Type, @@ -160,7 +161,7 @@ export function getKbqPopoverInvalidPositionError(position: string) { '(touchend)': 'handleTouchend()' } }) -export class KbqPopoverTrigger extends KbqPopUpTrigger implements AfterContentInit { +export class KbqPopoverTrigger extends KbqPopUpTrigger implements AfterContentInit, OnInit { protected scrollStrategy: () => ScrollStrategy = inject(KBQ_POPOVER_SCROLL_STRATEGY); @Input('kbqPopoverVisible') @@ -347,6 +348,15 @@ export class KbqPopoverTrigger extends KbqPopUpTrigger impl }; } + ngOnInit(): void { + super.ngOnInit(); + + this.scrollable + ?.elementScrolled() + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(this.hideIfNotInViewPort); + } + ngAfterContentInit(): void { if (this.closeOnScroll === null) { this.scrollDispatcher.scrolled().subscribe((scrollable: CdkScrollable | void) => { @@ -425,4 +435,22 @@ export class KbqPopoverTrigger extends KbqPopUpTrigger impl closingActions() { return merge(...this.closingActionsForClick(), this.closeOnScroll ? this.scrollDispatcher.scrolled() : NEVER); } + + private hideIfNotInViewPort = () => { + if (!this.scrollable) return; + + const rect = this.elementRef.nativeElement.getBoundingClientRect(); + const containerRect = this.scrollable.getElementRef().nativeElement.getBoundingClientRect(); + + if ( + !( + rect.bottom >= containerRect.top && + rect.right >= containerRect.left && + rect.top <= containerRect.bottom && + rect.left <= containerRect.right + ) + ) { + this.hide(); + } + }; } diff --git a/tools/public_api_guard/components/core.api.md b/tools/public_api_guard/components/core.api.md index 30fd2d69b..5f3f6b55a 100644 --- a/tools/public_api_guard/components/core.api.md +++ b/tools/public_api_guard/components/core.api.md @@ -13,6 +13,7 @@ import { AnimationTriggerMetadata } from '@angular/animations'; import { AsyncScheduler } from 'rxjs/internal/scheduler/AsyncScheduler'; import { BehaviorSubject } from 'rxjs'; import { CdkConnectedOverlay } from '@angular/cdk/overlay'; +import { CdkScrollable } from '@angular/cdk/overlay'; import { ChangeDetectorRef } from '@angular/core'; import { ComponentPortal } from '@angular/cdk/portal'; import { ConnectedOverlayPositionChange } from '@angular/cdk/overlay'; @@ -2333,6 +2334,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { // (undocumented) protected readonly scheduler: AsyncScheduler | undefined; // (undocumented) + protected readonly scrollable: CdkScrollable | null; + // (undocumented) protected readonly scrollDispatcher: ScrollDispatcher; // (undocumented) protected abstract scrollStrategy: () => ScrollStrategy; diff --git a/tools/public_api_guard/components/popover.api.md b/tools/public_api_guard/components/popover.api.md index 81de63353..e8a4a1c99 100644 --- a/tools/public_api_guard/components/popover.api.md +++ b/tools/public_api_guard/components/popover.api.md @@ -23,6 +23,7 @@ import { KbqComponentColors } from '@koobiq/components/core'; import { KbqPopUp } from '@koobiq/components/core'; import { KbqPopUpTrigger } from '@koobiq/components/core'; import { Observable } from 'rxjs'; +import { OnInit } from '@angular/core'; import { Overlay } from '@angular/cdk/overlay'; import { OverlayConfig } from '@angular/cdk/overlay'; import { PopUpPlacements } from '@koobiq/components/core'; @@ -154,7 +155,7 @@ export class KbqPopoverModule { export function kbqPopoverScrollStrategyFactory(overlay: Overlay): () => ScrollStrategy; // @public (undocumented) -export class KbqPopoverTrigger extends KbqPopUpTrigger implements AfterContentInit { +export class KbqPopoverTrigger extends KbqPopUpTrigger implements AfterContentInit, OnInit { // (undocumented) arrow: boolean; // (undocumented) @@ -162,7 +163,7 @@ export class KbqPopoverTrigger extends KbqPopUpTrigger impl get closeOnScroll(): boolean | null; set closeOnScroll(value: boolean); // (undocumented) - closingActions(): Observable; + closingActions(): Observable; // (undocumented) closingActionsForClick(): Observable[]; // (undocumented) @@ -199,6 +200,8 @@ export class KbqPopoverTrigger extends KbqPopUpTrigger impl // (undocumented) ngAfterContentInit(): void; // (undocumented) + ngOnInit(): void; + // (undocumented) offset: number | null; // (undocumented) protected originSelector: string;