>(ElementRef);
readonly changeDetectorRef = inject(ChangeDetectorRef);
@@ -248,6 +251,9 @@ export class KbqTreeSelect
/** Strategy that will be used to handle scrolling while the select panel is open. */
scrollStrategy = this.scrollStrategyFactory();
+ /** Whether to hide the tree-select panel when its trigger scrolls out of its scroll container boundary. */
+ readonly shouldHideOnScrollOut = input(false, { transform: booleanAttribute });
+
/**
* The y-offset of the overlay panel in relation to the trigger's top start corner.
* This must be adjusted to align the selected option text over the trigger text.
@@ -719,6 +725,10 @@ export class KbqTreeSelect
ngOnInit() {
this.stateChanges.next();
+ if (this.shouldHideOnScrollOut()) {
+ wireHideOnScroll(this.scrollStrategy, this.destroyRef, () => this.close());
+ }
+
// We need `distinctUntilChanged` here, because some browsers will
// fire the animation end event twice for the same animation. See:
// https://github.com/angular/angular/issues/24084
diff --git a/packages/docs-examples/components/popover/index.ts b/packages/docs-examples/components/popover/index.ts
index c968e8ee5..f2d58bcfe 100644
--- a/packages/docs-examples/components/popover/index.ts
+++ b/packages/docs-examples/components/popover/index.ts
@@ -5,11 +5,13 @@ import { PopoverCloseExample } from './popover-close/popover-close-example';
import { PopoverContentExample } from './popover-content/popover-content-example';
import { PopoverHeaderExample } from './popover-header/popover-header-example';
import { PopoverHeightExample } from './popover-height/popover-height-example';
+import { PopoverHideOnScrollExample } from './popover-hide-on-scroll/popover-hide-on-scroll-example';
import { PopoverHoverExample } from './popover-hover/popover-hover-example';
import { PopoverOverviewExample } from './popover-overview/popover-overview-example';
import { PopoverPaddingsExample } from './popover-paddings/popover-paddings-example';
import { PopoverPlacementCenterExample } from './popover-placement-center/popover-placement-center-example';
import { PopoverPlacementEdgesExample } from './popover-placement-edges/popover-placement-edges-example';
+import { PopoverScrollStrategyExample } from './popover-scroll-strategy/popover-scroll-strategy-example';
import { PopoverScrollExample } from './popover-scroll/popover-scroll-example';
import { PopoverScrollingAndLayeringExample } from './popover-scrolling-and-layering/popover-scrolling-and-layering-example';
import { PopoverSmallExample } from './popover-small/popover-small-example';
@@ -22,6 +24,7 @@ export {
PopoverContentExample,
PopoverHeaderExample,
PopoverHeightExample,
+ PopoverHideOnScrollExample,
PopoverHoverExample,
PopoverOverviewExample,
PopoverPaddingsExample,
@@ -29,6 +32,7 @@ export {
PopoverPlacementEdgesExample,
PopoverScrollExample,
PopoverScrollingAndLayeringExample,
+ PopoverScrollStrategyExample,
PopoverSmallExample,
PopoverWidthExample
};
@@ -43,6 +47,8 @@ const EXAMPLES = [
PopoverHeaderExample,
PopoverContentExample,
PopoverScrollExample,
+ PopoverHideOnScrollExample,
+ PopoverScrollStrategyExample,
PopoverPlacementCenterExample,
PopoverPlacementEdgesExample,
PopoverHoverExample,
diff --git a/packages/docs-examples/components/popover/popover-hide-on-scroll/popover-hide-on-scroll-example.ts b/packages/docs-examples/components/popover/popover-hide-on-scroll/popover-hide-on-scroll-example.ts
new file mode 100644
index 000000000..f3fdfa635
--- /dev/null
+++ b/packages/docs-examples/components/popover/popover-hide-on-scroll/popover-hide-on-scroll-example.ts
@@ -0,0 +1,58 @@
+import { CdkScrollableModule } from '@angular/cdk/scrolling';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { KbqButtonModule } from '@koobiq/components/button';
+import { KbqPopoverModule } from '@koobiq/components/popover';
+
+/**
+ * @title popover-hide-on-scroll
+ */
+@Component({
+ selector: 'popover-hide-on-scroll-example',
+ imports: [CdkScrollableModule, KbqButtonModule, KbqPopoverModule],
+ template: `
+
+ `,
+ styles: `
+ .popover-hide-on-scroll-example__container {
+ height: 200px;
+ overflow-y: auto;
+ border: 1px solid var(--kbq-line-contrast-less);
+ border-radius: 4px;
+ }
+
+ .popover-hide-on-scroll-example__spacer {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 200px;
+ color: var(--kbq-foreground-contrast-secondary);
+ }
+
+ .popover-hide-on-scroll-example__triggers {
+ display: flex;
+ gap: 16px;
+ padding: 16px;
+ }
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class PopoverHideOnScrollExample {}
diff --git a/packages/docs-examples/components/popover/popover-scroll-strategy/popover-scroll-strategy-example.ts b/packages/docs-examples/components/popover/popover-scroll-strategy/popover-scroll-strategy-example.ts
new file mode 100644
index 000000000..4cc92953d
--- /dev/null
+++ b/packages/docs-examples/components/popover/popover-scroll-strategy/popover-scroll-strategy-example.ts
@@ -0,0 +1,64 @@
+import { Overlay } from '@angular/cdk/overlay';
+import { CdkScrollableModule } from '@angular/cdk/scrolling';
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { KbqButtonModule } from '@koobiq/components/button';
+import { KBQ_POPOVER_SCROLL_STRATEGY, KbqPopoverModule } from '@koobiq/components/popover';
+
+/**
+ * @title popover-scroll-strategy
+ */
+@Component({
+ selector: 'popover-scroll-strategy-example',
+ imports: [CdkScrollableModule, KbqButtonModule, KbqPopoverModule],
+ template: `
+
+ `,
+ styles: `
+ .example-popover-scroll-strategy__container {
+ height: 200px;
+ overflow-y: auto;
+ border: 1px solid var(--kbq-line-contrast-less);
+ border-radius: 4px;
+ }
+
+ .example-popover-scroll-strategy__spacer {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 250px;
+ color: var(--kbq-foreground-contrast-secondary);
+ }
+
+ .example-popover-scroll-strategy__triggers {
+ display: flex;
+ gap: 16px;
+ padding: 16px;
+ }
+ `,
+ providers: [
+ {
+ provide: KBQ_POPOVER_SCROLL_STRATEGY,
+ useFactory: () => {
+ const overlay = inject(Overlay);
+
+ return () => overlay.scrollStrategies.reposition({ scrollThrottle: 20 });
+ }
+ }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class PopoverScrollStrategyExample {}