Skip to content

Commit

Permalink
feat: determine dropdownPosition automatically (#238)
Browse files Browse the repository at this point in the history
closes #237
  • Loading branch information
pablomaurer authored and anjmao committed Feb 7, 2018
1 parent db5988e commit d4404f7
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 16 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ map: {
| loadingText | string | `Loading...` | no | Set custom text when for loading items |
| [typeahead] | Subject | `-` | no | Custom autocomplete or filter. |
| [disableVirtualScroll] | boolean | false | no | Disable virtual scroll |
| dropdownPosition | `bottom`,`top` | `bottom` | no | Set the dropdown position on open |
| dropdownPosition | `bottom`,`top`,`auto` | `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 |
Expand Down
17 changes: 15 additions & 2 deletions demo/app/examples/dropdown-positions.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
</ng-select>
---
<hr>
<br>
<label>
<input [(ngModel)]="dropdownPosition" type="radio" [value]="'top'">
top
Expand All @@ -28,6 +28,19 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
<input [(ngModel)]="dropdownPosition" type="radio" [value]="'bottom'">
bottom
</label>
<hr>
<p>
Using "Auto" it still defaults to bottom, but if the dropdown would be out of view,
it will automatically change the dropdownPosition to top.
</p>
---html,true
<ng-select [dropdownPosition]="'auto'"
[searchable]="false"
[items]="cities">
</ng-select>
---
`
})
export class DropdownPositionsComponent {
Expand Down
2 changes: 1 addition & 1 deletion src/ng-select/ng-select.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
</span>
</div>

<div class="ng-select-dropdown-outer" [class.top]="dropdownPosition === 'top'" [class.bottom]="dropdownPosition === 'bottom'" [ngStyle]="{'visibility': isOpen ? 'visible': 'hidden'}" #dropdownPanel>
<div class="ng-select-dropdown-outer" [class.top]="currentDropdownPosition === 'top'" [class.bottom]="currentDropdownPosition === 'bottom'" [ngStyle]="{'visibility': isOpen ? 'visible': 'hidden'}" #dropdownPanel>
<div *ngIf="headerTemplate" class="ng-dropdown-header">
<ng-container [ngTemplateOutlet]="headerTemplate"></ng-container>
</div>
Expand Down
17 changes: 9 additions & 8 deletions src/ng-select/ng-select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,22 +721,22 @@ describe('NgSelectComponent', function () {
});

describe('Dropdown position', () => {
let fixture: ComponentFixture<NgSelectBasicTestCmp>

beforeEach(() => {
fixture = createTestingModule(
it('should be set to `bottom` by default', () => {
const fixture = createTestingModule(
NgSelectBasicTestCmp,
`<ng-select id="select"></ng-select>`);
});
`<ng-select id="select"></ng-select>`);

it('should be set to `bottom` by default', () => {
const classes = fixture.debugElement.query(By.css('ng-select')).classes;
expect(classes.bottom).toBeTruthy();
expect(classes.top).toBeFalsy();
});

it('should allow changing dropdown position', () => {
fixture.componentInstance.select.dropdownPosition = 'top';
const fixture = createTestingModule(
NgSelectBasicTestCmp,
`<ng-select id="select" [dropdownPosition]="dropdownPosition"></ng-select>`);

fixture.componentInstance.dropdownPosition = 'top';
fixture.detectChanges();

const classes = fixture.debugElement.query(By.css('ng-select')).classes;
Expand Down Expand Up @@ -1442,6 +1442,7 @@ class NgSelectBasicTestCmp {
@ViewChild(NgSelectComponent) select: NgSelectComponent;
selectedCity: { id: number; name: string };
multiple = false;
dropdownPosition = 'bottom';
cities = [
{ id: 1, name: 'Vilnius' },
{ id: 2, name: 'Kaunas' },
Expand Down
29 changes: 25 additions & 4 deletions src/ng-select/ng-select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ const NG_SELECT_VALUE_ACCESSOR = {
host: {
'role': 'dropdown',
'class': 'ng-select',
'[class.top]': 'dropdownPosition === "top"',
'[class.bottom]': 'dropdownPosition === "bottom"',
'[class.top]': 'currentDropdownPosition === "top"',
'[class.bottom]': 'currentDropdownPosition === "bottom"',
}
})
export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit, ControlValueAccessor {
Expand All @@ -73,7 +73,7 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
@Input() addTagText: string;
@Input() loadingText: string;
@Input() clearAllText: string;
@Input() dropdownPosition: 'bottom' | 'top' = 'bottom';
@Input() dropdownPosition: 'bottom' | 'top' | 'auto';
@Input() appendTo: string;
@Input() loading = false;
@Input() closeOnSelect = true;
Expand Down Expand Up @@ -117,6 +117,7 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
itemsList = new ItemsList(this);
viewPortItems: NgOption[] = [];
filterValue: string = null;
currentDropdownPosition: 'bottom' | 'top' | 'auto' = 'bottom';

private _ngModel: any = null;
private _defaultLabel = 'label';
Expand Down Expand Up @@ -156,6 +157,9 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
if (changes.items) {
this._setItems(changes.items.currentValue || []);
}
if (changes.dropdownPosition) {
this.currentDropdownPosition = changes.dropdownPosition.currentValue;
}
}

ngOnInit() {
Expand Down Expand Up @@ -277,6 +281,9 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
this._scrollToMarked();
this._focusSearchInput();
this.openEvent.emit();
if (this.dropdownPosition === 'auto') {
this._autoPositionDropdown();
}
if (this.appendTo) {
this._updateDropdownPosition();
}
Expand Down Expand Up @@ -503,13 +510,27 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
const selectRect = select.getBoundingClientRect();
const offsetTop = selectRect.top - bodyRect.top;
const offsetLeft = selectRect.left - bodyRect.left;
const topDelta = this.dropdownPosition === 'bottom' ? selectRect.height : -dropdownPanel.clientHeight;
const topDelta = this.currentDropdownPosition === 'bottom' ? selectRect.height : -dropdownPanel.clientHeight;
dropdownPanel.style.top = offsetTop + topDelta + 'px';
dropdownPanel.style.bottom = 'auto';
dropdownPanel.style.left = offsetLeft + 'px';
dropdownPanel.style.width = selectRect.width + 'px';
}

private _autoPositionDropdown() {
const selectRect = this.elementRef.nativeElement.getBoundingClientRect();
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const offsetTop = selectRect.top + window.pageYOffset;
const height = selectRect.height;
const dropdownHeight = this.dropdownPanel.nativeElement.getBoundingClientRect().height;

if (offsetTop + height + dropdownHeight > scrollTop + document.documentElement.clientHeight) {
this.currentDropdownPosition = 'top';
} else {
this.currentDropdownPosition = 'bottom';
}
}

private _validateWriteValue(value: any) {
if (!this._isDefined(value)) {
return;
Expand Down

0 comments on commit d4404f7

Please sign in to comment.