import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, ViewChild, } from '@angular/core'; import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { CommonModule } from '@angular/common'; // components import { CaAppTooltipV2Component } from '../ca-app-tooltip-v2/ca-app-tooltip-v2.component'; // modules import { AngularSvgIconModule } from 'angular-svg-icon'; import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; // enums import { eSearchMultipleStateAction } from './enums'; import { eStringPlaceholder, ePosition, eGeneralActions, eColor, } from '../../enums'; // animations import { searchInputExpandAnimation, puffAnimation, searchBarButtonOpacityAnimation, } from '../../animations'; // utils import { MultipleSearchStateSvg } from './util'; // interfaces import { ISearchQueryItem } from './interfaces'; // constants import { SearchMultipleStatesConstant } from '../../utils/constants'; @Component({ selector: 'ca-search-multiple-states-2', templateUrl: './ca-search-multiple-states-2.component.html', styleUrl: './ca-search-multiple-states-2.component.scss', animations: [ searchInputExpandAnimation, puffAnimation, searchBarButtonOpacityAnimation, ], imports: [ CommonModule, FormsModule, ReactiveFormsModule, HttpClientModule, // Third-party modules AngularSvgIconModule, NgbTooltipModule, // Components CaAppTooltipV2Component, ], }) export class CaSearchMultipleStates2Component implements OnInit { @HostListener('document:click', ['$event']) public onDocumentClick(event: MouseEvent): void { if ( !this.elementRef.nativeElement.contains(event.target) && !this._isDetailsPageSearch && this.searchQuery.length !== this.SEARCH_LIMIT ) { this.isSearchActive = false; this.resetSearchControl(); } } @ViewChild('searchInput') searchInput!: ElementRef; constructor( private cdr: ChangeDetectorRef, private elementRef: ElementRef ) {} @Input() set selectedTab(value: string) { if (this._selectedTab === value) return; this._selectedTab = value; this.resetComponentState(); } @Input() set isDetailsPageSearch(value: boolean) { this._isDetailsPageSearch = value; this.toggleSearch(); } @Input() inputPlaceholder!: string; @Input() isDisabled!: boolean; @Output() onSearchQueryChange: EventEmitter = new EventEmitter(); @Output() onToggleSearch: EventEmitter = new EventEmitter(); public readonly SEARCH_LIMIT: number = 3; public _isDetailsPageSearch: boolean = false; public searchQuery: ISearchQueryItem[] = []; public searchControl = new FormControl({ value: eStringPlaceholder.EMPTY, disabled: this.searchQuery?.length >= this.SEARCH_LIMIT, }); public _selectedTab!: string; public isSearchActive: boolean = false; // assets public eSearchMultipleStateAction = eSearchMultipleStateAction; public eCommonAction = eGeneralActions; public ePosition = ePosition; public eColor = eColor; public eStringPlaceholder = eStringPlaceholder; public multipleSearchStateSvg = MultipleSearchStateSvg; public ngOnInit(): void {} private focusSearchInput(): void { this.cdr.detectChanges(); requestAnimationFrame(() => { this.searchInput?.nativeElement.focus(); }); } public addSearchItem(): void { const colorClasses = SearchMultipleStatesConstant.COLOR_CLASSES; if (this.searchQuery?.length >= this.SEARCH_LIMIT) return; const searchItem: string | null = this.searchControl?.value; if (!searchItem) return; const colorClass = colorClasses.find( (color) => !this.searchQuery.some((item) => item.colorClass === color) ) ?? colorClasses[0]; this.searchQuery = [ ...this.searchQuery, { text: searchItem, colorClass }, ]; this.updateSearchControlDisabled(); this.propagateData(); this.resetSearchControl(); if ( this.isSearchActive && this.searchQuery.length < this.SEARCH_LIMIT ) { this.focusSearchInput(); } } public clearAll(): void { const isSearchLimitReached = this.searchQuery.length === this.SEARCH_LIMIT; this.resetComponentState(); this.updateSearchControlDisabled(); isSearchLimitReached ? (this.isSearchActive = false) : this.focusSearchInput(); this.propagateData(); } public onClearValuesAction(): void { const isSearchLimitReached = this.searchQuery.length === this.SEARCH_LIMIT; this.resetComponentState(); this.updateSearchControlDisabled(); isSearchLimitReached ? (this.isSearchActive = false) : this.focusSearchInput(); } public deleteSearchQueryItem(index: number): void { if (index < 0 || index > this.SEARCH_LIMIT) return; const filteredChips = this.searchQuery.filter( (_: ISearchQueryItem, ind: number) => index !== ind ); this.searchQuery = filteredChips?.length ? [...filteredChips] : []; this.updateSearchControlDisabled(); this.propagateData(); this.focusSearchInput(); } public removeSearchItem(searchItem: string): void { if (!searchItem) return; this.searchQuery = this.searchQuery?.filter( (item: ISearchQueryItem) => item.text !== searchItem ); this.updateSearchControlDisabled(); this.propagateData(); } public toggleSearch(): void { const canToggle = this.searchQuery?.length < this.SEARCH_LIMIT && !this.isDisabled; if (!canToggle) { return; } if (this.isSearchActive && this._isDetailsPageSearch) { this.clearAll(); this.isSearchActive = false; } else { this.isSearchActive = !this.isSearchActive; if (this.isSearchActive) { this.focusSearchInput(); } else { this.resetSearchControl(); } } this.onToggleSearch.emit(this.isSearchActive); } public clearSearch(): void { const searchItem: string | null = this.searchControl?.value; if (!searchItem) { this.toggleSearch(); return; } this.resetSearchControl(); this.propagateData(); this.focusSearchInput(); } public resetComponentState(): void { this.searchQuery = []; this.updateSearchControlDisabled(); this.resetSearchControl(); } public resetSearchControl(): void { this.searchControl.reset(); } private updateSearchControlDisabled(): void { const shouldBeDisabled = this.searchQuery?.length >= this.SEARCH_LIMIT; if (shouldBeDisabled && !this.searchControl.disabled) { this.searchControl.disable(); } else if (!shouldBeDisabled && this.searchControl.disabled) { this.searchControl.enable(); } } public propagateData(): void { this.onSearchQueryChange.emit(this.searchQuery); } }