import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation, signal, computed, inject, OnInit, OnDestroy, HostListener } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { Subject, takeUntil } from 'rxjs'; import { faBars, faXmark } from '@fortawesome/free-solid-svg-icons'; import { LandingHeaderConfig, NavigationItem, LogoConfig } from '../../interfaces/navigation.interfaces'; import { CTAButton } from '../../interfaces/shared.interfaces'; import { ButtonComponent, ContainerComponent, FlexComponent, IconButtonComponent } from 'ui-essentials'; @Component({ selector: 'ui-lp-header', standalone: true, imports: [ CommonModule, RouterModule, ButtonComponent, ContainerComponent, FlexComponent, IconButtonComponent ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
@if (config().logo.imageUrl) { } @else if (config().logo.text) { {{ config().logo.text }} }
@if (config().ctaButton) { @if (config().ctaButton!.icon) { {{ config().ctaButton!.icon }} } {{ config().ctaButton!.text }} } @if (config().showMobileMenu !== false) { }
@if (mobileMenuOpen() && config().showMobileMenu !== false) { } @if (mobileMenuOpen()) { }
`, styleUrl: './landing-header.component.scss' }) export class LandingHeaderComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); // FontAwesome icons faXmark = faXmark; faBars = faBars; config = signal({ logo: { text: 'Logo' }, navigation: [], transparent: false, sticky: true, showMobileMenu: true, size: 'xl', theme: 'light' }); isScrolled = signal(false); mobileMenuOpen = signal(false); openDropdown = signal(null); openMobileDropdown = signal(null); @Input() set configuration(value: LandingHeaderConfig) { this.config.set(value); } @Output() navigationClicked = new EventEmitter(); @Output() ctaClicked = new EventEmitter(); ngOnInit(): void { if (this.config().sticky) { this.setupScrollListener(); } } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } @HostListener('window:scroll', []) onWindowScroll(): void { if (this.config().sticky) { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; this.isScrolled.set(scrollTop > 10); } } @HostListener('document:click', ['$event']) onDocumentClick(event: Event): void { const target = event.target as HTMLElement; if (!target.closest('.ui-lp-header__nav-link--dropdown')) { this.openDropdown.set(null); } } @HostListener('window:resize', []) onWindowResize(): void { if (window.innerWidth > 768) { this.mobileMenuOpen.set(false); } } private setupScrollListener(): void { // Initial scroll check this.onWindowScroll(); } toggleMobileMenu(): void { this.mobileMenuOpen.set(!this.mobileMenuOpen()); this.openMobileDropdown.set(null); // Prevent body scroll when mobile menu is open document.body.style.overflow = this.mobileMenuOpen() ? 'hidden' : ''; } closeMobileMenu(): void { this.mobileMenuOpen.set(false); this.openMobileDropdown.set(null); document.body.style.overflow = ''; } toggleDropdown(itemId: string): void { this.openDropdown.set(this.openDropdown() === itemId ? null : itemId); } toggleMobileDropdown(itemId: string): void { this.openMobileDropdown.set(this.openMobileDropdown() === itemId ? null : itemId); } handleNavClick(item: NavigationItem): void { if (item.action) { item.action(); } this.navigationClicked.emit(item); this.closeMobileMenu(); this.openDropdown.set(null); } handleCTAClick(): void { const cta = this.config().ctaButton; if (cta) { cta.action(); this.ctaClicked.emit(cta); } } }