import { i18n, t } from '../i18n.js' import { router } from '../router.js' import { auth } from '../services/auth.js' import { notificationsService } from '../services/notifications.js' class AppHeader extends HTMLElement { constructor() { super() this.langDropdownOpen = false this.profileDropdownOpen = false this.handleOutsideClick = this.handleOutsideClick.bind(this) this.handleKeydown = this.handleKeydown.bind(this) this.handleScroll = this.handleScroll.bind(this) } connectedCallback() { this.render() this.setupEventListeners() document.addEventListener('click', this.handleOutsideClick) document.addEventListener('keydown', this.handleKeydown) window.addEventListener('scroll', this.handleScroll, { passive: true }) // Subscribe to auth changes (only once!) this.authUnsubscribe = auth.subscribe(() => { this.render() this.setupEventListeners() }) this.notifUnsubscribe = notificationsService.subscribe(() => { this.updateNotificationBadge() }) } disconnectedCallback() { document.removeEventListener('click', this.handleOutsideClick) document.removeEventListener('keydown', this.handleKeydown) window.removeEventListener('scroll', this.handleScroll) if (this.authUnsubscribe) this.authUnsubscribe() if (this.notifUnsubscribe) this.notifUnsubscribe() } handleScroll() { this.classList.toggle('scrolled', window.scrollY > 10) } handleOutsideClick() { if (this.langDropdownOpen) { this.closeLangDropdown() } if (this.profileDropdownOpen) { this.closeProfileDropdown() } } handleKeydown(e) { if (!this.langDropdownOpen) return const items = Array.from(this.querySelectorAll('.dropdown-item')) const currentIndex = items.findIndex(item => item === document.activeElement) switch (e.key) { case 'Escape': e.preventDefault() this.closeLangDropdown() this.querySelector('#lang-toggle')?.focus() break case 'ArrowDown': e.preventDefault() const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0 items[nextIndex]?.focus() break case 'ArrowUp': e.preventDefault() const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1 items[prevIndex]?.focus() break } } closeLangDropdown() { this.langDropdownOpen = false const langDropdown = this.querySelector('#lang-dropdown') const langToggle = this.querySelector('#lang-toggle') langDropdown?.classList.remove('open') langToggle?.setAttribute('aria-expanded', 'false') } openLangDropdown() { this.langDropdownOpen = true const langDropdown = this.querySelector('#lang-dropdown') const langToggle = this.querySelector('#lang-toggle') langDropdown?.classList.add('open') langToggle?.setAttribute('aria-expanded', 'true') this.querySelector('#lang-dropdown .dropdown-item.active')?.focus() } closeProfileDropdown() { this.profileDropdownOpen = false const dropdown = this.querySelector('#profile-dropdown') const toggle = this.querySelector('#profile-toggle') dropdown?.classList.remove('open') toggle?.setAttribute('aria-expanded', 'false') } openProfileDropdown() { this.profileDropdownOpen = true const dropdown = this.querySelector('#profile-dropdown') const toggle = this.querySelector('#profile-toggle') dropdown?.classList.add('open') toggle?.setAttribute('aria-expanded', 'true') } render() { this.innerHTML = /* html */`
${t('header.createListing')} ${!auth.isLoggedIn() ? ` ` : ''} ${auth.isLoggedIn() ? ` ${notificationsService.unreadCount > 0 ? `${notificationsService.unreadCount}` : ''} ` : ` `}
` } setupEventListeners() { const themeToggle = this.querySelector('#theme-toggle') themeToggle?.addEventListener('click', () => this.toggleTheme()) const langDropdown = this.querySelector('#lang-dropdown') const langToggle = this.querySelector('#lang-toggle') langToggle?.addEventListener('click', (e) => { e.stopPropagation() if (this.langDropdownOpen) { this.closeLangDropdown() } else { this.openLangDropdown() } }) langDropdown?.querySelectorAll('[data-locale]').forEach(btn => { btn.addEventListener('click', async () => { await i18n.setLocale(btn.dataset.locale) }) }) // Login button const loginBtn = this.querySelector('#login-btn') loginBtn?.addEventListener('click', () => { const authModal = document.querySelector('auth-modal') if (authModal) { authModal.show('login') } }) // Profile dropdown const profileDropdown = this.querySelector('#profile-dropdown') const profileToggle = this.querySelector('#profile-toggle') profileToggle?.addEventListener('click', (e) => { e.stopPropagation() if (this.profileDropdownOpen) { this.closeProfileDropdown() } else { this.openProfileDropdown() } }) // Close dropdown when clicking menu items profileDropdown?.querySelectorAll('.dropdown-item').forEach(item => { item.addEventListener('click', () => { this.closeProfileDropdown() }) }) // Logout button const logoutBtn = this.querySelector('#logout-btn') logoutBtn?.addEventListener('click', async () => { await auth.logout() router.navigate('/') }) this.updateThemeIcon() } toggleTheme() { const currentTheme = document.documentElement.dataset.theme const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches let newTheme if (!currentTheme) { newTheme = prefersDark ? 'light' : 'dark' } else if (currentTheme === 'dark') { newTheme = 'light' } else { newTheme = 'dark' } document.documentElement.dataset.theme = newTheme localStorage.setItem('theme', newTheme) this.updateThemeIcon() } updateThemeIcon() { const theme = document.documentElement.dataset.theme const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches const isDark = theme === 'dark' || (!theme && prefersDark) const sunIcon = this.querySelector('.icon-sun') const moonIcon = this.querySelector('.icon-moon') if (sunIcon && moonIcon) { sunIcon.style.display = isDark ? 'block' : 'none' moonIcon.style.display = isDark ? 'none' : 'block' } } updateNotificationBadge() { const bell = this.querySelector('.notification-bell') if (!bell) return const existing = bell.querySelector('.notification-badge') if (notificationsService.unreadCount > 0) { if (existing) { existing.textContent = notificationsService.unreadCount } else { const badge = document.createElement('span') badge.className = 'notification-badge' badge.textContent = notificationsService.unreadCount bell.appendChild(badge) } } else if (existing) { existing.remove() } } updateTranslations() { this.render() this.setupEventListeners() } } customElements.define('app-header', AppHeader)