From de81baf8b1c3904987d86ff04a628aad3fbac2ad Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Thu, 5 Feb 2026 18:25:44 +0530 Subject: [PATCH 1/9] docs(cdk/overlay): document native popover API usage - Add new section explaining the usePopover option - Include code examples for basic and advanced usage - List benefits of using the native Popover API - Add browser compatibility note Closes # --- src/cdk/overlay/overlay.md | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/cdk/overlay/overlay.md b/src/cdk/overlay/overlay.md index 8030aaa6b856..c85c1e8ba578 100644 --- a/src/cdk/overlay/overlay.md +++ b/src/cdk/overlay/overlay.md @@ -87,6 +87,46 @@ strategy will typically inject `ScrollDispatcher` (from `@angular/cdk/scrolling` of when scrolling takes place. See the documentation for `ScrollDispatcher` for more information on how scroll events are detected and dispatched. +#### Using the native Popover API +As of Angular v21, the CDK overlay supports rendering overlays as native popover elements instead +of using the traditional overlay container. This uses the browser's native [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API), +which provides improved accessibility, automatic focus management, and better handling of scrolling +and positioning. + +To enable the popover behavior, set the `usePopover` option to `true` when creating an overlay: + +```ts +const overlayRef = overlay.create({ + positionStrategy: this.overlay.position() + .flexibleConnectedTo(trigger) + .withPositions(positions), + usePopover: true, +}); +``` + +When using `FlexibleConnectedPositionStrategy` with popovers, you can also specify the popover +insertion point: + +```ts +const overlayRef = overlay.create({ + positionStrategy: this.overlay.position() + .flexibleConnectedTo(trigger) + .withPositions(positions) + .withPopoverInsertionPoint(triggerElement), + usePopover: 'auto', // Can also use 'auto', 'manual' +}); +``` + +**Benefits of using the native Popover API:** +- Better accessibility with automatic focus management +- Automatic dismissal on outside click (for auto popovers) +- Improved performance with less CSS in the critical path +- Native browser support for popover stacking +- Better integration with screen readers + +**Note:** The popover API is not supported on older browsers. Always provide a fallback or test +browser compatibility before using this feature in production. + ### The overlay container The `OverlayContainer` provides a handle to the container element in which all individual overlay elements are rendered. By default, the overlay container is appended directly to the document body From b130ddc04a0a8e000982a691210c5e7f93558ea4 Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Thu, 5 Feb 2026 18:31:27 +0530 Subject: [PATCH 2/9] docs(cdk/overlay): document native popover API usage - Add new section explaining the usePopover configuration option - Include code examples for basic and advanced usage patterns - List benefits of using the native Popover API - Add browser compatibility notes for older browsers --- src/cdk/overlay/overlay.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdk/overlay/overlay.md b/src/cdk/overlay/overlay.md index c85c1e8ba578..6b5908bcddfc 100644 --- a/src/cdk/overlay/overlay.md +++ b/src/cdk/overlay/overlay.md @@ -1,4 +1,4 @@ -The `overlay` package provides a way to open floating panels on the screen. +The `overlay` package provides a way to open floating panels on the screen. ### Initial setup The CDK overlays depend on a small set of structural styles to work correctly. If you're using From 96ef28e2ab21fef09a27dc59bf6fb3887648fbee Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Sat, 7 Feb 2026 14:41:21 +0530 Subject: [PATCH 3/9] chore: retrigger CLA check From 145c0a5394152cefa4c424d7121d18902be87fc2 Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Sat, 7 Feb 2026 15:24:28 +0530 Subject: [PATCH 4/9] ci(cla): retrigger CLA check From 6ac4dea62412135336162ffd36e04ddbb4b15038 Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Sat, 7 Feb 2026 15:36:35 +0530 Subject: [PATCH 5/9] docs(autocomplete): document consistent combobox overlay behavior (use popover/inline insertion) --- src/material/autocomplete/autocomplete.md | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/material/autocomplete/autocomplete.md b/src/material/autocomplete/autocomplete.md index 12306f3f9b7d..361268b09104 100644 --- a/src/material/autocomplete/autocomplete.md +++ b/src/material/autocomplete/autocomplete.md @@ -148,6 +148,55 @@ attribute, or the `aria-labelledby` attribute. `MatAutocomplete` preserves focus on the text trigger, using `aria-activedescendant` to support navigation though the autocomplete options. +### Making combobox/autocomplete overlays consistent with menus + +Some implementations render the combobox panel into the DOM always and hide it with CSS (for +example by setting `visibility: hidden` or `opacity: 0`). This can cause differences in behavior +compared to components (like menus) that attach the overlay only when the panel is opened. The +differences can affect keyboard event targeting, focus handling, and performance when many +comboboxes are present. + +Recommended approach: +- Render the overlay only when the panel is open (attach/detach) or use the CDK "popover" + insertion strategy which places the panel inline with the origin when appropriate. This keeps + the menu and combobox behavior consistent and avoids relying on CSS to toggle visibility. + +How to opt-in +- For template-driven overlays (via `cdkConnectedOverlay`) set the `cdkConnectedOverlayUsePopover` + input to `'inline'` or a `FlexibleOverlayPopoverLocation` value so that the overlay will be + inserted as a popover when supported: + +```html + + + +``` + +- For components that create an `OverlayRef` programmatically (like `MatAutocompleteTrigger`), + ensure the position strategy uses `withPopoverLocation('inline')` when appropriate. The + autocomplete implementation in this repo already uses `withPopoverLocation('inline')` in + `_getOverlayPosition()`; if you have custom combobox code, apply the same strategy: + +```ts +const strategy = createFlexibleConnectedPositionStrategy(injector, origin) + .withFlexibleDimensions(false) + .withPush(false) + .withPopoverLocation('inline'); + +const overlayRef = createOverlayRef(injector, {positionStrategy: strategy, width}); +``` + +Notes +- After switching to inline/popover insertion, verify keyboard navigation and focus behavior in + complex interactions (e.g., nested modals or form fields). If you rely on `visibility: hidden` + to keep the panel in the DOM for styling reasons, consider moving that styling into the + panel's classes and controlling rendering via the overlay open state instead. + + By default, `MatAutocomplete` displays a checkmark to identify the selected item. While you can hide the checkmark indicator via `hideSingleSelectionIndicator`, this makes the component less accessible by making it harder or impossible for users to visually identify selected items. From 046507fdb3c335c54232019fd8eee0247654e42e Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Sat, 7 Feb 2026 15:44:56 +0530 Subject: [PATCH 6/9] fix(combobox-examples): use cdkConnectedOverlay for attach/detach panels instead of manual popover --- .../combobox-manual-example.html | 45 ++++++++++--------- .../combobox-manual-example.ts | 29 +++++------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.html b/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.html index e80a360531a5..2d3164f964de 100644 --- a/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.html +++ b/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.html @@ -9,25 +9,30 @@ /> -
- -
- @for (option of options(); track option) { -
+
+ @for (option of options(); track option) { +
+ {{option}} + - {{option}} - -
- } -
- -
+
+ } +
+ diff --git a/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.ts b/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.ts index 4075b3cdae62..430f90f1972e 100644 --- a/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.ts +++ b/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.ts @@ -13,6 +13,7 @@ import { ComboboxPopupContainer, } from '@angular/aria/combobox'; import {Listbox, Option} from '@angular/aria/listbox'; +import {CdkConnectedOverlay} from '@angular/cdk/overlay'; import { afterRenderEffect, ChangeDetectionStrategy, @@ -37,14 +38,16 @@ import {FormsModule} from '@angular/forms'; Listbox, Option, FormsModule, + CdkConnectedOverlay, ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ComboboxManualExample { - popover = viewChild('popover'); listbox = viewChild>(Listbox); combobox = viewChild>(Combobox); + panelWidth = signal(undefined); + searchString = signal(''); options = computed(() => @@ -53,29 +56,17 @@ export class ComboboxManualExample { constructor() { afterRenderEffect(() => { - const popover = this.popover()!; const combobox = this.combobox()!; - combobox.expanded() ? this.showPopover() : popover.nativeElement.hidePopover(); + if (combobox.expanded()) { + const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); + this.panelWidth(comboboxRect?.width); + } else { + this.panelWidth(undefined); + } this.listbox()?.scrollActiveItemIntoView(); }); } - - showPopover() { - const popover = this.popover()!; - const combobox = this.combobox()!; - - const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); - const popoverEl = popover.nativeElement; - - if (comboboxRect) { - popoverEl.style.width = `${comboboxRect.width}px`; - popoverEl.style.top = `${comboboxRect.bottom + 4}px`; - popoverEl.style.left = `${comboboxRect.left - 1}px`; - } - - popover.nativeElement.showPopover(); - } } const states = [ From a4c086d9f98c8a7132a6f4a033fc423efe56c1fa Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Sat, 7 Feb 2026 15:46:37 +0530 Subject: [PATCH 7/9] fix(aria/combobox): attach combobox panels with cdkConnectedOverlay instead of manual popover --- .../combobox-auto-select-example.html | 45 ++++++++++--------- .../combobox-auto-select-example.ts | 39 ++++++++-------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.html b/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.html index bce80529017e..e1219b944f14 100644 --- a/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.html +++ b/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.html @@ -9,25 +9,30 @@ /> -
- -
- @for (option of options(); track option) { -
+
+ @for (option of options(); track option) { +
+ {{option}} + - {{option}} - -
- } -
- -
+
+ } +
+ diff --git a/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.ts b/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.ts index 5393e0af1fd3..fa69cb9cc9d4 100644 --- a/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.ts +++ b/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.ts @@ -22,20 +22,30 @@ import { signal, viewChild, } from '@angular/core'; +import {CdkConnectedOverlay} from '@angular/cdk/overlay'; /** @title Combobox with auto-select filtering. */ @Component({ selector: 'combobox-auto-select-example', templateUrl: 'combobox-auto-select-example.html', styleUrl: '../combobox-examples.css', - imports: [Combobox, ComboboxInput, ComboboxPopup, ComboboxPopupContainer, Listbox, Option], + imports: [ + Combobox, + ComboboxInput, + ComboboxPopup, + ComboboxPopupContainer, + Listbox, + Option, + CdkConnectedOverlay, + ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ComboboxAutoSelectExample { - popover = viewChild('popover'); listbox = viewChild>(Listbox); combobox = viewChild>(Combobox); + panelWidth = signal(undefined); + searchString = signal(''); options = computed(() => @@ -44,28 +54,17 @@ export class ComboboxAutoSelectExample { constructor() { afterRenderEffect(() => { - const popover = this.popover()!; const combobox = this.combobox()!; - combobox.expanded() ? this.showPopover() : popover.nativeElement.hidePopover(); + if (combobox.expanded()) { + const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); + this.panelWidth(comboboxRect?.width); + } else { + this.panelWidth(undefined); + } + this.listbox()?.scrollActiveItemIntoView(); }); } - - showPopover() { - const popover = this.popover()!; - const combobox = this.combobox()!; - - const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); - const popoverEl = popover.nativeElement; - - if (comboboxRect) { - popoverEl.style.width = `${comboboxRect.width}px`; - popoverEl.style.top = `${comboboxRect.bottom + 4}px`; - popoverEl.style.left = `${comboboxRect.left - 1}px`; - } - - popover.nativeElement.showPopover(); - } } const states = [ From 5fcb2d54bf659bd27cb00684321f3a5bfb3026c2 Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Sat, 7 Feb 2026 15:48:12 +0530 Subject: [PATCH 8/9] fix(aria/combobox): use cdkConnectedOverlay for disabled example panels --- .../combobox-disabled-example.html | 45 ++++++++++--------- .../combobox-disabled-example.ts | 29 +++++------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/components-examples/aria/combobox/combobox-disabled/combobox-disabled-example.html b/src/components-examples/aria/combobox/combobox-disabled/combobox-disabled-example.html index a791c1d1f5b4..7fc4bc984d4f 100644 --- a/src/components-examples/aria/combobox/combobox-disabled/combobox-disabled-example.html +++ b/src/components-examples/aria/combobox/combobox-disabled/combobox-disabled-example.html @@ -9,25 +9,30 @@ /> -
- -
- @for (option of options(); track option) { -
+
+ @for (option of options(); track option) { +
+ {{option}} + - {{option}} - -
- } -
- -
+
+ } +
+ diff --git a/src/components-examples/aria/combobox/combobox-disabled/combobox-disabled-example.ts b/src/components-examples/aria/combobox/combobox-disabled/combobox-disabled-example.ts index bf8e2808fd40..df0483c024f3 100644 --- a/src/components-examples/aria/combobox/combobox-disabled/combobox-disabled-example.ts +++ b/src/components-examples/aria/combobox/combobox-disabled/combobox-disabled-example.ts @@ -23,6 +23,7 @@ import { viewChild, } from '@angular/core'; import {FormsModule} from '@angular/forms'; +import {CdkConnectedOverlay} from '@angular/cdk/overlay'; /** @title Disabled combobox example. */ @Component({ @@ -37,14 +38,16 @@ import {FormsModule} from '@angular/forms'; Listbox, Option, FormsModule, + CdkConnectedOverlay, ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ComboboxDisabledExample { - popover = viewChild('popover'); listbox = viewChild>(Listbox); combobox = viewChild>(Combobox); + panelWidth = signal(undefined); + searchString = signal(''); options = computed(() => @@ -53,29 +56,17 @@ export class ComboboxDisabledExample { constructor() { afterRenderEffect(() => { - const popover = this.popover()!; const combobox = this.combobox()!; - combobox.expanded() ? this.showPopover() : popover.nativeElement.hidePopover(); + if (combobox.expanded()) { + const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); + this.panelWidth(comboboxRect?.width); + } else { + this.panelWidth(undefined); + } this.listbox()?.scrollActiveItemIntoView(); }); } - - showPopover() { - const popover = this.popover()!; - const combobox = this.combobox()!; - - const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); - const popoverEl = popover.nativeElement; - - if (comboboxRect) { - popoverEl.style.width = `${comboboxRect.width}px`; - popoverEl.style.top = `${comboboxRect.bottom + 4}px`; - popoverEl.style.left = `${comboboxRect.left - 1}px`; - } - - popover.nativeElement.showPopover(); - } } const states = [ From 8f1eeeadd9f9a3b40ea788995129bb3bd26d641f Mon Sep 17 00:00:00 2001 From: shivdev277 Date: Sat, 7 Feb 2026 16:11:56 +0530 Subject: [PATCH 9/9] fix(aria/combobox): replace manual popover usage with cdkConnectedOverlay attach/detach pattern --- .../combobox-auto-select-example.html | 1 + .../combobox-disabled-example.html | 1 + .../combobox-highlight-example.html | 16 +++++-- .../combobox-highlight-example.ts | 29 +++++------- .../combobox-manual-example.html | 1 + .../combobox-tree-auto-select-example.html | 1 + .../combobox-tree-highlight-example.html | 16 +++++-- .../combobox-tree-manual-example.html | 33 +++++++++----- .../combobox-tree-manual-example.ts | 29 +++++------- .../aria/toolbar/simple-toolbar.ts | 44 +++++++++---------- 10 files changed, 92 insertions(+), 79 deletions(-) diff --git a/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.html b/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.html index e1219b944f14..4e66c0cecb30 100644 --- a/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.html +++ b/src/components-examples/aria/combobox/combobox-auto-select/combobox-auto-select-example.html @@ -2,6 +2,7 @@
search search search
-
- + +
@for (option of options(); track option) {
}
- -
+
+
diff --git a/src/components-examples/aria/combobox/combobox-highlight/combobox-highlight-example.ts b/src/components-examples/aria/combobox/combobox-highlight/combobox-highlight-example.ts index 86f8ef5e07cf..2c70198d9b41 100644 --- a/src/components-examples/aria/combobox/combobox-highlight/combobox-highlight-example.ts +++ b/src/components-examples/aria/combobox/combobox-highlight/combobox-highlight-example.ts @@ -23,6 +23,7 @@ import { viewChild, } from '@angular/core'; import {FormsModule} from '@angular/forms'; +import {CdkConnectedOverlay} from '@angular/cdk/overlay'; /** @title Combobox with highlight filtering. */ @Component({ @@ -37,14 +38,16 @@ import {FormsModule} from '@angular/forms'; Listbox, Option, FormsModule, + CdkConnectedOverlay, ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ComboboxHighlightExample { - popover = viewChild('popover'); listbox = viewChild>(Listbox); combobox = viewChild>(Combobox); + panelWidth = signal(undefined); + searchString = signal(''); options = computed(() => @@ -53,29 +56,17 @@ export class ComboboxHighlightExample { constructor() { afterRenderEffect(() => { - const popover = this.popover()!; const combobox = this.combobox()!; - combobox.expanded() ? this.showPopover() : popover.nativeElement.hidePopover(); + if (combobox.expanded()) { + const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); + this.panelWidth(comboboxRect?.width); + } else { + this.panelWidth(undefined); + } this.listbox()?.scrollActiveItemIntoView(); }); } - - showPopover() { - const popover = this.popover()!; - const combobox = this.combobox()!; - - const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); - const popoverEl = popover.nativeElement; - - if (comboboxRect) { - popoverEl.style.width = `${comboboxRect.width}px`; - popoverEl.style.top = `${comboboxRect.bottom + 4}px`; - popoverEl.style.left = `${comboboxRect.left - 1}px`; - } - - popover.nativeElement.showPopover(); - } } const states = [ diff --git a/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.html b/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.html index 2d3164f964de..da9a4219421b 100644 --- a/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.html +++ b/src/components-examples/aria/combobox/combobox-manual/combobox-manual-example.html @@ -2,6 +2,7 @@
search search search
-
- + +
-
-
+ + diff --git a/src/components-examples/aria/combobox/combobox-tree-manual/combobox-tree-manual-example.html b/src/components-examples/aria/combobox/combobox-tree-manual/combobox-tree-manual-example.html index 41c67e436ab0..cc32fcd2d724 100644 --- a/src/components-examples/aria/combobox/combobox-tree-manual/combobox-tree-manual-example.html +++ b/src/components-examples/aria/combobox/combobox-tree-manual/combobox-tree-manual-example.html @@ -8,6 +8,7 @@
search
-
- -
    - -
-
-
+ + + +
+ +
+
+
+
diff --git a/src/components-examples/aria/combobox/combobox-tree-manual/combobox-tree-manual-example.ts b/src/components-examples/aria/combobox/combobox-tree-manual/combobox-tree-manual-example.ts index 5a7967f17d9f..336a9fdcdae8 100644 --- a/src/components-examples/aria/combobox/combobox-tree-manual/combobox-tree-manual-example.ts +++ b/src/components-examples/aria/combobox/combobox-tree-manual/combobox-tree-manual-example.ts @@ -22,6 +22,7 @@ import { signal, viewChild, } from '@angular/core'; +import {CdkConnectedOverlay} from '@angular/cdk/overlay'; import {TREE_NODES, TreeNode} from '../data'; import {NgTemplateOutlet} from '@angular/common'; @@ -39,14 +40,16 @@ import {NgTemplateOutlet} from '@angular/common'; TreeItem, TreeItemGroup, NgTemplateOutlet, + CdkConnectedOverlay, ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ComboboxTreeManualExample { - popover = viewChild('popover'); tree = viewChild>(Tree); combobox = viewChild>(Combobox); + panelWidth = signal(undefined); + searchString = signal(''); nodes = computed(() => this.filterTreeNodes(TREE_NODES)); @@ -79,26 +82,14 @@ export class ComboboxTreeManualExample { constructor() { afterRenderEffect(() => { - const popover = this.popover()!; const combobox = this.combobox()!; - combobox.expanded() ? this.showPopover() : popover.nativeElement.hidePopover(); + if (combobox.expanded()) { + const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); + this.panelWidth(comboboxRect?.width); + } else { + this.panelWidth(undefined); + } this.tree()?.scrollActiveItemIntoView(); }); } - - showPopover() { - const popover = this.popover()!; - const combobox = this.combobox()!; - - const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); - const popoverEl = popover.nativeElement; - - if (comboboxRect) { - popoverEl.style.width = `${comboboxRect.width}px`; - popoverEl.style.top = `${comboboxRect.bottom + 4}px`; - popoverEl.style.left = `${comboboxRect.left - 1}px`; - } - - popover.nativeElement.showPopover(); - } } diff --git a/src/components-examples/aria/toolbar/simple-toolbar.ts b/src/components-examples/aria/toolbar/simple-toolbar.ts index 56ed29dbd183..fe423d0cc0f0 100644 --- a/src/components-examples/aria/toolbar/simple-toolbar.ts +++ b/src/components-examples/aria/toolbar/simple-toolbar.ts @@ -16,6 +16,7 @@ import { signal, viewChild, } from '@angular/core'; +import {CdkConnectedOverlay} from '@angular/cdk/overlay'; @Directive({ selector: 'button[toolbar-button]', @@ -70,6 +71,7 @@ export class SimpleToolbarRadioButton { Listbox, Option, ToolbarWidget, + CdkConnectedOverlay, ], styleUrl: 'toolbar-common.css', host: {class: 'example-combobox-container'}, @@ -88,8 +90,15 @@ export class SimpleToolbarRadioButton { > -
- + +
@for (option of options; track option) {
@@ -100,43 +109,32 @@ export class SimpleToolbarRadioButton {
}
-
-
+ +
`, }) export class SimpleCombobox { dir = inject(Directionality).valueSignal; - popover = viewChild('popover'); listbox = viewChild>(Listbox); combobox = viewChild>(Combobox); + panelWidth = signal(undefined); + value = signal('Normal text'); options = ['Normal text', 'Title', 'Subtitle', 'Heading 1', 'Heading 2', 'Heading 3']; constructor() { afterRenderEffect(() => { - const popover = this.popover()!; const combobox = this.combobox()!; - combobox.expanded() ? this.showPopover() : popover.nativeElement.hidePopover(); + if (combobox.expanded()) { + const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); + this.panelWidth(comboboxRect?.width); + } else { + this.panelWidth(undefined); + } this.listbox()?.scrollActiveItemIntoView(); }); } - - showPopover() { - const popover = this.popover()!; - const combobox = this.combobox()!; - - const comboboxRect = combobox.inputElement()?.getBoundingClientRect(); - const popoverEl = popover.nativeElement; - - if (comboboxRect) { - popoverEl.style.width = `${comboboxRect.width}px`; - popoverEl.style.top = `${comboboxRect.bottom + 4}px`; - popoverEl.style.left = `${comboboxRect.left - 1}px`; - } - - popover.nativeElement.showPopover(); - } }