/**
* @fileoverview Application Header Component for Rantii
* @author retoor <retoor@molodetz.nl>
* @description Main navigation header with search and user controls
* @keywords header, navigation, search, menu, toolbar
*/
import { BaseComponent } from './base-component.js';
class AppHeader extends BaseComponent {
static get observedAttributes() {
return ['logged-in'];
}
init() {
this.render();
this.bindEvents();
}
render() {
const isLoggedIn = this.isLoggedIn();
const user = this.getCurrentUser();
this.setHtml(`
<div class="header-container">
<div class="header-left">
<button class="menu-toggle" aria-label="Toggle menu">
<span class="menu-icon"></span>
</button>
<a href="?" class="logo">
<span class="logo-text">Rantii</span>
</a>
</div>
<div class="header-center">
<div class="search-container">
<input type="search" class="search-input" placeholder="Search rants..." aria-label="Search">
<button class="search-btn" aria-label="Search">
<svg viewBox="0 0 24 24" width="20" height="20">
<path fill="currentColor" d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 0 0 1.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 0 0-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 0 0 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>
</button>
</div>
</div>
<div class="header-right">
${isLoggedIn ? `
<button class="header-btn notifications-btn" aria-label="Notifications">
<svg viewBox="0 0 24 24" width="24" height="24">
<path fill="currentColor" d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.63-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.64 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2zm-2 1H8v-6c0-2.48 1.51-4.5 4-4.5s4 2.02 4 4.5v6z"/>
</svg>
<span class="notification-badge" hidden>0</span>
</button>
<button class="header-btn post-btn" aria-label="Create post">
<svg viewBox="0 0 24 24" width="24" height="24">
<path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</svg>
</button>
<button class="header-btn user-btn" aria-label="User menu">
<user-avatar size="small" username="${user?.username || ''}"></user-avatar>
</button>
` : `
<button class="header-btn login-btn">Login</button>
`}
<button class="header-btn theme-btn" aria-label="Toggle theme">
<svg viewBox="0 0 24 24" width="24" height="24" class="theme-icon-dark">
<path fill="currentColor" d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/>
</svg>
<svg viewBox="0 0 24 24" width="24" height="24" class="theme-icon-light">
<path fill="currentColor" d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/>
</svg>
</button>
</div>
</div>
`);
}
bindEvents() {
this.on(this, 'click', this.handleClick);
this.on(this, 'submit', this.handleSubmit);
const searchInput = this.$('.search-input');
if (searchInput) {
this.on(searchInput, 'keydown', this.handleSearchKeydown);
}
window.addEventListener('rantii:auth-change', () => this.render());
}
handleClick(e) {
const target = e.target.closest('button, a');
if (!target) return;
if (target.classList.contains('menu-toggle')) {
e.preventDefault();
this.emit('menu-toggle');
} else if (target.classList.contains('logo') || target.closest('.logo')) {
e.preventDefault();
this.getRouter()?.goHome();
} else if (target.classList.contains('search-btn')) {
this.performSearch();
} else if (target.classList.contains('notifications-btn')) {
this.getRouter()?.goToNotifications();
} else if (target.classList.contains('post-btn')) {
this.emit('create-post');
} else if (target.classList.contains('user-btn')) {
this.emit('user-menu');
} else if (target.classList.contains('login-btn')) {
this.getRouter()?.goToLogin();
} else if (target.classList.contains('theme-btn')) {
this.getTheme()?.toggleDarkLight();
}
}
handleSearchKeydown(e) {
if (e.key === 'Enter') {
e.preventDefault();
this.performSearch();
}
}
handleSubmit(e) {
e.preventDefault();
}
performSearch() {
const input = this.$('.search-input');
if (input && input.value.trim()) {
this.getRouter()?.goToSearch(input.value.trim());
}
}
setNotificationCount(count) {
const badge = this.$('.notification-badge');
if (badge) {
badge.textContent = count > 99 ? '99+' : count;
badge.hidden = count === 0;
}
}
clearSearch() {
const input = this.$('.search-input');
if (input) {
input.value = '';
}
}
}
customElements.define('app-header', AppHeader);
export { AppHeader };