@@ -12,7 +12,9 @@ import {
1212 OnDestroy ,
1313 HostBinding ,
1414 TemplateRef ,
15- AfterViewInit
15+ AfterViewInit ,
16+ ChangeDetectionStrategy ,
17+ ChangeDetectorRef
1618} from "@angular/core" ;
1719import { NG_VALUE_ACCESSOR , ControlValueAccessor } from "@angular/forms" ;
1820
@@ -86,7 +88,7 @@ import { hasScrollableParents } from "carbon-components-angular/utils";
8688 'cds--dropdown--sm cds--list-box--sm': size === 'sm',
8789 'cds--dropdown--md cds--list-box--md': size === 'md',
8890 'cds--dropdown--lg cds--list-box--lg': size === 'lg',
89- 'cds--list-box--expanded': !menuIsClosed ,
91+ 'cds--list-box--expanded': isOpen ,
9092 'cds--list-box--invalid': invalid
9193 }"
9294 [attr.data-invalid]="invalid ? true : null">
@@ -98,8 +100,8 @@ import { hasScrollableParents } from "carbon-components-angular/utils";
98100 [id]="id"
99101 type="button"
100102 class="cds--list-box__field"
101- [ngClass]="{'a': !menuIsClosed }"
102- [attr.aria-expanded]="!menuIsClosed "
103+ [ngClass]="{'a': isOpen }"
104+ [attr.aria-expanded]="isOpen "
103105 [attr.aria-disabled]="disabled"
104106 [attr.aria-readonly]="readonly"
105107 aria-haspopup="listbox"
@@ -141,7 +143,7 @@ import { hasScrollableParents } from "carbon-components-angular/utils";
141143 cdsIcon="chevron--down"
142144 size="16"
143145 [attr.aria-label]="menuButtonLabel"
144- [ngClass]="{'cds--list-box__menu-icon--open': !menuIsClosed }">
146+ [ngClass]="{'cds--list-box__menu-icon--open': isOpen }">
145147 </svg>
146148 }
147149 </span>
@@ -164,7 +166,7 @@ import { hasScrollableParents } from "carbon-components-angular/utils";
164166 [ngClass]="{
165167 'cds--list-box--up': this.dropUp !== null && this.dropUp !== undefined ? dropUp : _dropUp
166168 }">
167- @if (!menuIsClosed ) {
169+ @if (isOpen ) {
168170 <ng-content />
169171 }
170172 </div>
@@ -208,7 +210,8 @@ import { hasScrollableParents } from "carbon-components-angular/utils";
208210 useExisting : Dropdown ,
209211 multi : true
210212 }
211- ]
213+ ] ,
214+ changeDetection : ChangeDetectionStrategy . OnPush
212215} )
213216export class Dropdown implements OnInit , AfterContentInit , AfterViewInit , OnDestroy , ControlValueAccessor {
214217 static dropdownCount = 0 ;
@@ -217,7 +220,7 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
217220 }
218221
219222 @HostBinding ( "class.cds--list-box__wrapper--fluid--focus" ) get fluidFocusClass ( ) {
220- return this . fluid && this . _isFocused && this . menuIsClosed ;
223+ return this . fluid && this . _isFocused && ! this . isOpen ;
221224 }
222225
223226 protected get writtenValue ( ) {
@@ -299,8 +302,8 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
299302 */
300303 @Input ( ) invalidText : string | TemplateRef < any > ;
301304 /**
302- * Set to `true` to show a warning (contents set by warningText)
303- */
305+ * Set to `true` to show a warning (contents set by warningText)
306+ */
304307 @Input ( ) warn = false ;
305308 /**
306309 * Sets the warning text
@@ -377,7 +380,13 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
377380 /**
378381 * Set to `true` if the dropdown is closed (not expanded).
379382 */
380- menuIsClosed = true ;
383+ @Input ( ) isOpen = false ;
384+
385+ /** Deprecated as of v6 - Will be removed in v7 */
386+ set menuIsClosed ( isClosed : boolean ) {
387+ this . isOpen = ! isClosed ;
388+ this . changeDetectorRef . markForCheck ( ) ;
389+ }
381390
382391 /**
383392 * controls whether the `drop-up` class is applied
@@ -407,7 +416,9 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
407416 protected elementRef : ElementRef ,
408417 protected i18n : I18n ,
409418 protected dropdownService : DropdownService ,
410- protected elementService : ElementService ) { }
419+ protected elementService : ElementService ,
420+ protected changeDetectorRef : ChangeDetectorRef
421+ ) { }
411422
412423 /**
413424 * Updates the `type` property in the `@ContentChild`.
@@ -550,7 +561,7 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
550561 /**
551562 * function passed in by `registerOnChange`
552563 */
553- propagateChange = ( _ : any ) => { } ;
564+ propagateChange = ( _ : any ) => { } ;
554565
555566 /**
556567 * `ControlValueAccessor` method to programmatically disable the dropdown.
@@ -572,32 +583,32 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
572583 return ;
573584 }
574585
575- if ( ( event . key === "Escape" ) && ! this . menuIsClosed ) {
586+ if ( ( event . key === "Escape" ) && this . isOpen ) {
576587 event . stopImmediatePropagation ( ) ; // don't unintentionally close other widgets that listen for Escape
577588 }
578589 if ( event . key === "Escape" ) {
579590 event . preventDefault ( ) ;
580591 this . closeMenu ( ) ;
581592 this . dropdownButton . nativeElement . focus ( ) ;
582- } else if ( this . menuIsClosed && ( event . key === " " || event . key === "ArrowDown" || event . key === "ArrowUp" ) ) {
593+ } else if ( ! this . isOpen && ( event . key === " " || event . key === "ArrowDown" || event . key === "ArrowUp" ) ) {
583594 if ( this . disableArrowKeys && ( event . key === "ArrowDown" || event . key === "ArrowUp" ) ) {
584595 return ;
585596 }
586597 event . preventDefault ( ) ;
587598 this . openMenu ( ) ;
588599 }
589600
590- if ( ! this . menuIsClosed && event . key === "Tab" && this . dropdownMenu . nativeElement . contains ( event . target as Node ) ) {
601+ if ( this . isOpen && event . key === "Tab" && this . dropdownMenu . nativeElement . contains ( event . target as Node ) ) {
591602 this . closeMenu ( ) ;
592603 }
593604
594- if ( ! this . menuIsClosed && event . key === "Tab" && event . shiftKey ) {
605+ if ( this . isOpen && event . key === "Tab" && event . shiftKey ) {
595606 this . closeMenu ( ) ;
596607 }
597608
598609 if ( this . type === "multi" ) { return ; }
599610
600- if ( this . menuIsClosed ) {
611+ if ( ! this . isOpen ) {
601612 this . closedDropdownNavigation ( event ) ;
602613 }
603614 }
@@ -681,7 +692,7 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
681692 return false ;
682693 }
683694
684- _noop ( ) { }
695+ _noop ( ) { }
685696 /**
686697 * Handles clicks outside of the `Dropdown`.
687698 */
@@ -694,22 +705,22 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
694705 }
695706 }
696707 _outsideKey ( event ) {
697- if ( ! this . menuIsClosed && event . key === "Tab" && this . dropdownMenu . nativeElement . contains ( event . target as Node ) ) {
708+ if ( this . isOpen && event . key === "Tab" && this . dropdownMenu . nativeElement . contains ( event . target as Node ) ) {
698709 this . closeMenu ( ) ;
699710 }
700711 }
701712 /**
702713 * Handles keyboard events so users are controlling the `Dropdown` instead of unintentionally controlling outside elements.
703714 */
704715 _keyboardNav ( event : KeyboardEvent ) {
705- if ( event . key === "Escape" && ! this . menuIsClosed ) {
716+ if ( event . key === "Escape" && this . isOpen ) {
706717 event . stopImmediatePropagation ( ) ; // don't unintentionally close modal if inside of it
707718 }
708719 if ( event . key === "Escape" ) {
709720 event . preventDefault ( ) ;
710721 this . closeMenu ( ) ;
711722 this . dropdownButton . nativeElement . focus ( ) ;
712- } else if ( ! this . menuIsClosed && event . key === "Tab" ) {
723+ } else if ( this . isOpen && event . key === "Tab" ) {
713724 // this way focus will start on the next focusable item from the dropdown
714725 // not the top of the body!
715726 this . dropdownButton . nativeElement . focus ( ) ;
@@ -731,7 +742,7 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
731742 */
732743 _appendToBody ( ) {
733744 const lightClass = this . theme === "light" ? " cds--list-box--light" : "" ;
734- const expandedClass = ! this . menuIsClosed ? " cds--list-box--expanded" : "" ;
745+ const expandedClass = this . isOpen ? " cds--list-box--expanded" : "" ;
735746 this . dropdownService . appendToBody (
736747 this . dropdownButton . nativeElement ,
737748 this . dropdownMenu . nativeElement ,
@@ -770,7 +781,7 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
770781 }
771782
772783 this . _dropUp = false ;
773- this . menuIsClosed = false ;
784+ this . isOpen = true ;
774785
775786 // move the dropdown list to the body if we're not appending inline
776787 // and position it relative to the dropdown wrapper
@@ -809,8 +820,8 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
809820 */
810821 closeMenu ( ) {
811822 // return early if the menu is already closed
812- if ( this . menuIsClosed ) { return ; }
813- this . menuIsClosed = true ;
823+ if ( ! this . isOpen ) { return ; }
824+ this . isOpen = false ;
814825 this . checkForReorder ( ) ;
815826 this . onClose . emit ( ) ;
816827 this . close . emit ( ) ;
@@ -838,7 +849,7 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
838849 * Controls toggling menu states between open/expanded and closed/collapsed.
839850 */
840851 toggleMenu ( ) {
841- if ( this . menuIsClosed ) {
852+ if ( ! this . isOpen ) {
842853 this . openMenu ( ) ;
843854 } else {
844855 this . closeMenu ( ) ;
@@ -860,7 +871,7 @@ export class Dropdown implements OnInit, AfterContentInit, AfterViewInit, OnDest
860871 * Controls when it's needed to apply the selection feedback
861872 */
862873 protected checkForReorder ( ) {
863- const topAfterReopen = this . menuIsClosed && this . selectionFeedback === "top-after-reopen" ;
874+ const topAfterReopen = ! this . isOpen && this . selectionFeedback === "top-after-reopen" ;
864875 if ( ( this . type === "multi" ) && ( topAfterReopen || this . selectionFeedback === "top" ) ) {
865876 this . view . reorderSelected ( ) ;
866877 }
0 commit comments