From 928a8918c67e64d479d6e35abe8131aaa12ffce8 Mon Sep 17 00:00:00 2001 From: "Patrik J. Braun" Date: Wed, 5 May 2021 20:47:02 +0200 Subject: [PATCH] Improving autocomplete and search --- src/common/SearchQueryParser.ts | 29 ++++++++++++++----- .../query-entry.search.gallery.component.css | 16 ++++++++-- .../query-entry.search.gallery.component.html | 10 +++++++ .../query-entry.search.gallery.component.ts | 8 +++++ .../search-field.gallery.component.css | 13 +++++++++ .../search-field.gallery.component.html | 2 +- .../search-field.gallery.component.ts | 6 +--- 7 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/common/SearchQueryParser.ts b/src/common/SearchQueryParser.ts index fb7eab9d..3e436efe 100644 --- a/src/common/SearchQueryParser.ts +++ b/src/common/SearchQueryParser.ts @@ -121,22 +121,26 @@ export class SearchQueryParser { if (tokenEnd !== str.length - 1) { if (str.startsWith(' ' + this.keywords.and, tokenEnd)) { + const rest = this.parse(str.slice(tokenEnd + (' ' + this.keywords.and).length), implicitOR); return { type: SearchQueryTypes.AND, list: [this.parse(str.slice(0, tokenEnd), implicitOR), // trim brackets - this.parse(str.slice(tokenEnd + (' ' + this.keywords.and).length), implicitOR)] + ...(rest.type === SearchQueryTypes.AND ? (rest as SearchListQuery).list : [rest])] } as ANDSearchQuery; } else if (str.startsWith(' ' + this.keywords.or, tokenEnd)) { + const rest = this.parse(str.slice(tokenEnd + (' ' + this.keywords.or).length), implicitOR); return { type: SearchQueryTypes.OR, list: [this.parse(str.slice(0, tokenEnd), implicitOR), // trim brackets - this.parse(str.slice(tokenEnd + (' ' + this.keywords.or).length), implicitOR)] + ...(rest.type === SearchQueryTypes.OR ? (rest as SearchListQuery).list : [rest])] } as ORSearchQuery; } else { // Relation cannot be detected + const t = implicitOR === true ? SearchQueryTypes.OR : SearchQueryTypes.UNKNOWN_RELATION; + const rest = this.parse(str.slice(tokenEnd), implicitOR); return { - type: implicitOR === true ? SearchQueryTypes.OR : SearchQueryTypes.UNKNOWN_RELATION, + type: t, list: [this.parse(str.slice(0, tokenEnd), implicitOR), // trim brackets - this.parse(str.slice(tokenEnd), implicitOR)] + ...(rest.type === t ? (rest as SearchListQuery).list : [rest])] } as SearchListQuery; } } @@ -249,24 +253,33 @@ export class SearchQueryParser { return {type: SearchQueryTypes.any_text, text: str} as TextSearch; } + public stringify(query: SearchQueryDTO): string { + const ret = this.stringifyOnEntry(query); + if (ret.charAt(0) === '(' && ret.charAt(ret.length - 1) === ')') { + return ret.slice(1, ret.length - 1); + } + return ret; + } + + private stringifyOnEntry(query: SearchQueryDTO): string { if (!query || !query.type) { return ''; } switch (query.type) { case SearchQueryTypes.AND: - return '(' + (query as SearchListQuery).list.map(q => this.stringify(q)).join(' ' + this.keywords.and + ' ') + ')'; + return '(' + (query as SearchListQuery).list.map(q => this.stringifyOnEntry(q)).join(' ' + this.keywords.and + ' ') + ')'; case SearchQueryTypes.OR: - return '(' + (query as SearchListQuery).list.map(q => this.stringify(q)).join(' ' + this.keywords.or + ' ') + ')'; + return '(' + (query as SearchListQuery).list.map(q => this.stringifyOnEntry(q)).join(' ' + this.keywords.or + ' ') + ')'; case SearchQueryTypes.SOME_OF: if ((query as SomeOfSearchQuery).min) { return (query as SomeOfSearchQuery).min + '-' + this.keywords.NSomeOf + ':(' + - (query as SearchListQuery).list.map(q => this.stringify(q)).join(' ') + ')'; + (query as SearchListQuery).list.map(q => this.stringifyOnEntry(q)).join(' ') + ')'; } return this.keywords.someOf + ':(' + - (query as SearchListQuery).list.map(q => this.stringify(q)).join(' ') + ')'; + (query as SearchListQuery).list.map(q => this.stringifyOnEntry(q)).join(' ') + ')'; case SearchQueryTypes.orientation: diff --git a/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.css b/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.css index de5a3ff5..1f491994 100644 --- a/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.css +++ b/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.css @@ -1,6 +1,18 @@ -.query-list{ +.query-list { padding-left: 25px; } -label{ + +label { margin-top: 0.3rem; } + +.match-type { + font-size: 2rem; + margin-top: -0.8rem; + font-weight: bold; + cursor: pointer; +} + +.match-type.exact-match { + color: #0069d9; +} diff --git a/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.html b/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.html index 446acd0e..76728b97 100644 --- a/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.html +++ b/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.html @@ -59,6 +59,11 @@
+ " + "
diff --git a/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.ts b/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.ts index bd4e4021..851849ad 100644 --- a/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.ts +++ b/src/frontend/app/ui/gallery/search/query-enrty/query-entry.search.gallery.component.ts @@ -9,6 +9,7 @@ import { SearchQueryTypes, SomeOfSearchQuery, TextSearch, + TextSearchQueryMatchTypes, TextSearchQueryTypes } from '../../../../../../common/entities/SearchQueryDTO'; import {Utils} from '../../../../../../common/Utils'; @@ -36,6 +37,7 @@ export class GallerySearchQueryEntryComponent implements ControlValueAccessor, V public queryEntry: SearchQueryDTO; public SearchQueryTypesEnum: { value: string; key: SearchQueryTypes }[]; public SearchQueryTypes = SearchQueryTypes; + public TextSearchQueryMatchTypes = TextSearchQueryMatchTypes; @Output() delete = new EventEmitter(); constructor() { @@ -145,6 +147,12 @@ export class GallerySearchQueryEntryComponent implements ControlValueAccessor, V this.propagateChange(this.queryEntry); } + public toggleMatchType(): void { + this.AsTextQuery.matchType = this.AsTextQuery.matchType === TextSearchQueryMatchTypes.exact_match ? + TextSearchQueryMatchTypes.like : TextSearchQueryMatchTypes.exact_match; + this.onChange(); + } + private propagateChange = (_: any): void => { }; diff --git a/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.css b/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.css index 76373845..984d50cd 100644 --- a/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.css +++ b/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.css @@ -13,6 +13,19 @@ z-index: 7; } +.insert-button { + margin-right: -15px; + display: none; + margin-top: 2px; +} + +.autocomplete-item-selected .insert-button { + display: block; +} +.autocomplete-item-selected .insert-button:hover { + color: black; +} + .autocomplete-item { cursor: pointer; padding-top: 2px; diff --git a/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.html b/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.html index a915c89f..f5d8a6b0 100644 --- a/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.html +++ b/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.html @@ -5,7 +5,6 @@ placeholder="Search" (keyup)="onSearchChange($event)" (blur)="onFocusLost()" - (focus)="onFocus()" [(ngModel)]="rawSearchText" (ngModelChange)="onChange()" (keydown.enter)="OnEnter($event)" @@ -49,6 +48,7 @@ {{item.preText}}{{item.highLightText}}{{item.postText}} +
diff --git a/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.ts b/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.ts index 41df275f..70d2ce9c 100644 --- a/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.ts +++ b/src/frontend/app/ui/gallery/search/search-field/search-field.gallery.component.ts @@ -109,16 +109,12 @@ export class GallerySearchFieldComponent implements ControlValueAccessor, Valida } public onFocusLost(): void { + return; if (this.mouseOverAutoComplete === false) { this.autoCompleteRenders = []; } } - public onFocus(): void { - // TODO: implement autocomplete - // this.autocomplete(this.searchText).catch(console.error); - } - applyHint($event: any): void { if ($event.target.selectionStart !== this.rawSearchText.length) {