UNPKG

ngx-magicsearch

Version:

Magic Search/Faceted Search Library for Angular 2.

588 lines (583 loc) 27.5 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common'), require('@angular/forms')) : typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@angular/common', '@angular/forms'], factory) : (factory((global['ngx-magicsearch'] = {}),global.ng.core,global.ng.common,global.ng.forms)); }(this, (function (exports,core,common,forms) { 'use strict'; var NgxMagicSearchComponent = (function () { function NgxMagicSearchComponent(differs) { this.differs = differs; this.strings = { remove: 'Remove facet', cancel: 'Clear search', prompt: 'Select facets or enter text', 'text': 'Text' }; this.facets_param = []; this.textSearchEvent = new core.EventEmitter(); this.searchUpdatedEvent = new core.EventEmitter(); this.setFocusedEventEmitter = false; this.promptString = this.strings.prompt; this.currentSearch = []; this.facetsObj = null; this.isMenuOpen = false; this.hostEvent = null; this.differ = differs.find({}).create(); this.searchInput = ''; } NgxMagicSearchComponent.prototype.ngOnInit = function () { this.initSearch(); }; NgxMagicSearchComponent.prototype.ngOnChanges = function () { this.initSearch(); }; NgxMagicSearchComponent.prototype.ngDoCheck = function () { var changes = this.differ.diff(this.facets_param); if (changes) { this.initSearch(); } }; NgxMagicSearchComponent.prototype.initSearch = function () { if (typeof this.facets_param === 'string') { var tmp = this.facets_param.replace(/__apos__/g, '\'').replace(/__dquote__/g, '\\"').replace(/__bslash__/g, '\\'); this.facetsObj = JSON.parse(tmp); } else { this.facetsObj = this.facets_param.slice(0); } this.facetsSave = this.copyFacets(this.facetsObj); this.currentSearch = []; this.initFacets(); }; NgxMagicSearchComponent.prototype.initFacets = function () { var _this = this; var that = this; var initialFacets = (window.location.hash.split('?')[1] === undefined) ? '' : '?' + window.location.hash.split('?')[1]; if (initialFacets.length < 1) { for (var i = 0; i < this.currentSearch.length; i++) { if (this.currentSearch[i].name.indexOf('text') !== 0) { if (initialFacets.length > 0) { initialFacets = initialFacets + '&'; } initialFacets = initialFacets + this.currentSearch[i].name; } } this.facetsObj = this.copyFacets(this.facetsSave); this.currentSearch = []; } if (initialFacets.indexOf('?') === 0) { initialFacets = initialFacets.slice(1); } initialFacets = initialFacets.split('&'); if (initialFacets.length > 1 || initialFacets[0].length > 0) { setTimeout(function () { _this.strings.prompt = ''; }, 0.1); } initialFacets.forEach(function (facet, idx) { var facetParts = facet.split('='); facetParts[1] = facet.split('=').splice(1).join('='); that.facetsObj.forEach(function (value, idx_value) { if (value.name === facetParts[0]) { if (value.options === undefined) { that.currentSearch.push({ 'name': facet, 'label': [value.label, facetParts[1]] }); } else { value.options.forEach(function (option, idx_option) { if (option.key === facetParts[1]) { that.currentSearch.push({ 'name': facet, 'label': [value.label, option.label] }); if (value.singleton === true) { that.deleteFacetEntirely(facetParts); } else { that.deleteFacetSelection(facetParts); } } }); } } }); }); if (this.textSearch !== undefined) { this.currentSearch.push({ 'name': 'text=' + this.textSearch, 'label': [this.strings.text, this.textSearch] }); } this.filteredObj = this.facetsObj; }; NgxMagicSearchComponent.prototype.addFacets = function (facets) { var that = this; facets.forEach(function (facet) { that.facetsObj.append(facet); }); }; NgxMagicSearchComponent.prototype.copyFacets = function (facets) { var ret = []; for (var i = 0; i < facets.length; i++) { var facet = Object.create(facets[i]); if (facets[i].options !== undefined) { facet.options = []; for (var j = 0; j < facets[i].options.length; j++) { facet.options.push(Object.create(facets[i].options[j])); } } ret.push(facet); } return ret; }; NgxMagicSearchComponent.prototype.deleteFacetSelection = function (facetParts) { var that = this; this.facetsObj.slice().forEach(function (facet, idx) { if (facet.name === facetParts[0]) { if (facet.options === undefined) { return; } for (var i = 0; i < facet.options.length; i++) { var option = facet.options[i]; if (option.key === facetParts[1]) { that.facetsObj[idx].options.splice(that.facetsObj[idx].options.indexOf(option), 1); } } if (facet.options.length === 0) { that.facetsObj.splice(that.facetsObj.indexOf(facet), 1); } } }); }; NgxMagicSearchComponent.prototype.deleteFacetEntirely = function (facetParts) { var that = this; this.facetsObj.slice().forEach(function (facet, idx) { if (facet.name === facetParts[0]) { that.facetsObj.splice(that.facetsObj.indexOf(facet), 1); } }); }; NgxMagicSearchComponent.prototype.filterFacets = function (searchVal) { var _this = this; var i, idx, label; var filtered = []; if (this.facetSelected === undefined) { this.filteredObj = this.facetsObj; for (i = 0; i < this.filteredObj.length; i++) { var facet = this.filteredObj[i]; idx = facet.label.toLowerCase().indexOf(searchVal); if (idx > -1) { label = [ facet.label.substring(0, idx), facet.label.substring(idx, idx + searchVal.length), facet.label.substring(idx + searchVal.length) ]; filtered.push({ 'name': facet.name, 'label': label, 'options': facet.options }); } } if (filtered.length > 0) { this.showMenu(); setTimeout(function () { _this.filteredObj = filtered; }, 0.1); } else { this.textSearchEvent.emit(searchVal); this.hideMenu(); } } else { this.filteredOptions = this.facetOptions; if (this.facetOptions === undefined) { return; } for (i = 0; i < this.filteredOptions.length; i++) { var option = this.filteredOptions[i]; idx = option.label.toLowerCase().indexOf(searchVal); if (idx > -1) { label = [ option.label.substring(0, idx), option.label.substring(idx, idx + searchVal.length), option.label.substring(idx + searchVal.length) ]; filtered.push({ 'key': option.key, 'label': label }); } } if (filtered.length > 0) { this.showMenu(); setTimeout(function () { _this.filteredOptions = filtered; }, 0.1); } } }; NgxMagicSearchComponent.prototype.isMatchLabel = function (label) { return Array.isArray(label); }; NgxMagicSearchComponent.prototype.resetState = function () { this.updateUrl(''); this.searchInput = ''; this.filteredObj = this.facetsObj; this.facetSelected = undefined; this.facetOptions = undefined; this.filteredOptions = undefined; if (this.currentSearch.length === 0) { this.strings.prompt = this.promptString; } }; NgxMagicSearchComponent.prototype.showMenu = function () { this.isMenuOpen = true; }; NgxMagicSearchComponent.prototype.hideMenu = function () { this.isMenuOpen = false; }; NgxMagicSearchComponent.prototype.updateUrl = function (query) { var url = window.location.href; if (url.indexOf('?') > -1) { url = url.split('?')[0]; } if (query.length > 0) { url = url + '?' + query; } window.history.pushState(query, '', url); }; NgxMagicSearchComponent.prototype.buildTermsArray = function () { var that = this; var returnArray = []; this.currentSearch.forEach(function (item) { var explode = item.name.split('='); explode[1] = item.name.split('=').splice(1).join('='); if (that.getIndexBy(returnArray, 'key', explode[0]) !== -1) { returnArray[that.getIndexBy(returnArray, 'key', explode[0])].values.push(explode[1]); } else { returnArray.push({ key: explode[0], values: [explode[1]] }); } }); return returnArray; }; NgxMagicSearchComponent.prototype.getIndexBy = function (array, key_name, value) { for (var i = 0; i < array.length; i++) { if (array[i][key_name] === value) { return i; } } return -1; }; NgxMagicSearchComponent.prototype.handleKeyDown = function (event) { var key = event.keyCode || event.charCode; if (key === 9) { event.preventDefault(); } }; NgxMagicSearchComponent.prototype.handleKeyUp = function (event) { var _this = this; if (event.metaKey === true) { return; } var searchVal = this.searchInput; var key = event.keyCode || event.charCode; if (key === 9) { if (this.facetSelected === undefined) { if (this.filteredObj.length !== 1) { return; } this.facetClicked(0, this.filteredObj[0].name); } else { if (this.filteredOptions === undefined || this.filteredOptions.length !== 1) { return; } this.optionClicked(0, this.filteredOptions[0].key); this.resetState(); } setTimeout(function () { _this.searchInput = ''; }, 0.1); return; } if (key === 27) { setTimeout(function () { _this.hideMenu(); _this.searchInput = ''; }, 0.1); this.resetState(); var textFilter = this.textSearch; if (textFilter === undefined) { textFilter = ''; } this.textSearchEvent.emit(searchVal); return; } if (key === 13) { if (this.facetSelected && this.facetSelected.options === undefined) { var curr = this.facetSelected; curr.name = curr.name + '=' + searchVal; curr.label[1] = searchVal; this.currentSearch.push(curr); this.resetState(); this.emitQuery(); this.showMenu(); } else { for (var i = 0; i < this.currentSearch.length; i++) { if (this.currentSearch[i].name.indexOf('text') === 0) { this.currentSearch.splice(i, 1); } } this.currentSearch.push({ 'name': 'text=' + searchVal, 'label': [this.strings.text, searchVal] }); this.hideMenu(); this.searchInput = ''; this.textSearchEvent.emit(searchVal); this.textSearch = searchVal; } this.filteredObj = this.facetsObj; } else { if (searchVal === '') { this.filteredObj = this.facetsObj; this.textSearchEvent.emit(''); if (this.facetSelected && this.facetSelected.options === undefined) { this.resetState(); } } else { this.filterFacets(searchVal); } } }; NgxMagicSearchComponent.prototype.handleKeyPress = function (event) { var _this = this; var searchVal = this.searchInput; var key = event.which || event.keyCode || event.charCode; if (key !== 8 && key !== 46 && key !== 13 && key !== 9 && key !== 27) { searchVal = searchVal + String.fromCharCode(key).toLowerCase(); } if (searchVal === ' ') { this.showMenu(); setTimeout(function () { _this.searchInput = ''; }, 0.1); return; } if (searchVal === '') { this.filteredObj = this.facetsObj; this.textSearchEvent.emit(''); if (this.facetSelected && this.facetSelected.options === undefined) { this.resetState(); } return; } if (key !== 8 && key !== 46) { this.filterFacets(searchVal); } }; NgxMagicSearchComponent.prototype.enableTextEntry = function () { this.setFocusedEventEmitter = true; this.showMenu(); }; NgxMagicSearchComponent.prototype.facetClicked = function (index, name) { var _this = this; this.hideMenu(); var facet = this.filteredObj[index]; var label = facet.label; if (Array.isArray(label)) { label = label.join(''); } this.facetSelected = { 'name': facet.name, 'label': [label, ''] }; if (facet.options !== undefined) { this.filteredOptions = this.facetOptions = facet.options; this.showMenu(); } setTimeout(function () { _this.searchInput = ''; }, 0.1); this.strings.prompt = ''; setTimeout(function () { _this.setFocusedEventEmitter = true; }, 0.1); }; NgxMagicSearchComponent.prototype.optionClicked = function (index, name) { var _this = this; var curr = this.facetSelected; curr.name = curr.name + '=' + name; curr.label[1] = this.filteredOptions[index].label; if (Array.isArray(curr.label[1])) { curr.label[1] = curr.label[1].join(''); } this.currentSearch.push(curr); this.resetState(); this.emitQuery(); setTimeout(function () { _this.hideMenu(); }, 0.1); }; NgxMagicSearchComponent.prototype.addFilterManually = function (category, option) { var indexCategory = this.filteredObj.findIndex(function (categoryElement) { return categoryElement.name === category; }); var indexOption = (indexCategory !== -1) ? this.filteredObj[indexCategory].options.findIndex(function (optionElement) { return optionElement.key === option; }) : -1; if (indexCategory !== -1 && indexOption !== -1) { this.facetClicked(indexCategory, category); this.optionClicked(indexOption, option); } }; NgxMagicSearchComponent.prototype.removeFilterManually = function (category, option) { var indexSearch = this.currentSearch.findIndex(function (searchElement) { return searchElement.name === category + '=' + option; }); if (indexSearch !== -1) { this.removeFacet(indexSearch); } }; NgxMagicSearchComponent.prototype.emitQuery = function (removed) { var that = this; var query = ''; for (var i = 0; i < this.currentSearch.length; i++) { if (this.currentSearch[i].name.indexOf('text') !== 0) { if (query.length > 0) { query = query + '&'; } query = query + this.currentSearch[i].name; } } if (removed !== undefined && removed.indexOf('text') === 0) { this.textSearchEvent.emit(''); this.textSearch = undefined; } else { this.searchUpdatedEvent.emit(this.buildTermsArray()); this.updateUrl(query); if (this.currentSearch.length > 0) { var newFacet = this.currentSearch[this.currentSearch.length - 1].name; var facetParts_1 = newFacet.split('='); this.facetsSave.forEach(function (facet, idx) { if (facet.name === facetParts_1[0]) { if (facet.singleton === true) { that.deleteFacetEntirely(facetParts_1); } else { that.deleteFacetSelection(facetParts_1); } } }); } } }; NgxMagicSearchComponent.prototype.removeFacet = function (index) { var removed = this.currentSearch[index].name; this.currentSearch.splice(index, 1); if (this.facetSelected === undefined) { this.emitQuery(removed); } else { this.resetState(); this.searchInput = ''; } if (this.currentSearch.length === 0) { this.strings.prompt = this.promptString; } this.facetsObj = this.copyFacets(this.facetsSave); this.initFacets(); }; NgxMagicSearchComponent.prototype.clearSearch = function () { if (this.currentSearch.length > 0) { this.currentSearch = []; this.facetsObj = this.copyFacets(this.facetsSave); this.resetState(); this.searchUpdatedEvent.emit(null); this.textSearchEvent.emit(''); } }; NgxMagicSearchComponent.prototype.compareEvent = function (globalEvent) { if (this.hostEvent === globalEvent) { return; } this.hideMenu(); }; NgxMagicSearchComponent.prototype.trackEvent = function (newHostEvent) { this.hostEvent = newHostEvent; }; return NgxMagicSearchComponent; }()); NgxMagicSearchComponent.decorators = [ { type: core.Component, args: [{ selector: 'ngx-magic-search', template: "<div class=\"magic-search\">\n <div class=\"search-bar\">\n <i class=\"fa fa-filter go\"></i>\n <div class=\"search-main-area\" (click)=\"enableTextEntry()\">\n <span class=\"item-list\" *ngIf=\"currentSearch\">\n <span *ngFor=\"let facet of currentSearch; let i = index;\" class=\"ngx-label radius secondary item\">\n <span>{{ facet.label[0] }}:<b>{{ facet.label[1] }}</b></span>\n <a class=\"remove\" (click)=\"removeFacet(i)\" title=\"{{ strings.remove }}\"><i class=\"fa fa-times\"></i></a>\n </span>\n </span>\n <span class=\"search-selected ngx-label\" *ngIf=\"facetSelected\">\n {{ facetSelected.label[0] }}:\n </span>\n <!-- For bootstrap, the dropdown attribute is moved from input up to div. -->\n <div [ngClass]=\"{'search-entry': true, 'dropdown': true, 'active': isMenuOpen}\">\n <input class=\"search-input\" type=\"text\" placeholder=\"{{ strings.prompt }}\" autocomplete=\"off\" (keyup)=\"handleKeyUp($event)\" (keydown)=\"handleKeyDown($event)\" (keypress)=\"handleKeyPress($event)\" [(ngModel)]=\"searchInput\" ngxMagicSearch=\"setFocusedEventEmitter\"\n />\n <div class=\"dropdown-content\" *ngIf=\"filteredObj.length > 0\">\n <div class=\"arrow-up\"></div>\n <ul class=\"ngx-dropdown-menu\">\n <ng-template [ngIf]=\"!facetSelected\">\n <li *ngFor=\"let facet of filteredObj; let i = index;\">\n <a (click)=\"facetClicked(i, facet.name)\" *ngIf=\"!isMatchLabel(facet.label)\">{{ facet.label }}</a>\n <a (click)=\"facetClicked(i, facet.name)\" *ngIf=\"isMatchLabel(facet.label)\">\n {{ facet.label[0] }}<span class=\"match\">{{ facet.label[1] }}</span>{{ facet.label[2] }}\n </a>\n </li>\n </ng-template>\n <ng-template [ngIf]=\"facetSelected\">\n <li *ngFor=\"let option of filteredOptions; let i = index;\">\n <a (click)=\"optionClicked(i, option.key)\" *ngIf=\"!isMatchLabel(option.label)\">\n {{ option.label }}\n </a>\n <a (click)=\"optionClicked(i, option.key)\" *ngIf=\"isMatchLabel(option.label)\">\n {{ option.label[0] }}<span class=\"match\">{{ option.label[1] }}</span>{{ option.label[2] }}\n </a>\n </li>\n </ng-template>\n </ul>\n </div>\n </div>\n </div>\n <a (click)=\"clearSearch()\" *ngIf=\"currentSearch.length > 0\" title=\"{{ strings.cancel }}\">\n <i class=\"fa fa-times cancel\"></i>\n </a>\n </div>\n</div>", styles: [".dropdown{position:relative;display:inline-block}.dropdown-content{display:none;position:absolute;min-width:160px;z-index:1;margin-top:4px}.dropdown.active .dropdown-content{display:block}.ngx-dropdown-menu{z-index:1000;min-width:160px;padding:5px 0;margin:0;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border-radius:3px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.ngx-dropdown-menu li:hover{cursor:pointer;background-color:#eaeaea}.arrow-up{width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #fff;margin-left:5px}.ngx-dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.ngx-label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}@-moz-document url-prefix(){.item-list .item,.search-selected{top:-.4rem}}.search-bar{font-size:14px;position:relative;border:1px solid #ccc;background-color:#fff;height:auto}.search-bar i.fa-filter{color:#6a737b;position:absolute;top:.75rem;left:.65rem;font-size:18px}.search-bar .search-main-area{position:relative;margin-left:2.75rem;margin-right:2.75rem;cursor:text;text-align:left}.search-bar .item-list{position:relative;margin-top:9px;float:left}.search-bar .item-list .item{color:#333;background-color:#e6e7e8;margin-right:.5rem;display:inline-block;padding:6px;font-size:.8rem}.search-bar .item-list .item a{color:#fff}.search-bar .item-list .item a.remove:hover{cursor:pointer}.search-bar .search-selected{position:relative;padding-left:0;padding-right:0;background-color:#fff;color:#444}.search-bar .search-entry{width:18.5rem}.search-bar .search-input{width:100%;border:0;-webkit-box-shadow:none;box-shadow:none;margin:6px 0;background-color:#fff;color:#444;height:28px}.search-bar .search-input:focus{-webkit-box-shadow:none;box-shadow:none;background-color:#fff}.search-bar .match{font-weight:700}.search-bar i.cancel{color:#6a737b;position:absolute;top:.75rem;right:.65rem;font-size:18px}.search-bar i.cancel:hover{color:#8b0000;cursor:pointer}"] },] }, ]; NgxMagicSearchComponent.ctorParameters = function () { return [ { type: core.KeyValueDiffers, }, ]; }; NgxMagicSearchComponent.propDecorators = { "strings": [{ type: core.Input, args: ['strings',] },], "facets_param": [{ type: core.Input, args: ['facets_param',] },], "textSearchEvent": [{ type: core.Output },], "searchUpdatedEvent": [{ type: core.Output },], "compareEvent": [{ type: core.HostListener, args: ['document:click', ['$event'],] },], "trackEvent": [{ type: core.HostListener, args: ['click', ['$event'],] },], }; var MyRenderer = (function () { function MyRenderer(platformId) { this.platformId = platformId; } MyRenderer.prototype.invokeElementMethod = function (eleRef, method) { if (common.isPlatformBrowser(this.platformId)) { eleRef.nativeElement[method](); } }; return MyRenderer; }()); MyRenderer.decorators = [ { type: core.Injectable }, ]; MyRenderer.ctorParameters = function () { return [ { type: Object, decorators: [{ type: core.Inject, args: [core.PLATFORM_ID,] },] }, ]; }; var NgxMagicSearchDirective = (function () { function NgxMagicSearchDirective(el, renderer) { this.el = el; this.renderer = renderer; } NgxMagicSearchDirective.prototype.ngOnChanges = function () { if (this.focusEvent) { this.renderer.invokeElementMethod(this.el.nativeElement, 'focus'); } }; return NgxMagicSearchDirective; }()); NgxMagicSearchDirective.decorators = [ { type: core.Directive, args: [{ selector: '[ngxMagicSearch]' },] }, ]; NgxMagicSearchDirective.ctorParameters = function () { return [ { type: core.ElementRef, }, { type: MyRenderer, }, ]; }; NgxMagicSearchDirective.propDecorators = { "focusEvent": [{ type: core.Input },], }; var NgxMagicSearchModule = (function () { function NgxMagicSearchModule() { } return NgxMagicSearchModule; }()); NgxMagicSearchModule.decorators = [ { type: core.NgModule, args: [{ imports: [ common.CommonModule, forms.FormsModule ], providers: [ MyRenderer, ], declarations: [ NgxMagicSearchComponent, NgxMagicSearchDirective ], exports: [ NgxMagicSearchComponent, NgxMagicSearchDirective ] },] }, ]; NgxMagicSearchModule.ctorParameters = function () { return []; }; exports.NgxMagicSearchModule = NgxMagicSearchModule; exports.ɵc = NgxMagicSearchDirective; exports.ɵb = NgxMagicSearchComponent; exports.ɵa = MyRenderer; Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=ngx-magicsearch.umd.js.map