mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-07-07 18:24:46 +00:00
[ACA-1822] input search options (#768)
* [ACA-1822] input search options - needs cleanup and styling * [ACA-1822] input search options cleanup - needs styling - needs tests * [ACA-1822] input search options cleanup - needs styling - needs tests * [ACA-1822] input search options styling * [ACA-1822] add translation keys * [ACA-1822] reformat with Prettier * [ACA-1822] fix pointer event on FF * [ACA-1822] fix e2e test * [ACA-1822] update unit tests * [ACA-1822] fix spellcheck * [ACA-1822] fix display * [ACA-1822] some theme cleanup * [ACA-1822] reposition icon * [ACA-1822] code review changes * [ACA-1822] more unit tests
This commit is contained in:
parent
37b4d9d00b
commit
2c9e68ada7
@ -36,15 +36,15 @@ export class Header extends Component {
|
|||||||
root: 'app-header',
|
root: 'app-header',
|
||||||
logoLink: by.css('.app-menu__title'),
|
logoLink: by.css('.app-menu__title'),
|
||||||
userInfo: by.css('aca-current-user'),
|
userInfo: by.css('aca-current-user'),
|
||||||
searchButton: by.css('#adf-search-button'),
|
searchButton: by.css('.app-search-button'),
|
||||||
searchBar: by.css('#adf-control-input'),
|
searchBar: by.css('#app-control-input'),
|
||||||
moreActions: by.id('app.header.more')
|
moreActions: by.id('app.header.more')
|
||||||
};
|
};
|
||||||
|
|
||||||
logoLink: ElementFinder = this.component.element(this.locators.logoLink);
|
logoLink: ElementFinder = this.component.element(this.locators.logoLink);
|
||||||
userInfo: UserInfo = new UserInfo(this.component);
|
userInfo: UserInfo = new UserInfo(this.component);
|
||||||
searchButton: ElementFinder = this.component.element(this.locators.searchButton);
|
searchButton: ElementFinder = this.component.element(this.locators.searchButton);
|
||||||
searchBar: ElementFinder = this.component.element(this.locators.searchBar);
|
searchBar: ElementFinder = browser.element(this.locators.searchBar);
|
||||||
moreActions: ElementFinder = browser.element(this.locators.moreActions);
|
moreActions: ElementFinder = browser.element(this.locators.moreActions);
|
||||||
|
|
||||||
menu: Menu = new Menu();
|
menu: Menu = new Menu();
|
||||||
|
@ -208,6 +208,26 @@ export const APP_ROUTES: Routes = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'search-libraries',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: SearchResultsComponent,
|
||||||
|
data: {
|
||||||
|
title: 'APP.BROWSE.SEARCH.TITLE',
|
||||||
|
reuse: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'preview/:nodeId',
|
||||||
|
loadChildren: './components/preview/preview.module#PreviewModule',
|
||||||
|
data: {
|
||||||
|
navigateSource: 'search'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '**',
|
path: '**',
|
||||||
component: GenericErrorComponent
|
component: GenericErrorComponent
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
<ng-container *ngIf="!isSmallScreen">
|
<ng-container *ngIf="!isSmallScreen">
|
||||||
<aca-search-input></aca-search-input>
|
<aca-search-input></aca-search-input>
|
||||||
<adf-toolbar-divider></adf-toolbar-divider>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<aca-current-user></aca-current-user>
|
<aca-current-user></aca-current-user>
|
||||||
|
@ -1,94 +1,22 @@
|
|||||||
<div class="adf-search-container">
|
<div class="app-search-container">
|
||||||
<div [@transitionMessages]="subscriptAnimationState" (@transitionMessages.done)="applySearchFocus($event)">
|
<button mat-icon-button
|
||||||
<button mat-icon-button
|
id="app-search-button"
|
||||||
id="adf-search-button"
|
class="app-search-button"
|
||||||
class="adf-search-button"
|
[title]="'SEARCH.BUTTON.TOOLTIP' | translate">
|
||||||
[title]="'SEARCH.BUTTON.TOOLTIP' | translate"
|
<mat-icon [attr.aria-label]="'SEARCH.BUTTON.ARIA-LABEL' | translate">search</mat-icon>
|
||||||
(click)="expandable && toggleSearchBar()"
|
</button>
|
||||||
(keyup.enter)="expandable && toggleSearchBar()">
|
<mat-form-field class="app-input-form-field">
|
||||||
<mat-icon [attr.aria-label]="'SEARCH.BUTTON.ARIA-LABEL' | translate">search</mat-icon>
|
<input matInput #searchInput
|
||||||
</button>
|
[attr.aria-label]="'SEARCH.INPUT.ARIA-LABEL' | translate"
|
||||||
<mat-form-field class="adf-input-form-field-divider">
|
[type]="inputType"
|
||||||
<ng-container [ngSwitch]="liveSearchEnabled">
|
id="app-control-input"
|
||||||
<input matInput #searchInput
|
[(ngModel)]="searchTerm"
|
||||||
*ngSwitchCase="liveSearchEnabled"
|
(ngModelChange)="inputChange($event)"
|
||||||
[attr.aria-label]="'SEARCH.INPUT.ARIA-LABEL' | translate"
|
(keyup.enter)="searchSubmit($event)">
|
||||||
[type]="inputType"
|
<mat-placeholder class="placeholder" *ngIf="!searchTerm.length">{{ 'SEARCH.INPUT.PLACEHOLDER' | translate }}</mat-placeholder>
|
||||||
[autocomplete]="getAutoComplete()"
|
|
||||||
id="adf-control-input"
|
|
||||||
[(ngModel)]="searchTerm"
|
|
||||||
(focus)="activateToolbar()"
|
|
||||||
(blur)="onBlur($event)"
|
|
||||||
(keyup.escape)="toggleSearchBar()"
|
|
||||||
(keyup.arrowdown)="selectFirstResult()"
|
|
||||||
(ngModelChange)="inputChange($event)"
|
|
||||||
[searchAutocomplete]="auto"
|
|
||||||
(keyup.enter)="searchSubmit($event)">
|
|
||||||
<input matInput
|
|
||||||
*ngSwitchDefault
|
|
||||||
[attr.aria-label]="'SEARCH.INPUT.ARIA-LABEL' | translate"
|
|
||||||
[type]="inputType"
|
|
||||||
[autocomplete]="getAutoComplete()"
|
|
||||||
[(ngModel)]="searchTerm"
|
|
||||||
(ngModelChange)="inputChange($event)"
|
|
||||||
(keyup.enter)="searchSubmit($event)">
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<div matSuffix class="adf-clear-search-icon-wrapper">
|
<div matSuffix class="app-suffix-search-icon-wrapper">
|
||||||
<mat-icon *ngIf="searchTerm.length > 0"
|
<mat-icon>arrow_drop_down</mat-icon>
|
||||||
(click)="clear()">clear
|
</div>
|
||||||
</mat-icon>
|
</mat-form-field>
|
||||||
</div>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<adf-search #search
|
|
||||||
#auto="searchAutocomplete"
|
|
||||||
class="adf-search-result-autocomplete"
|
|
||||||
[maxResults]="liveSearchMaxResults"
|
|
||||||
[queryBody]="customQueryBody">
|
|
||||||
<ng-template let-data>
|
|
||||||
<mat-list *ngIf="isSearchBarActive()" id="autocomplete-search-result-list">
|
|
||||||
<mat-list-item
|
|
||||||
*ngFor="let item of data?.list?.entries; let idx = index"
|
|
||||||
id="result_option_{{idx}}"
|
|
||||||
[attr.data-automation-id]="'autocomplete_for_' + item.entry.name"
|
|
||||||
[tabindex]="0"
|
|
||||||
(focus)="onFocus($event)"
|
|
||||||
(blur)="onBlur($event)"
|
|
||||||
(keyup.arrowdown)="onRowArrowDown($event)"
|
|
||||||
(keyup.arrowup)="onRowArrowUp($event)"
|
|
||||||
class="adf-search-autocomplete-item"
|
|
||||||
(click)="elementClicked(item)"
|
|
||||||
(keyup.enter)="elementClicked(item)"
|
|
||||||
(touchend)="elementClicked(item)">
|
|
||||||
<!-- This is a comment -->
|
|
||||||
<mat-icon mat-list-icon>
|
|
||||||
<img [src]="getMimeTypeIcon(item)" />
|
|
||||||
</mat-icon>
|
|
||||||
<h4 mat-line id="result_name_{{idx}}"
|
|
||||||
*ngIf="highlight; else elseBlock"
|
|
||||||
class="adf-search-fixed-text"
|
|
||||||
[innerHtml]="item.entry.name | highlight: searchTerm">
|
|
||||||
{{ item?.entry.name }}
|
|
||||||
</h4>
|
|
||||||
<ng-template #elseBlock>
|
|
||||||
<h4 class="adf-search-fixed-text" mat-line id="result_name_{{idx}}" [innerHtml]="item.entry.name"></h4>
|
|
||||||
</ng-template>
|
|
||||||
<p mat-line class="adf-search-fixed-text"> {{item?.entry?.createdByUser?.displayName}} </p>
|
|
||||||
</mat-list-item>
|
|
||||||
<mat-list-item id="search_no_result"
|
|
||||||
data-automation-id="search_no_result_found"
|
|
||||||
*ngIf="data?.list?.entries.length === 0">
|
|
||||||
<ng-content
|
|
||||||
selector="adf-empty-search-result"
|
|
||||||
*ngIf="isNoSearchTemplatePresent(); else defaultNoResult">
|
|
||||||
</ng-content>
|
|
||||||
<ng-template #defaultNoResult>
|
|
||||||
<p mat-line class="adf-search-fixed-text">{{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}</p>
|
|
||||||
</ng-template>
|
|
||||||
</mat-list-item>
|
|
||||||
</mat-list>
|
|
||||||
</ng-template>
|
|
||||||
</adf-search>
|
|
||||||
|
@ -1,8 +1,51 @@
|
|||||||
.adf-clear-search-icon-wrapper {
|
$top-margin: 12px;
|
||||||
width: 1em;
|
|
||||||
|
.app-search-container {
|
||||||
|
margin-top: -$top-margin;
|
||||||
|
padding-top: 2px;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
.mat-form-field-underline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field-label-wrapper {
|
||||||
|
cursor: text;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-input-form-field {
|
||||||
|
letter-spacing: -0.7px;
|
||||||
|
width: 90%;
|
||||||
|
|
||||||
|
.mat-input-element {
|
||||||
|
letter-spacing: -0.7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-focused .placeholder {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-search-button.mat-icon-button {
|
||||||
|
top: -2px;
|
||||||
|
left: -8px;
|
||||||
|
margin-left: 15px;
|
||||||
|
|
||||||
|
.mat-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-suffix-search-icon-wrapper {
|
||||||
|
height: 6px;
|
||||||
|
margin: 14px 13px;
|
||||||
|
float: left;
|
||||||
|
|
||||||
.mat-icon {
|
.mat-icon {
|
||||||
font-size: 100%;
|
font-size: 24px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,99 +23,32 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ThumbnailService } from '@alfresco/adf-core';
|
|
||||||
import {
|
|
||||||
animate,
|
|
||||||
state,
|
|
||||||
style,
|
|
||||||
transition,
|
|
||||||
trigger
|
|
||||||
} from '@angular/animations';
|
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
Input,
|
Input,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
|
||||||
Output,
|
Output,
|
||||||
QueryList,
|
|
||||||
ViewEncapsulation,
|
ViewEncapsulation,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ViewChildren,
|
ElementRef
|
||||||
ElementRef,
|
|
||||||
TemplateRef,
|
|
||||||
ContentChild
|
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MinimalNodeEntity, QueryBody } from 'alfresco-js-api';
|
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { MatListItem } from '@angular/material';
|
|
||||||
import { debounceTime, filter, takeUntil } from 'rxjs/operators';
|
|
||||||
import {
|
|
||||||
EmptySearchResultComponent,
|
|
||||||
SearchComponent
|
|
||||||
} from '@alfresco/adf-content-services';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-search-input-control',
|
selector: 'app-search-input-control',
|
||||||
templateUrl: './search-input-control.component.html',
|
templateUrl: './search-input-control.component.html',
|
||||||
styleUrls: ['./search-input-control.component.scss'],
|
styleUrls: ['./search-input-control.component.scss'],
|
||||||
animations: [
|
|
||||||
trigger('transitionMessages', [
|
|
||||||
state(
|
|
||||||
'active',
|
|
||||||
style({ transform: 'translateX(0%)', 'margin-left': '13px' })
|
|
||||||
),
|
|
||||||
state('inactive', style({ transform: 'translateX(81%)' })),
|
|
||||||
state(
|
|
||||||
'no-animation',
|
|
||||||
style({ transform: 'translateX(0%)', width: '100%' })
|
|
||||||
),
|
|
||||||
transition(
|
|
||||||
'inactive => active',
|
|
||||||
animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')
|
|
||||||
),
|
|
||||||
transition(
|
|
||||||
'active => inactive',
|
|
||||||
animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')
|
|
||||||
)
|
|
||||||
])
|
|
||||||
],
|
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
host: { class: 'adf-search-control' }
|
host: { class: 'app-search-control' }
|
||||||
})
|
})
|
||||||
export class SearchInputControlComponent implements OnInit, OnDestroy {
|
export class SearchInputControlComponent implements OnDestroy {
|
||||||
onDestroy$: Subject<boolean> = new Subject<boolean>();
|
onDestroy$: Subject<boolean> = new Subject<boolean>();
|
||||||
|
|
||||||
/** Toggles whether to use an expanding search control. If false
|
|
||||||
* then a regular input is used.
|
|
||||||
*/
|
|
||||||
@Input()
|
|
||||||
expandable = true;
|
|
||||||
|
|
||||||
/** Toggles highlighting of the search term in the results. */
|
|
||||||
@Input()
|
|
||||||
highlight = false;
|
|
||||||
|
|
||||||
/** Type of the input field to render, e.g. "search" or "text" (default). */
|
/** Type of the input field to render, e.g. "search" or "text" (default). */
|
||||||
@Input()
|
@Input()
|
||||||
inputType = 'text';
|
inputType = 'text';
|
||||||
|
|
||||||
/** Toggles auto-completion of the search input field. */
|
|
||||||
@Input()
|
|
||||||
autocomplete = false;
|
|
||||||
|
|
||||||
/** Toggles "find-as-you-type" suggestions for possible matches. */
|
|
||||||
@Input()
|
|
||||||
liveSearchEnabled = true;
|
|
||||||
|
|
||||||
/** Maximum number of results to show in the live search. */
|
|
||||||
@Input()
|
|
||||||
liveSearchMaxResults = 5;
|
|
||||||
|
|
||||||
/** @deprecated in 2.1.0 */
|
|
||||||
@Input()
|
|
||||||
customQueryBody: QueryBody;
|
|
||||||
|
|
||||||
/** Emitted when the search is submitted pressing ENTER button.
|
/** Emitted when the search is submitted pressing ENTER button.
|
||||||
* The search term is provided as value of the event.
|
* The search term is provided as value of the event.
|
||||||
*/
|
*/
|
||||||
@ -130,73 +63,10 @@ export class SearchInputControlComponent implements OnInit, OnDestroy {
|
|||||||
@Output()
|
@Output()
|
||||||
searchChange: EventEmitter<string> = new EventEmitter();
|
searchChange: EventEmitter<string> = new EventEmitter();
|
||||||
|
|
||||||
/** Emitted when a file item from the list of "find-as-you-type" results is selected. */
|
|
||||||
@Output()
|
|
||||||
optionClicked: EventEmitter<any> = new EventEmitter();
|
|
||||||
|
|
||||||
@ViewChild('search')
|
|
||||||
searchAutocomplete: SearchComponent;
|
|
||||||
|
|
||||||
@ViewChild('searchInput')
|
@ViewChild('searchInput')
|
||||||
searchInput: ElementRef;
|
searchInput: ElementRef;
|
||||||
|
|
||||||
@ViewChildren(MatListItem)
|
|
||||||
private listResultElement: QueryList<MatListItem>;
|
|
||||||
|
|
||||||
@ContentChild(EmptySearchResultComponent)
|
|
||||||
emptySearchTemplate: EmptySearchResultComponent;
|
|
||||||
|
|
||||||
searchTerm = '';
|
searchTerm = '';
|
||||||
subscriptAnimationState: string;
|
|
||||||
noSearchResultTemplate: TemplateRef<any> = null;
|
|
||||||
skipToggle = false;
|
|
||||||
toggleDebounceTime = 200;
|
|
||||||
|
|
||||||
private toggleSearch = new Subject<any>();
|
|
||||||
private focusSubject = new Subject<FocusEvent>();
|
|
||||||
|
|
||||||
constructor(private thumbnailService: ThumbnailService) {
|
|
||||||
this.toggleSearch
|
|
||||||
.asObservable()
|
|
||||||
.pipe(
|
|
||||||
debounceTime(this.toggleDebounceTime),
|
|
||||||
takeUntil(this.onDestroy$)
|
|
||||||
)
|
|
||||||
.subscribe(() => {
|
|
||||||
if (this.expandable && !this.skipToggle) {
|
|
||||||
this.subscriptAnimationState =
|
|
||||||
this.subscriptAnimationState === 'inactive' ? 'active' : 'inactive';
|
|
||||||
|
|
||||||
if (this.subscriptAnimationState === 'inactive') {
|
|
||||||
this.searchTerm = '';
|
|
||||||
this.searchAutocomplete.resetResults();
|
|
||||||
if (
|
|
||||||
document.activeElement.id === this.searchInput.nativeElement.id
|
|
||||||
) {
|
|
||||||
this.searchInput.nativeElement.blur();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.skipToggle = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
applySearchFocus(animationDoneEvent) {
|
|
||||||
if (animationDoneEvent.toState === 'active') {
|
|
||||||
this.searchInput.nativeElement.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.subscriptAnimationState = this.expandable
|
|
||||||
? 'inactive'
|
|
||||||
: 'no-animation';
|
|
||||||
this.setupFocusEventHandlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
isNoSearchTemplatePresent(): boolean {
|
|
||||||
return this.emptySearchTemplate ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.onDestroy$.next(true);
|
this.onDestroy$.next(true);
|
||||||
@ -205,117 +75,14 @@ export class SearchInputControlComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
searchSubmit(event: any) {
|
searchSubmit(event: any) {
|
||||||
this.submit.emit(event);
|
this.submit.emit(event);
|
||||||
this.toggleSearchBar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputChange(event: any) {
|
inputChange(event: any) {
|
||||||
this.searchChange.emit(event);
|
this.searchChange.emit(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAutoComplete(): string {
|
clear() {
|
||||||
return this.autocomplete ? 'on' : 'off';
|
|
||||||
}
|
|
||||||
|
|
||||||
getMimeTypeIcon(node: MinimalNodeEntity): string {
|
|
||||||
let mimeType;
|
|
||||||
|
|
||||||
if (node.entry.content && node.entry.content.mimeType) {
|
|
||||||
mimeType = node.entry.content.mimeType;
|
|
||||||
}
|
|
||||||
if (node.entry.isFolder) {
|
|
||||||
mimeType = 'folder';
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.thumbnailService.getMimeTypeIcon(mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
isSearchBarActive() {
|
|
||||||
return this.subscriptAnimationState === 'active' && this.liveSearchEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleSearchBar() {
|
|
||||||
if (this.toggleSearch) {
|
|
||||||
this.toggleSearch.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
elementClicked(item: any) {
|
|
||||||
if (item.entry) {
|
|
||||||
this.optionClicked.next(item);
|
|
||||||
this.toggleSearchBar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFocus($event): void {
|
|
||||||
this.focusSubject.next($event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onBlur($event): void {
|
|
||||||
this.focusSubject.next($event);
|
|
||||||
}
|
|
||||||
|
|
||||||
activateToolbar() {
|
|
||||||
if (!this.isSearchBarActive()) {
|
|
||||||
this.toggleSearchBar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectFirstResult() {
|
|
||||||
if (this.listResultElement && this.listResultElement.length > 0) {
|
|
||||||
const firstElement: MatListItem = <MatListItem>(
|
|
||||||
this.listResultElement.first
|
|
||||||
);
|
|
||||||
firstElement._getHostElement().focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRowArrowDown($event: KeyboardEvent): void {
|
|
||||||
const nextElement: any = this.getNextElementSibling(<Element>$event.target);
|
|
||||||
if (nextElement) {
|
|
||||||
nextElement.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRowArrowUp($event: KeyboardEvent): void {
|
|
||||||
const previousElement: any = this.getPreviousElementSibling(<Element>(
|
|
||||||
$event.target
|
|
||||||
));
|
|
||||||
if (previousElement) {
|
|
||||||
previousElement.focus();
|
|
||||||
} else {
|
|
||||||
this.searchInput.nativeElement.focus();
|
|
||||||
this.focusSubject.next(new FocusEvent('focus'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupFocusEventHandlers() {
|
|
||||||
this.focusSubject
|
|
||||||
.pipe(
|
|
||||||
debounceTime(50),
|
|
||||||
filter(($event: any) => {
|
|
||||||
return (
|
|
||||||
this.isSearchBarActive() &&
|
|
||||||
($event.type === 'blur' || $event.type === 'focusout')
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
takeUntil(this.onDestroy$)
|
|
||||||
)
|
|
||||||
.subscribe(() => {
|
|
||||||
this.toggleSearchBar();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear(event: any) {
|
|
||||||
this.searchTerm = '';
|
this.searchTerm = '';
|
||||||
this.searchChange.emit('');
|
this.searchChange.emit('');
|
||||||
this.skipToggle = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getNextElementSibling(node: Element): Element {
|
|
||||||
return node.nextElementSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPreviousElementSibling(node: Element): Element {
|
|
||||||
return node.previousElementSibling;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,35 @@
|
|||||||
<app-search-input-control #searchInputControl
|
<div class="app-search-container" [matMenuTriggerFor]="searchOptionsMenu">
|
||||||
[highlight]="true"
|
<button mat-icon-button
|
||||||
(optionClicked)="onItemClicked($event)"
|
class="app-search-button"
|
||||||
[expandable]="!onSearchResults"
|
[title]="'SEARCH.BUTTON.TOOLTIP' | translate">
|
||||||
[liveSearchEnabled]="enableLiveSearch"
|
<mat-icon [attr.aria-label]="'SEARCH.BUTTON.ARIA-LABEL' | translate">search</mat-icon>
|
||||||
(submit)="onSearchSubmit($event)"
|
</button>
|
||||||
(searchChange)="onSearchChange($event)">
|
<mat-form-field class="app-input-form-field">
|
||||||
</app-search-input-control>
|
<input matInput
|
||||||
|
[attr.aria-label]="'SEARCH.INPUT.ARIA-LABEL' | translate"
|
||||||
|
[type]="'text'"
|
||||||
|
[disabled]="true"
|
||||||
|
[value]="searchedWord">
|
||||||
|
<mat-placeholder class="placeholder" *ngIf="!searchedWord.length">{{ 'SEARCH.INPUT.PLACEHOLDER' | translate }}</mat-placeholder>
|
||||||
|
|
||||||
|
<div matSuffix class="app-suffix-search-icon-wrapper">
|
||||||
|
<mat-icon>arrow_drop_down</mat-icon>
|
||||||
|
</div>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-menu #searchOptionsMenu="matMenu" [overlapTrigger]="true" class="app-search-options-menu">
|
||||||
|
<app-search-input-control #searchInputControl
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
(submit)="onSearchSubmit($event)"
|
||||||
|
(searchChange)="onSearchChange($event)">
|
||||||
|
</app-search-input-control>
|
||||||
|
<div id="search-options">
|
||||||
|
<mat-checkbox *ngFor="let option of searchOptions"
|
||||||
|
[(ngModel)]="option.value"
|
||||||
|
[disabled]="option.shouldDisable()"
|
||||||
|
(click)="$event.stopPropagation()">
|
||||||
|
{{ option.key | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</mat-menu>
|
||||||
|
@ -35,12 +35,7 @@ import {
|
|||||||
import { SearchInputComponent } from './search-input.component';
|
import { SearchInputComponent } from './search-input.component';
|
||||||
import { AppTestingModule } from '../../../testing/app-testing.module';
|
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||||
import { Actions, ofType } from '@ngrx/effects';
|
import { Actions, ofType } from '@ngrx/effects';
|
||||||
import {
|
import { SEARCH_BY_TERM, SearchByTermAction } from '../../../store/actions';
|
||||||
NAVIGATE_FOLDER,
|
|
||||||
NavigateToFolder,
|
|
||||||
VIEW_FILE,
|
|
||||||
ViewFileAction
|
|
||||||
} from '../../../store/actions';
|
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
describe('SearchInputComponent', () => {
|
describe('SearchInputComponent', () => {
|
||||||
@ -63,35 +58,109 @@ describe('SearchInputComponent', () => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('onItemClicked()', () => {
|
describe('onSearchSubmit()', () => {
|
||||||
it('opens preview if node is file', fakeAsync(done => {
|
it('should call search action with correct search options', fakeAsync(done => {
|
||||||
|
const searchedTerm = 's';
|
||||||
|
const currentSearchOptions = [{ key: 'test' }];
|
||||||
actions$.pipe(
|
actions$.pipe(
|
||||||
ofType<ViewFileAction>(VIEW_FILE),
|
ofType<SearchByTermAction>(SEARCH_BY_TERM),
|
||||||
map(action => {
|
map(action => {
|
||||||
expect(action.payload.entry.id).toBe('node-id');
|
expect(action.searchOptions[0].key).toBe(currentSearchOptions[0].key);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
component.onSearchSubmit(<any>{ target: { value: searchedTerm } });
|
||||||
const node = {
|
|
||||||
entry: { isFile: true, id: 'node-id', parentId: 'parent-id' }
|
|
||||||
};
|
|
||||||
|
|
||||||
component.onItemClicked(node);
|
|
||||||
tick();
|
tick();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('navigates if node is folder', fakeAsync(done => {
|
it('should call search action with correct searched term', fakeAsync(done => {
|
||||||
|
const searchedTerm = 's';
|
||||||
actions$.pipe(
|
actions$.pipe(
|
||||||
ofType<NavigateToFolder>(NAVIGATE_FOLDER),
|
ofType<SearchByTermAction>(SEARCH_BY_TERM),
|
||||||
map(action => {
|
map(action => {
|
||||||
expect(action.payload.entry.id).toBe('folder-id');
|
expect(action.payload).toBe(searchedTerm);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const node = { entry: { id: 'folder-id', isFolder: true } };
|
component.onSearchSubmit(<any>{ target: { value: searchedTerm } });
|
||||||
component.onItemClicked(node);
|
|
||||||
tick();
|
tick();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('onSearchChange()', () => {
|
||||||
|
it('should call search action with correct search options', fakeAsync(done => {
|
||||||
|
const searchedTerm = 's';
|
||||||
|
const currentSearchOptions = [{ key: 'test' }];
|
||||||
|
actions$.pipe(
|
||||||
|
ofType<SearchByTermAction>(SEARCH_BY_TERM),
|
||||||
|
map(action => {
|
||||||
|
expect(action.searchOptions[0].key).toBe(currentSearchOptions[0].key);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
component.onSearchChange(searchedTerm);
|
||||||
|
tick(1000);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call search action with correct searched term', fakeAsync(done => {
|
||||||
|
const searchedTerm = 's';
|
||||||
|
actions$.pipe(
|
||||||
|
ofType<SearchByTermAction>(SEARCH_BY_TERM),
|
||||||
|
map(action => {
|
||||||
|
expect(action.payload).toBe(searchedTerm);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
component.onSearchChange(searchedTerm);
|
||||||
|
tick(1000);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isLibrariesChecked()', () => {
|
||||||
|
it('should return false by default', () => {
|
||||||
|
expect(component.isLibrariesChecked()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when libraries checked', () => {
|
||||||
|
const libItem = component.searchOptions.find(
|
||||||
|
item => item.key.toLowerCase().indexOf('libraries') > 0
|
||||||
|
);
|
||||||
|
libItem.value = true;
|
||||||
|
expect(component.isLibrariesChecked()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isContentChecked()', () => {
|
||||||
|
it('should return false by default', () => {
|
||||||
|
expect(component.isContentChecked()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when files checked', () => {
|
||||||
|
const filesItem = component.searchOptions.find(
|
||||||
|
item => item.key.toLowerCase().indexOf('files') > 0
|
||||||
|
);
|
||||||
|
filesItem.value = true;
|
||||||
|
expect(component.isContentChecked()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when folders checked', () => {
|
||||||
|
const foldersItem = component.searchOptions.find(
|
||||||
|
item => item.key.toLowerCase().indexOf('folders') > 0
|
||||||
|
);
|
||||||
|
foldersItem.value = true;
|
||||||
|
expect(component.isContentChecked()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when both files and folders checked', () => {
|
||||||
|
const filesItem = component.searchOptions.find(
|
||||||
|
item => item.key.toLowerCase().indexOf('files') > 0
|
||||||
|
);
|
||||||
|
filesItem.value = true;
|
||||||
|
const foldersItem = component.searchOptions.find(
|
||||||
|
item => item.key.toLowerCase().indexOf('folders') > 0
|
||||||
|
);
|
||||||
|
foldersItem.value = true;
|
||||||
|
expect(component.isContentChecked()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,37 +1,90 @@
|
|||||||
|
$search-width: 594px;
|
||||||
|
$search-height: 40px;
|
||||||
|
$search-background: rgba(#efefef, 0.54);
|
||||||
|
$search-border-radius: 4px;
|
||||||
|
$top-margin: 12px;
|
||||||
|
|
||||||
@mixin aca-search-input-theme($theme) {
|
@mixin aca-search-input-theme($theme) {
|
||||||
$background: map-get($theme, background);
|
$background: map-get($theme, background);
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
$border: 1px solid mat-color($foreground, divider, 0.07);
|
||||||
|
|
||||||
.aca-search-input {
|
.app-search-container {
|
||||||
display: flex;
|
color: mat-color($foreground, text, 0.54);
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
.adf-search-control {
|
.app-input-form-field {
|
||||||
color: mat-color($background, card);
|
.mat-input-element {
|
||||||
|
caret-color: mat-color($foreground, text, 0.54);
|
||||||
|
|
||||||
.mat-form-field-underline {
|
&:disabled {
|
||||||
background-color: mat-color($background, card);
|
color: mat-color($foreground, text, 0.54);
|
||||||
}
|
}
|
||||||
|
|
||||||
.adf-input-form-field-divider {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.adf-search-button.mat-icon-button {
|
|
||||||
left: -15px;
|
|
||||||
margin-left: 15px;
|
|
||||||
align-items: flex-start;
|
|
||||||
font: 400 11px system-ui;
|
|
||||||
color: mat-color($background, card);
|
|
||||||
|
|
||||||
.mat-icon {
|
|
||||||
font-size: 24px;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-search-options-menu {
|
||||||
|
&.mat-menu-panel {
|
||||||
|
background-color: mat-color($background, dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#search-options {
|
||||||
|
color: mat-color($foreground, text, 0.54);
|
||||||
|
border-top: $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
.mat-checkbox-frame {
|
||||||
|
border-color: mat-color($foreground, text, 0.54);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.aca-search-input {
|
||||||
|
margin-right: 7px;
|
||||||
|
background-color: $search-background;
|
||||||
|
border-radius: $search-border-radius;
|
||||||
|
height: $search-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-search-container {
|
||||||
|
width: $search-width;
|
||||||
|
height: $search-height + $top-margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-search-control {
|
||||||
|
margin-top: -$top-margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-search-options-menu {
|
||||||
|
&.mat-menu-panel {
|
||||||
|
width: $search-width;
|
||||||
|
max-width: unset;
|
||||||
|
border-radius: $search-border-radius;
|
||||||
|
margin-top: $top-margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-menu-content:not(:empty) {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-options {
|
||||||
|
padding: 20px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
letter-spacing: -0.7px;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
padding: 3px 24px 3px 19px;
|
||||||
|
|
||||||
|
.mat-checkbox-inner-container {
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-checkbox-label {
|
||||||
|
padding: 0 0 0 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,15 +33,10 @@ import {
|
|||||||
UrlSegmentGroup,
|
UrlSegmentGroup,
|
||||||
UrlTree
|
UrlTree
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { MinimalNodeEntity } from 'alfresco-js-api';
|
|
||||||
import { SearchInputControlComponent } from '../search-input-control/search-input-control.component';
|
import { SearchInputControlComponent } from '../search-input-control/search-input-control.component';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppStore } from '../../../store/states/app.state';
|
import { AppStore } from '../../../store/states/app.state';
|
||||||
import {
|
import { SearchByTermAction } from '../../../store/actions';
|
||||||
SearchByTermAction,
|
|
||||||
NavigateToFolder,
|
|
||||||
ViewFileAction
|
|
||||||
} from '../../../store/actions';
|
|
||||||
import { filter } from 'rxjs/operators';
|
import { filter } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -54,7 +49,25 @@ export class SearchInputComponent implements OnInit {
|
|||||||
hasOneChange = false;
|
hasOneChange = false;
|
||||||
hasNewChange = false;
|
hasNewChange = false;
|
||||||
navigationTimer: any;
|
navigationTimer: any;
|
||||||
enableLiveSearch = true;
|
|
||||||
|
searchedWord = null;
|
||||||
|
searchOptions: any = [
|
||||||
|
{
|
||||||
|
key: 'SEARCH.INPUT.FILES',
|
||||||
|
value: false,
|
||||||
|
shouldDisable: this.isLibrariesChecked.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SEARCH.INPUT.FOLDERS',
|
||||||
|
value: false,
|
||||||
|
shouldDisable: this.isLibrariesChecked.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SEARCH.INPUT.LIBRARIES',
|
||||||
|
value: false,
|
||||||
|
shouldDisable: this.isContentChecked.bind(this)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
@ViewChild('searchInputControl')
|
@ViewChild('searchInputControl')
|
||||||
searchInputControl: SearchInputControlComponent;
|
searchInputControl: SearchInputControlComponent;
|
||||||
@ -74,45 +87,21 @@ export class SearchInputComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showInputValue() {
|
showInputValue() {
|
||||||
|
this.searchedWord = '';
|
||||||
|
|
||||||
if (this.onSearchResults) {
|
if (this.onSearchResults) {
|
||||||
let searchedWord = null;
|
|
||||||
const urlTree: UrlTree = this.router.parseUrl(this.router.url);
|
const urlTree: UrlTree = this.router.parseUrl(this.router.url);
|
||||||
const urlSegmentGroup: UrlSegmentGroup =
|
const urlSegmentGroup: UrlSegmentGroup =
|
||||||
urlTree.root.children[PRIMARY_OUTLET];
|
urlTree.root.children[PRIMARY_OUTLET];
|
||||||
|
|
||||||
if (urlSegmentGroup) {
|
if (urlSegmentGroup) {
|
||||||
const urlSegments: UrlSegment[] = urlSegmentGroup.segments;
|
const urlSegments: UrlSegment[] = urlSegmentGroup.segments;
|
||||||
searchedWord = urlSegments[0].parameters['q'];
|
this.searchedWord = urlSegments[0].parameters['q'] || '';
|
||||||
}
|
|
||||||
|
|
||||||
if (this.searchInputControl) {
|
|
||||||
this.enableLiveSearch = false;
|
|
||||||
this.searchInputControl.searchTerm = searchedWord;
|
|
||||||
this.searchInputControl.subscriptAnimationState = 'no-animation';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.searchInputControl.subscriptAnimationState === 'no-animation') {
|
|
||||||
this.searchInputControl.subscriptAnimationState = 'active';
|
|
||||||
this.searchInputControl.searchTerm = '';
|
|
||||||
this.searchInputControl.toggleSearchBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.enableLiveSearch) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.enableLiveSearch = true;
|
|
||||||
}, this.searchInputControl.toggleDebounceTime + 100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
onItemClicked(node: MinimalNodeEntity) {
|
if (this.searchInputControl) {
|
||||||
if (node && node.entry) {
|
this.searchInputControl.searchTerm = this.searchedWord;
|
||||||
const { isFile, isFolder } = node.entry;
|
|
||||||
if (isFile) {
|
|
||||||
this.store.dispatch(new ViewFileAction(node));
|
|
||||||
} else if (isFolder) {
|
|
||||||
this.store.dispatch(new NavigateToFolder(node));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,33 +113,42 @@ export class SearchInputComponent implements OnInit {
|
|||||||
onSearchSubmit(event: KeyboardEvent) {
|
onSearchSubmit(event: KeyboardEvent) {
|
||||||
const searchTerm = (event.target as HTMLInputElement).value;
|
const searchTerm = (event.target as HTMLInputElement).value;
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
this.store.dispatch(new SearchByTermAction(searchTerm));
|
this.store.dispatch(
|
||||||
|
new SearchByTermAction(searchTerm, this.searchOptions)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchChange(searchTerm: string) {
|
onSearchChange(searchTerm: string) {
|
||||||
if (this.onSearchResults) {
|
if (this.hasOneChange) {
|
||||||
if (this.hasOneChange) {
|
this.hasNewChange = true;
|
||||||
this.hasNewChange = true;
|
} else {
|
||||||
} else {
|
this.hasOneChange = true;
|
||||||
this.hasOneChange = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hasNewChange) {
|
|
||||||
clearTimeout(this.navigationTimer);
|
|
||||||
this.hasNewChange = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.navigationTimer = setTimeout(() => {
|
|
||||||
if (searchTerm) {
|
|
||||||
this.store.dispatch(new SearchByTermAction(searchTerm));
|
|
||||||
}
|
|
||||||
this.hasOneChange = false;
|
|
||||||
}, 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.hasNewChange) {
|
||||||
|
clearTimeout(this.navigationTimer);
|
||||||
|
this.hasNewChange = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.navigationTimer = setTimeout(() => {
|
||||||
|
if (searchTerm) {
|
||||||
|
this.store.dispatch(
|
||||||
|
new SearchByTermAction(searchTerm, this.searchOptions)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.hasOneChange = false;
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
get onSearchResults() {
|
get onSearchResults() {
|
||||||
return this.router.url.indexOf('/search') === 0;
|
return this.router.url.indexOf('/search') === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isLibrariesChecked(): boolean {
|
||||||
|
return this.searchOptions[2].value;
|
||||||
|
}
|
||||||
|
isContentChecked(): boolean {
|
||||||
|
return this.searchOptions[0].value || this.searchOptions[1].value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,5 +29,5 @@ export const SEARCH_BY_TERM = 'SEARCH_BY_TERM';
|
|||||||
|
|
||||||
export class SearchByTermAction implements Action {
|
export class SearchByTermAction implements Action {
|
||||||
readonly type = SEARCH_BY_TERM;
|
readonly type = SEARCH_BY_TERM;
|
||||||
constructor(public payload: string) {}
|
constructor(public payload: string, public searchOptions?: any) {}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,15 @@ export class SearchEffects {
|
|||||||
searchByTerm$ = this.actions$.pipe(
|
searchByTerm$ = this.actions$.pipe(
|
||||||
ofType<SearchByTermAction>(SEARCH_BY_TERM),
|
ofType<SearchByTermAction>(SEARCH_BY_TERM),
|
||||||
map(action => {
|
map(action => {
|
||||||
this.router.navigateByUrl('/search;q=' + action.payload);
|
if (
|
||||||
|
action.searchOptions &&
|
||||||
|
action.searchOptions[2] &&
|
||||||
|
action.searchOptions[2].value
|
||||||
|
) {
|
||||||
|
this.router.navigateByUrl('/search-libraries;q=' + action.payload);
|
||||||
|
} else {
|
||||||
|
this.router.navigateByUrl('/search;q=' + action.payload);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -317,6 +317,12 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"SEARCH": {
|
"SEARCH": {
|
||||||
|
"INPUT": {
|
||||||
|
"PLACEHOLDER": "Search everywhere",
|
||||||
|
"FILES": "Files",
|
||||||
|
"FOLDERS": "Folders",
|
||||||
|
"LIBRARIES": "Libraries"
|
||||||
|
},
|
||||||
"SORT": {
|
"SORT": {
|
||||||
"RELEVANCE": "Relevance",
|
"RELEVANCE": "Relevance",
|
||||||
"FILENAME": "Filename",
|
"FILENAME": "Filename",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user