@@ -25,6 +25,7 @@ import {
2525 ComboboxPattern ,
2626 ComboboxListboxControls ,
2727 ComboboxTreeControls ,
28+ ComboboxDialogPattern ,
2829} from '@angular/aria/private' ;
2930import { Directionality } from '@angular/cdk/bidi' ;
3031import { toSignal } from '@angular/core/rxjs-interop' ;
@@ -84,7 +85,12 @@ export class Combobox<V> {
8485 readonly firstMatch = input < V | undefined > ( undefined ) ;
8586
8687 /** Whether the combobox is expanded. */
87- readonly expanded = computed ( ( ) => this . _pattern . expanded ( ) ) ;
88+ readonly expanded = computed ( ( ) => this . alwaysExpanded ( ) || this . _pattern . expanded ( ) ) ;
89+
90+ // TODO: Maybe make expanded a signal that can be passed in?
91+ // Or an "always expanded" option?
92+
93+ readonly alwaysExpanded = input ( false ) ;
8894
8995 /** Input element connected to the combobox, if any. */
9096 readonly inputElement = computed ( ( ) => this . _pattern . inputs . inputEl ( ) ) ;
@@ -103,7 +109,16 @@ export class Combobox<V> {
103109
104110 constructor ( ) {
105111 afterRenderEffect ( ( ) => {
106- if ( ! this . _deferredContentAware ?. contentVisible ( ) && this . _pattern . isFocused ( ) ) {
112+ if ( this . alwaysExpanded ( ) ) {
113+ this . _pattern . expanded . set ( true ) ;
114+ }
115+ } ) ;
116+
117+ afterRenderEffect ( ( ) => {
118+ if (
119+ ! this . _deferredContentAware ?. contentVisible ( ) &&
120+ ( this . _pattern . isFocused ( ) || this . alwaysExpanded ( ) )
121+ ) {
107122 this . _deferredContentAware ?. contentVisible . set ( true ) ;
108123 }
109124 } ) ;
@@ -146,10 +161,15 @@ export class ComboboxInput {
146161 ) ;
147162 this . combobox . _pattern . inputs . inputValue = this . value ;
148163
164+ const controls = this . combobox . popup ( ) ?. controls ( ) ;
165+ if ( controls instanceof ComboboxDialogPattern ) {
166+ return ;
167+ }
168+
149169 /** Focuses & selects the first item in the combobox if the user changes the input value. */
150170 afterRenderEffect ( ( ) => {
151171 this . value ( ) ;
152- this . combobox . popup ( ) ?. controls ( ) ?. items ( ) ;
172+ controls ?. items ( ) ;
153173 untracked ( ( ) => this . combobox . _pattern . onFilter ( ) ) ;
154174 } ) ;
155175 }
@@ -172,6 +192,58 @@ export class ComboboxPopup<V> {
172192
173193 /** The controls the popup exposes to the combobox. */
174194 readonly controls = signal <
175- ComboboxListboxControls < any , V > | ComboboxTreeControls < any , V > | undefined
195+ | ComboboxListboxControls < any , V >
196+ | ComboboxTreeControls < any , V >
197+ | ComboboxDialogPattern
198+ | undefined
176199 > ( undefined ) ;
177200}
201+
202+ @Directive ( {
203+ selector : 'dialog[ngComboboxDialog]' ,
204+ exportAs : 'ngComboboxDialog' ,
205+ host : {
206+ '[attr.data-open]' : 'combobox._pattern.expanded()' ,
207+ '(keydown)' : '_pattern.onKeydown($event)' ,
208+ '(click)' : '_pattern.onClick($event)' ,
209+ } ,
210+ hostDirectives : [ ComboboxPopup ] ,
211+ } )
212+ export class ComboboxDialog {
213+ /** The dialog element. */
214+ readonly element = inject ( ElementRef < HTMLDialogElement > ) ;
215+
216+ /** The combobox that the dialog belongs to. */
217+ readonly combobox = inject ( Combobox ) ;
218+
219+ /** A reference to the parent combobox popup, if one exists. */
220+ private readonly _popup = inject < ComboboxPopup < unknown > > ( ComboboxPopup , {
221+ optional : true ,
222+ } ) ;
223+
224+ _pattern : ComboboxDialogPattern ;
225+
226+ constructor ( ) {
227+ this . _pattern = new ComboboxDialogPattern ( {
228+ id : ( ) => '' ,
229+ element : ( ) => this . element . nativeElement ,
230+ combobox : this . combobox . _pattern ,
231+ } ) ;
232+
233+ if ( this . _popup ) {
234+ this . _popup . controls . set ( this . _pattern ) ;
235+ }
236+
237+ afterRenderEffect ( ( ) => {
238+ if ( this . element ) {
239+ this . combobox . _pattern . expanded ( )
240+ ? this . element . nativeElement . showModal ( )
241+ : this . element . nativeElement . close ( ) ;
242+ }
243+ } ) ;
244+ }
245+
246+ close ( ) {
247+ this . _popup ?. combobox ?. _pattern . close ( ) ;
248+ }
249+ }
0 commit comments