import {module} from '../module'; import './style.scss'; import validKey from '../lib/keyCodes'; export default class DropDown { constructor($element, $filter, $timeout) { this.$element = $element; this.$filter = $filter; this.$timeout = $timeout; this._search = null; this.itemsFiltered = []; this._activeOption = -1; this._focusingFilter = false; this._tryToShow = 0; } get container() { return this.$element[0].querySelector('ul.dropdown'); } get show() { return this._show; } set show(value) { let oldValue = this.show; // It 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; if (this.activeOption === -1) { this.activeOption = 0; } }, 250); } else { this._tryToShow = 0; this._show = value; this._toggleDropDown(value, oldValue); } } get search() { return this._search; } set search(value) { let val = (value === undefined || value === '') ? null : value; this._search = val; if (this.filterAction) this.onFilterRest(); else this.filterItems(); } get activeOption() { return this._activeOption; } set activeOption(value) { if (value < 0) { value = 0; } else if (value >= this.itemsFiltered.length) { value = this.showLoadMore ? this.itemsFiltered.length : this.itemsFiltered.length - 1; } this.$timeout(() => { this._activeOption = value; if (value && value >= this.itemsFiltered.length - 3 && !this.removeLoadMore) { this.loadItems(); } }); } _toggleDropDown(value, oldValue) { this.$timeout(() => { this._eventScroll(value); this._calculatePosition(value, oldValue); }); } _eventScroll(add, num) { let count = num || 0; if (add) { if (this.container) { this.container.addEventListener('scroll', e => this.loadFromScroll(e)); // this.$timeout(() => { // falla al entrar por primera vez xq pierde el foco y cierra el dropdown // this._setFocusInFilterInput(); // }); } else if (count < 4) { count++; this.$timeout(() => { // wait angular ngIf this._eventScroll(add, count); }, 250); } } else if (this.container) { this.container.removeEventListener('scroll', e => this.loadFromScroll(e)); } } _setFocusInFilterInput() { let inputFilterSearch = this.$element[0].querySelector('input'); this._focusingFilter = true; if (inputFilterSearch) this.$timeout(() => { inputFilterSearch.focus(); this._focusingFilter = false; }, 250); } _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); } } _calculatePosition(value, oldValue) { if (value && !oldValue) { if (this.parent === undefined) { this.parent = this.$element.parent().parent(); } let parentRect = this.parent.getBoundingClientRect(); let elemetRect = this.$element[0].getBoundingClientRect(); let instOffset = parentRect.bottom + elemetRect.height; 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'); } this.$element.removeClass('fixed-dropDown'); this._background(); } } filterItems() { this.itemsFiltered = this.search ? this.$filter('filter')(this.items, this.search) : this.items; } onFilterRest() { if (this.filterAction) { this.filterAction({search: this.search}); } } clearSearch() { this.search = null; } selectOption() { if (this.activeOption >= 0 && this.activeOption < this.items.length && this.items[this.activeOption]) { this.selected = this.items[this.activeOption]; this.show = false; this.clearSearch(); } else if (this.showLoadMore && this.activeOption === this.items.length) { this.loadMore(); } } onKeydown(event) { if (this.show) { if (event.keyCode === 13) { // Enter this.$timeout(() => { this.selectOption(); }); event.preventDefault(); } else if (event.keyCode === 27) { // Escape this.clearSearch(); } else if (event.keyCode === 38) { // Arrow up this.activeOption--; this.$timeout(() => { this.setScrollPosition(); }, 100); } else if (event.keyCode === 40) { // Arrow down this.activeOption++; this.$timeout(() => { this.setScrollPosition(); }, 100); } else if (event.keyCode === 35) { // End this.activeOption = this.itemsFiltered.length - 1; this.$timeout(() => { this.setScrollPosition(); }, 100); } else if (event.keyCode === 36) { // Start this.activeOption = 0; this.$timeout(() => { this.setScrollPosition(); }, 100); } else if (this.filter) { let oldValue = this.search || ''; if (validKey(event)) { this.search = oldValue + String.fromCharCode(event.keyCode); } else if (event.keyCode === 8) { // backSpace this.search = oldValue.slice(0, -1); } } /* else { console.error(`Error: keyCode ${event.keyCode} not supported`); } */ } } setScrollPosition() { let child = this.$element[0].querySelector('ul.dropdown li.active'); 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(); } } } selectItem(item) { this.selected = item; if (this.multiple) { item.checked = !item.checked; this.show = true; } else { this.show = false; } } loadItems() { if (this.showLoadMore && this.loadMore) { this.loadMore(); } this.show = true; } 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(); } } $onInit() { if (this.parent) this.parent.addEventListener('keydown', e => this.onKeydown(e)); } $onDestroy() { if (this.parent) this.parent.removeEventListener('keydown', e => this.onKeydown(e)); } } DropDown.$inject = ['$element', '$filter', '$timeout']; module.component('vnDropDown', { template: require('./drop-down.html'), controller: DropDown, bindings: { items: '<', show: '<', filter: '@?', selected: '=', search: '=?', loadMore: '&?', removeLoadMore: '