@knora/search
Version:
Knora ui module: search
717 lines (712 loc) • 102 kB
JavaScript
import { __values, __decorate, __metadata, __param } from 'tslib';
import { OverlayConfig, ConnectionPositionPair, Overlay } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { EventEmitter, Inject, ViewContainerRef, Input, Output, ViewChild, ElementRef, TemplateRef, Component, ViewChildren, QueryList, Host, NgModule } from '@angular/core';
import { MatMenuTrigger } from '@angular/material';
import { Router, ActivatedRoute } from '@angular/router';
import { Constants, KnoraApiConnection, ReadResource, KnoraApiConfig } from '@knora/api';
import { KnoraApiConnectionToken, OntologyCacheService, GravsearchGenerationService, ExtendedSearchParams, KnoraApiConfigToken, SearchParamsService, Exists, Equals, NotEquals, LessThan, LessThanEquals, GreaterThan, GreaterThanEquals, Like, Match, ComparisonOperatorAndValue, Property, CardinalityOccurrence, OntologyInformation, PropertyWithValue, Properties, ResourceClass, ValueLiteral, KnoraConstants, IRI, Utils, KuiCoreModule } from '@knora/core';
import { FormBuilder, FormGroup, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DateAdapter } from '@angular/material/core';
import { MatCalendar, MatDatepickerContent, MatDatepickerModule } from '@angular/material/datepicker';
import { JDNConvertibleCalendarDateAdapter, MatJDNConvertibleCalendarDateAdapterModule } from 'jdnconvertiblecalendardateadapter';
import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { CommonModule } from '@angular/common';
import { MatListModule } from '@angular/material/list';
import { MatMenuTrigger as MatMenuTrigger$1, MatMenuModule } from '@angular/material/menu';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { KuiActionModule } from '@knora/action';
import { KuiViewerModule } from '@knora/viewer';
/**
*
*/
var FulltextSearchComponent = /** @class */ (function () {
function FulltextSearchComponent(knoraApiConnection, _overlay, _router, _viewContainerRef) {
this.knoraApiConnection = knoraApiConnection;
this._overlay = _overlay;
this._router = _router;
this._viewContainerRef = _viewContainerRef;
/**
*
* @param {string} route Route to navigate after search.
* This route path should contain a component for search results.
*/
this.route = '/search';
/**
*
* @param {boolean} [projectfilter] If true it shows the selection
* of projects to filter by one of them
*/
this.projectfilter = false;
this.showState = new EventEmitter();
// previous search = full-text search history
this.prevSearch = JSON.parse(localStorage.getItem('prevSearch'));
this.defaultProjectLabel = 'All projects';
this.projectLabel = this.defaultProjectLabel;
// is search panel focused?
this.searchPanelFocus = false;
// do not show the following projects: default system projects from knora
this.doNotDisplay = [
Constants.SystemProjectIRI,
Constants.DefaultSharedOntologyIRI
];
}
FulltextSearchComponent.prototype.ngOnInit = function () {
// this.setFocus();
if (this.filterbyproject) {
this.getProject(this.filterbyproject);
}
if (this.projectfilter) {
this.getAllProjects();
if (localStorage.getItem('currentProject') !== null) {
this.setProject(JSON.parse(localStorage.getItem('currentProject')));
}
}
};
FulltextSearchComponent.prototype.openPanelWithBackdrop = function () {
var _this = this;
var config = new OverlayConfig({
hasBackdrop: true,
backdropClass: 'cdk-overlay-transparent-backdrop',
// backdropClass: 'cdk-overlay-dark-backdrop',
positionStrategy: this.getOverlayPosition(),
scrollStrategy: this._overlay.scrollStrategies.block()
});
this.overlayRef = this._overlay.create(config);
this.overlayRef.attach(new TemplatePortal(this.searchMenu, this._viewContainerRef));
this.overlayRef.backdropClick().subscribe(function () {
_this.searchPanelFocus = false;
_this.overlayRef.detach();
});
};
FulltextSearchComponent.prototype.getOverlayPosition = function () {
var positions = [
new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }),
new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' })
];
var overlayPosition = this._overlay.position().flexibleConnectedTo(this.searchPanel).withPositions(positions).withLockedPosition(false);
return overlayPosition;
};
FulltextSearchComponent.prototype.getAllProjects = function () {
var _this = this;
this.knoraApiConnection.admin.projectsEndpoint.getProjects().subscribe(function (response) {
_this.projects = response.body.projects;
// this.loadSystem = false;
if (localStorage.getItem('currentProject') !== null) {
_this.project = JSON.parse(localStorage.getItem('currentProject'));
}
}, function (error) {
console.error(error);
_this.error = error;
});
};
FulltextSearchComponent.prototype.getProject = function (id) {
var _this = this;
this.knoraApiConnection.admin.projectsEndpoint.getProjectByIri(id).subscribe(function (project) {
_this.setProject(project.body.project);
}, function (error) {
console.error(error);
});
};
// set current project and switch focus to input field
FulltextSearchComponent.prototype.setProject = function (project) {
if (!project) {
// set default project: all
this.projectLabel = this.defaultProjectLabel;
this.projectIri = undefined;
localStorage.removeItem('currentProject');
}
else {
// set current project shortname and id
this.projectLabel = project.shortname;
this.projectIri = project.id;
localStorage.setItem('currentProject', JSON.stringify(project));
}
};
FulltextSearchComponent.prototype.doSearch = function () {
var e_1, _a;
if (this.searchQuery !== undefined && this.searchQuery !== null) {
if (this.projectIri !== undefined) {
this._router.navigate([
this.route +
'/fulltext/' +
this.searchQuery +
'/' +
encodeURIComponent(this.projectIri)
]);
}
else {
this._router.navigate([
this.route + '/fulltext/' + this.searchQuery
]);
}
// push the search query into the local storage prevSearch array (previous search)
// to have a list of recent search requests
var existingPrevSearch = JSON.parse(localStorage.getItem('prevSearch'));
if (existingPrevSearch === null) {
existingPrevSearch = [];
}
var i = 0;
try {
for (var existingPrevSearch_1 = __values(existingPrevSearch), existingPrevSearch_1_1 = existingPrevSearch_1.next(); !existingPrevSearch_1_1.done; existingPrevSearch_1_1 = existingPrevSearch_1.next()) {
var entry = existingPrevSearch_1_1.value;
// remove entry, if exists already
if (this.searchQuery === entry.query && this.projectIri === entry.projectIri) {
existingPrevSearch.splice(i, 1);
}
i++;
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (existingPrevSearch_1_1 && !existingPrevSearch_1_1.done && (_a = existingPrevSearch_1.return)) _a.call(existingPrevSearch_1);
}
finally { if (e_1) throw e_1.error; }
}
// A search value is expected to have at least length of 3
if (this.searchQuery.length > 2) {
var currentQuery = {
query: this.searchQuery
};
if (this.projectIri) {
currentQuery = {
projectIri: this.projectIri,
projectLabel: this.projectLabel,
query: this.searchQuery
};
}
existingPrevSearch.push(currentQuery);
localStorage.setItem('prevSearch', JSON.stringify(existingPrevSearch));
}
}
this.resetSearch();
this.overlayRef.detach();
this.show = false;
this.showState.emit(this.show);
};
FulltextSearchComponent.prototype.resetSearch = function () {
this.searchPanelFocus = false;
this.searchInput.nativeElement.blur();
this.overlayRef.detach();
};
FulltextSearchComponent.prototype.setFocus = function () {
this.prevSearch = JSON.parse(localStorage.getItem('prevSearch'));
this.searchPanelFocus = true;
this.openPanelWithBackdrop();
};
FulltextSearchComponent.prototype.doPrevSearch = function (prevSearch) {
this.searchQuery = prevSearch.query;
if (prevSearch.projectIri !== undefined) {
this.projectIri = prevSearch.projectIri;
this.projectLabel = prevSearch.projectLabel;
this._router.navigate([this.route + '/fulltext/' + this.searchQuery + '/' + encodeURIComponent(prevSearch.projectIri)]);
}
else {
this.projectIri = undefined;
this.projectLabel = this.defaultProjectLabel;
this._router.navigate([this.route + '/fulltext/' + this.searchQuery]);
}
this.resetSearch();
this.overlayRef.detach();
};
FulltextSearchComponent.prototype.resetPrevSearch = function (prevSearch) {
if (prevSearch) {
// delete only this item with the name ...
var i = this.prevSearch.indexOf(prevSearch);
this.prevSearch.splice(i, 1);
localStorage.setItem('prevSearch', JSON.stringify(this.prevSearch));
}
else {
// delete the whole "previous search" array
localStorage.removeItem('prevSearch');
}
this.prevSearch = JSON.parse(localStorage.getItem('prevSearch'));
};
FulltextSearchComponent.prototype.changeFocus = function () {
this.selectProject.closeMenu();
this.searchInput.nativeElement.focus();
this.setFocus();
};
FulltextSearchComponent.ctorParameters = function () { return [
{ type: KnoraApiConnection, decorators: [{ type: Inject, args: [KnoraApiConnectionToken,] }] },
{ type: Overlay },
{ type: Router },
{ type: ViewContainerRef }
]; };
__decorate([
Input(),
__metadata("design:type", String)
], FulltextSearchComponent.prototype, "route", void 0);
__decorate([
Input(),
__metadata("design:type", Boolean)
], FulltextSearchComponent.prototype, "projectfilter", void 0);
__decorate([
Input(),
__metadata("design:type", String)
], FulltextSearchComponent.prototype, "filterbyproject", void 0);
__decorate([
Input(),
__metadata("design:type", Boolean)
], FulltextSearchComponent.prototype, "show", void 0);
__decorate([
Output(),
__metadata("design:type", Object)
], FulltextSearchComponent.prototype, "showState", void 0);
__decorate([
ViewChild('fulltextSearchPanel', { static: false }),
__metadata("design:type", ElementRef)
], FulltextSearchComponent.prototype, "searchPanel", void 0);
__decorate([
ViewChild('fulltextSearchInput', { static: false }),
__metadata("design:type", ElementRef)
], FulltextSearchComponent.prototype, "searchInput", void 0);
__decorate([
ViewChild('fulltextSearchMenu', { static: false }),
__metadata("design:type", TemplateRef)
], FulltextSearchComponent.prototype, "searchMenu", void 0);
__decorate([
ViewChild('btnToSelectProject', { static: false }),
__metadata("design:type", MatMenuTrigger)
], FulltextSearchComponent.prototype, "selectProject", void 0);
FulltextSearchComponent = __decorate([
Component({
selector: 'kui-fulltext-search',
template: "<!-- full-text search panel -->\n<div class=\"kui-fulltext-search-panel\" [class.active]=\"searchPanelFocus\" [class.with-project-filter]=\"projectfilter\"\n #fulltextSearchPanel cdkOverlayOrigin>\n\n <!-- DESKTOP / TABLET VERSION -->\n <div class=\"kui-project-filter\" *ngIf=\"projectfilter\">\n <button mat-button class=\"kui-project-filter-button\" [matMenuTriggerFor]=\"selectProject\"\n #btnToSelectProject=\"matMenuTrigger\" isIconButton>\n <p class=\"mat-caption placeholder\">Filter by project</p>\n <p class=\"label\">{{projectLabel}}</p>\n <mat-icon class=\"icon\" matSuffix>keyboard_arrow_down</mat-icon>\n </button>\n <mat-menu #selectProject=\"matMenu\">\n <div class=\"kui-project-filter-menu\">\n <button mat-menu-item class=\"center\"\n (click)=\"setProject();changeFocus()\">{{defaultProjectLabel}}</button>\n <mat-divider></mat-divider>\n <span *ngFor=\"let project of projects | kuiSortBy: 'shortname'\">\n <button mat-menu-item *ngIf=\"!doNotDisplay.includes(project.id)\"\n (click)=\"setProject(project);changeFocus()\" [matTooltip]=\"project.longname\"\n [matTooltipPosition]=\"'after'\">{{project.shortname}}</button>\n </span>\n </div>\n </mat-menu>\n </div>\n\n <div class=\"kui-fulltext-search\" [class.with-project-filter]=\"projectfilter\">\n <div class=\"kui-fulltext-search-field\">\n <input #fulltextSearchInput class=\"kui-fulltext-search-input\" type=\"search\" [(ngModel)]=\"searchQuery\"\n name=\"search\" minlength=\"3\" autocomplete=\"off\" [placeholder]=\"'Search'\" (keyup.esc)=\"resetSearch()\"\n (keyup.enter)=\"doSearch()\" (click)=\"setFocus()\">\n </div>\n <button class=\"kui-fulltext-search-button suffix\" (click)=\"doSearch()\" type=\"submit\">\n <mat-icon>search</mat-icon>\n </button>\n </div>\n\n <!-- PHONE VERSION *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** -->\n <div class=\"kui-project-filter-mobile\" *ngIf=\"projectfilter\">\n <button mat-stroked-button class=\"kui-project-filter-button kui-project-filter-button-mobile\"\n [matMenuTriggerFor]=\"selectProject\" #btnToSelectProject=\"matMenuTrigger\" isIconButton>\n <p class=\"mat-caption placeholder\">Filter by project</p>\n <p class=\"label\">{{projectLabel}}</p>\n <mat-icon class=\"icon\" matSuffix>keyboard_arrow_down</mat-icon>\n </button>\n <mat-menu #selectProject=\"matMenu\">\n <div class=\"kui-project-filter-menu-mobile\">\n <button mat-menu-item class=\"center\"\n (click)=\"setProject();changeFocus()\">{{defaultProjectLabel}}</button>\n <mat-divider></mat-divider>\n <span *ngFor=\"let project of projects | kuiSortBy: 'shortname'\">\n <button mat-menu-item *ngIf=\"!doNotDisplay.includes(project.id)\"\n (click)=\"setProject(project);changeFocus()\" [matTooltip]=\"project.longname\"\n [matTooltipPosition]=\"'after'\">{{project.shortname}}</button>\n </span>\n </div>\n </mat-menu>\n <!-- <mat-form-field class=\"kui-project-filter-select-mobile\">\n <mat-select [(ngModel)]=\"All projects\">\n <mat-option *ngFor=\"let project of projects | kuiSortBy: 'shortname'\" [value]=\"project.shortname\">\n {{project.shortname}}\n </mat-option>\n </mat-select>\n </mat-form-field> -->\n </div>\n\n <div class=\"kui-fulltext-search-mobile\" [class.with-project-filter]=\"projectfilter\">\n <div class=\"kui-fulltext-search-field-mobile\">\n <input #fulltextSearchInput class=\"kui-fulltext-search-input-mobile\" type=\"search\" [(ngModel)]=\"searchQuery\"\n name=\"search\" minlength=\"3\" autocomplete=\"off\" [placeholder]=\"'Search'\" (keyup.esc)=\"resetSearch()\"\n (keyup.enter)=\"doSearch()\" (click)=\"setFocus()\">\n </div>\n <button mat-stroked-button class=\"kui-fulltext-search-button-mobile suffix-mobile\" (click)=\"doSearch()\"\n type=\"submit\">\n Search\n </button>\n </div>\n <!-- *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** -->\n\n</div>\n\n<!-- full-text search menu - only for desktop/tablet versions -->\n<ng-template #fulltextSearchMenu>\n\n <div class=\"kui-search-menu\" [class.with-project-filter]=\"projectfilter\">\n <div class=\"kui-menu-content\">\n <mat-list class=\"kui-previous-search-list\">\n <div *ngFor=\"let item of prevSearch | kuiReverse; let i=index\">\n <mat-list-item *ngIf=\"i<10\">\n <h4 mat-line (click)=\"doPrevSearch(item)\" class=\"kui-previous-search-item\">\n <div class=\"kui-project-filter-label\" [class.not-empty]=\"item.projectIri\"\n *ngIf=\"projectfilter && !error && projects?.length > 0\">\n <span *ngIf=\"item.projectIri\">{{item.projectLabel}}</span>\n </div>\n <div class=\"kui-previous-search-query\" [class.fix-width]=\"projectfilter\">\n {{item.query}}\n </div>\n </h4>\n <button mat-icon-button (click)=\"resetPrevSearch(item)\">\n <mat-icon class=\"mat-list-close-icon\" aria-label=\"close\">close</mat-icon>\n </button>\n </mat-list-item>\n </div>\n </mat-list>\n </div>\n\n <div class=\"kui-menu-action\" *ngIf=\"prevSearch\">\n <mat-divider></mat-divider>\n <button mat-button color=\"primary\" class=\"center\" (click)=\"resetPrevSearch()\">Clear list\n </button>\n </div>\n </div>\n\n</ng-template>\n",
styles: ["input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration,input[type=search]::-webkit-search-results-button,input[type=search]::-webkit-search-results-decoration{display:none}input[type=search]{-moz-appearance:none;-webkit-appearance:none}.center{text-align:center}.kui-fulltext-search-panel{border-radius:4px;display:-webkit-box;display:flex;height:40px;position:relative;width:480px;z-index:100;background-color:#f9f9f9}.kui-fulltext-search-panel.active{box-shadow:0 1px 3px rgba(0,0,0,.5)}.kui-fulltext-search-panel.with-project-filter{width:calc(480px + 160px)}.kui-fulltext-search-panel .kui-project-filter-button{font-size:inherit;overflow:hidden;text-overflow:ellipsis;width:160px;margin:1px;border-radius:4px 0 0 4px}.kui-fulltext-search-panel .kui-fulltext-search{background-color:#f9f9f9;border-radius:4px;display:-webkit-inline-box;display:inline-flex;position:relative;z-index:10}.kui-fulltext-search-panel .kui-fulltext-search.with-project-filter{width:calc(480px + 160px);border-top-left-radius:0;border-bottom-left-radius:0}.kui-fulltext-search-panel .kui-fulltext-search .kui-fulltext-search-field{-webkit-box-flex:1;flex:1;width:calc(480px - 40px);margin:1px}.kui-fulltext-search-panel .kui-fulltext-search .kui-fulltext-search-field .kui-fulltext-search-input{border-style:none;font-size:14pt;height:38px;position:absolute;padding-left:12px;width:calc(100% - 40px)}.kui-fulltext-search-panel .kui-fulltext-search .kui-fulltext-search-field .kui-fulltext-search-input.with-project-filter{width:calc(100% - 40px - 160px)}.kui-fulltext-search-panel .kui-fulltext-search .kui-fulltext-search-field .kui-fulltext-search-input:active,.kui-fulltext-search-panel .kui-fulltext-search .kui-fulltext-search-field .kui-fulltext-search-input:focus{outline:0}.kui-fulltext-search-panel .kui-fulltext-search .kui-fulltext-search-button{background-color:#fff}.kui-fulltext-search-panel .kui-fulltext-search .suffix{margin:1px 0 1px -3px;border-radius:0 4px 4px 0}.kui-fulltext-search-panel .kui-fulltext-search .prefix{margin:1px 0 1px 3px;border-radius:4px 0 0 4px}.kui-fulltext-search-panel .kui-fulltext-search .prefix,.kui-fulltext-search-panel .kui-fulltext-search .suffix{border-style:none;color:rgba(41,41,41,.4);cursor:pointer;height:38px;outline:0;position:relative;width:39px}.kui-fulltext-search-panel .kui-fulltext-search .prefix.disabled,.kui-fulltext-search-panel .kui-fulltext-search .suffix.disabled{cursor:auto}.kui-fulltext-search-panel .kui-fulltext-search .prefix:active,.kui-fulltext-search-panel .kui-fulltext-search .suffix:active{color:#515151}.kui-search-menu{height:100%}.kui-search-menu .kui-menu-content{display:block}.kui-search-menu .kui-menu-content .mat-list{padding-bottom:8px}.kui-search-menu .kui-menu-content .mat-list .kui-previous-search-query{overflow:hidden;text-overflow:ellipsis}.kui-search-menu .kui-menu-content .mat-list .kui-previous-search-query.fix-width{width:calc(100% - 160px)}.kui-project-filter-button{height:38px!important;display:block;text-align:left}.kui-project-filter-button .placeholder{margin:0;padding:0;font-size:x-small}.kui-project-filter-button .icon,.kui-project-filter-button .label{display:inline;position:relative}.kui-project-filter-button .label{top:-12px;font-size:smaller;text-transform:capitalize}.kui-project-filter-button .icon{top:-6px;float:right}.kui-project-filter-button.kui-project-filter-button-mobile{height:100%!important}.kui-project-filter-button.kui-project-filter-button-mobile .icon,.kui-project-filter-button.kui-project-filter-button-mobile .label{top:0;font-size:inherit}@media (min-width:576px){.kui-fulltext-search-mobile,.kui-fulltext-search-mobile.with-project-filter,.kui-project-filter-mobile{display:none}}@media (max-width:576px){.kui-fulltext-search,.kui-project-filter{display:none}.kui-fulltext-search.with-project-filter{display:none!important}.kui-search-menu{display:none}.kui-search-menu.with-project-filter{display:none!important}.kui-fulltext-search-panel{height:100vh!important;background-color:rgba(220,218,218,.9);position:relative;width:100%!important;z-index:100;display:block;border-radius:0}.kui-fulltext-search-panel.with-project-filter{width:100%!important}.kui-fulltext-search-panel .kui-project-filter-mobile{height:54px;margin:0 2% 5%;padding-top:3%}.kui-fulltext-search-panel .kui-project-filter-mobile.mat-stroked-button{padding:0}.kui-fulltext-search-panel .kui-project-filter-mobile .kui-project-filter-button-mobile{width:calc(100% - 16px);margin:8px;height:100%;background-color:#b8b8b8;cursor:pointer}.kui-fulltext-search-panel .kui-project-filter-mobile .kui-project-filter-menu-mobile{width:calc(100% - 32px)!important;max-width:100%;margin:0 8px;height:100%!important}.kui-fulltext-search-panel .kui-fulltext-search-mobile{display:-webkit-box;display:flex;height:64px;margin-top:5%;margin-right:4%;margin-left:4%}.kui-fulltext-search-panel .kui-fulltext-search-mobile .kui-fulltext-search-field-mobile{width:80%;margin-right:2px}.kui-fulltext-search-panel .kui-fulltext-search-mobile .kui-fulltext-search-field-mobile .kui-fulltext-search-input-mobile{width:72%;height:64px;font-size:14pt;position:absolute;padding-left:12px;border-radius:5px;border:.6px solid #b8b8b8}.kui-fulltext-search-panel .kui-fulltext-search-mobile .kui-fulltext-search-field-mobile .kui-fulltext-search-input-mobile:active,.kui-fulltext-search-panel .kui-fulltext-search-mobile .kui-fulltext-search-field-mobile .kui-fulltext-search-input-mobile:focus{outline:0}.kui-fulltext-search-panel .kui-fulltext-search-mobile .kui-fulltext-search-button-mobile{background-color:#fff;width:20%;height:64px;padding:0;margin-left:2px;border-radius:5px;border:.8px solid #b8b8b8}.kui-fulltext-search-panel .kui-fulltext-search-mobile .suffix{margin:1px 0 1px -3px;border-radius:0 4px 4px 0}.kui-fulltext-search-panel .kui-fulltext-search-mobile .prefix,.kui-fulltext-search-panel .kui-fulltext-search-mobile .suffix{border-style:none;color:rgba(41,41,41,.4);cursor:pointer;height:100%;outline:0;position:relative;width:20%}.kui-fulltext-search-panel .kui-fulltext-search-mobile .prefix.disabled,.kui-fulltext-search-panel .kui-fulltext-search-mobile .suffix.disabled{cursor:auto}.kui-fulltext-search-panel .kui-fulltext-search-mobile .prefix:active,.kui-fulltext-search-panel .kui-fulltext-search-mobile .suffix:active{color:#515151}::ng-deep .cdk-overlay-pane .mat-menu-panel{box-shadow:none}::ng-deep .cdk-overlay-pane .mat-select-panel-wrap{margin-top:20%}::ng-deep .cdk-overlay-pane .mat-select-panel-wrap .mat-select-panel{max-height:100%!important}}"]
}),
__param(0, Inject(KnoraApiConnectionToken)),
__metadata("design:paramtypes", [KnoraApiConnection,
Overlay,
Router,
ViewContainerRef])
], FulltextSearchComponent);
return FulltextSearchComponent;
}());
/**
* The search-panel contains the kui-fulltext-search and the kui-extended-search components.
*/
var SearchPanelComponent = /** @class */ (function () {
function SearchPanelComponent(_overlay, _viewContainerRef) {
this._overlay = _overlay;
this._viewContainerRef = _viewContainerRef;
/**
* @param {string} route Route to navigate after search. This route path should contain a component for search results.
*/
this.route = '/search';
/**
*@param {boolean} [projectfilter] If true it shows the selection of projects to filter by one of them
*/
this.projectfilter = false;
/**
* @param {boolean} [advanced] Adds the extended / advanced search to the panel
*/
this.advanced = false;
/**
* @param {boolean} [expert] Adds the expert search / gravsearch editor to the panel
*/
this.expert = false;
}
SearchPanelComponent.prototype.openPanelWithBackdrop = function (type) {
var _this = this;
this.showAdvanced = (type === 'advanced');
var config = new OverlayConfig({
hasBackdrop: true,
backdropClass: 'cdk-overlay-transparent-backdrop',
// backdropClass: 'cdk-overlay-dark-backdrop',
positionStrategy: this.getOverlayPosition(),
scrollStrategy: this._overlay.scrollStrategies.block()
});
this.overlayRef = this._overlay.create(config);
this.overlayRef.attach(new TemplatePortal(this.searchMenu, this._viewContainerRef));
this.overlayRef.backdropClick().subscribe(function () {
_this.overlayRef.detach();
});
};
SearchPanelComponent.prototype.getOverlayPosition = function () {
var positions = [
new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }),
new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' })
];
var overlayPosition = this._overlay.position().flexibleConnectedTo(this.searchPanel).withPositions(positions).withLockedPosition(false);
return overlayPosition;
};
SearchPanelComponent.prototype.closeMenu = function () {
this.overlayRef.detach();
};
SearchPanelComponent.ctorParameters = function () { return [
{ type: Overlay },
{ type: ViewContainerRef }
]; };
__decorate([
Input(),
__metadata("design:type", String)
], SearchPanelComponent.prototype, "route", void 0);
__decorate([
Input(),
__metadata("design:type", Boolean)
], SearchPanelComponent.prototype, "projectfilter", void 0);
__decorate([
Input(),
__metadata("design:type", String)
], SearchPanelComponent.prototype, "filterbyproject", void 0);
__decorate([
Input(),
__metadata("design:type", Boolean)
], SearchPanelComponent.prototype, "advanced", void 0);
__decorate([
Input(),
__metadata("design:type", Boolean)
], SearchPanelComponent.prototype, "expert", void 0);
__decorate([
ViewChild('fullSearchPanel', { static: false }),
__metadata("design:type", ElementRef)
], SearchPanelComponent.prototype, "searchPanel", void 0);
__decorate([
ViewChild('searchMenu', { static: false }),
__metadata("design:type", TemplateRef)
], SearchPanelComponent.prototype, "searchMenu", void 0);
SearchPanelComponent = __decorate([
Component({
selector: 'kui-search-panel',
template: "<div class=\"kui-search-panel\" #fullSearchPanel cdkOverlayOrigin>\n\n <!-- DESKTOP VERSION -->\n <kui-fulltext-search class=\"kui-fulltext-search\" [route]=\"route\" [projectfilter]=\"projectfilter\"\n [filterbyproject]=\"filterbyproject\">\n </kui-fulltext-search>\n\n <!-- advanced search button: if advanced === true -->\n <button mat-button *ngIf=\"advanced && !expert\" color=\"primary\"\n (click)=\"openPanelWithBackdrop('advanced')\">advanced</button>\n\n <!-- expert search button: if expert === true -->\n <button mat-button *ngIf=\"!advanced && expert\" color=\"primary\"\n (click)=\"openPanelWithBackdrop('expert')\">expert</button>\n\n <!-- button to select advanced or expert search: if advanced AND expert === true; open menu to select -->\n <button mat-button *ngIf=\"advanced && expert\" [matMenuTriggerFor]=\"selectSearch\">\n <mat-icon>filter_list</mat-icon>\n </button>\n <mat-menu #selectSearch=\"matMenu\">\n <button mat-menu-item (click)=\"openPanelWithBackdrop('advanced')\">\n <span>Advanced search</span>\n </button>\n <button mat-menu-item (click)=\"openPanelWithBackdrop('expert')\">\n <span>Expert search</span>\n </button>\n </mat-menu>\n\n</div>\n\n<!-- full-text search menu -->\n<ng-template #searchMenu>\n <div class=\"kui-search-menu with-extended-search\" [class.with-project-filter]=\"projectfilter\">\n <div class=\"kui-menu-header\">\n <span class=\"kui-menu-title\">\n <h4 *ngIf=\"showAdvanced\">Advanced search</h4>\n <h4 *ngIf=\"!showAdvanced\">Expert search</h4>\n </span>\n <span class=\"fill-remaining-space\"></span>\n <span class=\"kui-menu-close\">\n <button mat-icon-button (click)=\"closeMenu()\">\n <mat-icon>close</mat-icon>\n </button>\n </span>\n </div>\n <div class=\"kui-menu-content\">\n <kui-extended-search *ngIf=\"showAdvanced\" [route]=\"route\" (toggleExtendedSearchForm)=\"closeMenu()\">\n </kui-extended-search>\n <kui-expert-search *ngIf=\"!showAdvanced\" [route]=\"route\" (toggleExpertSearchForm)=\"closeMenu()\">\n </kui-expert-search>\n </div>\n </div>\n</ng-template>\n",
styles: [".advanced-btn{margin-left:10px}.kui-search-panel{display:-webkit-box;display:flex;position:relative;z-index:100}.extended-search-box{margin:12px}@media (max-width:576px){.kui-fulltext-search{height:100%;width:100%!important}}"]
}),
__metadata("design:paramtypes", [Overlay,
ViewContainerRef])
], SearchPanelComponent);
return SearchPanelComponent;
}());
// https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror
var resolvedPromise = Promise.resolve(null);
var SelectResourceClassComponent = /** @class */ (function () {
function SelectResourceClassComponent(fb) {
this.fb = fb;
// event emitted to parent component once a resource class is selected by the user
this.resourceClassSelectedEvent = new EventEmitter();
}
Object.defineProperty(SelectResourceClassComponent.prototype, "resourceClasses", {
// getter method for resource classes (used in template)
get: function () {
return this._resourceClasses;
},
// setter method for resource classes when being updated by parent component
set: function (value) {
this.resourceClassSelected = undefined; // reset on updates
this._resourceClasses = value;
},
enumerable: true,
configurable: true
});
/**
* Returns the Iri of the selected resource class.
*
* @returns the Iri of the selected resource class or false in case no resource class is selected.
*/
SelectResourceClassComponent.prototype.getResourceClassSelected = function () {
if (this.resourceClassSelected !== undefined && this.resourceClassSelected !== null) {
return this.resourceClassSelected;
}
else {
return false;
}
};
/**
* Initalizes the FormGroup for the resource class selection.
* The initial value is set to null.
*/
SelectResourceClassComponent.prototype.initForm = function () {
var _this = this;
// build a form for the resource class selection
this.form = this.fb.group({
resourceClass: [null] // resource class selection is optional
});
// store and emit Iri of the resource class when selected
this.form.valueChanges.subscribe(function (data) {
_this.resourceClassSelected = data.resourceClass;
_this.resourceClassSelectedEvent.emit(_this.resourceClassSelected);
});
};
SelectResourceClassComponent.prototype.ngOnInit = function () {
this.initForm();
// add form to the parent form group
this.formGroup.addControl('resourceClass', this.form);
};
SelectResourceClassComponent.prototype.ngOnChanges = function () {
var _this = this;
if (this.form !== undefined) {
// resource classes have been reinitialized
// reset form
resolvedPromise.then(function () {
// remove this form from the parent form group
_this.formGroup.removeControl('resourceClass');
_this.initForm();
// add form to the parent form group
_this.formGroup.addControl('resourceClass', _this.form);
});
}
};
SelectResourceClassComponent.ctorParameters = function () { return [
{ type: FormBuilder, decorators: [{ type: Inject, args: [FormBuilder,] }] }
]; };
__decorate([
Input(),
__metadata("design:type", FormGroup)
], SelectResourceClassComponent.prototype, "formGroup", void 0);
__decorate([
Input(),
__metadata("design:type", Array),
__metadata("design:paramtypes", [Array])
], SelectResourceClassComponent.prototype, "resourceClasses", null);
__decorate([
Output(),
__metadata("design:type", Object)
], SelectResourceClassComponent.prototype, "resourceClassSelectedEvent", void 0);
SelectResourceClassComponent = __decorate([
Component({
selector: 'kui-select-resource-class',
template: "<mat-form-field *ngIf=\"resourceClasses.length > 0\" class=\"large-field\">\n <mat-select placeholder=\"Select a Resource Class (optional)\" [formControl]=\"form.controls['resourceClass']\">\n <mat-option [value]=\"null\">no selection</mat-option>\n <!-- undo selection of a resource class -->\n <mat-option *ngFor=\"let resourceClass of resourceClasses\" [value]=\"resourceClass.id\">{{ resourceClass.label }}\n </mat-option>\n </mat-select>\n</mat-form-field>\n",
styles: [""]
}),
__param(0, Inject(FormBuilder)),
__metadata("design:paramtypes", [FormBuilder])
], SelectResourceClassComponent);
return SelectResourceClassComponent;
}());
/**
* The extended search allows you to filter by project, by source type (resource class), or by the metadata (properties) of source types. Each filter can be standalone or combined. The metadata field can be precisely filtered with criteria such as "contains", "like", "equals to", "exists" or in case of a date value with "before" or "after". In addition, for a metadata field that is connected to another source type, it's possible to filter by this second source type. If you are looking for the source type "Photograph" with the metadata field "Photographer", which is connected to source type "Person", you can search for photograph(s) taken by person(s) who is born before February 1970. The result of this request will be an intersection of the two source types.
*/
var ExtendedSearchComponent = /** @class */ (function () {
function ExtendedSearchComponent(fb, knoraApiConnection, _route, _router, _cacheService, _gravSearchService) {
this.fb = fb;
this.knoraApiConnection = knoraApiConnection;
this._route = _route;
this._router = _router;
this._cacheService = _cacheService;
this._gravSearchService = _gravSearchService;
/**
* @param {boolean} toggleExtendedSearchForm Trigger toggle for extended search form.
*/
this.toggleExtendedSearchForm = new EventEmitter();
/**
* @param {string} gravsearch Send the gravsearch query back.
*/
this.gravsearch = new EventEmitter();
// all available ontologies
this.ontologies = [];
// properties specified by the user
this.activeProperties = [];
// resource classes for the selected ontology
this.resourceClasses = [];
this.result = [new ReadResource()];
// form validation status
this.formValid = false;
}
ExtendedSearchComponent.prototype.ngOnInit = function () {
var _this = this;
// parent form is empty, it gets passed to the child components
this.form = this.fb.group({});
// if form status changes, re-run validation
this.form.statusChanges.subscribe(function (data) {
_this.formValid = _this.validateForm();
// console.log(this.form);
});
// initialize ontologies to be used for the ontologies selection in the search form
this.initializeOntologies();
};
/**
* @ignore
* Add a property to the search form.
* @returns void
*/
ExtendedSearchComponent.prototype.addProperty = function () {
this.activeProperties.push(true);
};
/**
* @ignore
* Remove the last property from the search form.
* @returns void
*/
ExtendedSearchComponent.prototype.removeProperty = function () {
this.activeProperties.splice(-1, 1);
};
/**
* @ignore
* Gets all available ontologies for the search form.
* @returns void
*/
ExtendedSearchComponent.prototype.initializeOntologies = function () {
var _this = this;
this.knoraApiConnection.v2.onto.getOntologiesMetadata().subscribe(function (response) {
_this.ontologies = response.ontologies;
}, function (error) {
console.error(error);
});
};
/**
* @ignore
* Once an ontology has been selected, gets its classes and properties.
* The classes and properties will be made available to the user for selection.
*
* @param ontologyIri Iri of the ontology chosen by the user.
* @returns void
*/
ExtendedSearchComponent.prototype.getResourceClassesAndPropertiesForOntology = function (ontologyIri) {
var _this = this;
// reset active resource class definition
this.activeResourceClass = undefined;
// reset specified properties
this.activeProperties = [];
this.activeOntology = ontologyIri;
this._cacheService.getEntityDefinitionsForOntologies([ontologyIri]).subscribe(function (ontoInfo) {
_this.resourceClasses = ontoInfo.getResourceClassesAsArray(true);
_this.properties = ontoInfo.getProperties();
});
};
/**
* @ignore
* Once a resource class has been selected, gets its properties.
* The properties will be made available to the user for selection.
*
* @param resourceClassIri
* @returns void
*/
ExtendedSearchComponent.prototype.getPropertiesForResourceClass = function (resourceClassIri) {
var _this = this;
// reset specified properties
this.activeProperties = [];
// if the client undoes the selection of a resource class, use the active ontology as a fallback
if (resourceClassIri === null) {
this.getResourceClassesAndPropertiesForOntology(this.activeOntology);
}
else {
this._cacheService.getResourceClassDefinitions([resourceClassIri]).subscribe(function (ontoInfo) {
_this.properties = ontoInfo.getProperties();
_this.activeResourceClass = ontoInfo.getResourceClasses()[resourceClassIri];
});
}
};
/**
* @ignore
* Validates form and returns its status (boolean).
*/
ExtendedSearchComponent.prototype.validateForm = function () {
// check that either a resource class is selected or at least one property is specified
return this.form.valid &&
(this.propertyComponents.length > 0 || (this.resourceClassComponent !== undefined && this.resourceClassComponent.getResourceClassSelected() !== false));
};
/**
* @ignore
* Resets the form (selected resource class and specified properties) preserving the active ontology.
*/
ExtendedSearchComponent.prototype.resetForm = function () {
if (this.activeOntology !== undefined) {
this.getResourceClassesAndPropertiesForOntology(this.activeOntology);
}
};
/**
* @ignore
* Creates a GravSearch query with the given form values and calls the extended search route.
*/
ExtendedSearchComponent.prototype.submit = function () {
if (!this.formValid)
return; // check that from is valid
var resClassOption = this.resourceClassComponent.getResourceClassSelected();
var resClass;
if (resClassOption !== false) {
resClass = resClassOption;
}
var properties = this.propertyComponents.map(function (propComp) {
return propComp.getPropertySelectedWithValue();
});
var gravsearch = this._gravSearchService.createGravsearchQuery(properties, resClass, 0);
if (this.route) {
this._router.navigate([this.route + '/extended/', gravsearch], { relativeTo: this._route });
}
else {
this.gravsearch.emit(gravsearch);
}
// toggle extended search form
this.toggleExtendedSearchForm.emit(true);
};
ExtendedSearchComponent.ctorParameters = function () { return [
{ type: FormBuilder, decorators: [{ type: Inject, args: [FormBuilder,] }] },
{ type: KnoraApiConnection, decorators: [{ type: Inject, args: [KnoraApiConnectionToken,] }] },
{ type: ActivatedRoute },
{ type: Router },
{ type: OntologyCacheService },
{ type: GravsearchGenerationService }
]; };
__decorate([
Input(),
__metadata("design:type", Object)
], ExtendedSearchComponent.prototype, "route", void 0);
__decorate([
Output(),
__metadata("design:type", Object)
], ExtendedSearchComponent.prototype, "toggleExtendedSearchForm", void 0);
__decorate([
Output(),
__metadata("design:type", Object)
], ExtendedSearchComponent.prototype, "gravsearch", void 0);
__decorate([
ViewChild('resourceClass', { static: false }),
__metadata("design:type", SelectResourceClassComponent)
], ExtendedSearchComponent.prototype, "resourceClassComponent", void 0);
__decorate([
ViewChildren('property'),
__metadata("design:type", QueryList)
], ExtendedSearchComponent.prototype, "propertyComponents", void 0);
ExtendedSearchComponent = __decorate([
Component({
selector: 'kui-extended-search',
template: "<form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"kui-form-content\">\n\n <div>\n <kui-select-ontology *ngIf=\"ontologies.length > 0\" [formGroup]=\"form\" [ontologies]=\"ontologies\"\n (ontologySelected)=\"getResourceClassesAndPropertiesForOntology($event)\"></kui-select-ontology>\n </div>\n\n <div class=\"select-resource-class\" *ngIf=\"resourceClasses?.length > 0\">\n <kui-select-resource-class #resourceClass [formGroup]=\"form\" [resourceClasses]=\"resourceClasses\"\n (resourceClassSelectedEvent)=\"getPropertiesForResourceClass($event)\">\n </kui-select-resource-class>\n </div>\n\n <div class=\"select-property\" *ngIf=\"properties !== undefined\">\n <div *ngFor=\"let prop of activeProperties; let i = index\">\n\n <kui-select-property #property [activeResourceClass]=\"activeResourceClass\" [formGroup]=\"form\" [index]=\"i\"\n [properties]=\"properties\"></kui-select-property>\n\n </div>\n </div>\n\n\n <div class=\"select-property buttons\">\n <button mat-mini-fab class=\"property-button add-property-button\" color=\"primary\" type=\"button\"\n (click)=\"addProperty()\" [disabled]=\"activeOntology === undefined || activeProperties.length >= 4\">\n <mat-icon aria-label=\"add a property\">add</mat-icon>\n </button>\n\n <button mat-mini-fab class=\"property-button remove-property-button\" color=\"primary\" type=\"button\"\n (click)=\"removeProperty()\" [disabled]=\"activeProperties.length == 0\">\n <mat-icon aria-label=\"remove property\">remove</mat-icon>\n </button>\n </div>\n\n <!-- <div>\n <button mat-icon-button type=\"button\" (click)=\"resetForm()\" [disabled]=\"this.activeOntology === undefined\">\n <mat-icon aria-label=\"reset query form\">clear</mat-icon>\n </button>\n\n <button mat-icon-button type=\"submit\" [disabled]=\"!formValid\">\n <mat-icon aria-label=\"submit query\">send</mat-icon>\n </button>\n </div> -->\n\n <div class=\"kui-form-action\">\n <button class=\"reset\" mat-button type=\"button\" (click)=\"resetForm()\" [disabled]=\"this.activeOntology === undefined\">\n Reset\n </button>\n <span class=\"fill-remaining-space\"></span>\n <button class=\"extended-search-button\" mat-raised-button color=\"primary\" type=\"submit\" [disabled]=\"!formValid\">\n Search\n </button>\n </div>\n\n</form>\n",
styles: [".select-resource-class{margin-left:8px}.select-property{margin-left:16px}.select-property .property-button{margin:0 12px 64px 0}"]
}),
__param(0, Inject(FormBuilder)),
__param(1, Inject(KnoraApiConnectionToken)),
__metadata("design:paramtypes", [FormBuilder,
KnoraApiConnection,
ActivatedRoute,
Router,
OntologyCacheService,
GravsearchGenerationService])
], ExtendedSearchComponent);
return ExtendedSearchComponent;
}());
var ExpertSearchComponent = /** @class */ (function () {
function ExpertSearchComponent(knoraApiConfig, fb, _route, _router, _searchParamsService) {
this.knoraApiConfig = knoraApiConfig;
this.fb = fb;
this._route = _route;
this._router = _router;
this._searchParamsService = _searchParamsService;
/**
* @param gravsearch Send the gravsearch query back.
*/
this.gravsearch = new EventEmitter();
/**
* @param {boolean} toggleExtendedSearchForm Trigger toggle for extended search form.
*/
this.toggleExpertSearchForm = new EventEmitter();
}
ExpertSearchComponent.prototype.ngOnInit = function () {
this.initForm();
};
/**
* @ignore
* Initiate the form with predefined Gravsearch query as example.
*/
ExpertSearchComponent.prototype.initForm = function () {
this.expertSearchForm = this.fb.group({
gravquery: [
"\nPREFIX knora-api: <http://