init
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
@charset "UTF-8";
|
||||
:where(.mod-menu__toggle-sub) {
|
||||
color: currentColor;
|
||||
background-color: #0000;
|
||||
border: none;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
|
||||
&[aria-expanded="true"] .icon-chevron-down {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
:where(.mod-menu [class*="icon-"]) {
|
||||
margin-inline-start: .5rem;
|
||||
transition: all .2s, background-color .2s;
|
||||
}
|
||||
|
||||
:where(.mod-menu__sub[aria-hidden="true"]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:where(.mod-menu__sub[aria-hidden="false"]) {
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
@charset "UTF-8";:where(.mod-menu__toggle-sub){color:currentColor;background-color:#0000;border:none;align-items:center;padding:0;display:inline-flex;&[aria-expanded=true] .icon-chevron-down{transform:rotate(180deg)}}:where(.mod-menu [class*=icon-]){margin-inline-start:.5rem;transition:all .2s,background-color .2s}:where(.mod-menu__sub[aria-hidden=true]){display:none}:where(.mod-menu__sub[aria-hidden=false]){display:block}
|
||||
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
|
||||
"name": "mod_menu",
|
||||
"version": "6.0.0",
|
||||
"description": "Joomla CMS",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"assets": [
|
||||
{
|
||||
"name": "mod_menu.menu",
|
||||
"type": "preset",
|
||||
"dependencies": [
|
||||
"mod_menu.menu#style",
|
||||
"mod_menu.menu#script"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mod_menu.admin-menu",
|
||||
"type": "script",
|
||||
"uri": "mod_menu/admin-menu.min.js",
|
||||
"dependencies": [
|
||||
"core"
|
||||
],
|
||||
"attributes": {
|
||||
"type": "module"
|
||||
},
|
||||
"version": "06b3de"
|
||||
},
|
||||
{
|
||||
"name": "mod_menu.menu",
|
||||
"type": "script",
|
||||
"uri": "mod_menu/menu.min.js",
|
||||
"dependencies": [
|
||||
"core"
|
||||
],
|
||||
"attributes": {
|
||||
"type": "module"
|
||||
},
|
||||
"version": "5a565f"
|
||||
},
|
||||
{
|
||||
"name": "mod_menu.menu",
|
||||
"type": "style",
|
||||
"uri": "mod_menu/mod-menu.min.css",
|
||||
"version": "a45ede"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
const wrapper = document.getElementById('wrapper');
|
||||
const sidebar = document.getElementById('sidebar-wrapper');
|
||||
const menuToggleIcon = document.getElementById('menu-collapse-icon');
|
||||
|
||||
// Strip server-side mm-show/mm-active, then re-apply after init (no visual flash).
|
||||
// Workaround for MetisMenu to avoid isTransitioning lock when server-side active state is pre-rendered.
|
||||
document.querySelectorAll('ul.main-nav').forEach(menu => {
|
||||
const prerenderedShown = [];
|
||||
menu.querySelectorAll('ul.mm-show').forEach(ul => {
|
||||
ul.classList.remove('mm-show');
|
||||
prerenderedShown.push(ul);
|
||||
const li = ul.parentElement;
|
||||
if (li) {
|
||||
li.classList.remove('mm-active');
|
||||
}
|
||||
});
|
||||
new MetisMenu(menu);
|
||||
|
||||
// Re-apply the pre-rendered active state after MetisMenu has completed its clean initialisation.
|
||||
prerenderedShown.forEach(ul => {
|
||||
ul.classList.add('mm-show');
|
||||
const li = ul.parentElement;
|
||||
if (li) {
|
||||
li.classList.add('mm-active');
|
||||
|
||||
// MetisMenu set aria-expanded="false" on all triggers during init
|
||||
const trigger = li.querySelector(':scope > a');
|
||||
if (trigger) {
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// If the sidebar doesn't exist, for example, on edit views, then remove the "closed" class
|
||||
if (!sidebar) {
|
||||
wrapper.classList.remove('closed');
|
||||
}
|
||||
if (sidebar && !sidebar.getAttribute('data-hidden')) {
|
||||
// Sidebar
|
||||
const menuToggle = document.getElementById('menu-collapse');
|
||||
|
||||
// Apply 2nd level collapse
|
||||
sidebar.querySelectorAll('.collapse-level-1').forEach(first => {
|
||||
first.querySelectorAll('.collapse-level-1').forEach(second => {
|
||||
if (second) {
|
||||
second.classList.remove('collapse-level-1');
|
||||
second.classList.add('collapse-level-2');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Toggle menu
|
||||
menuToggle.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
wrapper.classList.toggle('closed');
|
||||
menuToggleIcon.classList.toggle('icon-toggle-on');
|
||||
menuToggleIcon.classList.toggle('icon-toggle-off');
|
||||
document.querySelectorAll('.main-nav > li').forEach(item => item.classList.remove('open'));
|
||||
const elem = document.querySelector('.child-open');
|
||||
if (elem) {
|
||||
elem.classList.remove('child-open');
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('joomla:menu-toggle', {
|
||||
detail: wrapper.classList.contains('closed') ? 'closed' : 'open',
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
}));
|
||||
});
|
||||
|
||||
// Sidebar Nav
|
||||
const mainNav = document.querySelector('ul.main-nav');
|
||||
|
||||
// Active path is normally pre-rendered server-side via CssMenu::setActivePath().
|
||||
// This client-side fallback only runs if server-side detection failed.
|
||||
if (!wrapper.querySelector('.mm-active')) {
|
||||
const currentUrl = window.location.href;
|
||||
|
||||
// Set active class
|
||||
wrapper.querySelectorAll('a.no-dropdown, .menu-dashboard > a').forEach(link => {
|
||||
if (!link.href.match(/index\.php$/) && currentUrl.indexOf(link.href) === 0 || link.href.match(/index\.php$/) && currentUrl.match(/index\.php$/)) {
|
||||
link.setAttribute('aria-current', 'page');
|
||||
link.classList.add('mm-active');
|
||||
|
||||
// Auto Expand Levels
|
||||
if (!link.parentNode.classList.contains('parent')) {
|
||||
let tempParent = link.parentNode;
|
||||
while (tempParent && !tempParent.classList.contains('metismenu')) {
|
||||
tempParent.parentNode.classList.add('mm-active');
|
||||
tempParent.classList.add('mm-show');
|
||||
tempParent = tempParent.parentNode.closest('ul');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Child open toggle
|
||||
const openToggle = ({
|
||||
currentTarget
|
||||
}) => {
|
||||
let menuItem = currentTarget.parentNode;
|
||||
if (menuItem.tagName.toLowerCase() === 'span') {
|
||||
menuItem = currentTarget.parentNode.parentNode;
|
||||
}
|
||||
if (menuItem.classList.contains('open')) {
|
||||
mainNav.classList.remove('child-open');
|
||||
menuItem.classList.remove('open');
|
||||
} else {
|
||||
const siblings = [].slice.call(menuItem.parentNode.children);
|
||||
siblings.forEach(sibling => {
|
||||
sibling.classList.remove('open');
|
||||
});
|
||||
wrapper.classList.remove('closed');
|
||||
if (menuToggleIcon.classList.contains('icon-toggle-off')) {
|
||||
menuToggleIcon.classList.toggle('icon-toggle-off');
|
||||
menuToggleIcon.classList.toggle('icon-toggle-on');
|
||||
}
|
||||
mainNav.classList.add('child-open');
|
||||
if (menuItem.parentNode.classList.contains('main-nav')) {
|
||||
menuItem.classList.add('open');
|
||||
}
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('joomla:menu-toggle', {
|
||||
detail: 'open',
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
}));
|
||||
};
|
||||
document.querySelectorAll('ul.main-nav li.parent > a').forEach(parent => {
|
||||
parent.addEventListener('click', openToggle);
|
||||
parent.addEventListener('keyup', openToggle);
|
||||
});
|
||||
|
||||
// Menu close
|
||||
document.querySelectorAll('ul.main-nav li.parent .close').forEach(subMenu => {
|
||||
subMenu.addEventListener('click', () => {
|
||||
mainNav.querySelectorAll('.open').forEach(menuChild => menuChild.classList.remove('open'));
|
||||
mainNav.classList.remove('child-open');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/const wrapper=document.getElementById("wrapper"),sidebar=document.getElementById("sidebar-wrapper"),menuToggleIcon=document.getElementById("menu-collapse-icon");if(document.querySelectorAll("ul.main-nav").forEach(a=>{const l=[];a.querySelectorAll("ul.mm-show").forEach(s=>{s.classList.remove("mm-show"),l.push(s);const t=s.parentElement;t&&t.classList.remove("mm-active")}),new MetisMenu(a),l.forEach(s=>{s.classList.add("mm-show");const t=s.parentElement;if(t){t.classList.add("mm-active");const e=t.querySelector(":scope > a");e&&e.setAttribute("aria-expanded","true")}})}),sidebar||wrapper.classList.remove("closed"),sidebar&&!sidebar.getAttribute("data-hidden")){const a=document.getElementById("menu-collapse");sidebar.querySelectorAll(".collapse-level-1").forEach(t=>{t.querySelectorAll(".collapse-level-1").forEach(e=>{e&&(e.classList.remove("collapse-level-1"),e.classList.add("collapse-level-2"))})}),a.addEventListener("click",t=>{t.preventDefault(),wrapper.classList.toggle("closed"),menuToggleIcon.classList.toggle("icon-toggle-on"),menuToggleIcon.classList.toggle("icon-toggle-off"),document.querySelectorAll(".main-nav > li").forEach(o=>o.classList.remove("open"));const e=document.querySelector(".child-open");e&&e.classList.remove("child-open"),window.dispatchEvent(new CustomEvent("joomla:menu-toggle",{detail:wrapper.classList.contains("closed")?"closed":"open",bubbles:!0,cancelable:!0}))});const l=document.querySelector("ul.main-nav");if(!wrapper.querySelector(".mm-active")){const t=window.location.href;wrapper.querySelectorAll("a.no-dropdown, .menu-dashboard > a").forEach(e=>{if((!e.href.match(/index\.php$/)&&t.indexOf(e.href)===0||e.href.match(/index\.php$/)&&t.match(/index\.php$/))&&(e.setAttribute("aria-current","page"),e.classList.add("mm-active"),!e.parentNode.classList.contains("parent"))){let o=e.parentNode;for(;o&&!o.classList.contains("metismenu");)o.parentNode.classList.add("mm-active"),o.classList.add("mm-show"),o=o.parentNode.closest("ul")}})}const s=({currentTarget:t})=>{let e=t.parentNode;e.tagName.toLowerCase()==="span"&&(e=t.parentNode.parentNode),e.classList.contains("open")?(l.classList.remove("child-open"),e.classList.remove("open")):([].slice.call(e.parentNode.children).forEach(c=>{c.classList.remove("open")}),wrapper.classList.remove("closed"),menuToggleIcon.classList.contains("icon-toggle-off")&&(menuToggleIcon.classList.toggle("icon-toggle-off"),menuToggleIcon.classList.toggle("icon-toggle-on")),l.classList.add("child-open"),e.parentNode.classList.contains("main-nav")&&e.classList.add("open")),window.dispatchEvent(new CustomEvent("joomla:menu-toggle",{detail:"open",bubbles:!0,cancelable:!0}))};document.querySelectorAll("ul.main-nav li.parent > a").forEach(t=>{t.addEventListener("click",s),t.addEventListener("keyup",s)}),document.querySelectorAll("ul.main-nav li.parent .close").forEach(t=>{t.addEventListener("click",()=>{l.querySelectorAll(".open").forEach(e=>e.classList.remove("open")),l.classList.remove("child-open")})})}
|
||||
Binary file not shown.
@@ -0,0 +1,263 @@
|
||||
function _extends() {
|
||||
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
||||
for (var e = 1; e < arguments.length; e++) {
|
||||
var t = arguments[e];
|
||||
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
||||
}
|
||||
return n;
|
||||
}, _extends.apply(null, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
(() => {
|
||||
|
||||
/**
|
||||
* Navigation menu
|
||||
*
|
||||
* Example usage:
|
||||
* // Default behavior (uses menuHoverClass = 'show-menu', dir = 'ltr')
|
||||
* new Nav(document.querySelector('.nav'));
|
||||
*
|
||||
* // Override defaults (e.g. custom open-class and RTL support)
|
||||
* new Nav(document.querySelector('.nav'), {
|
||||
* menuHoverClass: 'my-open-class',
|
||||
* dir: 'rtl'
|
||||
* });
|
||||
*
|
||||
* @param {HTMLElement} nav The root <ul class="nav"> element
|
||||
* @param {Object} [settings] Optional overrides for defaultSettings
|
||||
* @param {string} [settings.menuHoverClass='show-menu'] CSS class to toggle on open submenus
|
||||
* @param {string} [settings.dir='ltr'] Text direction for keyboard nav ('ltr'|'rtl')
|
||||
*/
|
||||
class Nav {
|
||||
constructor(nav, settings = {}) {
|
||||
var _settings$dir, _this$nav$id, _this$nav;
|
||||
this.nav = nav;
|
||||
|
||||
// read the HTML dir attribute or computed style, or fall back to defaultSettings.dir
|
||||
const browserDir = document.documentElement.getAttribute('dir') ||
|
||||
// <html dir="…">
|
||||
getComputedStyle(document.documentElement).direction ||
|
||||
// CSS direction
|
||||
|
||||
Nav.defaultSettings.dir;
|
||||
this.settings = _extends({}, Nav.defaultSettings, settings);
|
||||
|
||||
// merge defaults, browser‐detected dir, and any explicit overrides in `settings`
|
||||
this.settings = _extends({}, Nav.defaultSettings, {
|
||||
dir: (_settings$dir = settings.dir) != null ? _settings$dir : browserDir
|
||||
}, settings);
|
||||
|
||||
// Unique prefix for this nav instance - needed for the id of submenus and aria-controls
|
||||
this.idPrefix = (_this$nav$id = (_this$nav = this.nav) == null ? void 0 : _this$nav.id) != null ? _this$nav$id : `nav-${Math.floor(Math.random() * 100000)}`;
|
||||
this.topLevelNodes = this.nav.querySelectorAll(':scope > li');
|
||||
this.topLevelNodes.forEach(topLevelEl => {
|
||||
// get submenu ul elements within topLevelEl
|
||||
const levelChildUls = topLevelEl.querySelectorAll('ul');
|
||||
const ariaControls = [];
|
||||
levelChildUls.forEach(childUl => {
|
||||
childUl.setAttribute('aria-hidden', 'true');
|
||||
childUl.classList.remove(this.settings.menuHoverClass);
|
||||
childUl.id = `${this.idPrefix}-submenu${Nav.idCounter}`;
|
||||
Nav.idCounter += 1;
|
||||
ariaControls.push(childUl.id);
|
||||
});
|
||||
if (levelChildUls.length > 0) {
|
||||
const togglebtn = topLevelEl.querySelector('[aria-expanded]');
|
||||
togglebtn == null || togglebtn.setAttribute('aria-controls', ariaControls.join(' '));
|
||||
}
|
||||
});
|
||||
nav.addEventListener('keydown', this.onMenuKeyDown.bind(this));
|
||||
nav.addEventListener('click', this.onClick.bind(this));
|
||||
if (this.nav.classList.contains(this.settings.preventSubmenuOpenOnload)) {
|
||||
this.toggleAllForCurrentActive();
|
||||
}
|
||||
}
|
||||
onMenuKeyDown(event) {
|
||||
const target = event.target.closest('li');
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const subLists = target.querySelectorAll('ul');
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
this.tabPrev();
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
event.preventDefault();
|
||||
if (this.settings.dir === 'rtl') {
|
||||
this.tabNext();
|
||||
} else {
|
||||
this.tabPrev();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
this.tabNext();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
event.preventDefault();
|
||||
if (this.settings.dir === 'rtl') {
|
||||
this.tabPrev();
|
||||
} else {
|
||||
this.tabNext();
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
if (event.target.nodeName === 'SPAN' && event.target.parentNode.nodeName !== 'A' && subLists.length > 0) {
|
||||
var _subLists$;
|
||||
event.preventDefault();
|
||||
this.toggleSubMenu(target, subLists, ((_subLists$ = subLists[0]) == null ? void 0 : _subLists$.getAttribute('aria-hidden')) === 'true');
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
case 'Spacebar':
|
||||
if (subLists.length > 0) {
|
||||
var _subLists$2;
|
||||
event.preventDefault();
|
||||
this.toggleSubMenu(target, subLists, ((_subLists$2 = subLists[0]) == null ? void 0 : _subLists$2.getAttribute('aria-hidden')) === 'true');
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
{
|
||||
event.preventDefault();
|
||||
const currentTopLevelLi = this.getTopLevelParentLi(event.target);
|
||||
if (!currentTopLevelLi) {
|
||||
break;
|
||||
}
|
||||
const allChildListsFromTopLevelLi = currentTopLevelLi.querySelectorAll('ul');
|
||||
if (allChildListsFromTopLevelLi.length > 0) {
|
||||
this.toggleSubMenu(currentTopLevelLi, allChildListsFromTopLevelLi, false);
|
||||
}
|
||||
// set focus on the top level li child with tabindex
|
||||
currentTopLevelLi.querySelectorAll(':scope > [tabindex]:not([tabindex="-1"]), a, button').forEach(tabElement => {
|
||||
if (tabElement.hasAttribute(['aria-expanded'])) {
|
||||
tabElement.focus();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'End':
|
||||
{
|
||||
var _target$closest;
|
||||
event.preventDefault();
|
||||
const currentLiList = (_target$closest = target.closest('ul')) == null ? void 0 : _target$closest.querySelectorAll(':scope > li');
|
||||
for (let index = currentLiList.length - 1; index >= 0; index -= 1) {
|
||||
const lastTabbable = currentLiList[index].querySelector(':scope > [tabindex]:not([tabindex="-1"]), a, button');
|
||||
if (lastTabbable) {
|
||||
lastTabbable.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Home':
|
||||
{
|
||||
var _target$closest2;
|
||||
event.preventDefault();
|
||||
const firstLi = (_target$closest2 = target.closest('ul')) == null ? void 0 : _target$closest2.querySelector(':scope > li:first-child');
|
||||
if (firstLi) {
|
||||
var _firstLi$querySelecto;
|
||||
// set focus on first li child with tabindex within current list
|
||||
(_firstLi$querySelecto = firstLi.querySelector(':scope > [tabindex]:not([tabindex="-1"]), a, button')) == null || _firstLi$querySelecto.focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
onClick(event) {
|
||||
var _event$target, _event$target2, _event$target3, _event$target4;
|
||||
if (!((_event$target = event.target) != null && _event$target.hasAttribute('aria-expanded')) && !((_event$target2 = event.target) != null && _event$target2.closest('[aria-expanded'))) {
|
||||
return;
|
||||
}
|
||||
if (((_event$target3 = event.target) == null ? void 0 : _event$target3.nodeName) === 'A') {
|
||||
return;
|
||||
}
|
||||
if (((_event$target4 = event.target) == null ? void 0 : _event$target4.nodeName) === 'SPAN' && event.target.parentNode.nodeName === 'A') {
|
||||
return;
|
||||
}
|
||||
const target = event.target.closest('li');
|
||||
const subLists = target == null ? void 0 : target.querySelectorAll('ul');
|
||||
if (subLists && subLists.length > 0) {
|
||||
var _subLists$3;
|
||||
event.preventDefault();
|
||||
this.toggleSubMenu(target, subLists, ((_subLists$3 = subLists[0]) == null ? void 0 : _subLists$3.getAttribute('aria-hidden')) === 'true');
|
||||
}
|
||||
}
|
||||
toggleSubMenu(target, subLists, open = false) {
|
||||
var _target$querySelector;
|
||||
if (open) {
|
||||
// close all opened submenus before opening the new one
|
||||
const allSubMenus = this.nav.querySelectorAll('ul[aria-hidden="false"]');
|
||||
allSubMenus.forEach(ulChild => {
|
||||
var _this$getTopLevelPare;
|
||||
ulChild.setAttribute('aria-hidden', 'true');
|
||||
ulChild.classList.remove(this.settings.menuHoverClass);
|
||||
(_this$getTopLevelPare = this.getTopLevelParentLi(ulChild)) == null || (_this$getTopLevelPare = _this$getTopLevelPare.querySelector(':scope > [aria-expanded]')) == null || _this$getTopLevelPare.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
}
|
||||
subLists.forEach(ulChild => {
|
||||
ulChild.setAttribute('aria-hidden', open ? 'false' : 'true');
|
||||
ulChild.classList.toggle(this.settings.menuHoverClass, open);
|
||||
});
|
||||
(_target$querySelector = target.querySelector(':scope > [aria-expanded]')) == null || _target$querySelector.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
}
|
||||
focusTabbable(direction = 1) {
|
||||
const tabbables = Array.from(this.nav.querySelectorAll('[tabindex]:not([tabindex="-1"]), a, button')).filter(el => !el.disabled && el.tabIndex >= 0 && el.offsetParent !== null);
|
||||
const currentIndex = tabbables.indexOf(document.activeElement);
|
||||
if (tabbables.length === 0) return;
|
||||
const nextIndex = (currentIndex + direction + tabbables.length) % tabbables.length;
|
||||
tabbables[nextIndex].focus();
|
||||
}
|
||||
tabNext() {
|
||||
this.focusTabbable(1);
|
||||
}
|
||||
tabPrev() {
|
||||
this.focusTabbable(-1);
|
||||
}
|
||||
getTopLevelParentLi(element) {
|
||||
let currentLi = element.closest('li');
|
||||
// this.topLevelNodes is a NodeList of top-level li elements in this nav
|
||||
while (currentLi && !Array.from(this.topLevelNodes).includes(currentLi)) {
|
||||
const parentUl = currentLi.parentElement.closest('ul');
|
||||
currentLi = parentUl ? parentUl.closest('li') : null;
|
||||
}
|
||||
return currentLi; // top-level li or null if not found, or the
|
||||
}
|
||||
toggleAllForCurrentActive() {
|
||||
const active = this.nav.querySelector('.current.active');
|
||||
if (active) {
|
||||
this.getTopLevelParentLi(active);
|
||||
let currentLi = active;
|
||||
while (currentLi && !Array.from(this.topLevelNodes).includes(currentLi)) {
|
||||
const parentUl = currentLi.parentElement.closest('ul');
|
||||
currentLi = parentUl ? parentUl.closest('li') : null;
|
||||
if (currentLi) {
|
||||
var _subLists$4;
|
||||
const subLists = currentLi.querySelectorAll('ul');
|
||||
this.toggleSubMenu(currentLi, subLists, ((_subLists$4 = subLists[0]) == null ? void 0 : _subLists$4.getAttribute('aria-hidden')) === 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static idCounter for unique id generation of submenus
|
||||
// Default settings for the Nav class
|
||||
Nav.defaultSettings = {
|
||||
menuHoverClass: 'show-menu',
|
||||
dir: 'ltr',
|
||||
preventSubmenuOpenOnload: 'nav-active-open'
|
||||
};
|
||||
Nav.idCounter = 0;
|
||||
|
||||
// Initialize Nav instances for all nav elements on the page
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('.nav').forEach(nav => new Nav(nav));
|
||||
});
|
||||
})();
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
function _extends(){return _extends=Object.assign?Object.assign.bind():function(o){for(var d=1;d<arguments.length;d++){var e=arguments[d];for(var t in e)({}).hasOwnProperty.call(e,t)&&(o[t]=e[t])}return o},_extends.apply(null,arguments)}/**
|
||||
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/(()=>{class o{constructor(e,t={}){var r,a,n;this.nav=e;const u=document.documentElement.getAttribute("dir")||getComputedStyle(document.documentElement).direction||o.defaultSettings.dir;this.settings=_extends({},o.defaultSettings,t),this.settings=_extends({},o.defaultSettings,{dir:(r=t.dir)!=null?r:u},t),this.idPrefix=(a=(n=this.nav)==null?void 0:n.id)!=null?a:`nav-${Math.floor(Math.random()*1e5)}`,this.topLevelNodes=this.nav.querySelectorAll(":scope > li"),this.topLevelNodes.forEach(l=>{const c=l.querySelectorAll("ul"),s=[];if(c.forEach(i=>{i.setAttribute("aria-hidden","true"),i.classList.remove(this.settings.menuHoverClass),i.id=`${this.idPrefix}-submenu${o.idCounter}`,o.idCounter+=1,s.push(i.id)}),c.length>0){const i=l.querySelector("[aria-expanded]");i?.setAttribute("aria-controls",s.join(" "))}}),e.addEventListener("keydown",this.onMenuKeyDown.bind(this)),e.addEventListener("click",this.onClick.bind(this)),this.nav.classList.contains(this.settings.preventSubmenuOpenOnload)&&this.toggleAllForCurrentActive()}onMenuKeyDown(e){const t=e.target.closest("li");if(!t)return;const r=t.querySelectorAll("ul");switch(e.key){case"ArrowUp":e.preventDefault(),this.tabPrev();break;case"ArrowLeft":e.preventDefault(),this.settings.dir==="rtl"?this.tabNext():this.tabPrev();break;case"ArrowDown":e.preventDefault(),this.tabNext();break;case"ArrowRight":e.preventDefault(),this.settings.dir==="rtl"?this.tabPrev():this.tabNext();break;case"Enter":if(e.target.nodeName==="SPAN"&&e.target.parentNode.nodeName!=="A"&&r.length>0){var a;e.preventDefault(),this.toggleSubMenu(t,r,((a=r[0])==null?void 0:a.getAttribute("aria-hidden"))==="true")}break;case" ":case"Spacebar":if(r.length>0){var n;e.preventDefault(),this.toggleSubMenu(t,r,((n=r[0])==null?void 0:n.getAttribute("aria-hidden"))==="true")}break;case"Escape":{e.preventDefault();const s=this.getTopLevelParentLi(e.target);if(!s)break;const i=s.querySelectorAll("ul");i.length>0&&this.toggleSubMenu(s,i,!1),s.querySelectorAll(':scope > [tabindex]:not([tabindex="-1"]), a, button').forEach(b=>{b.hasAttribute(["aria-expanded"])&&b.focus()});break}case"End":{var u;e.preventDefault();const s=(u=t.closest("ul"))==null?void 0:u.querySelectorAll(":scope > li");for(let i=s.length-1;i>=0;i-=1){const b=s[i].querySelector(':scope > [tabindex]:not([tabindex="-1"]), a, button');if(b){b.focus();return}}break}case"Home":{var l;e.preventDefault();const s=(l=t.closest("ul"))==null?void 0:l.querySelector(":scope > li:first-child");if(s){var c;(c=s.querySelector(':scope > [tabindex]:not([tabindex="-1"]), a, button'))==null||c.focus()}break}}}onClick(e){var t,r,a,n;if(!((t=e.target)!=null&&t.hasAttribute("aria-expanded"))&&!((r=e.target)!=null&&r.closest("[aria-expanded"))||((a=e.target)==null?void 0:a.nodeName)==="A"||((n=e.target)==null?void 0:n.nodeName)==="SPAN"&&e.target.parentNode.nodeName==="A")return;const u=e.target.closest("li"),l=u?.querySelectorAll("ul");if(l&&l.length>0){var c;e.preventDefault(),this.toggleSubMenu(u,l,((c=l[0])==null?void 0:c.getAttribute("aria-hidden"))==="true")}}toggleSubMenu(e,t,r=!1){var a;r&&this.nav.querySelectorAll('ul[aria-hidden="false"]').forEach(u=>{var l;u.setAttribute("aria-hidden","true"),u.classList.remove(this.settings.menuHoverClass),(l=this.getTopLevelParentLi(u))==null||(l=l.querySelector(":scope > [aria-expanded]"))==null||l.setAttribute("aria-expanded","false")}),t.forEach(n=>{n.setAttribute("aria-hidden",r?"false":"true"),n.classList.toggle(this.settings.menuHoverClass,r)}),(a=e.querySelector(":scope > [aria-expanded]"))==null||a.setAttribute("aria-expanded",r?"true":"false")}focusTabbable(e=1){const t=Array.from(this.nav.querySelectorAll('[tabindex]:not([tabindex="-1"]), a, button')).filter(n=>!n.disabled&&n.tabIndex>=0&&n.offsetParent!==null),r=t.indexOf(document.activeElement);if(t.length===0)return;const a=(r+e+t.length)%t.length;t[a].focus()}tabNext(){this.focusTabbable(1)}tabPrev(){this.focusTabbable(-1)}getTopLevelParentLi(e){let t=e.closest("li");for(;t&&!Array.from(this.topLevelNodes).includes(t);){const r=t.parentElement.closest("ul");t=r?r.closest("li"):null}return t}toggleAllForCurrentActive(){const e=this.nav.querySelector(".current.active");if(e){this.getTopLevelParentLi(e);let r=e;for(;r&&!Array.from(this.topLevelNodes).includes(r);){const a=r.parentElement.closest("ul");if(r=a?a.closest("li"):null,r){var t;const n=r.querySelectorAll("ul");this.toggleSubMenu(r,n,((t=n[0])==null?void 0:t.getAttribute("aria-hidden"))==="true")}}}}}o.defaultSettings={menuHoverClass:"show-menu",dir:"ltr",preventSubmenuOpenOnload:"nav-active-open"},o.idCounter=0,document.addEventListener("DOMContentLoaded",()=>{document.querySelectorAll(".nav").forEach(d=>new o(d))})})();
|
||||
Binary file not shown.
Reference in New Issue
Block a user