diff --git a/_build/templates/default/sass/_a11y.scss b/_build/templates/default/sass/_a11y.scss index 46db5e716b..6f1e96a0b9 100644 --- a/_build/templates/default/sass/_a11y.scss +++ b/_build/templates/default/sass/_a11y.scss @@ -1,3 +1,7 @@ -.ext-webkit *:focus{ - outline: auto !important; +.ext-webkit { + #modx-header { + *:focus-visible { + outline: auto !important; + } + } } diff --git a/_build/templates/default/sass/_navbar.scss b/_build/templates/default/sass/_navbar.scss index db9c25b32f..05300b6f75 100644 --- a/_build/templates/default/sass/_navbar.scss +++ b/_build/templates/default/sass/_navbar.scss @@ -27,7 +27,7 @@ background: $colorSplash; max-width: $navbarWidth; position: absolute; - z-index: 2; + z-index: 9999; height: 100%; } @@ -184,6 +184,7 @@ } } +#modx-header, #modx-footer { .modx-subnav { border: 1px solid $navbarBorder; @@ -194,7 +195,7 @@ box-sizing: border-box; list-style: none; position: absolute; - z-index: 10000; + z-index: 99999999999; opacity: 0; visibility: hidden; transition: all .15s ease; @@ -235,6 +236,7 @@ border-radius: $borderRadius; background-color: $subnavBg; color: $subnavTitleColor; + font-size: 13px; font-weight: bold; line-height: 1.5; margin: 0; @@ -244,6 +246,7 @@ display: block; text-decoration: none; cursor: pointer; + text-align: left; .icon { display: inline-block; @@ -409,6 +412,7 @@ } } + #modx-header, #modx-footer { .modx-subnav { min-width: 300px; @@ -458,6 +462,7 @@ } @media (max-height: 520px) { + #modx-header, #modx-footer { .modx-subnav { .description { diff --git a/manager/assets/modext/core/modx.layout.js b/manager/assets/modext/core/modx.layout.js index 32134a342e..568b51e47b 100644 --- a/manager/assets/modext/core/modx.layout.js +++ b/manager/assets/modext/core/modx.layout.js @@ -61,6 +61,16 @@ Ext.extend(MODx.Layout, Ext.Viewport, { */ ,splitBarMargin: 8 + /** + * @property {Array} focusRestoreEls - Set Focus back on the last Element in array on close + */ + ,focusRestoreEls: [] + + /** + * @property {bool} subNavOpen - Check if Subnav is opened + */ + ,subNavOpen: false + /** * @property {Function} getSplitBarMargin - Utility getter for splitBarMargin * @returns {Number} @@ -141,7 +151,7 @@ Ext.extend(MODx.Layout, Ext.Viewport, { ,xtype: 'box' ,id: 'modx-header' ,applyTo: 'modx-header' - ,autoScroll: true + //,autoScroll: true ,width: this.menuBarWidth ,listeners: { afterrender: { fn: this.initPopper, scope: this } @@ -425,7 +435,6 @@ Ext.extend(MODx.Layout, Ext.Viewport, { ,initPopper: function() { var el = this; var buttons = document.getElementById('modx-navbar').getElementsByClassName('top'); - var focusRestore = false; var position = window.innerWidth <= 960 ? 'bottom' : 'right'; for (var i = 0; i < buttons.length; i++) { var submenu = document.getElementById(buttons[i].id + '-submenu'); @@ -466,23 +475,17 @@ Ext.extend(MODx.Layout, Ext.Viewport, { } } }); - buttons[i].onclick = function(e) { + var _this = this; + var openSubnav = + buttons[i].addEventListener('click', function(e) { e.stopPropagation(); - focusRestore = this; + _this.focusRestoreEls.push(this.querySelectorAll('a')[0]); el.showMenu(this); - }; + }); } window.addEventListener('click', function() { el.hideMenu(); }); - document.addEventListener('keydown', function(e) { - if (e.key == 'Escape') { - el.hideMenu(); - setTimeout(() => { - focusRestore?.querySelectorAll('a')[0].focus(); - }); - } - }); if (window.innerWidth > 960) { this.initSubPopper(); } @@ -497,7 +500,20 @@ Ext.extend(MODx.Layout, Ext.Viewport, { submenu.classList.add('active'); setTimeout(() => { submenu.querySelectorAll('a')[0].focus(); - }, 250); + }, 50); + var focusRestore = (e) => { + setTimeout(() => { + if (this.subNavOpen) { + return; + } + if (!submenu.contains(document.activeElement)) { + this.focusRestoreEls.pop()?.focus(); + this.hideMenu(); + window.removeEventListener('focusout', focusRestore); + } + }, 1); + }; + window.addEventListener('focusout', focusRestore); } this.hideSubMenu(); } @@ -507,10 +523,10 @@ Ext.extend(MODx.Layout, Ext.Viewport, { submenus[i].classList.remove('active'); } } - ,initSubPopper: function () { - var buttons = document.getElementById('modx-footer').querySelectorAll('.sub'); + var buttons = document.querySelectorAll('#modx-header .sub, #modx-footer .sub'); var position = window.innerWidth <= 960 ? 'bottom' : 'right'; + var _this = this; for (var i = 0; i < buttons.length; i++) { let popperInstance = null; @@ -552,34 +568,50 @@ Ext.extend(MODx.Layout, Ext.Viewport, { } } - function show(button, menu) { + function show(button) { + var menu = button.getElementsByTagName('ul')[0]; + button.classList.add('active'); menu.classList.add('active'); + _this.focusRestoreEls.push(button.querySelectorAll('a')[0]); + _this.subNavOpen = true; create(button, menu); + var focusRestore = (e) => { + requestAnimationFrame(() => { + if (!menu.contains(document.activeElement)) { + hide(button); + window.removeEventListener('focusout', focusRestore); + _this.focusRestoreEls.pop()?.parentNode?.nextSibling?.focus(); + } + }); + }; + window.addEventListener('focusout', focusRestore); } - function hide(menu) { - var buttons = menu.querySelectorAll('.sub'); + function hide(button) { + var parentmenu = button.closest('ul'); + button.classList.remove('active'); + var buttons = parentmenu.querySelectorAll('.sub'); for (var i = 0; i < buttons.length; i++) { var submenu = buttons[i].getElementsByTagName('ul')[0]; submenu.classList.remove('active'); submenu.removeAttribute('style'); buttons[i].classList.remove('active'); } + _this.subNavOpen = false; destroy(); } - - buttons[i].onmouseenter = function (e) { + buttons[i].addEventListener('mouseenter', function (e) { e.stopPropagation(); - var submenu = this.getElementsByTagName('ul')[0]; - this.classList.add('active'); - show(this, submenu); - }; - buttons[i].onmouseleave = function (e) { + show(this); + }); + buttons[i].querySelectorAll('a')[0].addEventListener('focus', function (e) { e.stopPropagation(); - var parentmenu = this.closest('ul'); - this.classList.remove('active'); - hide(parentmenu); - }; + show(this.parentNode); + }); + buttons[i].addEventListener('mouseleave', function (e) { + e.stopPropagation(); + hide(this); + }); } } diff --git a/manager/controllers/default/header.php b/manager/controllers/default/header.php index 9bec4ae5ef..6d862584d3 100644 --- a/manager/controllers/default/header.php +++ b/manager/controllers/default/header.php @@ -329,10 +329,10 @@ public function processSubMenus(&$output, array $menus = []) $attributes = ' href="?a='.$menu['action'].$menu['params'].'"'; } if (!empty($menu['handler'])) { - $attributes .= ' tabindex="0" onclick="{literal} '.str_replace('"','\'',$menu['handler']).'{/literal} "'; + $attributes .= ' onclick="{literal} '.str_replace('"','\'',$menu['handler']).'{/literal} "'; } $menu['icon'] = $menu['icon'] ?? ''; - $smTpl .= ''.$menu['text'].$menu['icon'].$description.''."\n"; + $smTpl .= ''.$menu['text'].$menu['icon'].$description.''."\n"; if (!empty($menu['children'])) { $smTpl .= '