mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
318 lines
10 KiB
TypeScript
318 lines
10 KiB
TypeScript
/*!
|
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
|
*
|
|
* Alfresco Example Content Application
|
|
*
|
|
* This file is part of the Alfresco Example Content Application.
|
|
* If the software was purchased under a paid Alfresco license, the terms of
|
|
* the paid license agreement will prevail. Otherwise, the software is
|
|
* provided under the following open source license terms:
|
|
*
|
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { AppHookService, AppService } from '@alfresco/aca-shared';
|
|
import { AppStore, SearchByTermAction, SearchOptionIds, SearchOptionModel, SnackbarErrorAction } from '@alfresco/aca-shared/store';
|
|
import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
|
import { AppConfigService } from '@alfresco/adf-core';
|
|
import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
|
import { NavigationEnd, PRIMARY_OUTLET, Router, RouterEvent, UrlSegment, UrlSegmentGroup, UrlTree } from '@angular/router';
|
|
import { Store } from '@ngrx/store';
|
|
import { Subject } from 'rxjs';
|
|
import { filter, takeUntil } from 'rxjs/operators';
|
|
import { SearchInputControlComponent } from '../search-input-control/search-input-control.component';
|
|
import { SearchNavigationService } from '../search-navigation.service';
|
|
import { SearchLibrariesQueryBuilderService } from '../search-libraries-results/search-libraries-query-builder.service';
|
|
import { CommonModule } from '@angular/common';
|
|
import { TranslateModule } from '@ngx-translate/core';
|
|
import { MatButtonModule } from '@angular/material/button';
|
|
import { MatIconModule } from '@angular/material/icon';
|
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
import { MatInputModule } from '@angular/material/input';
|
|
import { A11yModule } from '@angular/cdk/a11y';
|
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
import { FormsModule } from '@angular/forms';
|
|
|
|
@Component({
|
|
standalone: true,
|
|
imports: [
|
|
CommonModule,
|
|
TranslateModule,
|
|
MatMenuModule,
|
|
MatButtonModule,
|
|
MatIconModule,
|
|
MatFormFieldModule,
|
|
MatInputModule,
|
|
A11yModule,
|
|
MatCheckboxModule,
|
|
FormsModule,
|
|
SearchInputControlComponent
|
|
],
|
|
selector: 'aca-search-input',
|
|
templateUrl: './search-input.component.html',
|
|
styleUrls: ['./search-input.component.scss'],
|
|
encapsulation: ViewEncapsulation.None,
|
|
host: { class: 'aca-search-input' }
|
|
})
|
|
export class SearchInputComponent implements OnInit, OnDestroy {
|
|
onDestroy$: Subject<boolean> = new Subject<boolean>();
|
|
hasOneChange = false;
|
|
hasNewChange = false;
|
|
navigationTimer: any;
|
|
has400LibraryError = false;
|
|
hasLibrariesConstraint = false;
|
|
searchOnChange: boolean;
|
|
|
|
searchedWord: string = null;
|
|
searchOptions: Array<SearchOptionModel> = [
|
|
{
|
|
id: SearchOptionIds.Files,
|
|
key: 'SEARCH.INPUT.FILES',
|
|
value: false,
|
|
shouldDisable: this.isLibrariesChecked.bind(this)
|
|
},
|
|
{
|
|
id: SearchOptionIds.Folders,
|
|
key: 'SEARCH.INPUT.FOLDERS',
|
|
value: false,
|
|
shouldDisable: this.isLibrariesChecked.bind(this)
|
|
},
|
|
{
|
|
id: SearchOptionIds.Libraries,
|
|
key: 'SEARCH.INPUT.LIBRARIES',
|
|
value: false,
|
|
shouldDisable: this.isContentChecked.bind(this)
|
|
}
|
|
];
|
|
|
|
@ViewChild('searchInputControl', { static: true })
|
|
searchInputControl: SearchInputControlComponent;
|
|
|
|
@ViewChild(MatMenuTrigger, { static: true })
|
|
trigger: MatMenuTrigger;
|
|
|
|
constructor(
|
|
private queryBuilder: SearchQueryBuilderService,
|
|
private queryLibrariesBuilder: SearchLibrariesQueryBuilderService,
|
|
private config: AppConfigService,
|
|
private router: Router,
|
|
private store: Store<AppStore>,
|
|
private appHookService: AppHookService,
|
|
private appService: AppService,
|
|
public searchInputService: SearchNavigationService
|
|
) {
|
|
this.searchOnChange = this.config.get<boolean>('search.aca:triggeredOnChange', true);
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.showInputValue();
|
|
|
|
this.router.events
|
|
.pipe(takeUntil(this.onDestroy$))
|
|
.pipe(filter((e) => e instanceof RouterEvent))
|
|
.subscribe((event) => {
|
|
if (event instanceof NavigationEnd) {
|
|
this.showInputValue();
|
|
}
|
|
});
|
|
|
|
this.appHookService.library400Error.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
|
|
this.has400LibraryError = true;
|
|
this.hasLibrariesConstraint = this.evaluateLibrariesConstraint();
|
|
});
|
|
}
|
|
|
|
exitSearch() {
|
|
this.searchInputService.navigateBack();
|
|
}
|
|
|
|
showInputValue() {
|
|
this.appService.setAppNavbarMode('collapsed');
|
|
this.has400LibraryError = false;
|
|
this.hasLibrariesConstraint = this.evaluateLibrariesConstraint();
|
|
this.searchedWord = this.getUrlSearchTerm();
|
|
|
|
if (this.searchInputControl) {
|
|
this.searchInputControl.searchTerm = this.searchedWord;
|
|
}
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
this.appService.setAppNavbarMode('expanded');
|
|
this.onDestroy$.next(true);
|
|
this.onDestroy$.complete();
|
|
this.removeContentFilters();
|
|
}
|
|
|
|
onMenuOpened() {
|
|
if (this.searchInputControl) {
|
|
this.searchInputControl.searchInput?.nativeElement?.focus();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when the user submits the search, e.g. hits enter or clicks submit
|
|
*
|
|
* @param event Parameters relating to the search
|
|
*/
|
|
onSearchSubmit(event: any) {
|
|
const searchTerm = event.target ? (event.target as HTMLInputElement).value : event;
|
|
if (searchTerm) {
|
|
this.searchedWord = searchTerm;
|
|
|
|
this.searchByOption();
|
|
} else {
|
|
this.store.dispatch(new SnackbarErrorAction('APP.BROWSE.SEARCH.EMPTY_SEARCH'));
|
|
}
|
|
|
|
if (this.trigger) {
|
|
this.trigger.closeMenu();
|
|
}
|
|
}
|
|
|
|
onSearchChange(searchTerm: string) {
|
|
if (!this.searchOnChange) {
|
|
return;
|
|
}
|
|
|
|
this.has400LibraryError = false;
|
|
this.hasLibrariesConstraint = this.evaluateLibrariesConstraint();
|
|
this.searchedWord = searchTerm;
|
|
|
|
if (this.hasOneChange) {
|
|
this.hasNewChange = true;
|
|
} else {
|
|
this.hasOneChange = true;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
searchByOption() {
|
|
this.syncInputValues();
|
|
this.has400LibraryError = false;
|
|
if (this.isLibrariesChecked()) {
|
|
this.hasLibrariesConstraint = this.evaluateLibrariesConstraint();
|
|
if (this.onLibrariesSearchResults && this.isSameSearchTerm()) {
|
|
this.queryLibrariesBuilder.update();
|
|
} else if (this.searchedWord) {
|
|
this.store.dispatch(new SearchByTermAction(this.searchedWord, this.searchOptions));
|
|
}
|
|
} else {
|
|
if (this.isFoldersChecked() && !this.isFilesChecked()) {
|
|
this.filterContent(SearchOptionIds.Folders);
|
|
} else if (this.isFilesChecked() && !this.isFoldersChecked()) {
|
|
this.filterContent(SearchOptionIds.Files);
|
|
} else {
|
|
this.removeContentFilters();
|
|
}
|
|
|
|
if (this.onSearchResults && this.isSameSearchTerm()) {
|
|
this.queryBuilder.update();
|
|
} else if (this.searchedWord) {
|
|
this.store.dispatch(new SearchByTermAction(this.searchedWord, this.searchOptions));
|
|
}
|
|
}
|
|
}
|
|
|
|
get onLibrariesSearchResults() {
|
|
return this.router.url.indexOf('/search-libraries') === 0;
|
|
}
|
|
|
|
get onSearchResults() {
|
|
return !this.onLibrariesSearchResults && this.router.url.indexOf('/search') === 0;
|
|
}
|
|
|
|
isFilesChecked(): boolean {
|
|
return this.isOptionChecked(SearchOptionIds.Files);
|
|
}
|
|
|
|
isFoldersChecked(): boolean {
|
|
return this.isOptionChecked(SearchOptionIds.Folders);
|
|
}
|
|
|
|
isLibrariesChecked(): boolean {
|
|
return this.isOptionChecked(SearchOptionIds.Libraries);
|
|
}
|
|
|
|
isOptionChecked(optionId: string): boolean {
|
|
const libItem = this.searchOptions.find((item) => item.id === optionId);
|
|
return !!libItem && libItem.value;
|
|
}
|
|
|
|
isContentChecked(): boolean {
|
|
return this.isFilesChecked() || this.isFoldersChecked();
|
|
}
|
|
|
|
evaluateLibrariesConstraint(): boolean {
|
|
if (this.isLibrariesChecked()) {
|
|
return this.has400LibraryError || this.searchInputControl.isTermTooShort();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
filterContent(option: SearchOptionIds.Folders | SearchOptionIds.Files) {
|
|
const oppositeOption = option === SearchOptionIds.Folders ? SearchOptionIds.Files : SearchOptionIds.Folders;
|
|
|
|
this.queryBuilder.addFilterQuery(`+TYPE:'cm:${option}'`);
|
|
this.queryBuilder.removeFilterQuery(`+TYPE:'cm:${oppositeOption}'`);
|
|
}
|
|
|
|
removeContentFilters() {
|
|
this.queryBuilder.removeFilterQuery(`+TYPE:'cm:${SearchOptionIds.Files}'`);
|
|
this.queryBuilder.removeFilterQuery(`+TYPE:'cm:${SearchOptionIds.Folders}'`);
|
|
}
|
|
|
|
syncInputValues() {
|
|
if (this.searchInputControl.searchTerm !== this.searchedWord) {
|
|
if (this.searchInputControl.searchTerm) {
|
|
this.searchedWord = this.searchInputControl.searchTerm;
|
|
} else {
|
|
this.searchInputControl.searchTerm = this.searchedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
getUrlSearchTerm(): string {
|
|
let searchTerm = '';
|
|
if (this.onSearchResults || this.onLibrariesSearchResults) {
|
|
const urlTree: UrlTree = this.router.parseUrl(this.router.url);
|
|
const urlSegmentGroup: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
|
|
|
|
if (urlSegmentGroup) {
|
|
const urlSegments: UrlSegment[] = urlSegmentGroup.segments;
|
|
searchTerm = urlSegments[0].parameters['q'] ? decodeURIComponent(urlSegments[0].parameters['q']) : '';
|
|
}
|
|
}
|
|
|
|
return searchTerm;
|
|
}
|
|
|
|
isSameSearchTerm(): boolean {
|
|
const urlSearchTerm = this.getUrlSearchTerm();
|
|
return this.searchedWord === urlSearchTerm;
|
|
}
|
|
}
|