1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2024-11-03 21:04:03 +08:00

Improve Search field

This commit is contained in:
Patrik J. Braun 2021-03-26 20:57:13 +01:00
parent 24942b2ee1
commit 5e2b8a44dd
9 changed files with 399 additions and 289 deletions

View File

@ -97,6 +97,7 @@ import {GallerySearchQueryEntryComponent} from './ui/gallery/search/query-enrty/
import {StringifySearchQuery} from './pipes/StringifySearchQuery';
import {AutoCompleteService} from './ui/gallery/search/autocomplete.service';
import {SearchQueryParserService} from './ui/gallery/search/search-query-parser.service';
import {GallerySearchFieldComponent} from './ui/gallery/search/search-field/search-field.gallery.component';
@Injectable()
@ -179,6 +180,7 @@ export function translationsFactory(locale: string) {
FrameComponent,
GallerySearchComponent,
GallerySearchQueryEntryComponent,
GallerySearchFieldComponent,
GalleryShareComponent,
GalleryNavigatorComponent,
GalleryPhotoComponent,

View File

@ -32,16 +32,11 @@
</div>
<hr/>
<form #searchPanelForm="ngForm" class="form-horizontal">
<input type="text"
class="form-control"
i18n-placeholder
placeholder="Search"
[(ngModel)]="rawSearchText"
(ngModelChange)="validateRawSearchText()"
size="30"
name="srch-term-preview"
id="srch-term-preview"
autocomplete="off">
<app-gallery-search-field [(ngModel)]="rawSearchText"
(ngModelChange)="validateRawSearchText()"
name="form-search-field">
</app-gallery-search-field>
<app-gallery-search-query-entry
[(ngModel)]="searchQueryDTO"

View File

@ -1,4 +1,4 @@
import {Component, EventEmitter, forwardRef, OnChanges, Output} from '@angular/core';
import {Component, EventEmitter, forwardRef, Output} from '@angular/core';
import {
DistanceSearch,
ListSearchQueryTypes,
@ -32,7 +32,7 @@ import {ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Val
}
]
})
export class GallerySearchQueryEntryComponent implements ControlValueAccessor, Validator, OnChanges {
export class GallerySearchQueryEntryComponent implements ControlValueAccessor, Validator {
public queryEntry: SearchQueryDTO;
public SearchQueryTypesEnum: { value: string; key: SearchQueryTypes }[];
public SearchQueryTypes = SearchQueryTypes;
@ -114,7 +114,7 @@ export class GallerySearchQueryEntryComponent implements ControlValueAccessor, V
} else {
delete this.AsOrientationQuery.landscape;
}
this.onChange(this.queryEntry);
this.onChange();
}
deleteItem() {
@ -125,18 +125,12 @@ export class GallerySearchQueryEntryComponent implements ControlValueAccessor, V
this.AsListQuery.list.splice(i, 1);
}
ngOnChanges(): void {
// console.log('ngOnChanges', this.queryEntry);
}
public onTouched(): void {
}
public writeValue(obj: any): void {
this.queryEntry = obj;
// console.log('write value', this.queryEntry);
this.ngOnChanges();
}
registerOnChange(fn: (_: any) => void): void {
@ -147,7 +141,7 @@ export class GallerySearchQueryEntryComponent implements ControlValueAccessor, V
this.propagateTouch = fn;
}
public onChange(event: any) {
public onChange() {
this.propagateChange(this.queryEntry);
}

View File

@ -0,0 +1,49 @@
.autocomplete-list {
position: absolute;
left: 0;
top: 34px;
background-color: white;
width: 100%;
border: 1px solid #ccc;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 5px 0;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
z-index: 7;
}
.autocomplete-item {
cursor: pointer;
padding-top: 2px;
padding-bottom: 2px;
font-size: 17px;
}
.autocomplete-item {
color: #333;
padding: 0 20px;
line-height: 1.42857143;
font-weight: 400;
display: block;
}
.autocomplete-item-selected {
background-color: #007bff;
color: #FFF;
}
.search-text {
z-index: 6;
width: 100%;
background: transparent;
}
.search-hint {
z-index: 1;
width: 100%;
position: absolute;
margin-left: 0 !important;
}

View File

@ -0,0 +1,58 @@
<div class="input-group">
<input type="text"
class="form-control search-text"
i18n-placeholder
placeholder="Search"
(keyup)="onSearchChange($event)"
(blur)="onFocusLost()"
(focus)="onFocus()"
[(ngModel)]="rawSearchText"
(ngModelChange)="onChange()"
(keydown.enter)="OnEnter($event)"
(keydown.arrowRight)="applyHint($event)"
(keydown.arrowUp)="selectAutocompleteUp()"
(keydown.arrowDown)="selectAutocompleteDown()"
(scroll)="Scrolled()"
(selectionchange)="Scrolled()"
#name="ngModel"
size="30"
ngControl="search"
name="srch-term"
id="srch-term"
#SearchField
autocomplete="off">
<input type="text"
class="form-control search-hint"
[ngModel]="SearchHint"
size="30"
name="srch-term-hint"
id="srch-term-hint"
#SearchHintField
autocomplete="off">
<div class="autocomplete-list" *ngIf="autoCompleteRenders.length > 0"
(mouseover)="setMouseOverAutoComplete(true)" (mouseout)="setMouseOverAutoComplete(false)">
<div class="autocomplete-item"
[ngClass]="{'autocomplete-item-selected':highlightedAutoCompleteItem == i}"
(mouseover)="setMouseOverAutoCompleteItem(i)"
(click)="applyAutoComplete(item)"
*ngFor="let item of autoCompleteRenders; let i = index">
<div>
<span [ngSwitch]="item.type">
<span *ngSwitchCase="SearchQueryTypes.caption" class="oi oi-image"></span>
<span *ngSwitchCase="SearchQueryTypes.directory" class="oi oi-folder"></span>
<span *ngSwitchCase="SearchQueryTypes.file_name" class="oi oi-image"></span>
<span *ngSwitchCase="SearchQueryTypes.keyword" class="oi oi-tag"></span>
<span *ngSwitchCase="SearchQueryTypes.person" class="oi oi-person"></span>
<span *ngSwitchCase="SearchQueryTypes.position" class="oi oi-map-marker"></span>
</span>
{{item.preText}}<strong>{{item.highLightText}}</strong>{{item.postText}}
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,254 @@
import {Component, ElementRef, EventEmitter, forwardRef, OnDestroy, Output, ViewChild} from '@angular/core';
import {ActivatedRoute, Router, RouterLink} from '@angular/router';
import {BehaviorSubject, Subscription} from 'rxjs';
import {AutoCompleteService, RenderableAutoCompleteItem} from '../autocomplete.service';
import {MetadataSearchQueryTypes, SearchQueryTypes} from '../../../../../../common/entities/SearchQueryDTO';
import {SearchQueryParserService} from '../search-query-parser.service';
import {GalleryService} from '../../gallery.service';
import {NavigationService} from '../../../../model/navigation.service';
import {Config} from '../../../../../../common/config/public/Config';
import {ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator} from '@angular/forms';
@Component({
selector: 'app-gallery-search-field',
templateUrl: './search-field.gallery.component.html',
styleUrls: ['./search-field.gallery.component.css'],
providers: [
AutoCompleteService, RouterLink,
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => GallerySearchFieldComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => GallerySearchFieldComponent),
multi: true
}
]
})
export class GallerySearchFieldComponent implements ControlValueAccessor, Validator, OnDestroy {
@ViewChild('SearchField', {static: false}) searchField: ElementRef;
@ViewChild('SearchHintField', {static: false}) searchHintField: ElementRef;
@Output() search = new EventEmitter<void>();
autoCompleteRenders: AutoCompleteRenderItem[] = [];
public rawSearchText = '';
mouseOverAutoComplete = false;
readonly SearchQueryTypes: typeof SearchQueryTypes;
public readonly MetadataSearchQueryTypes: { value: string; key: SearchQueryTypes }[];
public highlightedAutoCompleteItem = 0;
private cache = {
lastAutocomplete: '',
lastInstantSearch: ''
};
private autoCompleteItemsSubscription: Subscription = null;
private autoCompleteItems: BehaviorSubject<RenderableAutoCompleteItem[]>;
constructor(private _autoCompleteService: AutoCompleteService,
private _searchQueryParserService: SearchQueryParserService,
private _galleryService: GalleryService,
private navigationService: NavigationService,
private _route: ActivatedRoute,
public router: Router) {
this.SearchQueryTypes = SearchQueryTypes;
this.MetadataSearchQueryTypes = MetadataSearchQueryTypes.map(v => ({key: v, value: SearchQueryTypes[v]}));
}
get SearchHint() {
if (!this.autoCompleteItems ||
!this.autoCompleteItems.value || this.autoCompleteItems.value.length === 0) {
return this.rawSearchText;
}
const searchText = this.getAutocompleteToken();
if (searchText.current === '') {
return this.rawSearchText + this.autoCompleteItems.value[this.highlightedAutoCompleteItem].queryHint;
}
if (this.autoCompleteItems.value[0].queryHint.startsWith(searchText.current)) {
return this.rawSearchText + this.autoCompleteItems
.value[this.highlightedAutoCompleteItem].queryHint.substr(searchText.current.length);
}
return this.rawSearchText;
}
ngOnDestroy() {
if (this.autoCompleteItemsSubscription) {
this.autoCompleteItemsSubscription.unsubscribe();
this.autoCompleteItemsSubscription = null;
}
}
getAutocompleteToken(): { current: string, prev: string } {
if (this.rawSearchText.trim().length === 0) {
return {current: '', prev: ''};
}
const tokens = this.rawSearchText.split(' ');
return {
current: tokens[tokens.length - 1],
prev: (tokens.length > 2 ? tokens[tokens.length - 2] : '')
};
}
onSearchChange(event: KeyboardEvent) {
const searchText = this.getAutocompleteToken();
if (Config.Client.Search.AutoComplete.enabled &&
this.cache.lastAutocomplete !== searchText.current) {
this.cache.lastAutocomplete = searchText.current;
this.autocomplete(searchText).catch(console.error);
}
}
public setMouseOverAutoComplete(value: boolean) {
this.mouseOverAutoComplete = value;
}
public onFocusLost() {
if (this.mouseOverAutoComplete === false) {
this.autoCompleteRenders = [];
}
}
public onFocus() {
// TODO: implement autocomplete
// this.autocomplete(this.searchText).catch(console.error);
}
applyHint($event: any) {
if ($event.target.selectionStart !== this.rawSearchText.length) {
return;
}
this.rawSearchText = this.SearchHint;
this.onChange();
}
applyAutoComplete(item: AutoCompleteRenderItem) {
const token = this.getAutocompleteToken();
this.rawSearchText = this.rawSearchText.substr(0, this.rawSearchText.length - token.current.length)
+ item.queryHint;
this.onChange();
this.emptyAutoComplete();
}
setMouseOverAutoCompleteItem(i: number) {
this.highlightedAutoCompleteItem = i;
}
selectAutocompleteUp() {
if (this.highlightedAutoCompleteItem > 0) {
this.highlightedAutoCompleteItem--;
}
}
selectAutocompleteDown() {
if (this.autoCompleteItems &&
this.highlightedAutoCompleteItem < this.autoCompleteItems.value.length - 1) {
this.highlightedAutoCompleteItem++;
}
}
OnEnter($event: any) {
if (this.autoCompleteRenders.length === 0) {
this.search.emit();
return;
}
this.applyAutoComplete(this.autoCompleteRenders[this.highlightedAutoCompleteItem]);
}
public onTouched(): void {
}
public writeValue(obj: any): void {
this.rawSearchText = obj;
}
registerOnChange(fn: (_: any) => void): void {
this.propagateChange = fn;
}
registerOnTouched(fn: () => void): void {
this.propagateTouch = fn;
}
public onChange() {
this.propagateChange(this.rawSearchText);
}
validate(control: FormControl): ValidationErrors {
return {required: true};
}
Scrolled() {
this.searchHintField.nativeElement.scrollLeft = this.searchField.nativeElement.scrollLeft;
}
private emptyAutoComplete() {
this.highlightedAutoCompleteItem = 0;
this.autoCompleteRenders = [];
}
private async autocomplete(searchText: { current: string, prev: string }) {
if (!Config.Client.Search.AutoComplete.enabled) {
return;
}
if (this.rawSearchText.trim().length > 0) { // are we searching for anything?
try {
if (this.autoCompleteItems) {
this.autoCompleteItems.unsubscribe();
}
this.autoCompleteItems = this._autoCompleteService.autoComplete(searchText);
this.autoCompleteItemsSubscription = this.autoCompleteItems.subscribe(() => {
this.showSuggestions(this.autoCompleteItems.value, searchText.current);
});
} catch (error) {
console.error(error);
}
} else {
this.emptyAutoComplete();
}
}
private showSuggestions(suggestions: RenderableAutoCompleteItem[], searchText: string) {
this.emptyAutoComplete();
suggestions.forEach((item: RenderableAutoCompleteItem) => {
const renderItem = new AutoCompleteRenderItem(item.text, searchText, item.type, item.queryHint);
this.autoCompleteRenders.push(renderItem);
});
}
private propagateChange = (_: any) => {
};
private propagateTouch = (_: any) => {
};
}
class AutoCompleteRenderItem {
public preText = '';
public highLightText = '';
public postText = '';
public type: SearchQueryTypes;
public queryHint: string;
constructor(public text: string, searchText: string, type: SearchQueryTypes, queryHint: string) {
const preIndex = text.toLowerCase().indexOf(searchText.toLowerCase());
if (preIndex > -1) {
this.preText = text.substring(0, preIndex);
this.highLightText = text.substring(preIndex, preIndex + searchText.length);
this.postText = text.substring(preIndex + searchText.length);
} else {
this.postText = text;
}
this.type = type;
this.queryHint = queryHint;
}
}

View File

@ -1,54 +1,15 @@
.autocomplete-list {
position: absolute;
left: 0;
top: 34px;
background-color: white;
width: 100%;
border: 1px solid #ccc;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 5px 0;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
z-index: 7;
}
.autocomplete-item {
cursor: pointer;
padding-top: 2px;
padding-bottom: 2px;
font-size: 17px;
}
.autocomplete-item {
color: #333;
padding: 0 20px;
line-height: 1.42857143;
font-weight: 400;
display: block;
}
.autocomplete-item-selected {
background-color: #007bff;
color: #FFF;
}
.search-text {
border: 0;
z-index: 6;
width: 500px;
background: transparent;
}
.search-hint {
width: 500px;
background: white;
z-index: 1;
position: absolute;
border: 0;
}
form {
padding-right: 1em;
}
@media screen and ( min-width: 1200px) {
.search-field {
width: 400px;
}
}
@media screen and ( min-width: 1400px) {
.search-field {
width: 500px;
}
}

View File

@ -1,57 +1,13 @@
<form class="navbar-form" role="search" #SearchForm="ngForm">
<div class="input-group">
<input type="text"
class="form-control search-text"
i18n-placeholder
placeholder="Search"
(keyup)="onSearchChange($event)"
(blur)="onFocusLost()"
(focus)="onFocus()"
[(ngModel)]="rawSearchText"
(ngModelChange)="validateRawSearchText()"
(keydown.enter)="OnEnter($event)"
(keydown.arrowRight)="applyHint($event)"
(keydown.arrowUp)="selectAutocompleteUp()"
(keydown.arrowDown)="selectAutocompleteDown()"
#name="ngModel"
size="30"
ngControl="search"
name="srch-term"
id="srch-term"
autocomplete="off">
<input type="text"
class="form-control search-hint"
[ngModel]="SearchHint"
i18n-placeholder
placeholder="Search"
disabled
value="test hint"
size="30"
name="srch-term-hint"
id="srch-term-hint"
autocomplete="off">
<app-gallery-search-field [(ngModel)]="rawSearchText"
(ngModelChange)="validateRawSearchText()"
class="search-field"
(search)="Search()"
name="search-field">
<div class="autocomplete-list" *ngIf="autoCompleteRenders.length > 0"
(mouseover)="setMouseOverAutoComplete(true)" (mouseout)="setMouseOverAutoComplete(false)">
<div class="autocomplete-item"
[ngClass]="{'autocomplete-item-selected':highlightedAutoCompleteItem == i}"
(mouseover)="setMouseOverAutoCompleteItem(i)"
(click)="applyAutoComplete(item)"
*ngFor="let item of autoCompleteRenders; let i = index">
<div>
<span [ngSwitch]="item.type">
<span *ngSwitchCase="SearchQueryTypes.caption" class="oi oi-image"></span>
<span *ngSwitchCase="SearchQueryTypes.directory" class="oi oi-folder"></span>
<span *ngSwitchCase="SearchQueryTypes.file_name" class="oi oi-image"></span>
<span *ngSwitchCase="SearchQueryTypes.keyword" class="oi oi-tag"></span>
<span *ngSwitchCase="SearchQueryTypes.person" class="oi oi-person"></span>
<span *ngSwitchCase="SearchQueryTypes.position" class="oi oi-map-marker"></span>
</span>
{{item.preText}}<strong>{{item.highLightText}}</strong>{{item.postText}}
</div>
</div>
</div>
</app-gallery-search-field>
<div class="input-group-btn" style="display: block">
<button class="btn btn-light" type="button"
@ -79,16 +35,12 @@
</div>
<div class="modal-body">
<form #searchPanelForm="ngForm" class="form-horizontal">
<input type="text"
class="form-control search-text"
i18n-placeholder
placeholder="Search"
[(ngModel)]="rawSearchText"
(ngModelChange)="validateRawSearchText()"
size="30"
name="srch-term-preview"
id="srch-term-preview"
autocomplete="off">
<app-gallery-search-field [(ngModel)]="rawSearchText"
(ngModelChange)="validateRawSearchText()"
(search)="Search()"
name="form-search-field">
</app-gallery-search-field>
<app-gallery-search-query-entry
[(ngModel)]="searchQueryDTO"

View File

@ -1,9 +1,8 @@
import {Component, OnDestroy, TemplateRef} from '@angular/core';
import {AutoCompleteService, RenderableAutoCompleteItem} from './autocomplete.service';
import {AutoCompleteService} from './autocomplete.service';
import {ActivatedRoute, Params, Router, RouterLink} from '@angular/router';
import {GalleryService} from '../gallery.service';
import {BehaviorSubject, Subscription} from 'rxjs';
import {Config} from '../../../../../common/config/public/Config';
import {Subscription} from 'rxjs';
import {NavigationService} from '../../../model/navigation.service';
import {QueryParams} from '../../../../../common/QueryParams';
import {MetadataSearchQueryTypes, SearchQueryDTO, SearchQueryTypes, TextSearch} from '../../../../../common/entities/SearchQueryDTO';
@ -19,20 +18,13 @@ import {SearchQueryParserService} from './search-query-parser.service';
})
export class GallerySearchComponent implements OnDestroy {
autoCompleteRenders: AutoCompleteRenderItem[] = [];
public searchQueryDTO: SearchQueryDTO = <TextSearch>{type: SearchQueryTypes.any_text, text: ''};
public rawSearchText = '';
mouseOverAutoComplete = false;
readonly SearchQueryTypes: typeof SearchQueryTypes;
modalRef: BsModalRef;
public readonly MetadataSearchQueryTypes: { value: string; key: SearchQueryTypes }[];
public highlightedAutoCompleteItem = 0;
private cache = {
lastAutocomplete: '',
lastInstantSearch: ''
};
private readonly subscription: Subscription = null;
private autoCompleteItems: BehaviorSubject<RenderableAutoCompleteItem[]>;
constructor(private _autoCompleteService: AutoCompleteService,
private _searchQueryParserService: SearchQueryParserService,
@ -57,21 +49,6 @@ export class GallerySearchComponent implements OnDestroy {
});
}
get SearchHint() {
if (!this.autoCompleteItems ||
!this.autoCompleteItems.value || this.autoCompleteItems.value.length === 0) {
return this.rawSearchText;
}
const searchText = this.getAutocompleteToken();
if (searchText.current === '') {
return this.rawSearchText + this.autoCompleteItems.value[this.highlightedAutoCompleteItem].queryHint;
}
if (this.autoCompleteItems.value[0].queryHint.startsWith(searchText.current)) {
return this.rawSearchText + this.autoCompleteItems.value[this.highlightedAutoCompleteItem].queryHint.substr(searchText.current.length);
}
return this.rawSearchText;
}
get HTMLSearchQuery() {
return JSON.stringify(this.searchQueryDTO);
}
@ -83,43 +60,6 @@ export class GallerySearchComponent implements OnDestroy {
}
}
getAutocompleteToken(): { current: string, prev: string } {
if (this.rawSearchText.trim().length === 0) {
return {current: '', prev: ''};
}
const tokens = this.rawSearchText.split(' ');
return {
current: tokens[tokens.length - 1],
prev: (tokens.length > 2 ? tokens[tokens.length - 2] : '')
};
}
onSearchChange(event: KeyboardEvent) {
const searchText = this.getAutocompleteToken();
if (Config.Client.Search.AutoComplete.enabled &&
this.cache.lastAutocomplete !== searchText.current) {
this.cache.lastAutocomplete = searchText.current;
this.autocomplete(searchText).catch(console.error);
}
}
public setMouseOverAutoComplete(value: boolean) {
this.mouseOverAutoComplete = value;
}
public onFocusLost() {
if (this.mouseOverAutoComplete === false) {
this.autoCompleteRenders = [];
}
}
public onFocus() {
// TODO: implement autocomplete
// this.autocomplete(this.searchText).catch(console.error);
}
public async openModal(template: TemplateRef<any>) {
this.modalRef = this.modalService.show(template, {class: 'modal-lg'});
document.body.style.paddingRight = '0px';
@ -136,7 +76,7 @@ export class GallerySearchComponent implements OnDestroy {
onQueryChange() {
this.rawSearchText = this._searchQueryParserService.stringify(this.searchQueryDTO);
this.validateRawSearchText();
// this.validateRawSearchText();
}
validateRawSearchText() {
@ -151,102 +91,7 @@ export class GallerySearchComponent implements OnDestroy {
this.router.navigate(['/search', this.HTMLSearchQuery]).catch(console.error);
}
applyHint($event: any) {
if ($event.target.selectionStart !== this.rawSearchText.length) {
return;
}
this.rawSearchText = this.SearchHint;
this.validateRawSearchText();
}
applyAutoComplete(item: AutoCompleteRenderItem) {
const token = this.getAutocompleteToken();
this.rawSearchText = this.rawSearchText.substr(0, this.rawSearchText.length - token.current.length)
+ item.queryHint;
this.emptyAutoComplete();
this.validateRawSearchText();
}
setMouseOverAutoCompleteItem(i: number) {
this.highlightedAutoCompleteItem = i;
}
selectAutocompleteUp() {
if (this.highlightedAutoCompleteItem > 0) {
this.highlightedAutoCompleteItem--;
}
}
selectAutocompleteDown() {
if (this.autoCompleteItems &&
this.highlightedAutoCompleteItem < this.autoCompleteItems.value.length - 1) {
this.highlightedAutoCompleteItem++;
}
}
OnEnter($event: any) {
if (this.autoCompleteRenders.length === 0) {
this.Search();
return;
}
this.applyAutoComplete(this.autoCompleteRenders[this.highlightedAutoCompleteItem]);
}
private emptyAutoComplete() {
this.highlightedAutoCompleteItem = 0;
this.autoCompleteRenders = [];
}
private async autocomplete(searchText: { current: string, prev: string }) {
if (!Config.Client.Search.AutoComplete.enabled) {
return;
}
if (this.rawSearchText.trim().length > 0) { // are we searching for anything?
try {
if (this.autoCompleteItems) {
this.autoCompleteItems.unsubscribe();
}
this.autoCompleteItems = this._autoCompleteService.autoComplete(searchText);
this.autoCompleteItems.subscribe(() => {
this.showSuggestions(this.autoCompleteItems.value, searchText.current);
});
} catch (error) {
console.error(error);
}
} else {
this.emptyAutoComplete();
}
}
private showSuggestions(suggestions: RenderableAutoCompleteItem[], searchText: string) {
this.emptyAutoComplete();
suggestions.forEach((item: RenderableAutoCompleteItem) => {
const renderItem = new AutoCompleteRenderItem(item.text, searchText, item.type, item.queryHint);
this.autoCompleteRenders.push(renderItem);
});
}
}
class AutoCompleteRenderItem {
public preText = '';
public highLightText = '';
public postText = '';
public type: SearchQueryTypes;
public queryHint: string;
constructor(public text: string, searchText: string, type: SearchQueryTypes, queryHint: string) {
const preIndex = text.toLowerCase().indexOf(searchText.toLowerCase());
if (preIndex > -1) {
this.preText = text.substring(0, preIndex);
this.highLightText = text.substring(preIndex, preIndex + searchText.length);
this.postText = text.substring(preIndex + searchText.length);
} else {
this.postText = text;
}
this.type = type;
this.queryHint = queryHint;
}
}