1+ import { DOCUMENT } from '@angular/common' ;
12import {
23AfterContentInit ,
34AfterViewInit ,
45booleanAttribute ,
56ChangeDetectorRef ,
67Component ,
78ContentChild ,
9+ DestroyRef ,
810Directive ,
911ElementRef ,
1012EventEmitter ,
1113forwardRef ,
1214HostBinding ,
1315HostListener ,
16+ inject ,
1417Inject ,
1518Input ,
1619NgZone ,
1720OnChanges ,
1821OnDestroy ,
1922OnInit ,
20- Optional ,
2123Output ,
2224Renderer2 ,
25+ signal ,
2326SimpleChanges
2427} from '@angular/core' ;
25- import { DOCUMENT } from '@angular/common ' ;
28+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop ' ;
2629import { Subscription } from 'rxjs' ;
2730import { filter } from 'rxjs/operators' ;
2831
@@ -42,12 +45,11 @@ export abstract class DropdownToken {}
4245standalone :true
4346} )
4447export class DropdownToggleDirective implements AfterViewInit {
45-
46- constructor (
47- public elementRef :ElementRef ,
48- private dropdownService :DropdownService ,
49- @Optional ( ) public dropdown ?:DropdownToken
50- ) { }
48+ // injections
49+ readonly #destroyRef= inject ( DestroyRef ) ;
50+ public readonly elementRef = inject ( ElementRef ) ;
51+ #dropdownService= inject ( DropdownService ) ;
52+ public dropdown = inject ( DropdownToken , { optional :true } ) ;
5153
5254/**
5355 * Toggle the disabled state for the toggler.
@@ -70,7 +72,8 @@ export class DropdownToggleDirective implements AfterViewInit {
7072 @Input ( ) caret = true ;
7173
7274/**
73- * Create split button dropdowns with virtually the same markup as single button dropdowns, but with the addition of `.dropdown-toggle-split` class for proper spacing around the dropdown caret.
75+ * Create split button dropdowns with virtually the same markup as single button dropdowns,
76+ * but with the addition of `.dropdown-toggle-split` class for proper spacing around the dropdown caret.
7477 *@type boolean
7578 *@default false
7679 */
@@ -85,16 +88,29 @@ export class DropdownToggleDirective implements AfterViewInit {
8588} ;
8689}
8790
91+ #ariaExpanded= signal ( false ) ;
92+
93+ @HostBinding ( 'attr.aria-expanded' )
94+ get ariaExpanded ( ) {
95+ return this . #ariaExpanded( ) ;
96+ }
97+
8898 @HostListener ( 'click' , [ '$event' ] )
8999public onClick ( $event :MouseEvent ) :void {
90100$event . preventDefault ( ) ;
91- ! this . disabled && this . dropdownService . toggle ( { visible :'toggle' , dropdown :this . dropdown } ) ;
101+ ! this . disabled && this . # dropdownService. toggle ( { visible :'toggle' , dropdown :this . dropdown } ) ;
92102}
93103
94104ngAfterViewInit ( ) :void {
95105if ( this . dropdownComponent ) {
96106this . dropdown = this . dropdownComponent ;
97- this . dropdownService = this . dropdownComponent ?. dropdownService ;
107+ this . #dropdownService= this . dropdownComponent ?. dropdownService ;
108+ }
109+ if ( this . dropdown ) {
110+ const dropdown = < DropdownComponent > this . dropdown ;
111+ dropdown ?. visibleChange ?. pipe ( takeUntilDestroyed ( this . #destroyRef) ) . subscribe ( ( visible ) => {
112+ this . #ariaExpanded. set ( visible ) ;
113+ } ) ;
98114}
99115}
100116}
@@ -109,7 +125,6 @@ export class DropdownToggleDirective implements AfterViewInit {
109125hostDirectives :[ { directive :ThemeDirective , inputs :[ 'dark' ] } ]
110126} )
111127export class DropdownComponent implements AfterContentInit , OnChanges , OnDestroy , OnInit {
112-
113128constructor (
114129 @Inject ( DOCUMENT ) private document :Document ,
115130private elementRef :ElementRef ,
@@ -136,7 +151,8 @@ export class DropdownComponent implements AfterContentInit, OnChanges, OnDestroy
136151 @Input ( ) direction ?:'center' | 'dropup' | 'dropup-center' | 'dropend' | 'dropstart' ;
137152
138153/**
139- * Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property.
154+ * Describes the placement of your component after Popper.js has applied all the modifiers
155+ * that may have flipped or altered the originally provided placement property.
140156 *@type Placement
141157 */
142158 @Input ( ) placement :Placement = 'bottom-start' ;
@@ -155,7 +171,7 @@ export class DropdownComponent implements AfterContentInit, OnChanges, OnDestroy
155171 @Input ( )
156172set popperOptions ( value :Partial < Options > ) {
157173this . _popperOptions = { ...this . _popperOptions , ...value } ;
158- } ;
174+ }
159175
160176get popperOptions ( ) :Partial < Options > {
161177let placement = this . placement ;
@@ -237,12 +253,10 @@ export class DropdownComponent implements AfterContentInit, OnChanges, OnDestroy
237253 @HostBinding ( 'class' )
238254get hostClasses ( ) :any {
239255return {
240- dropdown :
241- ( this . variant === 'dropdown' || this . variant === 'nav-item' ) &&
242- ! this . direction ,
256+ dropdown :( this . variant === 'dropdown' || this . variant === 'nav-item' ) && ! this . direction ,
243257[ `${ this . direction } ` ] :! ! this . direction ,
244258[ `${ this . variant } ` ] :! ! this . variant ,
245- ' dropup' :this . direction === 'dropup' || this . direction === 'dropup-center' ,
259+ dropup :this . direction === 'dropup' || this . direction === 'dropup-center' ,
246260show :this . visible
247261} ;
248262}
@@ -262,16 +276,15 @@ export class DropdownComponent implements AfterContentInit, OnChanges, OnDestroy
262276
263277dropdownStateSubscribe ( subscribe :boolean = true ) :void {
264278if ( subscribe ) {
265- this . dropdownStateSubscription =
266- this . dropdownService . dropdownState$ . pipe (
279+ this . dropdownStateSubscription = this . dropdownService . dropdownState$
280+ . pipe (
267281filter ( ( state ) => {
268282return this === state . dropdown ;
269283} )
270- ) . subscribe ( ( state ) => {
284+ )
285+ . subscribe ( ( state ) => {
271286if ( 'visible' in state ) {
272- state ?. visible === 'toggle'
273- ?this . toggleDropdown ( )
274- :( this . visible = state . visible ) ;
287+ state ?. visible === 'toggle' ?this . toggleDropdown ( ) :( this . visible = state . visible ) ;
275288}
276289} ) ;
277290} else {