UNPKG

@jchinc/ng-multiselect

Version:

Control de selección múltiple de elementos

377 lines (369 loc) 23.9 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/forms'), require('@angular/common'), require('rxjs/add/operator/debounceTime')) : typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@angular/forms', '@angular/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 }); })));