11import { DOCUMENT } from '@angular/common' ;
22import {
3- AfterContentInit ,
43AfterViewInit ,
54booleanAttribute ,
65ChangeDetectorRef ,
76Component ,
87computed ,
9- ContentChild ,
8+ contentChild ,
109DestroyRef ,
1110Directive ,
1211effect ,
1312ElementRef ,
1413forwardRef ,
15- Inject ,
1614inject ,
1715input ,
1816linkedSignal ,
@@ -24,7 +22,7 @@ import {
2422signal ,
2523untracked
2624} from '@angular/core' ;
27- import { Subscription } from 'rxjs' ;
25+ import { takeUntilDestroyed } from '@angular/core/ rxjs-interop ' ;
2826import { filter } from 'rxjs/operators' ;
2927
3028import { createPopper , Instance , Options , Placement } from '@popperjs/core' ;
@@ -128,15 +126,16 @@ export class DropdownToggleDirective implements AfterViewInit {
128126'(click)' :'onHostClick($event)'
129127}
130128} )
131- export class DropdownComponent implements AfterContentInit , OnDestroy , OnInit {
132- constructor (
133- @Inject ( DOCUMENT ) private document :Document ,
134- private elementRef :ElementRef ,
135- private renderer :Renderer2 ,
136- private ngZone :NgZone ,
137- private changeDetectorRef :ChangeDetectorRef ,
138- public dropdownService :DropdownService
139- ) {
129+ export class DropdownComponent implements OnDestroy , OnInit {
130+ readonly #destroyRef= inject ( DestroyRef ) ;
131+ readonly #document= inject ( DOCUMENT ) ;
132+ readonly #elementRef= inject ( ElementRef ) ;
133+ readonly #renderer= inject ( Renderer2 ) ;
134+ readonly #ngZone= inject ( NgZone ) ;
135+ readonly #changeDetectorRef= inject ( ChangeDetectorRef ) ;
136+ readonly dropdownService = inject ( DropdownService ) ;
137+
138+ constructor ( ) {
140139this . dropdownStateSubscribe ( ) ;
141140}
142141
@@ -177,7 +176,7 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
177176 */
178177readonly popperOptionsInput = input < Partial < Options > > ( { } , { alias :'popperOptions' } ) ;
179178
180- readonly popperOptionsEffect = effect ( ( ) => {
179+ readonly # popperOptionsEffect= effect ( ( ) => {
181180this . popperOptions = { ...untracked ( this . #popperOptions) , ...this . popperOptionsInput ( ) } ;
182181} ) ;
183182
@@ -239,7 +238,7 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
239238computation :( value ) => value
240239} ) ;
241240
242- readonly visibleEffect = effect ( ( ) => {
241+ readonly # visibleEffect= effect ( ( ) => {
243242const visible = this . visible ( ) ;
244243this . activeTrap = visible ;
245244visible ?this . createPopperInstance ( ) :this . destroyPopperInstance ( ) ;
@@ -250,13 +249,12 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
250249readonly visibleChange = output < boolean > ( ) ;
251250
252251dropdownContext = { $implicit :this . visible ( ) } ;
253- @ ContentChild ( DropdownToggleDirective ) _toggler ! : DropdownToggleDirective ;
254- @ ContentChild ( DropdownMenuDirective ) _menu ! : DropdownMenuDirective ;
255- @ ContentChild ( DropdownMenuDirective , { read :ElementRef } ) _menuElementRef ! : ElementRef ;
252+ readonly _toggler = contentChild ( DropdownToggleDirective ) ;
253+ readonly _menu = contentChild ( DropdownMenuDirective ) ;
254+ readonly _menuElementRef = contentChild ( DropdownMenuDirective , { read :ElementRef } ) ;
256255
257256public activeTrap = false ;
258257
259- private dropdownStateSubscription ! :Subscription ;
260258private popperInstance ! :Instance | undefined ;
261259private listeners :( ( ) => void ) [ ] = [ ] ;
262260
@@ -283,47 +281,45 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
283281this . clickedTarget = $event . target as HTMLElement ;
284282}
285283
286- dropdownStateSubscribe ( subscribe :boolean = true ) :void {
287- if ( subscribe ) {
288- this . dropdownStateSubscription = this . dropdownService . dropdownState$
289- . pipe (
290- filter ( ( state ) => {
291- return this === state . dropdown ;
292- } )
293- )
294- . subscribe ( ( state ) => {
295- if ( 'visible' in state ) {
296- state ?. visible === 'toggle' ?this . toggleDropdown ( ) :this . visible . set ( state . visible ) ;
297- }
298- } ) ;
299- } else {
300- this . dropdownStateSubscription ?. unsubscribe ( ) ;
301- }
284+ dropdownStateSubscribe ( ) :void {
285+ this . dropdownService . dropdownState$
286+ . pipe (
287+ filter ( ( state ) => {
288+ return this === state . dropdown ;
289+ } ) ,
290+ takeUntilDestroyed ( this . #destroyRef)
291+ )
292+ . subscribe ( ( state ) => {
293+ if ( 'visible' in state ) {
294+ state ?. visible === 'toggle' ?this . toggleDropdown ( ) :this . visible . set ( state . visible ) ;
295+ }
296+ } ) ;
302297}
303298
304299toggleDropdown ( ) :void {
305300this . visible . update ( ( visible ) => ! visible ) ;
306301}
307302
308303onClick ( event :any ) :void {
309- if ( ! this . _toggler ?. elementRef . nativeElement . contains ( event . target ?. closest ( '[cDropdownToggle]' ) ) ) {
304+ if ( ! this . _toggler ( ) ?. elementRef . nativeElement . contains ( event . target ?. closest ( '[cDropdownToggle]' ) ) ) {
310305this . toggleDropdown ( ) ;
311306}
312307}
313308
314- ngAfterContentInit ( ) :void {
315- if ( this . variant ( ) === 'nav-item' ) {
316- this . renderer . addClass ( this . _toggler . elementRef . nativeElement , 'nav-link' ) ;
309+ readonly #togglerEffect= effect ( ( ) => {
310+ const variant = this . variant ( ) ;
311+ const _toggler = this . _toggler ( ) ;
312+ if ( variant === 'nav-item' && _toggler ) {
313+ this . #renderer. addClass ( _toggler . elementRef . nativeElement , 'nav-link' ) ;
317314}
318- }
315+ } ) ;
319316
320317ngOnInit ( ) :void {
321318this . setVisibleState ( this . visible ( ) ) ;
322319}
323320
324321ngOnDestroy ( ) :void {
325322this . clearListeners ( ) ;
326- this . dropdownStateSubscribe ( false ) ;
327323this . destroyPopperInstance ( ) ;
328324}
329325
@@ -333,22 +329,22 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
333329
334330// todo: turn off popper in navbar-nav
335331createPopperInstance ( ) :void {
336- if ( this . _toggler && this . _menu ) {
337- this . ngZone . runOutsideAngular ( ( ) => {
332+ const _toggler = this . _toggler ( ) ;
333+ const _menu = this . _menu ( ) ;
334+ if ( _toggler && _menu ) {
335+ this . #ngZone. runOutsideAngular ( ( ) => {
338336// workaround for popper position calculate (see also: dropdown-menu.component)
339- this . _menu . elementRef . nativeElement . style . visibility = 'hidden' ;
340- this . _menu . elementRef . nativeElement . style . display = 'block' ;
337+ _menu . elementRef . nativeElement . style . visibility = 'hidden' ;
338+ _menu . elementRef . nativeElement . style . display = 'block' ;
341339if ( this . popper ( ) ) {
342- this . popperInstance = createPopper (
343- this . _toggler . elementRef . nativeElement ,
344- this . _menu . elementRef . nativeElement ,
345- { ...this . popperOptions }
346- ) ;
340+ this . popperInstance = createPopper ( _toggler . elementRef . nativeElement , _menu . elementRef . nativeElement , {
341+ ...this . popperOptions
342+ } ) ;
347343}
348- this . ngZone . run ( ( ) => {
344+ this . # ngZone. run ( ( ) => {
349345this . setListeners ( ) ;
350- this . changeDetectorRef . markForCheck ( ) ;
351- this . changeDetectorRef . detectChanges ( ) ;
346+ this . # changeDetectorRef. markForCheck ( ) ;
347+ this . # changeDetectorRef. detectChanges ( ) ;
352348} ) ;
353349} ) ;
354350}
@@ -358,17 +354,17 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
358354this . clearListeners ( ) ;
359355this . popperInstance ?. destroy ( ) ;
360356this . popperInstance = undefined ;
361- this . changeDetectorRef . markForCheck ( ) ;
357+ this . # changeDetectorRef. markForCheck ( ) ;
362358}
363359
364360private setListeners ( ) :void {
365361this . listeners . push (
366- this . renderer . listen ( this . document , 'click' , ( event ) => {
362+ this . # renderer. listen ( this . # document, 'click' , ( event ) => {
367363const target = event . target as HTMLElement ;
368- if ( this . _menuElementRef ?. nativeElement . contains ( event . target ) ) {
364+ if ( this . _menuElementRef ( ) ?. nativeElement . contains ( event . target ) ) {
369365this . clickedTarget = target ;
370366}
371- if ( this . _toggler ?. elementRef . nativeElement . contains ( event . target ) ) {
367+ if ( this . _toggler ( ) ?. elementRef . nativeElement . contains ( event . target ) ) {
372368return ;
373369}
374370const autoClose = this . autoClose ( ) ;
@@ -387,7 +383,7 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
387383} )
388384) ;
389385this . listeners . push (
390- this . renderer . listen ( this . elementRef . nativeElement , 'keyup' , ( event ) => {
386+ this . # renderer. listen ( this . # elementRef. nativeElement , 'keyup' , ( event ) => {
391387if ( event . key === 'Escape' && this . autoClose ( ) !== false ) {
392388event . stopPropagation ( ) ;
393389this . setVisibleState ( false ) ;
@@ -396,11 +392,11 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
396392} )
397393) ;
398394this . listeners . push (
399- this . renderer . listen ( this . document , 'keyup' , ( event ) => {
395+ this . # renderer. listen ( this . # document, 'keyup' , ( event ) => {
400396if (
401397event . key === 'Tab' &&
402398this . autoClose ( ) !== false &&
403- ! this . elementRef . nativeElement . contains ( event . target )
399+ ! this . # elementRef. nativeElement . contains ( event . target )
404400) {
405401this . setVisibleState ( false ) ;
406402return ;