diff --git a/build/media_src/mod_menu/js/menu.es6.js b/build/media_src/mod_menu/js/menu.es6.js new file mode 100644 index 0000000000000..5ed4492a534c3 --- /dev/null +++ b/build/media_src/mod_menu/js/menu.es6.js @@ -0,0 +1,151 @@ +/** + * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +(() => { + 'use strict'; + + function topLevelMouseOver(el, settings) { + const ulChild = el.querySelector('ul'); + if (ulChild) { + ulChild.setAttribute('aria-hidden', 'false'); + ulChild.classList.add(settings.menuHoverClass); + } + } + + function topLevelMouseOut(el, settings) { + const ulChild = el.querySelector('ul'); + if (ulChild) { + ulChild.setAttribute('aria-hidden', 'true'); + ulChild.classList.remove(settings.menuHoverClass); + } + } + + function setupNavigation(nav) { + const settings = { + menuHoverClass: 'show-menu', + dir: 'ltr', + }; + const topLevelChilds = nav.querySelectorAll(':scope > li'); + + // Set tabIndex to -1 so that top_level_childs can't receive focus until menu is open + topLevelChilds.forEach((topLevelEl) => { + const linkEl = topLevelEl.querySelector('a'); + if (linkEl) { + linkEl.tabIndex = '0'; + linkEl.addEventListener('mouseover', topLevelMouseOver(topLevelEl, settings)); + linkEl.addEventListener('mouseout', topLevelMouseOut(topLevelEl, settings)); + } + const spanEl = topLevelEl.querySelector('span'); + if (spanEl) { + spanEl.tabIndex = '0'; + spanEl.addEventListener('mouseover', topLevelMouseOver(topLevelEl, settings)); + spanEl.addEventListener('mouseout', topLevelMouseOut(topLevelEl, settings)); + } + + topLevelEl.addEventListener('mouseover', (event) => { + const curEl = event.target; + const ulChild = curEl.querySelector('ul'); + if (ulChild) { + ulChild.setAttribute('aria-hidden', 'false'); + ulChild.classList.add(settings.menuHoverClass); + } + }); + + topLevelEl.addEventListener('mouseout', (event) => { + const curEl = event.target; + const ulChild = curEl.querySelector('ul'); + if (ulChild) { + ulChild.setAttribute('aria-hidden', 'true'); + ulChild.classList.remove(settings.menuHoverClass); + } + }); + + topLevelEl.addEventListener('focus', (event) => { + const curEl = event.target; + const ulChild = curEl.querySelector('ul'); + if (ulChild) { + ulChild.setAttribute('aria-hidden', 'true'); + ulChild.classList.add(settings.menuHoverClass); + } + }); + + topLevelEl.addEventListener('blur', (event) => { + const curEl = event.target; + const ulChild = curEl.querySelector('ul'); + if (ulChild) { + ulChild.setAttribute('aria-hidden', 'false'); + ulChild.classList.remove(settings.menuHoverClass); + } + }); + + topLevelEl.addEventListener('keydown', (event) => { + const keyName = event.key; + const curEl = event.target; + const curLiEl = curEl.parentElement; + const curUlEl = curLiEl.parentElement; + let prevLiEl = curLiEl.previousElementSibling; + let nextLiEl = curLiEl.nextElementSibling; + if (!prevLiEl) { + prevLiEl = curUlEl.children[curUlEl.children.length - 1]; + } + if (!nextLiEl) { + [nextLiEl] = curUlEl.children; + } + switch (keyName) { + case 'ArrowLeft': + event.preventDefault(); + if (settings.dir === 'rtl') { + nextLiEl.children[0].focus(); + } else { + prevLiEl.children[0].focus(); + } + break; + case 'ArrowRight': + event.preventDefault(); + if (settings.dir === 'rtl') { + prevLiEl.children[0].focus(); + } else { + nextLiEl.children[0].focus(); + } + break; + case 'ArrowUp': + { + event.preventDefault(); + const parent = curLiEl.parentElement.parentElement; + if (parent.nodeName === 'LI') { + parent.children[0].focus(); + } else { + prevLiEl.children[0].focus(); + } + break; + } + case 'ArrowDown': + event.preventDefault(); + if (curLiEl.classList.contains('parent')) { + const child = curLiEl.querySelector('ul'); + if (child != null) { + const childLi = child.querySelector('li'); + childLi.children[0].focus(); + } else { + nextLiEl.children[0].focus(); + } + } else { + nextLiEl.children[0].focus(); + } + break; + default: + break; + } + }); + }); + } + + document.addEventListener('DOMContentLoaded', () => { + const navs = document.querySelectorAll('.nav'); + [].forEach.call(navs, (nav) => { + setupNavigation(nav); + }); + }); +})(); diff --git a/modules/mod_menu/tmpl/default.php b/modules/mod_menu/tmpl/default.php index 182d7da5c6667..1094c9440225f 100644 --- a/modules/mod_menu/tmpl/default.php +++ b/modules/mod_menu/tmpl/default.php @@ -9,8 +9,11 @@ defined('_JEXEC') or die; +use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Helper\ModuleHelper; +HTMLHelper::_('script', 'mod_menu/menu.min.js', array('version' => 'auto', 'relative' => true)); + $id = ''; if ($tagId = $params->get('tag_id', ''))