208 lines
10 KiB
JavaScript
208 lines
10 KiB
JavaScript
|
|
/**
|
||
|
|
* @fileoverview Application Navigation Component for Rantii
|
||
|
|
* @author retoor <retoor@molodetz.nl>
|
||
|
|
* @description Side navigation menu with main app sections
|
||
|
|
* @keywords navigation, menu, sidebar, nav, links
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { BaseComponent } from './base-component.js';
|
||
|
|
|
||
|
|
class AppNav extends BaseComponent {
|
||
|
|
static get observedAttributes() {
|
||
|
|
return ['active', 'expanded'];
|
||
|
|
}
|
||
|
|
|
||
|
|
init() {
|
||
|
|
this.render();
|
||
|
|
this.bindEvents();
|
||
|
|
}
|
||
|
|
|
||
|
|
render() {
|
||
|
|
const active = this.getAttr('active') || 'home';
|
||
|
|
const isLoggedIn = this.isLoggedIn();
|
||
|
|
|
||
|
|
this.setHtml(`
|
||
|
|
<nav class="nav-container">
|
||
|
|
<ul class="nav-list">
|
||
|
|
<li class="nav-item ${active === 'home' ? 'active' : ''}">
|
||
|
|
<a href="?" class="nav-link" data-route="home">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<path fill="currentColor" d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
|
||
|
|
</svg>
|
||
|
|
<span class="nav-label">Home</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item ${active === 'weekly' ? 'active' : ''}">
|
||
|
|
<a href="?weekly" class="nav-link" data-route="weekly">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<path fill="currentColor" d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/>
|
||
|
|
</svg>
|
||
|
|
<span class="nav-label">Weekly</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item ${active === 'collabs' ? 'active' : ''}">
|
||
|
|
<a href="?collabs" class="nav-link" data-route="collabs">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<path fill="currentColor" d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/>
|
||
|
|
</svg>
|
||
|
|
<span class="nav-label">Collabs</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item ${active === 'stories' ? 'active' : ''}">
|
||
|
|
<a href="?stories" class="nav-link" data-route="stories">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<path fill="currentColor" d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"/>
|
||
|
|
</svg>
|
||
|
|
<span class="nav-label">Stories</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item ${active === 'search' ? 'active' : ''}">
|
||
|
|
<a href="?search" class="nav-link" data-route="search">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<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>
|
||
|
|
<span class="nav-label">Search</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
${isLoggedIn ? `
|
||
|
|
<li class="nav-divider"></li>
|
||
|
|
<li class="nav-item ${active === 'notifications' ? 'active' : ''}">
|
||
|
|
<a href="?notifications" class="nav-link" data-route="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="nav-label">Notifications</span>
|
||
|
|
<span class="nav-badge" hidden>0</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item ${active === 'profile' ? 'active' : ''}">
|
||
|
|
<a href="?user=${this.getCurrentUser()?.username || ''}" class="nav-link" data-route="profile">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<path fill="currentColor" d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
|
||
|
|
</svg>
|
||
|
|
<span class="nav-label">Profile</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item">
|
||
|
|
<a href="#" class="nav-link nav-link-danger" data-route="logout">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<path fill="currentColor" d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/>
|
||
|
|
</svg>
|
||
|
|
<span class="nav-label">Logout</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
` : `
|
||
|
|
<li class="nav-divider"></li>
|
||
|
|
<li class="nav-item">
|
||
|
|
<a href="?login" class="nav-link" data-route="login">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<path fill="currentColor" d="M11 7L9.6 8.4l2.6 2.6H2v2h10.2l-2.6 2.6L11 17l5-5-5-5zm9 12h-8v2h8c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-8v2h8v14z"/>
|
||
|
|
</svg>
|
||
|
|
<span class="nav-label">Login</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
`}
|
||
|
|
<li class="nav-divider"></li>
|
||
|
|
<li class="nav-item ${active === 'settings' ? 'active' : ''}">
|
||
|
|
<a href="?settings" class="nav-link" data-route="settings">
|
||
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
|
|
<path fill="currentColor" d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>
|
||
|
|
</svg>
|
||
|
|
<span class="nav-label">Settings</span>
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
</ul>
|
||
|
|
</nav>
|
||
|
|
`);
|
||
|
|
}
|
||
|
|
|
||
|
|
bindEvents() {
|
||
|
|
this.on(this, 'click', this.handleClick);
|
||
|
|
window.addEventListener('rantii:auth-change', () => this.render());
|
||
|
|
window.addEventListener('rantii:route-change', (e) => {
|
||
|
|
this.setAttr('active', e.detail.route);
|
||
|
|
this.render();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
handleClick(e) {
|
||
|
|
const link = e.target.closest('.nav-link');
|
||
|
|
if (!link) return;
|
||
|
|
|
||
|
|
e.preventDefault();
|
||
|
|
const route = link.dataset.route;
|
||
|
|
|
||
|
|
switch (route) {
|
||
|
|
case 'home':
|
||
|
|
this.getRouter()?.goHome();
|
||
|
|
break;
|
||
|
|
case 'weekly':
|
||
|
|
this.getRouter()?.goToWeekly();
|
||
|
|
break;
|
||
|
|
case 'collabs':
|
||
|
|
this.getRouter()?.goToCollabs();
|
||
|
|
break;
|
||
|
|
case 'stories':
|
||
|
|
this.getRouter()?.goToStories();
|
||
|
|
break;
|
||
|
|
case 'search':
|
||
|
|
this.getRouter()?.goToSearch();
|
||
|
|
break;
|
||
|
|
case 'notifications':
|
||
|
|
this.getRouter()?.goToNotifications();
|
||
|
|
break;
|
||
|
|
case 'profile':
|
||
|
|
const username = this.getCurrentUser()?.username;
|
||
|
|
if (username) {
|
||
|
|
this.getRouter()?.goToUser(username);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 'settings':
|
||
|
|
this.getRouter()?.goToSettings();
|
||
|
|
break;
|
||
|
|
case 'login':
|
||
|
|
this.getRouter()?.goToLogin();
|
||
|
|
break;
|
||
|
|
case 'logout':
|
||
|
|
this.getAuth()?.logout();
|
||
|
|
this.getRouter()?.goHome();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.emit('nav-click', { route });
|
||
|
|
}
|
||
|
|
|
||
|
|
setActive(route) {
|
||
|
|
this.setAttr('active', route);
|
||
|
|
this.render();
|
||
|
|
}
|
||
|
|
|
||
|
|
setNotificationBadge(count) {
|
||
|
|
const badge = this.$('.nav-badge');
|
||
|
|
if (badge) {
|
||
|
|
badge.textContent = count > 99 ? '99+' : count;
|
||
|
|
badge.hidden = count === 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
expand() {
|
||
|
|
this.setAttr('expanded', '');
|
||
|
|
}
|
||
|
|
|
||
|
|
collapse() {
|
||
|
|
this.removeAttribute('expanded');
|
||
|
|
}
|
||
|
|
|
||
|
|
toggle() {
|
||
|
|
if (this.hasAttr('expanded')) {
|
||
|
|
this.collapse();
|
||
|
|
} else {
|
||
|
|
this.expand();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
customElements.define('app-nav', AppNav);
|
||
|
|
|
||
|
|
export { AppNav };
|