import { i18n, t } from '../i18n.js' import { router } from '../router.js' import { auth } from '../services/auth.js' import { notificationsService } from '../services/notifications.js' import { generateAvatar } from '../services/identity.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 */`