@jchinc/ng-multiselect
Version:
Control de selección múltiple de elementos
377 lines (369 loc) • 23.9 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('/core'), require('@angular/forms'), require('/common'), require('rxjs/add/operator/debounceTime')) :
typeof define === 'function' && define.amd ? define(['exports', '/core', '@angular/forms', '/common', 'rxjs/add/operator/debounceTime'], factory) :
(factory((global.ngMultiselect = {}),global.ng.core,global.ng.forms,global.ng.common));
}(this, (function (exports,core,forms,common) { 'use strict';
var NgMultiselectItem = /** @class */ (function () {
function NgMultiselectItem(key, value, valueSecondary, selected, filters) {
if (key === void 0) { key = ''; }
if (value === void 0) { value = ''; }
if (valueSecondary === void 0) { valueSecondary = ''; }
if (selected === void 0) { selected = false; }
if (filters === void 0) { filters = []; }
this.key = key;
this.value = value;
this.valueSecondary = valueSecondary;
this.selected = selected;
this.filters = filters;
}
return NgMultiselectItem;
}());
var NgMultiselectComponent = /** @class */ (function () {
function NgMultiselectComponent(_renderer, _formBuilder) {
this._renderer = _renderer;
this._formBuilder = _formBuilder;
this.dropdownVisible = false;
this.filteredItems = [];
this.selectedItemsKeys = '';
this._hoveredItemIndex = -1;
this._selectedItems = [];
/**
* Altura de los elementos de la caja de selección.
*/
this._listItemHeight = 44;
this.source = [];
this.top = 0;
/**
* Para permitir que funcione como un select de 1 sólo registro.
*/
this.onlyOneRow = false;
this.inputSearchPlaceHolder = 'Buscar';
this.disabled = false;
this.toggleButtonText = '';
this.noRowsText = 'No existen registros';
this.accentInsensitive = false;
this.toggleButtonClasses = [];
this.selectedItemsChanged = new core.EventEmitter();
this.selectedItemsKeysChanged = new core.EventEmitter();
this._createForm();
}
Object.defineProperty(NgMultiselectComponent.prototype, "term", {
get: function () {
return this.selectForm.get('term').value;
},
set: function (value) {
this.selectForm.get('term').setValue(value);
},
enumerable: true,
configurable: true
});
/**
* Evento click del documento para determinar si se oculta el dropdown de elementos.
* @param event Evento click del mouse
*/
NgMultiselectComponent.prototype.documentClick = function (event) {
if (!event.target) {
return;
}
// Verifica si el elemento donde se realizó el evento está contenido en el elemento HOST de la directiva.
var contains = this._containerRef.nativeElement.contains(event.target);
if (!contains) {
this._hideDropdown();
}
};
/**
* Evento tecla ESCAPE del documento para determinar si se oculta el dropdown de elementos.
* @param event Evento tecla
*/
NgMultiselectComponent.prototype.documentKeyup = function (event) {
// Tecla SCAPE.
if (event.keyCode === 27) {
this._hideDropdown();
}
};
NgMultiselectComponent.prototype.ngOnChanges = function (changes) {
// Si cambia el origen de datos del control se reinicia.
if (changes['source']) {
this._initialize();
}
};
NgMultiselectComponent.prototype.ngOnInit = function () {
this._initialize();
};
/**
* Selecciona el elemento indicado
* @param item Elemento a seleccionar
*/
NgMultiselectComponent.prototype.selectItem = function (item) {
// Deselecciona cualquier elemento seleccionado.
if (this.onlyOneRow) {
this.source.forEach(function (item) {
item.selected = false;
});
}
// Elemento seleccionado.
item.selected = !item.selected;
// Elementos seleccionados.
this._setSelectedItems();
// Inicializa el item sombreado con el teclado. Si se hubiese indicado alguno.
this._hoveredItemIndex = -1;
this.hoveredItem = null;
if (this.onlyOneRow) {
this._hideDropdown();
}
};
NgMultiselectComponent.prototype.toggleButtonClick = function () {
if (this.dropdownVisible) {
this._hideDropdown();
}
else {
this._showDropdown();
}
};
/**
* Selecciona/desselecciona todos los registros filtrados.
*/
NgMultiselectComponent.prototype.selectUnselectAll = function () {
var _this = this;
this.itemAll.selected = !this.itemAll.selected;
this.filteredItems.forEach(function (item) {
item.selected = _this.itemAll.selected;
});
// Elementos seleccionados.
this._setSelectedItems();
};
NgMultiselectComponent.prototype.clearTerm = function () {
this.term = '';
this._inputRef.nativeElement.focus();
};
NgMultiselectComponent.prototype.inputKeyup = function (event) {
var itemsLength = this.filteredItems.length;
if (itemsLength === 0) {
return;
}
switch (event.keyCode) {
case 13:// ENTER
event.preventDefault();
if (this.filteredItems.length > 0 && this._hoveredItemIndex !== -1) {
this.selectItem(this.hoveredItem);
}
break;
case 38:// UP
if (this._hoveredItemIndex > 0) {
// Seleciona el elemento anterior.
this._hoveredItemIndex -= 1;
}
else {
// Selecciona último elemento.
this._hoveredItemIndex = itemsLength - 1;
}
this.hoveredItem = this.filteredItems[this._hoveredItemIndex];
this._scrollToView(this._hoveredItemIndex);
break;
case 40:// DOWN
if (this._hoveredItemIndex < (itemsLength - 1)) {
// Selecciona siguiente elemento.
this._hoveredItemIndex += 1;
}
else {
// Selecciona primer elemento.
this._hoveredItemIndex = 0;
}
this.hoveredItem = this.filteredItems[this._hoveredItemIndex];
this._scrollToView(this._hoveredItemIndex);
break;
}
};
NgMultiselectComponent.prototype._initialize = function () {
this.itemAll = new NgMultiselectItem('0', 'Seleccionar todo');
this.term = '';
this.selectedItemsKeys = this.toggleButtonText;
// Inicializa los registros filtrados. Todos los items.
this._filterData();
// Marca o asigna elementos seleccionados.
this._setSelectedItems();
};
NgMultiselectComponent.prototype._createForm = function () {
var _this = this;
// Control para captura. Término de búsqueda.
var term = this._formBuilder.control('');
term.valueChanges
.debounceTime(50)
.subscribe(function (value) {
_this._filterData(value);
});
// Formulario.
this.selectForm = this._formBuilder.group({
term: term
});
};
NgMultiselectComponent.prototype._filterData = function (term) {
// Límite de registros filtrados.
var top = (this.top > 0) ? this.top : this.source.length;
// En caso de que el término de búsqueda sea vacío, devuelve toda la lista original (limitado por top si aplica).
if (!term) {
this.filteredItems = this.source.slice(0, top);
return;
}
this.filteredItems.length = 0;
for (var _i = 0, _a = this.source; _i < _a.length; _i++) {
var item = _a[_i];
// Verifica si el término de búsqueda corresponde con alguno de los campos (value y campos adicionales de búsqueda).
if (this._match(item, term)) {
this.filteredItems.push(item);
}
// Verifica si los registros filtrados han llegado al límite establecido.
// Dejaría de verificar registros coincidentes.
if (this.filteredItems.length === top) {
break;
}
}
};
NgMultiselectComponent.prototype._match = function (item, term) {
// Por defecto el registro no coincide.
var match = false;
// Variable local para optimizar proceso.
var localTerm = this.accentInsensitive ? _.deburr(term.toLowerCase()) : term.toLowerCase();
// Verifica si corresponde con el campo value del item.
var localItemValue = this.accentInsensitive ? _.deburr(item.value.toLowerCase()) : item.value.toLowerCase();
if (localItemValue.indexOf(localTerm) !== -1) {
match = true;
}
else if (item.filters) {
for (var _i = 0, _a = item.filters; _i < _a.length; _i++) {
var filter = _a[_i];
// No considera valores vacíos.
if (!filter) {
continue;
}
var filterValue = (this.accentInsensitive ? _.deburr(filter.toLowerCase()) : filter.toLowerCase());
if (filterValue.indexOf(localTerm) !== -1) {
// Retornaría que sí coincide la búsqueda en caso de que algún valor de filtro coíncida.
match = true;
break;
}
}
}
return match;
};
NgMultiselectComponent.prototype._showDropdown = function () {
this.dropdownVisible = true;
this._renderer.setStyle(this._dropdownRef.nativeElement, 'display', 'flex');
this._inputRef.nativeElement.focus();
};
NgMultiselectComponent.prototype._hideDropdown = function () {
this.dropdownVisible = false;
this._renderer.setStyle(this._dropdownRef.nativeElement, 'display', 'none');
};
NgMultiselectComponent.prototype._setSelectedItems = function () {
var _this = this;
// Número de registros seleccionados previamente.
var selectedItemsLength = this._selectedItems.length;
this._selectedItems = [];
this.selectedItemsKeys = '';
// Elementos seleccionados.
this.source
.filter(function (item) { return item.selected; })
.forEach(function (item) {
_this._selectedItems.push(item);
if (_this.onlyOneRow) {
// Visualiza el texto del elemento seleccionado.
_this.selectedItemsKeys = item.value;
selectedItemsLength = 0;
}
else {
// Visualiza los registros seleccionados.
_this.selectedItemsKeys =
_this.selectedItemsKeys +
(_this.selectedItemsKeys.length ? ',' : '') +
item.key;
}
});
// En caso de que no haya elemento seleccionado se coloca el texto especificado para el botón toggle.
if (this._selectedItems.length === 0) {
this.selectedItemsKeys = this.toggleButtonText;
}
// Indica un cambio de registros seleccionados.
if (selectedItemsLength !== this._selectedItems.length) {
this.selectedItemsChanged.emit(this._selectedItems);
}
};
/**
* Ajusta el scroll para visualizar el elemento actualmente seleccionado
*/
NgMultiselectComponent.prototype._scrollToView = function (index) {
if (!this._dropdownItemsRef) {
return;
}
var dropdownItems = this._dropdownItemsRef.nativeElement;
var scrollTop = dropdownItems.scrollTop;
var viewport = scrollTop + dropdownItems.offsetHeight;
var scrollOffset = this._listItemHeight * index;
// scrollOffset < scrollTop : Cuando el elemento seleccionado esté por arriba del espacio desplazado.
// (scrollOffset + this.listItemHeight) > viewport : Cuando el elemento seleccionado esté por abajo del espacio desplazado + altura del espacio de visualización.
if (scrollOffset < scrollTop || (scrollOffset + this._listItemHeight) > viewport) {
dropdownItems.scrollTop = scrollOffset;
}
};
NgMultiselectComponent.decorators = [
{ type: core.Component, args: [{
selector: 'ng-multiselect',
template: "\n <div #container\n class=\"ng-multiselect\"\n [class.ng-multiselect--disabled]=\"disabled\">\n\n <!-- Bot\u00F3n toggle -->\n <div class=\"ng-multiselect__toggle-button\"\n [ngClass]=\"toggleButtonClasses\"\n (click)=\"toggleButtonClick()\">\n <span class=\"ng-multiselect__toggle-button-value\">{{selectedItemsKeys}}</span>\n <span class=\"ng-multiselect__toggle-button-caret\"></span>\n </div>\n\n <!-- Elementos -->\n <ul #dropdown\n class=\"ng-multiselect__dropdown ng-multiselect__dropdown--raised\">\n\n <!-- Elemento: Seleccionar todo -->\n <li *ngIf=\"!onlyOneRow\"\n class=\"ng-multiselect__item ng-multiselect__item--bordered ng-multiselect__item--accent\"\n (click)=\"selectUnselectAll()\">\n <i *ngIf=\"itemAll.selected\"\n class=\"material-icons ng-multiselect__icon\">check_box</i>\n <i *ngIf=\"!itemAll.selected\"\n class=\"material-icons ng-multiselect__icon\">check_box_outline_blank</i>\n <div class=\"ng-multiselect__item-values\">\n {{itemAll.value}}\n </div>\n </li>\n\n <!-- Campo b\u00FAsqueda -->\n <form [formGroup]=\"selectForm\">\n <li class=\"ng-multiselect__item ng-multiselect__item--bordered ng-multiselect__item--accent\">\n <i class=\"material-icons ng-multiselect__icon\">search</i>\n <input #input\n type=\"text\"\n class=\"ng-multiselect__search\"\n [placeholder]=\"inputSearchPlaceHolder\"\n (keyup)=\"inputKeyup($event)\"\n formControlName=\"term\">\n <i [style.display]=\"term?'inherit':'none'\"\n class=\"material-icons ng-multiselect__icon ng-multiselect__icon--close\"\n (click)=\"clearTerm()\">close</i>\n </li>\n </form>\n\n <!-- Elementos -->\n <div #dropdownItems\n class=\"ng-multiselect__items\">\n\n <!-- No existen registros -->\n <li *ngIf=\"!filteredItems.length\"\n class=\"ng-multiselect__item ng-multiselect__item--no-rows\">\n {{noRowsText}}\n </li>\n\n <!-- Registros filtrados -->\n <li *ngFor=\"let item of filteredItems\"\n class=\"ng-multiselect__item\"\n [class.ng-multiselect__item--selected]=\"!onlyOneRow && item===hoveredItem\"\n [class.ng-multiselect__item--selected]=\"onlyOneRow && (item.selected || item===hoveredItem)\"\n (click)=\"selectItem(item, $event)\">\n <!-- Elemento seleccionado -->\n <i *ngIf=\"item.selected && !onlyOneRow\"\n class=\"material-icons ng-multiselect__icon\">check_box</i>\n <!-- Elemento NO seleccionado -->\n <i *ngIf=\"!item.selected && !onlyOneRow\"\n class=\"material-icons ng-multiselect__icon\">check_box_outline_blank</i>\n <!-- Texto -->\n <div class=\"ng-multiselect__item-values\">\n <span class=\"ng-multiselect__item-value\"\n [title]=\"item.value\">\n {{item.value}}\n </span>\n <span *ngIf=\"item.valueSecondary\"\n class=\"ng-multiselect__item-value ng-multiselect__item-value--secondary\"\n [title]=\"item.valueSecondary\">\n {{item.valueSecondary}}\n </span>\n </div>\n </li>\n\n </div>\n\n </ul>\n </div>\n ",
styles: ["\n .ng-multiselect {\n color: #59595A;\n position: relative;\n font-family: Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n width: 100%;\n }\n\n .ng-multiselect--disabled {\n pointer-events: none;\n background-color: rgb(235, 235, 228);\n }\n\n .ng-multiselect__toggle-button {\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: 30px;\n padding: 5px;\n border: 1px solid #CCC;\n border-radius: 2px;\n cursor: pointer;\n }\n\n .ng-multiselect__toggle-button-value {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .ng-multiselect__toggle-button-caret {\n width: 0;\n height: 0;\n border-top: 4px solid;\n border-left: 4px solid transparent;\n border-right: 4px solid transparent;\n margin-left: 11px;\n margin-right: 6px;\n }\n\n .ng-multiselect__dropdown {\n position: absolute;\n display: none;\n flex-direction: column;\n background-color: #FFFFFF;\n width: 100%;\n min-width: 240px;\n max-height: 360px;\n list-style-type: none;\n margin-top: 2px;\n padding: 0;\n text-align: left;\n font-weight: 500;\n border: 1px solid #CCCCCC;\n animation: slideDown .1s;\n cursor: default;\n z-index: 1000;\n }\n\n .ng-multiselect__dropdown-container {\n display: flex;\n flex-direction: column;\n }\n\n .ng-multiselect__dropdown--raised {\n box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n }\n\n .ng-multiselect__items {\n overflow-y: auto;\n }\n\n .ng-multiselect__item {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n padding: 0 10px;\n height: 44px;\n font-size: 15px;\n font-weight: 500;\n overflow: hidden;\n }\n\n .ng-multiselect__item--selected {\n background-color: #338FFF;\n font-weight: 700;\n color: white;\n }\n\n .ng-multiselect__item--bordered {\n border-bottom: 1px solid #CCCCCC;\n }\n\n .ng-multiselect__item--accent {\n background-color: rgba(0, 0, 0, 0.04);\n }\n\n .ng-multiselect__item--no-rows {\n justify-content: center;\n }\n\n .ng-multiselect__item-values {\n display: flex;\n flex-direction: column;\n width: 100%;\n padding-left: 10px;\n padding-right: 23px;\n }\n\n .ng-multiselect__item-value {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .ng-multiselect__item-value--secondary {\n font-size: 0.8em;\n opacity: 0.6;\n }\n\n .ng-multiselect__item:hover:not(.ng-multiselect__item--no-rows):not(.ng-multiselect__item--selected) {\n background-color: rgba(0, 0, 0, .12);\n }\n\n .ng-multiselect__icon {\n font-size: 22px;\n pointer-events: none;\n }\n\n .ng-multiselect__icon--close {\n cursor: pointer;\n pointer-events: initial;\n opacity: 0.8;\n }\n\n .ng-multiselect__search {\n width: 100%;\n padding: 10px 3px 10px 10px;\n border: none;\n background-color: transparent;\n outline-style: none;\n color: inherit;\n font-size: inherit;\n font-weight: 400;\n }\n\n\n /*\n Animaci\u00F3n al visualizar el dropdown.\n */\n\n @keyframes slideDown {\n 0% {\n transform: translateY(-10px);\n }\n 100% {\n transform: translateY(0px);\n }\n }\n "]
},] },
];
/** @nocollapse */
NgMultiselectComponent.ctorParameters = function () { return [
{ type: core.Renderer2, },
{ type: forms.FormBuilder, },
]; };
NgMultiselectComponent.propDecorators = {
'source': [{ type: core.Input },],
'top': [{ type: core.Input },],
'onlyOneRow': [{ type: core.Input },],
'inputSearchPlaceHolder': [{ type: core.Input },],
'disabled': [{ type: core.Input },],
'toggleButtonText': [{ type: core.Input },],
'noRowsText': [{ type: core.Input },],
'accentInsensitive': [{ type: core.Input },],
'toggleButtonClasses': [{ type: core.Input },],
'selectedItemsChanged': [{ type: core.Output },],
'selectedItemsKeysChanged': [{ type: core.Output },],
'_containerRef': [{ type: core.ViewChild, args: ['container',] },],
'_dropdownRef': [{ type: core.ViewChild, args: ['dropdown',] },],
'_dropdownItemsRef': [{ type: core.ViewChild, args: ['dropdownItems',] },],
'_inputRef': [{ type: core.ViewChild, args: ['input',] },],
'documentClick': [{ type: core.HostListener, args: ['document:click', ['$event'],] },],
'documentKeyup': [{ type: core.HostListener, args: ['document:keyup', ['$event'],] },],
};
return NgMultiselectComponent;
}());
var NgMultiselectModule = /** @class */ (function () {
function NgMultiselectModule() {
}
NgMultiselectModule.decorators = [
{ type: core.NgModule, args: [{
imports: [
common.CommonModule,
forms.ReactiveFormsModule
],
exports: [
NgMultiselectComponent
],
declarations: [
NgMultiselectComponent
]
},] },
];
/** @nocollapse */
NgMultiselectModule.ctorParameters = function () { return []; };
return NgMultiselectModule;
}());
/**
* Generated bundle index. Do not edit.
*/
exports.NgMultiselectComponent = NgMultiselectComponent;
exports.NgMultiselectModule = NgMultiselectModule;
exports.NgMultiselectItem = NgMultiselectItem;
Object.defineProperty(exports, '__esModule', { value: true });
})));