feat: modify the menu behavior
This commit is contained in:
parent
12f22bf593
commit
91f875474b
2 changed files with 138 additions and 28 deletions
|
|
@ -91,6 +91,8 @@ body {
|
|||
left: 50%;
|
||||
width: 80%;
|
||||
transform: translateX(-50%);
|
||||
max-height: calc(100dvh - 8rem);
|
||||
overflow-y: auto;
|
||||
|
||||
.list-badge {
|
||||
display: flex;
|
||||
|
|
@ -130,6 +132,11 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
#main-nav:has(.nav-link[aria-expanded="true"])
|
||||
.nav-link-active:not([aria-expanded="true"])::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// * Mobile Offcanvas Nav
|
||||
#offcanvas-main-menu {
|
||||
.btn-club {
|
||||
|
|
@ -137,11 +144,23 @@ body {
|
|||
margin-top: -2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.navbar-nav {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
position: relative;
|
||||
font-size: 1.375rem;
|
||||
|
||||
&-arrow {
|
||||
padding-inline-end: 2rem;
|
||||
&::after {
|
||||
transition: .3s;
|
||||
|
||||
|
|
@ -174,6 +193,11 @@ body {
|
|||
|
||||
.nav-collapse {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
.list-badge {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +225,8 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
.nav-link:hover,
|
||||
.nav-link[aria-expanded="true"] {
|
||||
overflow: visible;
|
||||
|
||||
&::before {
|
||||
|
|
@ -220,6 +245,21 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
//underline and semi-bold for submenu
|
||||
.nav-collapse a:not(.btn, .badge) {
|
||||
text-decoration: underline !important;
|
||||
text-decoration-color: transparent !important;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 0.25em;
|
||||
transition: text-decoration-color 0.2s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
font-weight: 600;
|
||||
text-decoration-color: currentColor !important;
|
||||
}
|
||||
}
|
||||
|
||||
// * Adapt header in export pages because all links are hidden
|
||||
.header-export {
|
||||
min-height: 62px;
|
||||
|
|
|
|||
|
|
@ -1,33 +1,103 @@
|
|||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// * COLLAPSE INIT
|
||||
const navBarCollapseDesktop = document.querySelectorAll(
|
||||
"#main-nav .nav-collapse"
|
||||
) as NodeListOf<HTMLDivElement>;
|
||||
navBarCollapseDesktop.forEach(collapse => {
|
||||
const bsCollapse = new window.bootstrap.Collapse(collapse, { toggle: false });
|
||||
bsCollapse.hide();
|
||||
const navLinkCollapse = document.querySelector("#main-nav .nav-link[data-bs-target='#" + collapse.id + "']") as HTMLLinkElement;
|
||||
navLinkCollapse.addEventListener("mouseover", () => {
|
||||
bsCollapse.show();
|
||||
});
|
||||
navLinkCollapse.parentElement?.addEventListener("mouseleave", () => {
|
||||
setTimeout(() => {
|
||||
bsCollapse.hide();
|
||||
}, 350);
|
||||
type CollapseEntry = {
|
||||
collapse: HTMLDivElement;
|
||||
trigger: HTMLAnchorElement;
|
||||
instance: bootstrap.Collapse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up the submenus for desktop and mobile.
|
||||
*/
|
||||
function getCollapseEntries(root: HTMLElement): CollapseEntry[] {
|
||||
return Array.from(root.querySelectorAll<HTMLDivElement>(".nav-collapse"))
|
||||
.map(collapse => {
|
||||
const trigger = root.querySelector<HTMLAnchorElement>(
|
||||
`.nav-link[data-bs-target="#${collapse.id}"]`
|
||||
);
|
||||
if (!trigger) return null;
|
||||
|
||||
trigger.removeAttribute("data-bs-toggle");
|
||||
collapse.classList.remove("show");
|
||||
|
||||
const instance = window.bootstrap.Collapse.getOrCreateInstance(collapse, { toggle: false });
|
||||
|
||||
trigger.classList.add("collapsed");
|
||||
trigger.setAttribute("aria-expanded", "false");
|
||||
trigger.setAttribute("aria-controls", collapse.id);
|
||||
|
||||
// mark the link as open when submenu is open
|
||||
collapse.addEventListener("show.bs.collapse", e => {
|
||||
if (e.target !== collapse) return;
|
||||
trigger.classList.remove("collapsed");
|
||||
trigger.setAttribute("aria-expanded", "true");
|
||||
});
|
||||
// When is closed
|
||||
collapse.addEventListener("hide.bs.collapse", e => {
|
||||
if (e.target !== collapse) return;
|
||||
trigger.classList.add("collapsed");
|
||||
trigger.setAttribute("aria-expanded", "false");
|
||||
});
|
||||
|
||||
return { collapse, trigger, instance };
|
||||
})
|
||||
.filter((entry): entry is CollapseEntry => entry !== null);
|
||||
}
|
||||
|
||||
function initDesktopMenu(): void {
|
||||
const nav = document.getElementById("main-nav");
|
||||
if (!nav) return;
|
||||
|
||||
const entries = getCollapseEntries(nav);
|
||||
const closeAll = (except?: CollapseEntry) =>
|
||||
entries.forEach(entry => entry !== except && entry.instance.hide());
|
||||
|
||||
// open on click, only one submenu at a time
|
||||
entries.forEach(entry => {
|
||||
entry.trigger.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
closeAll(entry);
|
||||
entry.instance.toggle();
|
||||
});
|
||||
});
|
||||
|
||||
// * COLLAPSE MOBILE
|
||||
const navBarLinkMobile = document.querySelectorAll(
|
||||
"#offcanvas-main-menu .nav-link"
|
||||
) as NodeListOf<HTMLAnchorElement>;
|
||||
navBarLinkMobile.forEach(anchor => {
|
||||
anchor.classList.remove("collapsed");
|
||||
// close when click outside
|
||||
document.addEventListener("click", event => {
|
||||
if (!event.isTrusted) return;
|
||||
if (!nav.contains(event.target as Node)) closeAll();
|
||||
});
|
||||
const navBarCollapseMobile = document.querySelectorAll(
|
||||
"#offcanvas-main-menu .nav-collapse"
|
||||
) as NodeListOf<HTMLDivElement>;
|
||||
navBarCollapseMobile.forEach(collapse => {
|
||||
(new window.bootstrap.Collapse(collapse, { toggle: true })).show();
|
||||
|
||||
// click to ESC
|
||||
document.addEventListener("keydown", event => {
|
||||
if (event.key !== "Escape") return;
|
||||
const open = entries.find(e => e.trigger.getAttribute("aria-expanded") === "true");
|
||||
closeAll();
|
||||
open?.trigger.focus();
|
||||
});
|
||||
}
|
||||
|
||||
function initMobileMenu(): void {
|
||||
const offcanvas = document.getElementById("offcanvas-main-menu");
|
||||
if (!offcanvas) return;
|
||||
|
||||
const entries = getCollapseEntries(offcanvas);
|
||||
const closeOthers = (except: CollapseEntry) =>
|
||||
entries.forEach(entry => entry !== except && entry.instance.hide());
|
||||
|
||||
// one submenu at a time
|
||||
entries.forEach(entry => {
|
||||
entry.trigger.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
closeOthers(entry);
|
||||
entry.instance.toggle();
|
||||
});
|
||||
});
|
||||
|
||||
// close all submenus when menu is closed
|
||||
offcanvas.addEventListener("hidden.bs.offcanvas", () => {
|
||||
entries.forEach(entry => entry.instance.hide());
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
initDesktopMenu();
|
||||
initMobileMenu();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue