ngx-magicsearch
Version:
Magic Search/Faceted Search Library for Angular 2.
588 lines (583 loc) • 27.5 kB
JavaScript
(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