@@ -15,17 +15,12 @@ import {
1515 inject ,
1616 input ,
1717 model ,
18- OnDestroy ,
1918 OnInit ,
2019 signal ,
2120 Renderer2 ,
2221} from '@angular/core' ;
23- import {
24- DeferredContent ,
25- DeferredContentAware ,
26- SimpleComboboxPattern ,
27- SimpleComboboxPopupPattern ,
28- } from '@angular/aria/private' ;
22+ import { DeferredContentAware , SimpleComboboxPattern } from '@angular/aria/private' ;
23+ import type { ComboboxPopup } from './simple-combobox-popup' ;
2924
3025/**
3126 * The container element that wraps a combobox input and popup, and orchestrates its behavior.
@@ -133,136 +128,3 @@ export class Combobox extends DeferredContentAware implements OnInit {
133128 this . _popup . set ( undefined ) ;
134129 }
135130}
136-
137- /**
138- * A structural directive that marks the `ng-template` to be used as the popup
139- * for a combobox. This content is conditionally rendered.
140- *
141- * The content of the popup can be any element with the `ngComboboxWidget` directive.
142- *
143- * ```html
144- * <ng-template ngComboboxPopup>
145- * <div ngComboboxWidget>
146- * <!-- ... options ... -->
147- * </div>
148- * </ng-template>
149- * ```
150- */
151- @Directive ( {
152- selector : 'ng-template[ngComboboxPopup]' ,
153- exportAs : 'ngComboboxPopup' ,
154- hostDirectives : [ DeferredContent ] ,
155- } )
156- export class ComboboxPopup implements OnInit , OnDestroy {
157- private readonly _deferredContent = inject ( DeferredContent ) ;
158-
159- /** The combobox that the popup belongs to. */
160- readonly combobox = input . required < Combobox > ( ) ;
161-
162- /** The widget contained within the popup. */
163- readonly _widget = signal < ComboboxWidget | undefined > ( undefined ) ;
164-
165- /** The element that serves as the control target for the popup. */
166- readonly controlTarget = computed ( ( ) => this . _widget ( ) ?. element ) ;
167-
168- /** The ID of the popup. */
169- readonly popupId = computed ( ( ) => this . _widget ( ) ?. popupId ( ) ) ;
170-
171- /** The ID of the active descendant in the popup. */
172- readonly activeDescendant = computed ( ( ) => this . _widget ( ) ?. activeDescendant ( ) ) ;
173-
174- /** The type of the popup (e.g., listbox, tree, grid, dialog). */
175- readonly popupType = input < 'listbox' | 'tree' | 'grid' | 'dialog' > ( 'listbox' ) ;
176-
177- /** The popup pattern. */
178- readonly _pattern = new SimpleComboboxPopupPattern ( {
179- ...this ,
180- } ) ;
181-
182- ngOnInit ( ) {
183- this . combobox ( ) . _registerPopup ( this ) ;
184- this . _deferredContent . deferredContentAware . set ( this . combobox ( ) ) ;
185- }
186-
187- ngOnDestroy ( ) {
188- this . combobox ( ) . _unregisterPopup ( ) ;
189- }
190-
191- /** Registers a widget with the popup. */
192- _registerWidget ( widget : ComboboxWidget ) {
193- this . _widget . set ( widget ) ;
194- }
195-
196- /** Unregisters the widget from the popup. */
197- _unregisterWidget ( ) {
198- this . _widget . set ( undefined ) ;
199- }
200- }
201-
202- /**
203- * Identifies an element as a widget within a combobox popup.
204- *
205- * This directive should be applied to the element that contains the options or content
206- * of the popup. It handles the communication of ID and active descendant information
207- * to the combobox.
208- */
209- @Directive ( {
210- selector : '[ngComboboxWidget]' ,
211- exportAs : 'ngComboboxWidget' ,
212- host : {
213- '(focusin)' : 'onFocusin()' ,
214- '(focusout)' : 'onFocusout($event)' ,
215- } ,
216- } )
217- export class ComboboxWidget implements OnInit , OnDestroy {
218- /** The element that the popup widget is attached to. */
219- private readonly _elementRef = inject < ElementRef < HTMLElement > > ( ElementRef ) ;
220- private readonly _popup = inject ( ComboboxPopup ) ;
221-
222- /** A reference to the popup widget element. */
223- readonly element = this . _elementRef . nativeElement ;
224-
225- /** The ID of the popup widget. */
226- readonly popupId = signal < string | undefined > ( undefined ) ;
227-
228- /** The ID of the active descendant in the widget. */
229- readonly activeDescendant = input < string | undefined > ( undefined ) ;
230-
231- private _observer : MutationObserver | undefined ;
232-
233- constructor ( ) {
234- const el = this . element ;
235- this . _observer = new MutationObserver ( mutations => {
236- for ( const mutation of mutations ) {
237- if ( mutation . attributeName === 'id' ) {
238- this . popupId . set ( el . id ) ;
239- }
240- }
241- } ) ;
242-
243- this . _observer . observe ( el , {
244- attributes : true ,
245- attributeFilter : [ 'id' ] ,
246- } ) ;
247- }
248-
249- ngOnInit ( ) {
250- this . popupId . set ( this . element . id ) ;
251- this . _popup . _registerWidget ( this ) ;
252- }
253-
254- ngOnDestroy ( ) : void {
255- this . _observer ?. disconnect ( ) ;
256- this . _popup . _unregisterWidget ( ) ;
257- }
258-
259- /** Handles focus in events for the widget. */
260- onFocusin ( ) {
261- this . _popup . _pattern . onFocusin ( ) ;
262- }
263-
264- /** Handles focus out events for the widget. */
265- onFocusout ( event : FocusEvent ) {
266- this . _popup . _pattern . onFocusout ( event ) ;
267- }
268- }
0 commit comments