feat(Nav): Refactor navigation structure to support nested items and improve visibility logic
feat(Radio): - Redesigned Queue modal, added drag & drop capabilities - Added stream quality selector, currently offering: AAC @ 128kbps, AAC @ 320kbps & FLAC (lossless) fix(middleware): Import API_URL from config and remove hardcoded API_URL definition security(api): Enhance discord image and video caching with improved signature verification and error handling, updated image proxy to include production checks for signing secret
This commit is contained in:
@@ -30,6 +30,16 @@
|
||||
}
|
||||
};
|
||||
|
||||
function setLenisEnabled(enabled) {
|
||||
const lenis = window.__lenis;
|
||||
if (!lenis) return;
|
||||
if (enabled) {
|
||||
lenis.start();
|
||||
} else {
|
||||
lenis.stop();
|
||||
}
|
||||
}
|
||||
|
||||
function initMobileMenu() {
|
||||
const menuBtn = document.getElementById("mobile-menu-btn");
|
||||
const mobileMenu = document.getElementById("mobile-menu");
|
||||
@@ -51,10 +61,12 @@
|
||||
mobileMenu.classList.remove("open");
|
||||
menuIcon.style.display = "block";
|
||||
closeIcon.style.display = "none";
|
||||
setLenisEnabled(true);
|
||||
} else {
|
||||
mobileMenu.classList.add("open");
|
||||
menuIcon.style.display = "none";
|
||||
closeIcon.style.display = "block";
|
||||
setLenisEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -64,6 +76,7 @@
|
||||
mobileMenu.classList.remove("open");
|
||||
menuIcon.style.display = "block";
|
||||
closeIcon.style.display = "none";
|
||||
setLenisEnabled(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,6 +86,7 @@
|
||||
mobileMenu.classList.remove("open");
|
||||
menuIcon.style.display = "block";
|
||||
closeIcon.style.display = "none";
|
||||
setLenisEnabled(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -81,7 +95,93 @@
|
||||
document.addEventListener("click", closeHandler);
|
||||
}
|
||||
|
||||
const ready = () => initMobileMenu();
|
||||
function initDropdownExternalLinks() {
|
||||
// Close desktop dropdown when external links are clicked
|
||||
const dropdownLinks = document.querySelectorAll('.nav-dropdown a[target="_blank"]');
|
||||
dropdownLinks.forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
// Blur the parent nav item to close the CSS hover-based dropdown
|
||||
const navItem = link.closest('.nav-item--has-children');
|
||||
if (navItem) {
|
||||
const parentLink = navItem.querySelector(':scope > a');
|
||||
if (parentLink) {
|
||||
parentLink.blur();
|
||||
}
|
||||
// Force close by temporarily disabling pointer events
|
||||
navItem.style.pointerEvents = 'none';
|
||||
setTimeout(() => {
|
||||
navItem.style.pointerEvents = '';
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initDesktopDropdownToggle() {
|
||||
// Desktop: click parent to toggle dropdown open/closed
|
||||
const desktopNav = document.querySelector('nav .hidden.md\\:flex');
|
||||
if (!desktopNav) return;
|
||||
|
||||
const parentItems = desktopNav.querySelectorAll('.nav-item--has-children');
|
||||
parentItems.forEach((navItem) => {
|
||||
const parentLink = navItem.querySelector(':scope > a');
|
||||
if (!parentLink) return;
|
||||
|
||||
parentLink.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const isOpen = navItem.classList.contains('dropdown-open');
|
||||
|
||||
// Close any other open dropdowns
|
||||
parentItems.forEach((item) => item.classList.remove('dropdown-open'));
|
||||
|
||||
if (!isOpen) {
|
||||
navItem.classList.add('dropdown-open');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!desktopNav.contains(e.target)) {
|
||||
parentItems.forEach((item) => item.classList.remove('dropdown-open'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initMobileSubmenus() {
|
||||
const mobileMenu = document.getElementById("mobile-menu");
|
||||
if (!mobileMenu) return;
|
||||
|
||||
// Find all parent links that have a sibling mobile-subnav
|
||||
const parentLinks = mobileMenu.querySelectorAll('a:has(+ .mobile-subnav)');
|
||||
|
||||
parentLinks.forEach((link) => {
|
||||
const subnav = link.nextElementSibling;
|
||||
const caret = link.querySelector('.mobile-caret');
|
||||
|
||||
if (!subnav || !subnav.classList.contains('mobile-subnav')) return;
|
||||
|
||||
// Clone to remove existing listeners
|
||||
const newLink = link.cloneNode(true);
|
||||
link.parentNode.replaceChild(newLink, link);
|
||||
|
||||
const newCaret = newLink.querySelector('.mobile-caret');
|
||||
|
||||
newLink.addEventListener('click', (e) => {
|
||||
// Toggle subnav open/closed on click
|
||||
e.preventDefault();
|
||||
subnav.classList.toggle('open');
|
||||
if (newCaret) newCaret.classList.toggle('open');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const ready = () => {
|
||||
initMobileMenu();
|
||||
initDropdownExternalLinks();
|
||||
initDesktopDropdownToggle();
|
||||
initMobileSubmenus();
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", ready, { once: true });
|
||||
@@ -89,5 +189,10 @@
|
||||
ready();
|
||||
}
|
||||
|
||||
document.addEventListener("astro:page-load", initMobileMenu);
|
||||
document.addEventListener("astro:page-load", () => {
|
||||
initMobileMenu();
|
||||
initDropdownExternalLinks();
|
||||
initDesktopDropdownToggle();
|
||||
initMobileSubmenus();
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user