|
|
@ -8,12 +8,7 @@ import {
|
|
|
|
Output,
|
|
|
|
Output,
|
|
|
|
ViewChild
|
|
|
|
ViewChild
|
|
|
|
} from '@angular/core';
|
|
|
|
} from '@angular/core';
|
|
|
|
import {TagService} from "../../services/tag/tag.service";
|
|
|
|
|
|
|
|
import {FileService} from "../../services/file/file.service";
|
|
|
|
import {FileService} from "../../services/file/file.service";
|
|
|
|
import {FormControl} from "@angular/forms";
|
|
|
|
|
|
|
|
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
|
|
|
|
|
|
|
import {map, startWith} from "rxjs/operators";
|
|
|
|
|
|
|
|
import {Observable} from "rxjs";
|
|
|
|
|
|
|
|
import {TagQuery} from "../../models/TagQuery";
|
|
|
|
import {TagQuery} from "../../models/TagQuery";
|
|
|
|
import {SortKey} from "../../models/SortKey";
|
|
|
|
import {SortKey} from "../../models/SortKey";
|
|
|
|
import {MatDialog} from "@angular/material/dialog";
|
|
|
|
import {MatDialog} from "@angular/material/dialog";
|
|
|
@ -35,24 +30,20 @@ import {Tag} from "../../models/Tag";
|
|
|
|
export class FileSearchComponent implements AfterViewChecked, OnInit {
|
|
|
|
export class FileSearchComponent implements AfterViewChecked, OnInit {
|
|
|
|
public sortExpression: SortKey[] = [new SortKey("FileImportedTime",
|
|
|
|
public sortExpression: SortKey[] = [new SortKey("FileImportedTime",
|
|
|
|
"Ascending", undefined)];
|
|
|
|
"Ascending", undefined)];
|
|
|
|
public formControl = new FormControl();
|
|
|
|
|
|
|
|
public filters: FilterExpression[] = [];
|
|
|
|
public filters: FilterExpression[] = [];
|
|
|
|
public suggestionTags: Observable<string[]>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Input() availableTags: Tag[] = [];
|
|
|
|
@Input() availableTags: Tag[] = [];
|
|
|
|
@Input() validTags: string[] = [];
|
|
|
|
|
|
|
|
@Output() searchStartEvent = new EventEmitter<void>();
|
|
|
|
@Output() searchStartEvent = new EventEmitter<void>();
|
|
|
|
@Output() searchEndEvent = new EventEmitter<void>();
|
|
|
|
@Output() searchEndEvent = new EventEmitter<void>();
|
|
|
|
|
|
|
|
|
|
|
|
@ViewChild("tagInput") tagInput!: ElementRef<HTMLInputElement>;
|
|
|
|
@ViewChild("tagInput") tagInput!: ElementRef<HTMLInputElement>;
|
|
|
|
@ViewChild("tagInputList") inputList!: ElementRef;
|
|
|
|
@ViewChild("tagInputList") inputList!: ElementRef;
|
|
|
|
|
|
|
|
|
|
|
|
constructor(private errorBroker: ErrorBrokerService, private tagService: TagService, private fileService: FileService, public dialog: MatDialog) {
|
|
|
|
constructor(
|
|
|
|
this.suggestionTags = this.formControl.valueChanges.pipe(startWith(null),
|
|
|
|
private errorBroker: ErrorBrokerService,
|
|
|
|
map(
|
|
|
|
private fileService: FileService,
|
|
|
|
(tag: string | null) => tag ? this.filterSuggestionTag(
|
|
|
|
public dialog: MatDialog
|
|
|
|
tag) : this.validTags.slice(0, 20)));
|
|
|
|
) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async ngOnInit() {
|
|
|
|
public async ngOnInit() {
|
|
|
|
await this.searchForFiles();
|
|
|
|
await this.searchForFiles();
|
|
|
@ -82,12 +73,16 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async removeAllSearchTags() {
|
|
|
|
public getValidSearchTags(): Tag[] {
|
|
|
|
|
|
|
|
return this.availableTags.filter(t => this.filters.findIndex(f => f.partiallyEq(t.getNormalizedOutput())) < 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async removeAllSearchTags() {
|
|
|
|
this.filters = [];
|
|
|
|
this.filters = [];
|
|
|
|
await this.searchForFiles();
|
|
|
|
await this.searchForFiles();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async removeFilterExpression(expr: FilterExpression) {
|
|
|
|
public async removeFilterExpression(expr: FilterExpression) {
|
|
|
|
const index = this.filters.indexOf(expr);
|
|
|
|
const index = this.filters.indexOf(expr);
|
|
|
|
if (index >= 0) {
|
|
|
|
if (index >= 0) {
|
|
|
|
this.filters.splice(index, 1);
|
|
|
|
this.filters.splice(index, 1);
|
|
|
@ -95,26 +90,7 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
|
|
|
|
await this.searchForFiles();
|
|
|
|
await this.searchForFiles();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async addSearchTagByInput(event: KeyboardEvent) {
|
|
|
|
public openSortDialog() {
|
|
|
|
if (event.key === "Enter") {
|
|
|
|
|
|
|
|
const tag = (this.formControl.value as string ?? "").trim();
|
|
|
|
|
|
|
|
if (tag.length > 0 && this.validTags.includes(tag.replace(/-/g, ''))) {
|
|
|
|
|
|
|
|
this.addSearchTag(tag);
|
|
|
|
|
|
|
|
this.formControl.setValue(null);
|
|
|
|
|
|
|
|
await this.searchForFiles();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async addSearchTagByAutocomplete(event: MatAutocompleteSelectedEvent) {
|
|
|
|
|
|
|
|
const tag = event.option.viewValue;
|
|
|
|
|
|
|
|
this.addSearchTag(tag);
|
|
|
|
|
|
|
|
this.formControl.setValue(null);
|
|
|
|
|
|
|
|
this.tagInput.nativeElement.value = '';
|
|
|
|
|
|
|
|
await this.searchForFiles();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
openSortDialog() {
|
|
|
|
|
|
|
|
const sortEntries = this.sortExpression.map(
|
|
|
|
const sortEntries = this.sortExpression.map(
|
|
|
|
key => JSON.parse(JSON.stringify(key))).map(
|
|
|
|
key => JSON.parse(JSON.stringify(key))).map(
|
|
|
|
key => new SortKey(key.sortType, key.sortDirection, key.namespaceName))
|
|
|
|
key => new SortKey(key.sortType, key.sortDirection, key.namespaceName))
|
|
|
@ -133,22 +109,11 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private filterSuggestionTag(tag: string) {
|
|
|
|
|
|
|
|
const negated = tag.startsWith("-");
|
|
|
|
|
|
|
|
const normalizedTag = tag.replace(/^-/, "");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this.validTags.filter(
|
|
|
|
|
|
|
|
t => t.includes(normalizedTag) && this.filters.findIndex(
|
|
|
|
|
|
|
|
f => f.eq(t)) < 0)
|
|
|
|
|
|
|
|
.map(t => negated ? "-" + t : t)
|
|
|
|
|
|
|
|
.slice(0, 20);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public openFilterDialog(): void {
|
|
|
|
public openFilterDialog(): void {
|
|
|
|
const filterEntries = this.filters.map(f => f.clone());
|
|
|
|
const filterEntries = this.filters.map(f => f.clone());
|
|
|
|
const filterDialog = this.dialog.open(FilterDialogComponent, {
|
|
|
|
const filterDialog = this.dialog.open(FilterDialogComponent, {
|
|
|
|
minWidth: "25vw",
|
|
|
|
minWidth: "25vw",
|
|
|
|
height: "80vh",
|
|
|
|
maxHeight: "80vh",
|
|
|
|
data: {
|
|
|
|
data: {
|
|
|
|
filterEntries,
|
|
|
|
filterEntries,
|
|
|
|
availableTags: this.availableTags,
|
|
|
|
availableTags: this.availableTags,
|
|
|
|