Really-amin's picture
Upload 1460 files
96af7c9 verified
/**
* ═══════════════════════════════════════════════════════════════════
* COMPLETE MENU SYSTEM
* All Menus Implementation with Smooth Animations
* ═══════════════════════════════════════════════════════════════════
*/
class MenuSystem {
constructor() {
this.menus = new Map();
this.activeMenu = null;
this.init();
}
init() {
this.setupDropdownMenus();
this.setupContextMenus();
this.setupMobileMenus();
this.setupSubmenus();
this.setupKeyboardNavigation();
}
/**
* Dropdown Menus
*/
setupDropdownMenus() {
document.querySelectorAll('[data-menu-trigger]').forEach(trigger => {
const menuId = trigger.dataset.menuTrigger;
const menu = document.querySelector(`[data-menu="${menuId}"]`);
if (!menu) return;
// Show menu initially for positioning
menu.style.display = 'block';
menu.style.visibility = 'hidden';
this.menus.set(menuId, { trigger, menu, type: 'dropdown' });
trigger.addEventListener('click', (e) => {
e.stopPropagation();
this.toggleMenu(menuId);
});
// Handle menu item clicks
menu.querySelectorAll('.menu-item').forEach(item => {
item.addEventListener('click', (e) => {
e.stopPropagation();
const action = item.dataset.action;
if (action) {
this.handleMenuAction(action);
}
this.closeMenu(menu);
});
});
});
// Close on outside click
document.addEventListener('click', (e) => {
if (!e.target.closest('[data-menu]') && !e.target.closest('[data-menu-trigger]')) {
this.closeAllMenus();
}
});
}
/**
* Context Menus (Right-click)
*/
setupContextMenus() {
document.querySelectorAll('[data-context-menu]').forEach(element => {
const menuId = element.dataset.contextMenu;
const menu = document.querySelector(`[data-context-menu-target="${menuId}"]`);
if (!menu) return;
element.addEventListener('contextmenu', (e) => {
e.preventDefault();
this.showContextMenu(menu, e.clientX, e.clientY);
});
});
// Close context menu on click
document.addEventListener('click', () => {
document.querySelectorAll('[data-context-menu-target]').forEach(menu => {
menu.classList.remove('context-menu-open');
});
});
}
/**
* Mobile Menu
*/
setupMobileMenus() {
const mobileMenuToggle = document.querySelector('[data-mobile-menu-toggle]');
const mobileMenu = document.querySelector('[data-mobile-menu]');
if (mobileMenuToggle && mobileMenu) {
mobileMenuToggle.addEventListener('click', () => {
mobileMenu.classList.toggle('mobile-menu-open');
mobileMenuToggle.classList.toggle('mobile-menu-active');
});
}
}
/**
* Submenus
*/
setupSubmenus() {
document.querySelectorAll('[data-submenu-trigger]').forEach(trigger => {
const submenu = trigger.nextElementSibling;
if (!submenu || !submenu.classList.contains('submenu')) return;
trigger.addEventListener('mouseenter', () => {
this.showSubmenu(submenu, trigger);
});
trigger.addEventListener('mouseleave', () => {
setTimeout(() => {
if (!submenu.matches(':hover')) {
this.hideSubmenu(submenu);
}
}, 200);
});
submenu.addEventListener('mouseleave', () => {
this.hideSubmenu(submenu);
});
});
}
/**
* Keyboard Navigation
*/
setupKeyboardNavigation() {
document.addEventListener('keydown', (e) => {
// ESC to close menus
if (e.key === 'Escape') {
this.closeAllMenus();
}
// Arrow keys for navigation
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
const activeMenu = document.querySelector('.menu-open, .context-menu-open');
if (activeMenu) {
e.preventDefault();
this.navigateMenu(activeMenu, e.key === 'ArrowDown' ? 1 : -1);
}
}
});
}
toggleMenu(menuId) {
const menuData = this.menus.get(menuId);
if (!menuData) return;
const { menu, trigger } = menuData;
// Close other menus
if (this.activeMenu && this.activeMenu !== menu) {
this.closeMenu(this.activeMenu);
}
// Toggle current menu
if (menu.classList.contains('menu-open')) {
this.closeMenu(menu);
} else {
this.openMenu(menu, trigger);
}
}
openMenu(menu, trigger) {
menu.style.visibility = 'visible';
menu.classList.add('menu-open');
trigger?.classList.add('menu-trigger-active');
this.activeMenu = menu;
// Animate in
this.animateMenuIn(menu, trigger);
}
closeMenu(menu) {
menu.classList.remove('menu-open');
const trigger = Array.from(this.menus.values()).find(m => m.menu === menu)?.trigger;
trigger?.classList.remove('menu-trigger-active');
if (this.activeMenu === menu) {
this.activeMenu = null;
}
// Animate out
this.animateMenuOut(menu);
}
closeAllMenus() {
document.querySelectorAll('.menu-open, .context-menu-open').forEach(menu => {
this.closeMenu(menu);
});
}
showContextMenu(menu, x, y) {
// Close other context menus
document.querySelectorAll('[data-context-menu-target]').forEach(m => {
m.classList.remove('context-menu-open');
});
menu.style.left = `${x}px`;
menu.style.top = `${y}px`;
menu.classList.add('context-menu-open');
this.activeMenu = menu;
this.animateMenuIn(menu);
}
showSubmenu(submenu, trigger) {
const triggerRect = trigger.getBoundingClientRect();
submenu.style.top = `${triggerRect.top}px`;
submenu.style.left = `${triggerRect.right + 8}px`;
submenu.classList.add('submenu-open');
}
hideSubmenu(submenu) {
submenu.classList.remove('submenu-open');
}
navigateMenu(menu, direction) {
const items = menu.querySelectorAll('.menu-item:not(.disabled)');
if (items.length === 0) return;
let currentIndex = Array.from(items).findIndex(item => item.classList.contains('menu-item-active'));
if (currentIndex === -1) {
currentIndex = direction > 0 ? 0 : items.length - 1;
} else {
currentIndex += direction;
if (currentIndex < 0) currentIndex = items.length - 1;
if (currentIndex >= items.length) currentIndex = 0;
}
items.forEach((item, index) => {
item.classList.toggle('menu-item-active', index === currentIndex);
});
items[currentIndex]?.focus();
}
animateMenuIn(menu, trigger) {
menu.style.opacity = '0';
menu.style.transform = 'translateY(-10px) scale(0.95)';
menu.style.pointerEvents = 'none';
requestAnimationFrame(() => {
menu.style.transition = 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)';
menu.style.opacity = '1';
menu.style.transform = 'translateY(0) scale(1)';
menu.style.pointerEvents = 'auto';
});
}
animateMenuOut(menu) {
menu.style.transition = 'all 0.15s cubic-bezier(0.4, 0, 0.2, 1)';
menu.style.opacity = '0';
menu.style.transform = 'translateY(-10px) scale(0.95)';
setTimeout(() => {
menu.style.pointerEvents = 'none';
menu.style.visibility = 'hidden';
}, 150);
}
handleMenuAction(action) {
switch(action) {
case 'theme-light':
document.body.setAttribute('data-theme', 'light');
break;
case 'theme-dark':
document.body.setAttribute('data-theme', 'dark');
break;
case 'settings':
// Navigate to settings page
const settingsBtn = document.querySelector('[data-nav="page-settings"]');
if (settingsBtn) settingsBtn.click();
break;
default:
console.log('Menu action:', action);
}
}
}
// Initialize menu system
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
window.menuSystem = new MenuSystem();
});
} else {
window.menuSystem = new MenuSystem();
}