2017-06-13 11:08:06 +00:00
|
|
|
import {module} from '../module';
|
|
|
|
import './style.scss';
|
|
|
|
|
2017-06-15 05:45:01 +00:00
|
|
|
export default class DropDown {
|
2017-09-14 11:40:55 +00:00
|
|
|
constructor($element, $filter, $timeout) {
|
2017-06-15 05:45:01 +00:00
|
|
|
this.$element = $element;
|
2017-06-29 11:56:52 +00:00
|
|
|
this.$filter = $filter;
|
2017-09-14 11:40:55 +00:00
|
|
|
this.$timeout = $timeout;
|
|
|
|
|
|
|
|
this._search = null;
|
2017-06-29 11:56:52 +00:00
|
|
|
this.itemsFiltered = [];
|
2017-09-14 11:40:55 +00:00
|
|
|
this._activeOption = -1;
|
2017-09-27 10:27:18 +00:00
|
|
|
this._focusingFilter = false;
|
2017-11-23 13:07:55 +00:00
|
|
|
this._tryToShow = 0;
|
2017-09-14 11:40:55 +00:00
|
|
|
}
|
2017-12-12 12:38:23 +00:00
|
|
|
|
|
|
|
get container() {
|
|
|
|
return this.$element[0].querySelector('ul.dropdown');
|
|
|
|
}
|
2017-09-21 11:10:30 +00:00
|
|
|
get show() {
|
|
|
|
return this._show;
|
|
|
|
}
|
2017-10-04 06:47:16 +00:00
|
|
|
|
2017-09-21 11:10:30 +00:00
|
|
|
set show(value) {
|
|
|
|
let oldValue = this.show;
|
2017-11-23 13:07:55 +00:00
|
|
|
// I wait up to 1 second if the dropdown opens but there is no data to show
|
|
|
|
if (value && !oldValue && !this.itemsFiltered.length && this._tryToShow < 4) {
|
|
|
|
this.$timeout(() => {
|
|
|
|
this._tryToShow++;
|
|
|
|
this.show = true;
|
|
|
|
}, 250);
|
|
|
|
} else {
|
|
|
|
this._tryToShow = 0;
|
|
|
|
this._show = value;
|
|
|
|
this._setFocusInFilterInput(value, oldValue);
|
|
|
|
this.$timeout(() => {
|
|
|
|
this._calculatePosition(value, oldValue);
|
|
|
|
});
|
|
|
|
}
|
2017-10-04 06:47:16 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 11:40:55 +00:00
|
|
|
get search() {
|
|
|
|
return this._search;
|
|
|
|
}
|
2017-10-04 06:47:16 +00:00
|
|
|
|
2017-09-14 11:40:55 +00:00
|
|
|
set search(value) {
|
2017-10-11 10:57:13 +00:00
|
|
|
let val = (value === undefined || value === '') ? null : value;
|
2017-09-14 11:40:55 +00:00
|
|
|
this._search = val;
|
|
|
|
|
|
|
|
if (this.filterAction)
|
|
|
|
this.onFilterRest();
|
|
|
|
else
|
|
|
|
this.filterItems();
|
|
|
|
}
|
2017-10-04 06:47:16 +00:00
|
|
|
|
2017-09-14 11:40:55 +00:00
|
|
|
get activeOption() {
|
|
|
|
return this._activeOption;
|
|
|
|
}
|
2017-10-04 06:47:16 +00:00
|
|
|
|
2017-09-14 11:40:55 +00:00
|
|
|
set activeOption(value) {
|
|
|
|
if (value < 0) {
|
|
|
|
value = 0;
|
2017-11-22 12:10:33 +00:00
|
|
|
} else if (value >= this.itemsFiltered.length) {
|
|
|
|
value = this.showLoadMore ? this.itemsFiltered.length : this.itemsFiltered.length - 1;
|
2017-09-14 11:40:55 +00:00
|
|
|
}
|
|
|
|
this.$timeout(() => {
|
|
|
|
this._activeOption = value;
|
2017-11-22 12:10:33 +00:00
|
|
|
if (value && value >= this.itemsFiltered.length - 3 && !this.removeLoadMore) {
|
2017-10-03 10:10:30 +00:00
|
|
|
this.loadItems();
|
|
|
|
}
|
2017-09-14 11:40:55 +00:00
|
|
|
});
|
2017-06-15 05:45:01 +00:00
|
|
|
}
|
2017-06-29 11:56:52 +00:00
|
|
|
|
2017-11-22 12:10:33 +00:00
|
|
|
_setFocusInFilterInput(value, oldValue) {
|
|
|
|
if (value && !this._focusingFilter && oldValue !== value && this.filter) {
|
|
|
|
this.$timeout(() => {
|
2017-12-12 12:38:23 +00:00
|
|
|
let inputFilterSearch = this.$element[0].querySelector('input');
|
|
|
|
this._focusingFilter = true;
|
|
|
|
if (inputFilterSearch)
|
|
|
|
this.$timeout(() => {
|
|
|
|
inputFilterSearch.focus();
|
|
|
|
this._focusingFilter = false;
|
|
|
|
}, 250);
|
|
|
|
});
|
2017-11-22 12:10:33 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-23 13:07:55 +00:00
|
|
|
_background(create) {
|
|
|
|
let el = document.getElementById('ddBack');
|
|
|
|
if (el) {
|
|
|
|
el.parentNode.removeChild(el);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (create) {
|
|
|
|
el = document.createElement('div');
|
|
|
|
el.id = 'ddBack';
|
|
|
|
document.body.appendChild(el);
|
|
|
|
}
|
|
|
|
}
|
2017-11-22 12:10:33 +00:00
|
|
|
_calculatePosition(value, oldValue) {
|
2017-11-23 13:07:55 +00:00
|
|
|
if (value && !oldValue) {
|
2017-11-22 12:10:33 +00:00
|
|
|
if (this.parent === undefined) {
|
|
|
|
this.parent = this.$element.parent().parent();
|
|
|
|
}
|
|
|
|
|
|
|
|
let parentRect = this.parent.getBoundingClientRect();
|
|
|
|
let elemetRect = this.$element[0].getBoundingClientRect();
|
2017-11-23 13:07:55 +00:00
|
|
|
let instOffset = parentRect.bottom + elemetRect.height;
|
2017-11-22 12:10:33 +00:00
|
|
|
|
2017-11-23 13:07:55 +00:00
|
|
|
if (instOffset >= window.innerHeight) {
|
|
|
|
this._background(true);
|
|
|
|
this.$element.addClass('fixed-dropDown');
|
|
|
|
this.$element.css('top', `${(parentRect.top - elemetRect.height)}px`);
|
|
|
|
this.$element.css('left', `${(parentRect.x)}px`);
|
|
|
|
this.$element.css('height', `${elemetRect.height}px`);
|
|
|
|
}
|
|
|
|
} else if (!value && oldValue) {
|
|
|
|
this.$element.removeAttr('style');
|
|
|
|
if (this.itemWidth) {
|
|
|
|
this.$element.css('width', this.itemWidth + 'px');
|
2017-11-22 12:10:33 +00:00
|
|
|
}
|
2017-11-23 13:07:55 +00:00
|
|
|
this.$element.removeClass('fixed-dropDown');
|
|
|
|
this._background();
|
2017-11-22 12:10:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-29 11:56:52 +00:00
|
|
|
filterItems() {
|
|
|
|
this.itemsFiltered = this.search ? this.$filter('filter')(this.items, this.search) : this.items;
|
2017-06-29 06:13:30 +00:00
|
|
|
}
|
2017-06-29 11:56:52 +00:00
|
|
|
|
|
|
|
onFilterRest() {
|
2017-10-11 10:57:13 +00:00
|
|
|
if (this.filterAction) {
|
|
|
|
this.filterAction({search: this.search});
|
|
|
|
}
|
2017-06-29 06:13:30 +00:00
|
|
|
}
|
|
|
|
|
2017-06-22 10:03:01 +00:00
|
|
|
clearSearch() {
|
2017-09-14 11:40:55 +00:00
|
|
|
this.search = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
selectOption() {
|
2017-09-20 09:50:53 +00:00
|
|
|
if (this.activeOption >= 0 && this.activeOption < this.items.length && this.items[this.activeOption]) {
|
2017-09-14 11:40:55 +00:00
|
|
|
this.selected = this.items[this.activeOption];
|
2017-09-20 09:50:53 +00:00
|
|
|
this.show = false;
|
2017-09-14 11:40:55 +00:00
|
|
|
this.clearSearch();
|
2017-09-20 09:50:53 +00:00
|
|
|
} else if (this.showLoadMore && this.activeOption === this.items.length) {
|
|
|
|
this.loadMore();
|
2017-06-29 11:56:52 +00:00
|
|
|
}
|
2017-06-22 10:03:01 +00:00
|
|
|
}
|
2017-09-14 11:40:55 +00:00
|
|
|
|
|
|
|
onKeydown(event) {
|
|
|
|
if (this.show) {
|
|
|
|
switch (event.keyCode) {
|
|
|
|
case 13: // Enter
|
|
|
|
this.$timeout(() => {
|
|
|
|
this.selectOption();
|
|
|
|
});
|
|
|
|
event.preventDefault();
|
|
|
|
break;
|
|
|
|
case 27: // Escape
|
|
|
|
this.clearSearch();
|
|
|
|
break;
|
|
|
|
case 38: // Arrow up
|
|
|
|
this.activeOption--;
|
2017-09-20 09:50:53 +00:00
|
|
|
this.$timeout(() => {
|
|
|
|
this.setScrollPosition();
|
|
|
|
}, 100);
|
2017-09-14 11:40:55 +00:00
|
|
|
break;
|
|
|
|
case 40: // Arrow down
|
|
|
|
this.activeOption++;
|
2017-09-20 09:50:53 +00:00
|
|
|
this.$timeout(() => {
|
|
|
|
this.setScrollPosition();
|
|
|
|
}, 100);
|
2017-09-14 11:40:55 +00:00
|
|
|
break;
|
2017-11-22 12:10:33 +00:00
|
|
|
case 35: // End
|
|
|
|
this.activeOption = this.itemsFiltered.length - 1;
|
|
|
|
this.$timeout(() => {
|
|
|
|
this.setScrollPosition();
|
|
|
|
}, 100);
|
|
|
|
break;
|
|
|
|
case 36: // Start
|
|
|
|
this.activeOption = 0;
|
|
|
|
this.$timeout(() => {
|
|
|
|
this.setScrollPosition();
|
|
|
|
}, 100);
|
|
|
|
break;
|
2017-09-14 11:40:55 +00:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-20 09:50:53 +00:00
|
|
|
setScrollPosition() {
|
2017-11-22 12:10:33 +00:00
|
|
|
let child = this.$element[0].querySelector('ul.dropdown li.active');
|
2017-12-12 12:38:23 +00:00
|
|
|
if (child) {
|
|
|
|
let childRect = child.getBoundingClientRect();
|
|
|
|
let containerRect = this.container.getBoundingClientRect();
|
|
|
|
if (typeof child.scrollIntoView === 'function' && (childRect.top > containerRect.top + containerRect.height || childRect.top < containerRect.top)) {
|
|
|
|
child.scrollIntoView();
|
|
|
|
}
|
2017-09-20 09:50:53 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-20 11:52:53 +00:00
|
|
|
selectItem(item) {
|
|
|
|
this.selected = item;
|
|
|
|
if (this.multiple) {
|
|
|
|
item.checked = !item.checked;
|
|
|
|
this.show = true;
|
|
|
|
} else {
|
|
|
|
this.show = false;
|
|
|
|
}
|
|
|
|
}
|
2017-10-03 07:16:02 +00:00
|
|
|
loadItems() {
|
|
|
|
if (this.showLoadMore && this.loadMore) {
|
|
|
|
this.loadMore();
|
|
|
|
}
|
|
|
|
this.show = true;
|
|
|
|
}
|
2017-11-22 12:10:33 +00:00
|
|
|
loadFromScroll(e) {
|
|
|
|
let containerRect = e.target.getBoundingClientRect();
|
|
|
|
if (e.target.scrollHeight - e.target.scrollTop - containerRect.height <= 50) {
|
|
|
|
this.loadItems();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$onChanges(changesObj) {
|
|
|
|
if (changesObj.show && changesObj.itemWidth && changesObj.itemWidth.currentValue) {
|
|
|
|
this.$element.css('width', changesObj.itemWidth.currentValue + 'px');
|
|
|
|
}
|
|
|
|
if (changesObj.items) {
|
|
|
|
this.filterItems();
|
|
|
|
}
|
|
|
|
}
|
2017-09-14 11:40:55 +00:00
|
|
|
$onInit() {
|
|
|
|
if (this.parent)
|
|
|
|
this.parent.addEventListener('keydown', e => this.onKeydown(e));
|
2017-11-22 12:10:33 +00:00
|
|
|
if (this.container)
|
|
|
|
this.container.addEventListener('scroll', e => this.loadFromScroll(e));
|
2017-09-14 11:40:55 +00:00
|
|
|
}
|
|
|
|
$onDestroy() {
|
|
|
|
if (this.parent)
|
|
|
|
this.parent.removeEventListener('keydown', e => this.onKeydown(e));
|
2017-11-22 12:10:33 +00:00
|
|
|
if (this.container)
|
|
|
|
this.container.removeEventListener('scroll', e => this.loadFromScroll(e));
|
2017-09-14 11:40:55 +00:00
|
|
|
}
|
2017-06-15 05:45:01 +00:00
|
|
|
}
|
2017-10-04 06:47:16 +00:00
|
|
|
|
2017-09-14 11:40:55 +00:00
|
|
|
DropDown.$inject = ['$element', '$filter', '$timeout'];
|
2017-06-13 11:08:06 +00:00
|
|
|
|
|
|
|
module.component('vnDropDown', {
|
|
|
|
template: require('./drop-down.html'),
|
2017-06-15 05:45:01 +00:00
|
|
|
controller: DropDown,
|
2017-06-13 11:08:06 +00:00
|
|
|
bindings: {
|
|
|
|
items: '<',
|
|
|
|
show: '<',
|
2017-06-21 11:16:37 +00:00
|
|
|
filter: '@?',
|
2017-06-15 05:45:01 +00:00
|
|
|
selected: '=',
|
2017-09-14 11:40:55 +00:00
|
|
|
search: '=?',
|
2017-06-29 06:13:30 +00:00
|
|
|
loadMore: '&?',
|
2017-10-10 10:56:03 +00:00
|
|
|
removeLoadMore: '<?',
|
2017-06-29 11:56:52 +00:00
|
|
|
filterAction: '&?',
|
2017-10-10 09:14:35 +00:00
|
|
|
showLoadMore: '<?',
|
2017-09-14 11:40:55 +00:00
|
|
|
itemWidth: '<?',
|
2017-09-20 11:52:53 +00:00
|
|
|
parent: '<?',
|
|
|
|
multiple: '<?'
|
2017-09-20 09:50:53 +00:00
|
|
|
},
|
|
|
|
transclude: {
|
|
|
|
vnItem: '?vnItem'
|
2017-06-21 11:16:37 +00:00
|
|
|
}
|
2017-06-13 11:08:06 +00:00
|
|
|
});
|