Merge pull request #4976 from GabrielInTheWorld/improvingSuperSearch
Improves the global search to find IDs
This commit is contained in:
commit
20f1f982ae
@ -192,16 +192,16 @@ export class SearchService {
|
|||||||
*
|
*
|
||||||
* @param query The search query
|
* @param query The search query
|
||||||
* @param inCollectionStrings All connection strings which should be used for searching.
|
* @param inCollectionStrings All connection strings which should be used for searching.
|
||||||
* @param sortingProperty Sorting by `id` or `title`.
|
|
||||||
* @param dedicatedId Optional parameter. Useful to look for a specific id in the given collectionStrings.
|
* @param dedicatedId Optional parameter. Useful to look for a specific id in the given collectionStrings.
|
||||||
|
* @param searchOnlyById Optional parameter. Decides, whether all models should only be filtered by their id.
|
||||||
*
|
*
|
||||||
* @returns All search results sorted by the model's title (via `getTitle()`).
|
* @returns All search results sorted by the model's title (via `getTitle()`).
|
||||||
*/
|
*/
|
||||||
public search(
|
public search(
|
||||||
query: string,
|
query: string,
|
||||||
inCollectionStrings: string[],
|
inCollectionStrings: string[],
|
||||||
sortingProperty: 'title' | 'id' = 'title',
|
dedicatedId?: number,
|
||||||
dedicatedId?: number
|
searchOnlyById: boolean = false
|
||||||
): SearchResult[] {
|
): SearchResult[] {
|
||||||
query = query.toLowerCase();
|
query = query.toLowerCase();
|
||||||
return this.searchModels
|
return this.searchModels
|
||||||
@ -211,20 +211,15 @@ export class SearchService {
|
|||||||
.getAll(searchModel.collectionString)
|
.getAll(searchModel.collectionString)
|
||||||
.map(x => x as (BaseViewModel & Searchable))
|
.map(x => x as (BaseViewModel & Searchable))
|
||||||
.filter(model =>
|
.filter(model =>
|
||||||
dedicatedId
|
!searchOnlyById
|
||||||
? model.id === dedicatedId
|
? model.id === dedicatedId ||
|
||||||
: model
|
model
|
||||||
.formatForSearch()
|
.formatForSearch()
|
||||||
.searchValue.some(text => text && text.toLowerCase().indexOf(query) !== -1)
|
.searchValue.some(text => text && text.toLowerCase().indexOf(query) !== -1)
|
||||||
|
: model.id === dedicatedId
|
||||||
)
|
)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => this.languageCollator.compare(a.getTitle(), b.getTitle()));
|
||||||
switch (sortingProperty) {
|
|
||||||
case 'id':
|
|
||||||
return a.id - b.id;
|
|
||||||
case 'title':
|
|
||||||
return this.languageCollator.compare(a.getTitle(), b.getTitle());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return {
|
return {
|
||||||
collectionString: searchModel.collectionString,
|
collectionString: searchModel.collectionString,
|
||||||
verboseName: results.length === 1 ? searchModel.verboseNameSingular : searchModel.verboseNamePlural,
|
verboseName: results.length === 1 ? searchModel.verboseNameSingular : searchModel.verboseNamePlural,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
.filter-menu-content-wrapper {
|
.filter-menu-content-wrapper {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
div.indent {
|
div.indent {
|
||||||
margin-left: 24px;
|
margin-left: 24px;
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
<!-- Search bar -->
|
<!-- Search bar -->
|
||||||
<os-rounded-input
|
<os-rounded-input
|
||||||
|
#searchField
|
||||||
[model]="searchFieldInput"
|
[model]="searchFieldInput"
|
||||||
[size]="'small'"
|
[size]="'small'"
|
||||||
[fullWidth]="false"
|
[fullWidth]="false"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core';
|
import { Component, EventEmitter, HostListener, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
import { MatBottomSheet } from '@angular/material/bottom-sheet';
|
import { MatBottomSheet } from '@angular/material/bottom-sheet';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@ -9,6 +9,7 @@ import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service
|
|||||||
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { FilterMenuComponent } from './filter-menu/filter-menu.component';
|
import { FilterMenuComponent } from './filter-menu/filter-menu.component';
|
||||||
|
import { RoundedInputComponent } from '../rounded-input/rounded-input.component';
|
||||||
import { SortBottomSheetComponent } from './sort-bottom-sheet/sort-bottom-sheet.component';
|
import { SortBottomSheetComponent } from './sort-bottom-sheet/sort-bottom-sheet.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,6 +33,9 @@ import { SortBottomSheetComponent } from './sort-bottom-sheet/sort-bottom-sheet.
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class SortFilterBarComponent<V extends BaseViewModel> {
|
export class SortFilterBarComponent<V extends BaseViewModel> {
|
||||||
|
@ViewChild('searchField', { static: true })
|
||||||
|
public searchField: RoundedInputComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The currently active sorting service for the list view
|
* The currently active sorting service for the list view
|
||||||
*/
|
*/
|
||||||
@ -225,4 +229,12 @@ export class SortFilterBarComponent<V extends BaseViewModel> {
|
|||||||
const itemProperty = option.property as string;
|
const itemProperty = option.property as string;
|
||||||
return itemProperty.charAt(0).toUpperCase() + itemProperty.slice(1);
|
return itemProperty.charAt(0).toUpperCase() + itemProperty.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('document:keydown', ['$event']) public onKeyDown(event: KeyboardEvent): void {
|
||||||
|
if (event.ctrlKey && event.key === 'f') {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
this.searchField.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,18 +9,23 @@
|
|||||||
></os-rounded-input>
|
></os-rounded-input>
|
||||||
<button mat-icon-button [matMenuTriggerFor]="filterMenu"><mat-icon>filter_list</mat-icon></button>
|
<button mat-icon-button [matMenuTriggerFor]="filterMenu"><mat-icon>filter_list</mat-icon></button>
|
||||||
<mat-menu #filterMenu="matMenu">
|
<mat-menu #filterMenu="matMenu">
|
||||||
|
<button mat-menu-item (click)="setSearchStringForID()">
|
||||||
|
<mat-icon>{{ !!searchStringForID ? 'checked' : '' }}</mat-icon>
|
||||||
|
ID
|
||||||
|
</button>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
<button
|
<button
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
*ngFor="let model of registeredModels"
|
*ngFor="let model of registeredModels"
|
||||||
(click)="setCollection(model.verboseNamePlural)"
|
(click)="setCollection(model.verboseNamePlural)"
|
||||||
>
|
>
|
||||||
<mat-icon>{{ model.verboseNamePlural === searchCollection ? 'checked' : '' }}</mat-icon>
|
<mat-icon>{{ model.collectionString === specificCollectionString ? 'checked' : '' }}</mat-icon>
|
||||||
{{ model.verboseNamePlural | translate }}
|
{{ model.verboseNamePlural | translate }}
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</div>
|
</div>
|
||||||
<h4 *ngIf="searchResultCount > 0" class="result-count">
|
<h4 *ngIf="searchResultCount > 0" class="result-count">
|
||||||
{{ searchResultCount }} {{ searchResultCount === 1 ? ( 'result' | translate ) : ( 'results' | translate ) }}
|
{{ searchResultCount }} {{ searchResultCount === 1 ? ('result' | translate) : ('results' | translate) }}
|
||||||
</h4>
|
</h4>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<div class="result-view" *ngIf="searchResults.length > 0">
|
<div class="result-view" *ngIf="searchResults.length > 0">
|
||||||
@ -37,7 +42,9 @@
|
|||||||
<mat-basic-chip
|
<mat-basic-chip
|
||||||
class="lightblue filter-count"
|
class="lightblue filter-count"
|
||||||
disableRipple
|
disableRipple
|
||||||
matTooltip="{{ result.models.length === 1 ? ( 'result' | translate ) : ( 'results' | translate ) }}"
|
matTooltip="{{
|
||||||
|
result.models.length === 1 ? ('result' | translate) : ('results' | translate)
|
||||||
|
}}"
|
||||||
>{{ result.models.length }}</mat-basic-chip
|
>{{ result.models.length }}</mat-basic-chip
|
||||||
>
|
>
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
@ -84,7 +91,9 @@
|
|||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
<div class="no-results" *ngIf="!selectedModel && searchString.length > 0">
|
<div class="no-results" *ngIf="!selectedModel && searchString.length > 0">
|
||||||
<span translate>No search result found</span>
|
<span translate>No search result found</span>
|
||||||
<span *ngIf="searchCollection"> ({{ 'with filter' | translate }} "{{ searchCollection | translate }}")</span>.
|
<span *ngIf="searchCollection"
|
||||||
|
> ({{ 'with filter' | translate }} "{{ searchCollection | translate }}")</span
|
||||||
|
>.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider [vertical]="true"></mat-divider>
|
<mat-divider [vertical]="true"></mat-divider>
|
||||||
|
@ -36,8 +36,20 @@ export class SuperSearchComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the collection-string of the specific collection.
|
* Holds the collection-string of the specific collection.
|
||||||
|
*
|
||||||
|
* Is set, if the user has entered a collection.
|
||||||
*/
|
*/
|
||||||
private specificCollectionString: string = null;
|
public specificCollectionString: string = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the input text the user entered to search for a specific id.
|
||||||
|
*/
|
||||||
|
public searchStringForID: string = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The specific id the user searches for.
|
||||||
|
*/
|
||||||
|
private specificID: number = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The results for the given query.
|
* The results for the given query.
|
||||||
@ -119,11 +131,11 @@ export class SuperSearchComponent implements OnInit {
|
|||||||
this.collectionStrings = this.registeredModels.map(rm => rm.collectionString);
|
this.collectionStrings = this.registeredModels.map(rm => rm.collectionString);
|
||||||
this.translatedCollectionStrings = this.searchService.getTranslatedCollectionStrings();
|
this.translatedCollectionStrings = this.searchService.getTranslatedCollectionStrings();
|
||||||
|
|
||||||
this.searchForm.valueChanges.pipe(debounceTime(250)).subscribe(value => {
|
this.searchForm.valueChanges.pipe(debounceTime(250)).subscribe((value: string) => {
|
||||||
if (value.trim() === '') {
|
if (value.trim() === '') {
|
||||||
this.clearResults();
|
this.clearResults();
|
||||||
} else {
|
} else {
|
||||||
this.specificCollectionString = this.searchSpecificCollection(value.trim());
|
this.prepareForSearch(value.trim());
|
||||||
}
|
}
|
||||||
this.search();
|
this.search();
|
||||||
});
|
});
|
||||||
@ -136,22 +148,11 @@ export class SuperSearchComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
private search(): void {
|
private search(): void {
|
||||||
if (this.searchString !== '') {
|
if (this.searchString !== '') {
|
||||||
// Local variable to check, if the user searches for a specific id.
|
|
||||||
let dedicatedId: number;
|
|
||||||
|
|
||||||
const query = this.searchString;
|
|
||||||
// Looks, if the query matches variations of 'nr.' followed by at least one digit.
|
|
||||||
// If so, the user searches for a specific id in some collections.
|
|
||||||
// Everything not case-sensitive.
|
|
||||||
if (query.match(/n\w*r\.?\:?\s*\d+/gi)) {
|
|
||||||
// If so, this expression finds the number.
|
|
||||||
dedicatedId = +query.match(/\d+/g);
|
|
||||||
}
|
|
||||||
this.searchResults = this.searchService.search(
|
this.searchResults = this.searchService.search(
|
||||||
query,
|
this.searchString,
|
||||||
this.specificCollectionString ? [this.specificCollectionString] : this.collectionStrings,
|
this.specificCollectionString ? [this.specificCollectionString] : this.collectionStrings,
|
||||||
'title',
|
this.specificID,
|
||||||
dedicatedId
|
!!this.searchStringForID
|
||||||
);
|
);
|
||||||
this.selectFirstResult();
|
this.selectFirstResult();
|
||||||
} else {
|
} else {
|
||||||
@ -162,33 +163,71 @@ export class SuperSearchComponent implements OnInit {
|
|||||||
.reduce((acc, current) => acc + current, 0);
|
.reduce((acc, current) => acc + current, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to check several things.
|
||||||
|
*
|
||||||
|
* First the query is splitted and the first part is tested
|
||||||
|
* for a specific collection.
|
||||||
|
*
|
||||||
|
* Second the next part is tested for a specific id.
|
||||||
|
* It's looking for the word `id` or any kind of `nr.`
|
||||||
|
* and a number.
|
||||||
|
*
|
||||||
|
* @param query The user's input, he searches for.
|
||||||
|
*/
|
||||||
|
private prepareForSearch(query: string): void {
|
||||||
|
// The query is splitted by the first whitespace or the first ':'.
|
||||||
|
const splittedQuery = query.split(/\s*(?::|\s+)\s*/);
|
||||||
|
|
||||||
|
this.specificCollectionString = this.searchSpecificCollection(splittedQuery[0]);
|
||||||
|
if (this.specificCollectionString) {
|
||||||
|
this.searchCollection = splittedQuery.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searchStringForID = this.searchSpecificId(splittedQuery[0]) ? splittedQuery.shift() : null;
|
||||||
|
|
||||||
|
// This test, whether the query includes an number --> Then get this number.
|
||||||
|
if (/\b\d+\b/g.test(splittedQuery[0])) {
|
||||||
|
this.specificID = +query.match(/\d+/g);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rest will be joined to one string.
|
||||||
|
this.searchString = splittedQuery.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function test, if the query matches some of the `collectionStrings`.
|
* This function test, if the query matches some of the `collectionStrings`.
|
||||||
*
|
*
|
||||||
* That indicates, that the user looks for items in a specific collection.
|
* That indicates, that the user looks for items in a specific collection.
|
||||||
*
|
*
|
||||||
* @returns { { collection: string, query: string[] } | null } Either an object containing the found collection and the query
|
* @returns { string | null } Either an object containing the found collection and the query
|
||||||
* or null, if there exists none.
|
* or null, if there exists none.
|
||||||
*/
|
*/
|
||||||
private searchSpecificCollection(query: string): string | null {
|
private searchSpecificCollection(query: string): string | null {
|
||||||
// The query is splitted by the first whitespace or the first ':'.
|
// The query is splitted by the first whitespace or the first ':'.
|
||||||
const splittedQuery = query.split(/\s*(?::|\s+)\s*/);
|
|
||||||
const nextCollection = this.translatedCollectionStrings.find(item =>
|
const nextCollection = this.translatedCollectionStrings.find(item =>
|
||||||
// The value of the item should match the query plus any further
|
// The value of the item should match the query plus any further
|
||||||
// characters (useful for splitted words in the query).
|
// characters (useful for splitted words in the query).
|
||||||
// This will look, if the user searches in a specific collection.
|
// This will look, if the user searches in a specific collection.
|
||||||
// Flag 'i' tells, that cases are ignored.
|
// Flag 'i' tells, that cases are ignored.
|
||||||
new RegExp(item.value, 'i').test(splittedQuery[0])
|
// new RegExp(item.value, 'i').test(splittedQuery[0])
|
||||||
|
new RegExp(item.value, 'i').test(query)
|
||||||
);
|
);
|
||||||
if (!!nextCollection) {
|
return !!nextCollection ? nextCollection.collection : null;
|
||||||
this.searchString = splittedQuery.slice(1).join(' ');
|
|
||||||
this.searchCollection = splittedQuery[0];
|
|
||||||
return nextCollection.collection;
|
|
||||||
} else {
|
|
||||||
this.searchString = query;
|
|
||||||
this.searchCollection = '';
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to see, whether a string matches the word `id` or any kind of `nr`.
|
||||||
|
*
|
||||||
|
* @param query The query, which is tested for the word `id` or `nr`.
|
||||||
|
*
|
||||||
|
* @returns {boolean} If the given string matches any kind of the test-string.
|
||||||
|
*/
|
||||||
|
private searchSpecificId(query: string = ''): boolean {
|
||||||
|
// Looks, if the query matches variations of 'nr.' or 'id'
|
||||||
|
// If so, the user searches for a specific id in some collections.
|
||||||
|
// Everything not case-sensitive.
|
||||||
|
return !!(query.match(/\bn\w*r\.?\:?\b/gi) || query.match(/\bid\.?\:?\b/gi));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -215,6 +254,19 @@ export class SuperSearchComponent implements OnInit {
|
|||||||
private selectNextResult(up: boolean): void {
|
private selectNextResult(up: boolean): void {
|
||||||
const tmp = this.searchResults.flatMap((result: SearchResult) => result.models);
|
const tmp = this.searchResults.flatMap((result: SearchResult) => result.models);
|
||||||
this.changeModel(tmp[(tmp.indexOf(this.selectedModel) + (up ? -1 : 1)).modulo(tmp.length)]);
|
this.changeModel(tmp[(tmp.indexOf(this.selectedModel) + (up ? -1 : 1)).modulo(tmp.length)]);
|
||||||
|
|
||||||
|
this.scrollToSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to scroll with the current selected model, if the user uses the keyboard to navigate.
|
||||||
|
*/
|
||||||
|
private scrollToSelected(): void {
|
||||||
|
const selectedElement = document.getElementsByClassName('selected')[0];
|
||||||
|
selectedElement.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'center'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,12 +275,27 @@ export class SuperSearchComponent implements OnInit {
|
|||||||
* @param collectionName The `verboseName` of the selected collection.
|
* @param collectionName The `verboseName` of the selected collection.
|
||||||
*/
|
*/
|
||||||
public setCollection(collectionName: string): void {
|
public setCollection(collectionName: string): void {
|
||||||
this.searchCollection = this.searchCollection === collectionName ? '' : collectionName;
|
this.searchCollection =
|
||||||
if (this.searchCollection !== '') {
|
this.searchCollection.toLowerCase() === collectionName.toLowerCase() ? '' : collectionName;
|
||||||
this.searchForm.setValue(this.searchCollection + ': ' + this.searchString);
|
this.setSearch();
|
||||||
} else {
|
|
||||||
this.searchForm.setValue(this.searchString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sets the string for id or clears the variable, if already existing.
|
||||||
|
*/
|
||||||
|
public setSearchStringForID(): void {
|
||||||
|
this.searchStringForID = !!this.searchStringForID ? null : 'id';
|
||||||
|
this.setSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function puts the various strings together.
|
||||||
|
*/
|
||||||
|
private setSearch(): void {
|
||||||
|
this.searchForm.setValue(
|
||||||
|
[this.searchCollection, this.searchStringForID].map(value => (value ? value + ': ' : '')).join('') +
|
||||||
|
this.searchString
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -274,6 +341,7 @@ export class SuperSearchComponent implements OnInit {
|
|||||||
this.selectedModel = null;
|
this.selectedModel = null;
|
||||||
this.searchCollection = '';
|
this.searchCollection = '';
|
||||||
this.searchString = '';
|
this.searchString = '';
|
||||||
|
this.searchStringForID = null;
|
||||||
this.saveQueryToStorage(null);
|
this.saveQueryToStorage(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,6 +372,10 @@ export class SuperSearchComponent implements OnInit {
|
|||||||
* @param event KeyboardEvent to listen to keyboard-inputs.
|
* @param event KeyboardEvent to listen to keyboard-inputs.
|
||||||
*/
|
*/
|
||||||
@HostListener('document:keydown', ['$event']) public onKeyNavigation(event: KeyboardEvent): void {
|
@HostListener('document:keydown', ['$event']) public onKeyNavigation(event: KeyboardEvent): void {
|
||||||
|
if (event.ctrlKey && event.key === 'f') {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
if (!!this.selectedModel) {
|
if (!!this.selectedModel) {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
this.viewResult(this.selectedModel);
|
this.viewResult(this.selectedModel);
|
||||||
|
@ -114,7 +114,7 @@ export class ViewCategory extends BaseViewModel<Category> implements CategoryTit
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getDetailStateURL(): string {
|
public getDetailStateURL(): string {
|
||||||
return '/motions/category';
|
return `motions/category/${this.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -315,6 +315,8 @@ export class SiteComponent extends BaseComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@HostListener('document:keydown', ['$event']) public onKeyNavigation(event: KeyboardEvent): void {
|
@HostListener('document:keydown', ['$event']) public onKeyNavigation(event: KeyboardEvent): void {
|
||||||
if (event.altKey && event.shiftKey && event.code === 'KeyF') {
|
if (event.altKey && event.shiftKey && event.code === 'KeyF') {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
this.overlayService.showSearch();
|
this.overlayService.showSearch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user