ng2-bootstrap-base-modified
Version:
Native Angular Bootstrap Components Typeahead modified
205 lines (174 loc) • 6.15 kB
text/typescript
/* tslint:disable:max-file-line-count */
import {
ChangeDetectorRef, Directive, ElementRef, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output
} from '@angular/core';
import { isBs3 } from '../utils/ng2-bootstrap-config';
import { DropdownService } from './dropdown.service';
import { DropdownConfig } from './dropdown.config';
/**
* Mark dropdown content with this directive
*/
export class DropdownDirective implements OnInit, OnDestroy {
private dropdownService: DropdownService;
/** if `true` dropdown will be opened */
public get isOpen(): boolean {
return this._isOpen;
}
public set isOpen(value: boolean) {
if (this._isOpen === !!value) {
// don't emit events
return;
}
this._isOpen = !!value;
// todo: implement after porting position
// if (this.appendToBody && this.menuEl) {
//
// }
// todo: $animate open<->close transitions, as soon as ng2Animate will be
// ready
if (this.isOpen) {
this.focusToggleElement();
this.dropdownService.open(this);
} else {
this.dropdownService.close(this);
this.selectedOption = void 0;
}
this.onToggle.emit(this.isOpen);
this.isOpenChange.emit(this.isOpen);
this._changeDetector.markForCheck();
// todo: implement call to setIsOpen if set and function
}
/** behaviour vary:
* - nonInput - (default) automatically closes the dropdown when any of its elements is clicked — as long as the clicked element is not an input or a textarea.
* - always - automatically closes the dropdown when any of its elements is clicked
* - outsideClick - closes the dropdown automatically only when the user clicks any element outside the dropdown
* - disabled - disables the auto close. You can then control the open/close status of the dropdown manually, by using is-open. Please notice that the dropdown will still close if the toggle is clicked, the esc key is pressed or another dropdown is open
*/
public autoClose: string;
/** if true will enable navigation of dropdown list elements with the arrow keys */
public keyboardNav: boolean;
// enum string: ['always', 'outsideClick', 'disabled']
/** Allows to attach dropdown to body, will be replaced with container="body" */
public appendToBody: boolean;
/** fired when dropdown toggles, $event:boolean equals dropdown isOpen state */
public onToggle: EventEmitter<boolean> = new EventEmitter<boolean>(false);
/** fired when isOpen value changes */
public isOpenChange: EventEmitter<boolean> = new EventEmitter<boolean>(false);
public addClass: boolean = true;
public get isBs3(): boolean {
return isBs3();
}
// index of selected element
public selectedOption: number;
// drop menu html
public menuEl: ElementRef;
// drop down toggle element
public toggleEl: ElementRef;
public el: ElementRef;
protected _isOpen: boolean = false;
protected _changeDetector: ChangeDetectorRef;
public constructor(
el: ElementRef,
ref: ChangeDetectorRef,
dropdownService: DropdownService,
config: DropdownConfig
) {
// @Query('dropdownMenu', {descendants: false})
// dropdownMenuList:QueryList<ElementRef>) {
this.el = el;
this._changeDetector = ref;
this.dropdownService = dropdownService;
Object.assign(this, config);
// todo: bind to route change event
}
public ngOnInit(): void {
if (this.isOpen) {
// todo: watch for event get-isOpen?
}
}
public ngOnDestroy(): void {
if (this.appendToBody && this.menuEl) {
this.menuEl.nativeElement.remove();
}
}
public set dropDownMenu(dropdownMenu: { el: ElementRef }) {
// init drop down menu
this.menuEl = dropdownMenu.el;
if (this.appendToBody) {
window.document.body.appendChild(this.menuEl.nativeElement);
}
}
public set dropDownToggle(dropdownToggle: { el: ElementRef }) {
// init toggle element
this.toggleEl = dropdownToggle.el;
}
public show():void {
/** prevent global event handling */
this.dropdownService.preventEventHandling();
this.isOpen = true;
}
public hide():void {
/** prevent global event handling */
this.dropdownService.preventEventHandling();
this.isOpen = false;
}
public toggle(open?: boolean): boolean {
return this.isOpen = arguments.length ? !!open : !this.isOpen;
}
public focusDropdownEntry(keyCode: number): void {
// If append to body is used.
let hostEl = this.menuEl ?
this.menuEl.nativeElement :
this.el.nativeElement.getElementsByTagName('ul')[0];
if (!hostEl) {
// todo: throw exception?
return;
}
let elems = hostEl.getElementsByTagName('a');
if (!elems || !elems.length) {
// todo: throw exception?
return;
}
// todo: use parseInt to detect isNumber?
// todo: or implement selectedOption as a get\set pair with parseInt on set
switch (keyCode) {
case (40):
if (typeof this.selectedOption !== 'number') {
this.selectedOption = 0;
break;
}
if (this.selectedOption === elems.length - 1) {
break;
}
this.selectedOption++;
break;
case (38):
if (typeof this.selectedOption !== 'number') {
return;
}
if (this.selectedOption === 0) {
// todo: return?
break;
}
this.selectedOption--;
break;
default:
break;
}
elems[this.selectedOption].focus();
}
public focusToggleElement(): void {
if (this.toggleEl) {
this.toggleEl.nativeElement.focus();
}
}
}