Files
codey.lol/public/scripts/nav-controls.js
codey ef15b646cc 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
2026-02-22 13:53:43 -05:00

199 lines
6.0 KiB
JavaScript

(function () {
const scriptEl = document.currentScript;
const API_URL = scriptEl?.dataset?.apiUrl;
window.toggleTheme = () => {
const currentTheme = document.documentElement.getAttribute("data-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
// Toggle the dark class for Tailwind
if (newTheme === "dark") {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
// Dispatch event for astro-themes and other listeners
document.dispatchEvent(new CustomEvent("set-theme", { detail: newTheme }));
};
window.handleLogout = async () => {
try {
await fetch(`${API_URL}/auth/logout`, {
method: "POST",
credentials: "include",
});
} catch (error) {
console.error("Logout failed", error);
} finally {
window.location.href = "/";
}
};
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");
const menuIcon = document.getElementById("menu-icon");
const closeIcon = document.getElementById("close-icon");
if (!menuBtn || !mobileMenu || !menuIcon || !closeIcon) {
return;
}
const newMenuBtn = menuBtn.cloneNode(true);
menuBtn.parentNode.replaceChild(newMenuBtn, menuBtn);
newMenuBtn.addEventListener("click", (e) => {
e.stopPropagation();
const isOpen = mobileMenu.classList.contains("open");
if (isOpen) {
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);
}
});
const mobileLinks = mobileMenu.querySelectorAll("a");
mobileLinks.forEach((link) => {
link.addEventListener("click", () => {
mobileMenu.classList.remove("open");
menuIcon.style.display = "block";
closeIcon.style.display = "none";
setLenisEnabled(true);
});
});
const closeHandler = (e) => {
if (!mobileMenu.contains(e.target) && !newMenuBtn.contains(e.target)) {
if (mobileMenu.classList.contains("open")) {
mobileMenu.classList.remove("open");
menuIcon.style.display = "block";
closeIcon.style.display = "none";
setLenisEnabled(true);
}
}
};
document.removeEventListener("click", closeHandler);
document.addEventListener("click", closeHandler);
}
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 });
} else {
ready();
}
document.addEventListener("astro:page-load", () => {
initMobileMenu();
initDropdownExternalLinks();
initDesktopDropdownToggle();
initMobileSubmenus();
});
})();