11import {
22 Component ,
33 OnChanges ,
4- OnInit ,
54 ContentChild ,
65 Input ,
76 Output ,
@@ -11,12 +10,12 @@ import {
1110 EventEmitter ,
1211 AfterViewInit ,
1312 AfterContentInit ,
14- HostBinding
13+ HostBinding ,
14+ OnInit
1515} from "@angular/core" ;
16- import { PillInput } from "../pill-input/pill-input.component" ;
1716import { AbstractDropdownView } from "./../dropdown/abstract-dropdown-view.class" ;
1817import { ListItem } from "./../dropdown/list-item.interface" ;
19- import { NG_VALUE_ACCESSOR , ControlValueAccessor } from "@angular/forms" ;
18+ import { NG_VALUE_ACCESSOR } from "@angular/forms" ;
2019
2120/**
2221 * ComboBoxes are similar to dropdowns, except a combobox provides an input field for users to search items and (optionally) add their own.
@@ -32,37 +31,65 @@ import { NG_VALUE_ACCESSOR, ControlValueAccessor } from "@angular/forms";
3231 selector : "ibm-combo-box" ,
3332 template : `
3433 <div
35- role="combobox"
36- [attr.aria-expanded]="open">
37- <ibm-pill-input
38- [pills]="pills"
39- [placeholder]="placeholder"
40- [displayValue]="selectedValue"
41- [type]="type"
42- [disabled]="disabled"
43- (updatePills)="updatePills()"
44- (search)="onSearch($event)"
45- (submit)="onSubmit($event)">
46- </ibm-pill-input>
47- <button
48- #dropdownButton
34+ [attr.aria-expanded]="open"
35+ role="button"
36+ class="bx--list-box__field"
37+ tabindex="0"
38+ type="button"
39+ aria-label="close menu"
40+ aria-haspopup="true">
41+ <div
42+ *ngIf="type === 'multi' && pills.length > 0"
43+ (click)="clearSelected()"
4944 role="button"
50- class="btn--add-on"
51- type="button"
45+ class="bx--list-box__selection bx--list-box__selection--multi"
46+ tabindex="0"
47+ title="Clear all selected items">
48+ {{ pills.length }}
49+ <svg
50+ fill-rule="evenodd"
51+ height="10"
52+ role="img"
53+ viewBox="0 0 10 10"
54+ width="10"
55+ focusable="false"
56+ aria-label="Clear all selected items"
57+ alt="Clear all selected items">
58+ <title>Clear all selected items</title>
59+ <path d="M6.32 5L10 8.68 8.68 10 5 6.32 1.32 10 0 8.68 3.68 5 0 1.32 1.32 0 5 3.68 8.68 0 10 1.32 6.32 5z"></path>
60+ </svg>
61+ </div>
62+ <input
5263 [disabled]="disabled"
53- [ngStyle]="{
54- height: open?null:'30px'
55- }"
56- (click)="toggleDropdown()">
57- <ibm-static-icon
58- icon="chevron_down" [size]="(size === 'sm' ? 'sm' : 'md')"
59- [ngClass]="{
60- open: open
61- }">
62- </ibm-static-icon>
63- </button>
64+ [attr.aria-expanded]="open"
65+ (click)="toggleDropdown()"
66+ (keyup)="onSearch($event.target.value)"
67+ [value]="selectedValue"
68+ class="bx--text-input"
69+ aria-label="ListBox input field"
70+ role="combobox"
71+ aria-autocomplete="list"
72+ autocomplete="off"
73+ placeholder="Filter..."/>
74+ <div
75+ [ngClass]="{'bx--list-box__menu-icon--open': open}"
76+ class="bx--list-box__menu-icon">
77+ <svg
78+ fill-rule="evenodd"
79+ height="5"
80+ role="img"
81+ viewBox="0 0 10 5"
82+ width="10"
83+ alt="Close menu"
84+ aria-label="Close menu">
85+ <title>Close menu</title>
86+ <path d="M0 0l5 4.998L10 0z"></path>
87+ </svg>
88+ </div>
6489 </div>
65- <div class="dropdown_menu">
90+ <div
91+ #dropdownMenu
92+ *ngIf="open">
6693 <ng-content></ng-content>
6794 </div>
6895 ` ,
@@ -74,7 +101,7 @@ import { NG_VALUE_ACCESSOR, ControlValueAccessor } from "@angular/forms";
74101 }
75102 ]
76103} )
77- export class ComboBox implements OnChanges , AfterViewInit , AfterContentInit {
104+ export class ComboBox implements OnChanges , OnInit , AfterViewInit , AfterContentInit {
78105 /**
79106 * List of items to fill the content with.
80107 *
@@ -171,12 +198,9 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit {
171198 @Output ( ) close : EventEmitter < any > = new EventEmitter < any > ( ) ;
172199 /** ContentChild reference to the instantiated dropdown list */
173200 @ContentChild ( AbstractDropdownView ) view : AbstractDropdownView ;
174- /** ContentChild reference to the instantiated dropdown button */
175- @ViewChild ( "dropdownButton" ) dropdownButton ;
176- /** ViewChild of the pill input component */
177- @ViewChild ( PillInput ) pillInput : PillInput ;
178-
179- @HostBinding ( "attr.class" ) class = "combobox" ;
201+ @ViewChild ( "dropdownMenu" ) dropdownMenu ;
202+ @HostBinding ( "class" ) class = "bx--combo-box bx--list-box" ;
203+ @HostBinding ( "attr.role" ) role = "listbox" ;
180204 @HostBinding ( "style.display" ) display = "block" ;
181205
182206 public open = false ;
@@ -185,8 +209,6 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit {
185209 public pills = [ ] ;
186210 /** used to update the displayValue of `n-pill-input` */
187211 public selectedValue = "" ;
188- /** internal reference to the dropdown list */
189- private dropdown ;
190212
191213 private noop = this . _noop . bind ( this ) ;
192214 private onTouchedCallback : ( ) => void = this . _noop ;
@@ -213,6 +235,12 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit {
213235 }
214236 }
215237
238+ ngOnInit ( ) {
239+ if ( this . type === "multi" ) {
240+ this . class = "bx--multi-select bx--combo-box bx--list-box" ;
241+ }
242+ }
243+
216244 /**
217245 * Sets initial state that depends on child components
218246 * Subscribes to select events and handles focus/filtering/initial list updates
@@ -253,10 +281,8 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit {
253281 * Binds event handlers against the rendered view
254282 */
255283 ngAfterViewInit ( ) {
256- this . dropdown = this . elementRef . nativeElement . querySelector ( ".dropdown_menu" ) ;
257284 document . addEventListener ( "click" , ev => {
258285 if ( ! this . elementRef . nativeElement . contains ( ev . target ) ) {
259- this . pillInput . expanded = false ;
260286 if ( this . open ) {
261287 this . closeDropdown ( ) ;
262288 }
@@ -272,11 +298,11 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit {
272298 hostkeys ( ev : KeyboardEvent ) {
273299 if ( ev . key === "Escape" ) {
274300 this . closeDropdown ( ) ;
275- } else if ( ev . key === "ArrowDown" && ! this . dropdown . contains ( ev . target ) ) {
301+ } else if ( ev . key === "ArrowDown" && ! this . dropdownMenu . nativeElement . contains ( ev . target ) ) {
276302 ev . stopPropagation ( ) ;
277303 this . openDropdown ( ) ;
278304 setTimeout ( ( ) => this . view . getCurrentElement ( ) . focus ( ) , 0 ) ;
279- } else if ( ev . key === "ArrowUp" && this . dropdown . contains ( ev . target ) && ! this . view [ "hasPrevElement" ] ( ) ) {
305+ } else if ( ev . key === "ArrowUp" && this . dropdownMenu . nativeElement . contains ( ev . target ) && ! this . view [ "hasPrevElement" ] ( ) ) {
280306 this . elementRef . nativeElement . querySelector ( ".combobox_input" ) . focus ( ) ;
281307 }
282308 }
@@ -319,6 +345,17 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit {
319345 this . propagateChangeCallback ( this . view . getSelected ( ) ) ;
320346 }
321347
348+ public clearSelected ( ) {
349+ this . items = this . items . map ( item => {
350+ if ( ! item . disabled ) {
351+ item . selected = false ;
352+ }
353+ return item ;
354+ } ) ;
355+ this . view [ "updateList" ] ( this . items ) ;
356+ this . updatePills ( ) ;
357+ }
358+
322359 /**
323360 * Closes the dropdown and emits the close event.
324361 * @memberof ComboBox
0 commit comments