import { Component, Input, EventEmitter, Output, ElementRef, ChangeDetectorRef, AfterViewInit, Renderer2, ViewChild, } from '@angular/core'; // modules import { CommonModule } from '@angular/common'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; // models import { Tabs } from '../../models/tabs.model'; import { CustomPeriodRange } from '../../models/dashboard/custom-period-range.model'; import { DropdownListItem } from '../../models/dashboard/dropdown-list-item.model'; @Component({ selector: 'app-ca-tab-switch', templateUrl: './ca-tab-switch.component.html', styleUrls: ['./ca-tab-switch.component.scss'], imports: [CommonModule, NgbModule], }) export class CaTabSwitchComponent implements AfterViewInit { @ViewChild('tabSwitchHolder') tabSwitchHolder!: ElementRef; @Input() set tabs(tabs: Tabs[]) { this._tabs = tabs; this.setSwitchActive(tabs); } @Input() type!: string; @Input() hasDashboardHeight?: boolean = false; @Input() subPeriodDropdownList?: DropdownListItem[] = []; @Input() selectedSubPeriod?: DropdownListItem; @Input() isClearCustomPeriodRangeValue?: boolean = false; @Input() isDisabled?: boolean = false; @Input() isBold: boolean = false; @Input() isMarginTopDisabled: boolean = false; @Output() switchClicked = new EventEmitter<{ id: number; name?: string }>(); @Output() customPeriodRangeEmitter = new EventEmitter(); @Output() customPeriodRangeSubperiodEmitter = new EventEmitter(); @Output() popoverClosedEmitter = new EventEmitter(); public indexSwitch: number = -1; private selectedItem: HTMLElement | null = null; private isAnimating: boolean = false; public _tabs!: Tabs[]; get tabs() { return this._tabs; } constructor( private renderer: Renderer2, private cdRef: ChangeDetectorRef ) {} ngAfterViewInit() { this.setSwitchActive(this.tabs); } private setSwitchActive(tabs: Tabs[]): void { const selectedIndex = tabs?.findIndex( (item: Tabs) => item.checked && !item.disabled ); this.indexSwitch = selectedIndex; if (selectedIndex === -1) return; const tabSwitchHolder = this.tabSwitchHolder?.nativeElement; if (!tabSwitchHolder) return; const tabSwitchItem = tabSwitchHolder.children[this.indexSwitch] as | HTMLElement | undefined; if (!tabSwitchItem) return; if (this.selectedItem && this.selectedItem !== tabSwitchItem) { this.isAnimating = true; this.animateTabSwitch(tabSwitchItem); } else { this.selectedItem = tabSwitchItem; this.renderer.removeClass( tabSwitchItem.children[0] as HTMLElement, 'opacity-0' ); this.renderer.addClass( tabSwitchItem.children[0] as HTMLElement, 'opacity-100' ); this.cdRef.detectChanges(); } } private animateTabSwitch(clickedElement: HTMLElement) { const oldSelectedItem = this.selectedItem!; if (!oldSelectedItem || !clickedElement) return; const newSelectedItem = clickedElement; const animatedIndicator = oldSelectedItem .children[0] as HTMLElement | null; if (!animatedIndicator) return; const tabSwitchHolder = this.tabSwitchHolder?.nativeElement; const oldPosition = oldSelectedItem.getBoundingClientRect().left; const newPosition = newSelectedItem.getBoundingClientRect().left; const translateX = newPosition - oldPosition; this.renderer.setStyle( animatedIndicator, 'width', getComputedStyle(newSelectedItem).width ); this.renderer.setStyle( animatedIndicator, 'transform', `translateX(${translateX}px)` ); if (tabSwitchHolder) { this.renderer.setStyle(tabSwitchHolder, 'pointerEvents', 'none'); } animatedIndicator.addEventListener( 'transitionend', () => { tabSwitchHolder?.removeAttribute('style'); this.finalizeTabSwitch(animatedIndicator, newSelectedItem); }, { once: true } ); this.cdRef.detectChanges(); } private finalizeTabSwitch( animatedIndicator: HTMLElement, newSelectedItem: HTMLElement ) { this.renderer.setStyle(animatedIndicator, 'transform', ''); this.renderer.setStyle(animatedIndicator, 'width', ''); this.renderer.setStyle( newSelectedItem.children[0] as HTMLElement, 'transform', '' ); this.selectedItem = newSelectedItem; this.renderer.removeClass(animatedIndicator, 'opacity-100'); this.renderer.addClass(animatedIndicator, 'opacity-0'); this.renderer.addClass( newSelectedItem.children[0] as HTMLElement, 'opacity-100' ); this.renderer.removeClass( newSelectedItem.children[0] as HTMLElement, 'opacity-0' ); this.isAnimating = false; this.cdRef.detectChanges(); } public switchTab(e: MouseEvent, indx: number, item: { id: number }): void { e.stopPropagation(); if (this.isAnimating) { return; } const clickedElement = (e.currentTarget as HTMLElement) || (e.target as HTMLElement); const currentTab = this.tabs.find((tab) => tab.id === item.id); if (this.selectedItem === clickedElement || currentTab?.checked) { return; } this.indexSwitch = indx; this.tabs.map((tab) => { if (tab.id === item.id) tab.checked = true; else tab.checked = false; }); if (this.selectedItem && this.selectedItem !== clickedElement) { this.isAnimating = true; this.animateTabSwitch(clickedElement); } this.switchClicked.emit(item); } public handleSetCustomPeriodRangeClick( customPeriodRange: CustomPeriodRange ): void { this.customPeriodRangeEmitter.emit(customPeriodRange); } public handleCustomPeriodRangeSubperiodEmit( selectedDaysRange: number ): void { this.customPeriodRangeSubperiodEmitter.emit(selectedDaysRange); } public handlePopoverClose(): void { this.popoverClosedEmitter.emit(); } }