Skip to content

Commit

Permalink
feat: Close dropdown on select by default (#226)
Browse files Browse the repository at this point in the history
closes #222
  • Loading branch information
anjmao committed Feb 2, 2018
1 parent 0831789 commit 77d36ca
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 145 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ map: {
| dropdownPosition | `bottom`,`top` | `bottom` | no | Set the dropdown position on open |
| appendTo | string | null | no | Append drodown to body or any other element using css selector |
| loading | boolean | `-` | no | you can set the loading state from the outside (e.g. async items loading) |
| closeOnSelect | boolean | true | no | whether to close the menu when a value is selected |

| Output | Description |
| ------------- | ------------- |
Expand Down
57 changes: 29 additions & 28 deletions demo/app/examples/multi.component.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { DataService } from '../shared/data.service';

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<label>Select multiple elements</label>
---html,true
<ng-select
[items]="companiesNames"
[items]="people$1 | async"
[multiple]="true"
[(ngModel)]="selectedCompanies">
bindLabel="name"
[(ngModel)]="selectedPeople1">
</ng-select>
---
<p>
Selected value: {{selectedCompanies | json}} <br>
<div class="mt-3">
Selected value: <br />
<ul>
<li *ngFor="let item of selectedPeople1">{{item.name}}</li>
</ul>
<button (click)="clearModel()" class="btn btn-secondary btn-sm">Clear model</button>
</p>
</div>
<hr/>
<label>Disabled multiple elements</label>
---html,true
<ng-select
[items]="companies"
[items]="people$2 | async"
bindLabel="name"
[multiple]="true"
[disabled]="disable"
[(ngModel)]="selectedCompaniesDisabled">
[(ngModel)]="selectedPeople2">
</ng-select>
---
<br>
Expand All @@ -35,7 +39,7 @@ import { Observable } from 'rxjs/Observable';
<label>Custom label templates</label>
---html,true
<ng-select
[items]="users"
[items]="githubUsers$ | async"
[multiple]="true"
[(ngModel)]="selectedUsers">
Expand All @@ -55,36 +59,33 @@ import { Observable } from 'rxjs/Observable';
})
export class SelectMultiComponent {

users: any[] = [];
companies: any[] = [];
selectedCompanies: any;
selectedCompaniesDisabled: any;
people$1: Observable<any[]>;
selectedPeople1 = [];

people$2: Observable<any[]>;
selectedPeople2 = [];
disable = true;

/* tslint:disable */
companiesNames = ['Miškas', 'Žalias', 'Flexigen', 'Rooforia', 'Rooblia', 'Tropoli', 'Eargo', 'Gadtron', 'Elentrix', 'Terragen', 'Medalert', 'Xelegyl', 'Bristo', 'Xylar', 'Imperium', 'Kangle', 'Earwax', 'Zanity', 'Portico', 'Tsunamia', 'Kage', 'Comstar', 'Radiantix', 'Bostonic', 'Geekko', 'Eventex', 'Stockpost', 'Silodyne', 'Enersave', 'Perkle', 'Pyramis', 'Accuprint', 'Papricut', 'Pathways', 'Circum', 'Gology', 'Buzzworks', 'Dancerity', 'Zounds', 'Diginetic', 'Snips', 'Chillium', 'Exotechno', 'Accufarm', 'Vidto', 'Signidyne', 'Escenta', 'Sureplex', 'Quarmony', 'Interfind', 'Exoswitch', 'Mondicil', 'Pyramia', 'Digitalus', 'Earthplex', 'Limozen', 'Twiist', 'Tubalum', 'Securia', 'Uni', 'Biospan', 'Zensus', 'Memora'];
/* tslint:enable */
githubUsers$: Observable<any[]>;
selectedUsers = [];

constructor(private http: HttpClient) { }
constructor(private dataService: DataService) { }

ngOnInit() {
this.companiesNames.forEach((c, i) => {
this.companies.push({ id: i, name: c });
});
this.people$1 = this.dataService.getPeople();
this.people$2 = this.dataService.getPeople();
this.githubUsers$ = this.dataService.getGithubAccounts('anjm');

this.selectedCompaniesDisabled = [{ id: 0, name: 'Miškas' }, { id: 1, name: 'Žalias' }];
this.loadGithubUsers('anjm').subscribe(users => {
this.users = users;
})
this.selectedPeople2 = [
{ id: '5a15b13c2340978ec3d2c0ea', name: 'Rochelle Estes' },
{ id: '5a15b13c728cd3f43cc0fe8a', name: 'Marquez Nolan' }
];
}

clearModel() {
this.selectedCompanies = [];
this.selectedPeople1 = [];
}

loadGithubUsers(term: string): Observable<any[]> {
return this.http.get<any>(`https://api.github.com/search/users?q=${term}`).map(rsp => rsp.items);
}
}


72 changes: 37 additions & 35 deletions src/ng-select/items-list.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,59 @@
import { NgOption } from './ng-select.types';
import * as searchHelper from './search-helper';
import { NgSelectComponent } from './ng-select.component';

export class ItemsList {

items: NgOption[] = [];
filteredItems: NgOption[] = [];

private _items: NgOption[] = [];
private _filteredItems: NgOption[] = [];
private _markedIndex = -1;
private _selected: NgOption[] = [];
private _multiple = false;
private _bindLabel: string;

constructor(private _ngSelect: NgSelectComponent) {}

get items(): NgOption[] {
return this._items;
}

get filteredItems(): NgOption[] {
return this._filteredItems;
}

get value(): NgOption[] {
return this._selected;
}

get markedItem(): NgOption {
return this.filteredItems[this._markedIndex];
return this._filteredItems[this._markedIndex];
}

get markedIndex(): number {
return this._markedIndex;
}

setItems(items: any[], bindLabel: string, simple: boolean = false) {
this._bindLabel = bindLabel;
this.items = items.map((item, index) => this.mapItem(item, simple, index));
this.filteredItems = [...this.items];
}

setMultiple(multiple: boolean) {
this._multiple = multiple;
this.clearSelected();
setItems(items: any[], simple = false) {
this._items = items.map((item, index) => this.mapItem(item, simple, index));
this._filteredItems = [...this._items];
}

select(item: NgOption) {
if (item.selected) {
return;
}
if (!this._multiple) {
if (!this._ngSelect.multiple) {
this.clearSelected();
}
this._selected.push(item);
item.selected = true;
}

findItem(value: any, bindValue: string): NgOption {
if (bindValue) {
return this.items.find(item => item.value[bindValue] === value);
findItem(value: any): NgOption {
if (this._ngSelect.bindValue) {
return this._items.find(item => item.value[this._ngSelect.bindValue] === value);
}
const index = this.items.findIndex(x => x.value === value);
return index > -1 ? this.items[index] :
this.items.find(item => item.label && item.label === this.resolveNested(value, this._bindLabel));
const index = this._items.findIndex(x => x.value === value);
return index > -1 ? this._items[index] :
this._items.find(item => item.label && item.label === this.resolveNested(value, this._ngSelect.bindLabel));
}

unselect(item: NgOption) {
Expand All @@ -70,12 +72,12 @@ export class ItemsList {

addItem(item: any) {
const option = {
index: this.items.length,
label: this.resolveNested(item, this._bindLabel),
index: this._items.length,
label: this.resolveNested(item, this._ngSelect.bindLabel),
value: item
}
this.items.push(option);
this.filteredItems.push(option);
this._items.push(option);
this._filteredItems.push(option);
return option;
}

Expand All @@ -89,11 +91,11 @@ export class ItemsList {

filter(term: string) {
const filterFuncVal = this._getDefaultFilterFunc(term);
this.filteredItems = term ? this.items.filter(val => filterFuncVal(val)) : this.items;
this._filteredItems = term ? this._items.filter(val => filterFuncVal(val)) : this._items;
}

clearFilter() {
this.filteredItems = [...this.items];
this._filteredItems = [...this._items];
}

unmarkItem() {
Expand All @@ -109,16 +111,16 @@ export class ItemsList {
}

markItem(item: NgOption) {
this._markedIndex = this.filteredItems.indexOf(item);
this._markedIndex = this._filteredItems.indexOf(item);
}

markSelectedOrDefault(markDefault: boolean) {
if (this.filteredItems.length === 0) {
if (this._filteredItems.length === 0) {
return;
}

if (this._lastSelectedItem) {
this._markedIndex = this.filteredItems.indexOf(this._lastSelectedItem);
this._markedIndex = this._filteredItems.indexOf(this._lastSelectedItem);
} else {
this._markedIndex = markDefault ? 0 : -1;
}
Expand Down Expand Up @@ -147,7 +149,7 @@ export class ItemsList {
option = item;
label = item;
} else {
label = this.resolveNested(option, this._bindLabel);
label = this.resolveNested(option, this._ngSelect.bindLabel);
}
return {
index: index,
Expand All @@ -159,13 +161,13 @@ export class ItemsList {

private _getNextItemIndex(steps: number) {
if (steps > 0) {
return (this._markedIndex === this.filteredItems.length - 1) ? 0 : (this._markedIndex + 1);
return (this._markedIndex === this._filteredItems.length - 1) ? 0 : (this._markedIndex + 1);
}
return (this._markedIndex <= 0) ? (this.filteredItems.length - 1) : (this._markedIndex - 1);
return (this._markedIndex <= 0) ? (this._filteredItems.length - 1) : (this._markedIndex - 1);
}

private _stepToItem(steps: number) {
if (this.filteredItems.length === 0) {
if (this._filteredItems.length === 0) {
return;
}

Expand Down
4 changes: 2 additions & 2 deletions src/ng-select/ng-select.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<div *ngIf="headerTemplate" class="ng-dropdown-header">
<ng-container [ngTemplateOutlet]="headerTemplate"></ng-container>
</div>
<virtual-scroll role="listbox" class="ng-select-dropdown" [disabled]="disableVirtualScroll" [bufferAmount]="4" [items]="itemsList.filteredItems" (update)="viewPortItems = $event">
<ng-select-virtual-scroll class="ng-select-dropdown" [disabled]="disableVirtualScroll" [bufferAmount]="4" [items]="itemsList.filteredItems" (update)="viewPortItems = $event">
<div class="ng-option" role="option" (click)="toggleItem(item)" (mousedown)="$event.preventDefault()" (mouseover)="onItemHover(item)"
*ngFor="let item of viewPortItems"
[class.disabled]="item.disabled"
Expand All @@ -65,7 +65,7 @@
<div class="ng-option" [class.marked]="!itemsList.markedItem" (mouseover)="itemsList.unmarkItem()" role="option" (click)="selectTag()" *ngIf="showAddTag()">
<span><span class="ng-tag-label">{{addTagText}}</span>"{{filterValue}}"</span>
</div>
</virtual-scroll>
</ng-select-virtual-scroll>

<div class="ng-select-dropdown" *ngIf="showNoItemsFound() && !addTag">
<div class="ng-option disabled">
Expand Down
31 changes: 31 additions & 0 deletions src/ng-select/ng-select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,37 @@ describe('NgSelectComponent', function () {
});
});

describe('Dropdown', () => {
it('should close on option select by default', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectBasicTestCmp,
`<ng-select [items]="cities"
bindLabel="name"
[(ngModel)]="city">
</ng-select>`);

selectOption(fixture, KeyCode.ArrowDown, 0);
tickAndDetectChanges(fixture);

expect(fixture.componentInstance.select.isOpen).toBeFalsy();
}));

it('should not close on option select when [closeOnSelect]="false"', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectBasicTestCmp,
`<ng-select [items]="cities"
bindLabel="name"
[closeOnSelect]="false"
[(ngModel)]="city">
</ng-select>`);

selectOption(fixture, KeyCode.ArrowDown, 0);
tickAndDetectChanges(fixture);

expect(fixture.componentInstance.select.isOpen).toBeTruthy();
}));
});

describe('Keyboard events', () => {
let fixture: ComponentFixture<NgSelectBasicTestCmp>;

Expand Down

0 comments on commit 77d36ca

Please sign in to comment.