From 75b5acd90329bb170712797becab033f096d742e Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Thu, 8 May 2025 17:00:11 +0100 Subject: [PATCH] MDL-85418 user: prevent default click event/scroll in user sub-menus. --- public/lib/amd/build/usermenu.min.js | 2 +- public/lib/amd/build/usermenu.min.js.map | 2 +- public/lib/amd/src/usermenu.js | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/lib/amd/build/usermenu.min.js b/public/lib/amd/build/usermenu.min.js index a7fbae531bd..e70a278f31b 100644 --- a/public/lib/amd/build/usermenu.min.js +++ b/public/lib/amd/build/usermenu.min.js @@ -6,6 +6,6 @@ define("core/usermenu",["exports","theme_boost/bootstrap/carousel","core/key_cod * @copyright 2021 Moodle * @author Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_carousel=(obj=_carousel)&&obj.__esModule?obj:{default:obj};const selectors_userMenu=".usermenu",selectors_userMenuCarousel=".usermenu #usermenu-carousel",selectors_userMenuCarouselItem=".usermenu #usermenu-carousel .carousel-item",selectors_userMenuCarouselItemActive=".usermenu #usermenu-carousel .carousel-item.active",selectors_userMenuCarouselNavigationLink=".usermenu #usermenu-carousel .carousel-navigation-link";var _default={init:()=>{(()=>{const userMenu=document.querySelector(selectors_userMenu),userMenuCarousel=document.querySelector(selectors_userMenuCarousel);userMenu.addEventListener("shown.bs.dropdown",(()=>{const activeCarouselItem=document.querySelector(selectors_userMenuCarouselItemActive);activeCarouselItem.focus(),userMenu.querySelectorAll(selectors_userMenuCarouselItem).forEach((element=>{element.classList.contains("active")||(element.style.width=activeCarouselItem.offsetWidth+"px",element.style.height=activeCarouselItem.offsetHeight+"px")}))})),userMenu.addEventListener("click",(e=>{e.target.matches(selectors_userMenuCarouselNavigationLink)&&carouselManagement(e)})),userMenu.addEventListener("keydown",(e=>{e.keyCode!==_key_codes.space&&e.keyCode!==_key_codes.enter||!e.target.matches(selectors_userMenuCarouselNavigationLink)||(e.preventDefault(),carouselManagement(e))}));const carouselManagement=e=>{e.stopPropagation();const targetedCarouselItemId=e.target.dataset.carouselTargetId,targetedCarouselItem=userMenu.querySelector("#"+targetedCarouselItemId),index=Array.from(targetedCarouselItem.parentNode.children).indexOf(targetedCarouselItem);_carousel.default.getOrCreateInstance(userMenuCarousel).to(index)};userMenu.addEventListener("hide.bs.dropdown",(()=>{_carousel.default.getOrCreateInstance(userMenuCarousel).to(0)})),null==userMenuCarousel||userMenuCarousel.addEventListener("slid.bs.carousel",(()=>{userMenu.querySelector(selectors_userMenuCarouselItemActive).focus()}))})()}};return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_carousel=(obj=_carousel)&&obj.__esModule?obj:{default:obj};const selectors_userMenu=".usermenu",selectors_userMenuCarousel=".usermenu #usermenu-carousel",selectors_userMenuCarouselItem=".usermenu #usermenu-carousel .carousel-item",selectors_userMenuCarouselItemActive=".usermenu #usermenu-carousel .carousel-item.active",selectors_userMenuCarouselNavigationLink=".usermenu #usermenu-carousel .carousel-navigation-link";var _default={init:()=>{(()=>{const userMenu=document.querySelector(selectors_userMenu),userMenuCarousel=document.querySelector(selectors_userMenuCarousel);userMenu.addEventListener("shown.bs.dropdown",(()=>{const activeCarouselItem=document.querySelector(selectors_userMenuCarouselItemActive);activeCarouselItem.focus(),userMenu.querySelectorAll(selectors_userMenuCarouselItem).forEach((element=>{element.classList.contains("active")||(element.style.width=activeCarouselItem.offsetWidth+"px",element.style.height=activeCarouselItem.offsetHeight+"px")}))})),userMenu.addEventListener("click",(e=>{e.target.matches(selectors_userMenuCarouselNavigationLink)&&carouselManagement(e)})),userMenu.addEventListener("keydown",(e=>{e.keyCode!==_key_codes.space&&e.keyCode!==_key_codes.enter||!e.target.matches(selectors_userMenuCarouselNavigationLink)||(e.preventDefault(),carouselManagement(e))}));const carouselManagement=e=>{e.stopPropagation(),e.preventDefault();const targetedCarouselItemId=e.target.dataset.carouselTargetId,targetedCarouselItem=userMenu.querySelector("#"+targetedCarouselItemId),index=Array.from(targetedCarouselItem.parentNode.children).indexOf(targetedCarouselItem);_carousel.default.getOrCreateInstance(userMenuCarousel).to(index)};userMenu.addEventListener("hide.bs.dropdown",(()=>{_carousel.default.getOrCreateInstance(userMenuCarousel).to(0)})),null==userMenuCarousel||userMenuCarousel.addEventListener("slid.bs.carousel",(()=>{userMenu.querySelector(selectors_userMenuCarouselItemActive).focus()}))})()}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=usermenu.min.js.map \ No newline at end of file diff --git a/public/lib/amd/build/usermenu.min.js.map b/public/lib/amd/build/usermenu.min.js.map index 27bc40e980e..b74e5e41854 100644 --- a/public/lib/amd/build/usermenu.min.js.map +++ b/public/lib/amd/build/usermenu.min.js.map @@ -1 +1 @@ -{"version":3,"file":"usermenu.min.js","sources":["../src/usermenu.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Initializes and handles events in the user menu.\n *\n * @module core/usermenu\n * @copyright 2021 Moodle\n * @author Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Carousel from 'theme_boost/bootstrap/carousel';\nimport {space, enter} from 'core/key_codes';\n\n/**\n * User menu constants.\n */\nconst selectors = {\n userMenu: '.usermenu',\n userMenuCarousel: '.usermenu #usermenu-carousel',\n userMenuCarouselItem: '.usermenu #usermenu-carousel .carousel-item',\n userMenuCarouselItemActive: '.usermenu #usermenu-carousel .carousel-item.active',\n userMenuCarouselNavigationLink: '.usermenu #usermenu-carousel .carousel-navigation-link',\n};\n\n/**\n * Register event listeners.\n */\nconst registerEventListeners = () => {\n const userMenu = document.querySelector(selectors.userMenu);\n const userMenuCarousel = document.querySelector(selectors.userMenuCarousel);\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n userMenu.addEventListener('shown.bs.dropdown', () => {\n const activeCarouselItem = document.querySelector(selectors.userMenuCarouselItemActive);\n // Set the focus on the active carousel item.\n activeCarouselItem.focus();\n\n userMenu.querySelectorAll(selectors.userMenuCarouselItem).forEach(element => {\n // Resize all non-active carousel items to match the height and width of the current active (main)\n // carousel item to avoid sizing inconsistencies. This has to be done once the dropdown menu is fully\n // displayed ('shown.bs.dropdown') as the offsetWidth and offsetHeight cannot be obtained when the\n // element is hidden.\n if (!element.classList.contains('active')) {\n element.style.width = activeCarouselItem.offsetWidth + 'px';\n element.style.height = activeCarouselItem.offsetHeight + 'px';\n }\n });\n });\n\n // Handle click events in the user menu.\n userMenu.addEventListener('click', (e) => {\n\n // Handle click event on the carousel navigation (control) links in the user menu.\n if (e.target.matches(selectors.userMenuCarouselNavigationLink)) {\n carouselManagement(e);\n }\n });\n\n userMenu.addEventListener('keydown', e => {\n // Handle keydown event on the carousel navigation (control) links in the user menu.\n if ((e.keyCode === space ||\n e.keyCode === enter) &&\n e.target.matches(selectors.userMenuCarouselNavigationLink)) {\n e.preventDefault();\n carouselManagement(e);\n }\n });\n\n /**\n * We do the same actions here even if the caller was a click or button press.\n *\n * @param {Event} e The triggering element and key presses etc.\n */\n const carouselManagement = e => {\n // By default the user menu dropdown element closes on a click event. This behaviour is not desirable\n // as we need to be able to navigate through the carousel items (submenus of the user menu) within the\n // user menu. Therefore, we need to prevent the propagation of this event and then manually call the\n // carousel transition.\n e.stopPropagation();\n // The id of the targeted carousel item.\n const targetedCarouselItemId = e.target.dataset.carouselTargetId;\n const targetedCarouselItem = userMenu.querySelector('#' + targetedCarouselItemId);\n // Get the position (index) of the targeted carousel item within the parent container element.\n const index = Array.from(targetedCarouselItem.parentNode.children).indexOf(targetedCarouselItem);\n // Navigate to the targeted carousel item.\n Carousel.getOrCreateInstance(userMenuCarousel).to(index);\n };\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n userMenu.addEventListener('hide.bs.dropdown', () => {\n // Reset the state once the user menu dropdown is closed and return back to the first (main) carousel item\n // if necessary.\n Carousel.getOrCreateInstance(userMenuCarousel).to(0);\n });\n\n // Handle the 'slid.bs.carousel' event (Fired when the carousel has completed its slide transition).\n userMenuCarousel?.addEventListener('slid.bs.carousel', () => {\n const activeCarouselItem = userMenu.querySelector(selectors.userMenuCarouselItemActive);\n // Set the focus on the newly activated carousel item.\n activeCarouselItem.focus();\n });\n};\n\n/**\n * Initialize the user menu.\n */\nconst init = () => {\n registerEventListeners();\n};\n\nexport default {\n init: init,\n};\n"],"names":["selectors","init","userMenu","document","querySelector","userMenuCarousel","addEventListener","activeCarouselItem","focus","querySelectorAll","forEach","element","classList","contains","style","width","offsetWidth","height","offsetHeight","e","target","matches","carouselManagement","keyCode","space","enter","preventDefault","stopPropagation","targetedCarouselItemId","dataset","carouselTargetId","targetedCarouselItem","index","Array","from","parentNode","children","indexOf","getOrCreateInstance","to","registerEventListeners"],"mappings":";;;;;;;;uJA8BMA,mBACQ,YADRA,2BAEgB,+BAFhBA,+BAGoB,8CAHpBA,qCAI0B,qDAJ1BA,yCAK8B,sEAyFrB,CACXC,KALS,KA/EkB,YACrBC,SAAWC,SAASC,cAAcJ,oBAClCK,iBAAmBF,SAASC,cAAcJ,4BAGhDE,SAASI,iBAAiB,qBAAqB,WACrCC,mBAAqBJ,SAASC,cAAcJ,sCAElDO,mBAAmBC,QAEnBN,SAASO,iBAAiBT,gCAAgCU,SAAQC,UAKzDA,QAAQC,UAAUC,SAAS,YAC5BF,QAAQG,MAAMC,MAAQR,mBAAmBS,YAAc,KACvDL,QAAQG,MAAMG,OAASV,mBAAmBW,aAAe,YAMrEhB,SAASI,iBAAiB,SAAUa,IAG5BA,EAAEC,OAAOC,QAAQrB,2CACjBsB,mBAAmBH,MAI3BjB,SAASI,iBAAiB,WAAWa,IAE5BA,EAAEI,UAAYC,kBACfL,EAAEI,UAAYE,mBACdN,EAAEC,OAAOC,QAAQrB,4CACjBmB,EAAEO,iBACFJ,mBAAmBH,aASrBG,mBAAqBH,IAKvBA,EAAEQ,wBAEIC,uBAAyBT,EAAEC,OAAOS,QAAQC,iBAC1CC,qBAAuB7B,SAASE,cAAc,IAAMwB,wBAEpDI,MAAQC,MAAMC,KAAKH,qBAAqBI,WAAWC,UAAUC,QAAQN,wCAElEO,oBAAoBjC,kBAAkBkC,GAAGP,QAItD9B,SAASI,iBAAiB,oBAAoB,uBAGjCgC,oBAAoBjC,kBAAkBkC,GAAG,MAItDlC,MAAAA,kBAAAA,iBAAkBC,iBAAiB,oBAAoB,KACxBJ,SAASE,cAAcJ,sCAE/BQ,YAQvBgC"} \ No newline at end of file +{"version":3,"file":"usermenu.min.js","sources":["../src/usermenu.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Initializes and handles events in the user menu.\n *\n * @module core/usermenu\n * @copyright 2021 Moodle\n * @author Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Carousel from 'theme_boost/bootstrap/carousel';\nimport {space, enter} from 'core/key_codes';\n\n/**\n * User menu constants.\n */\nconst selectors = {\n userMenu: '.usermenu',\n userMenuCarousel: '.usermenu #usermenu-carousel',\n userMenuCarouselItem: '.usermenu #usermenu-carousel .carousel-item',\n userMenuCarouselItemActive: '.usermenu #usermenu-carousel .carousel-item.active',\n userMenuCarouselNavigationLink: '.usermenu #usermenu-carousel .carousel-navigation-link',\n};\n\n/**\n * Register event listeners.\n */\nconst registerEventListeners = () => {\n const userMenu = document.querySelector(selectors.userMenu);\n const userMenuCarousel = document.querySelector(selectors.userMenuCarousel);\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n userMenu.addEventListener('shown.bs.dropdown', () => {\n const activeCarouselItem = document.querySelector(selectors.userMenuCarouselItemActive);\n // Set the focus on the active carousel item.\n activeCarouselItem.focus();\n\n userMenu.querySelectorAll(selectors.userMenuCarouselItem).forEach(element => {\n // Resize all non-active carousel items to match the height and width of the current active (main)\n // carousel item to avoid sizing inconsistencies. This has to be done once the dropdown menu is fully\n // displayed ('shown.bs.dropdown') as the offsetWidth and offsetHeight cannot be obtained when the\n // element is hidden.\n if (!element.classList.contains('active')) {\n element.style.width = activeCarouselItem.offsetWidth + 'px';\n element.style.height = activeCarouselItem.offsetHeight + 'px';\n }\n });\n });\n\n // Handle click events in the user menu.\n userMenu.addEventListener('click', (e) => {\n\n // Handle click event on the carousel navigation (control) links in the user menu.\n if (e.target.matches(selectors.userMenuCarouselNavigationLink)) {\n carouselManagement(e);\n }\n });\n\n userMenu.addEventListener('keydown', e => {\n // Handle keydown event on the carousel navigation (control) links in the user menu.\n if ((e.keyCode === space ||\n e.keyCode === enter) &&\n e.target.matches(selectors.userMenuCarouselNavigationLink)) {\n e.preventDefault();\n carouselManagement(e);\n }\n });\n\n /**\n * We do the same actions here even if the caller was a click or button press.\n *\n * @param {Event} e The triggering element and key presses etc.\n */\n const carouselManagement = e => {\n // By default the user menu dropdown element closes on a click event. This behaviour is not desirable\n // as we need to be able to navigate through the carousel items (submenus of the user menu) within the\n // user menu. Therefore, we need to prevent the propagation of this event and then manually call the\n // carousel transition.\n e.stopPropagation();\n e.preventDefault();\n\n // The id of the targeted carousel item.\n const targetedCarouselItemId = e.target.dataset.carouselTargetId;\n const targetedCarouselItem = userMenu.querySelector('#' + targetedCarouselItemId);\n // Get the position (index) of the targeted carousel item within the parent container element.\n const index = Array.from(targetedCarouselItem.parentNode.children).indexOf(targetedCarouselItem);\n // Navigate to the targeted carousel item.\n Carousel.getOrCreateInstance(userMenuCarousel).to(index);\n };\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n userMenu.addEventListener('hide.bs.dropdown', () => {\n // Reset the state once the user menu dropdown is closed and return back to the first (main) carousel item\n // if necessary.\n Carousel.getOrCreateInstance(userMenuCarousel).to(0);\n });\n\n // Handle the 'slid.bs.carousel' event (Fired when the carousel has completed its slide transition).\n userMenuCarousel?.addEventListener('slid.bs.carousel', () => {\n const activeCarouselItem = userMenu.querySelector(selectors.userMenuCarouselItemActive);\n // Set the focus on the newly activated carousel item.\n activeCarouselItem.focus();\n });\n};\n\n/**\n * Initialize the user menu.\n */\nconst init = () => {\n registerEventListeners();\n};\n\nexport default {\n init: init,\n};\n"],"names":["selectors","init","userMenu","document","querySelector","userMenuCarousel","addEventListener","activeCarouselItem","focus","querySelectorAll","forEach","element","classList","contains","style","width","offsetWidth","height","offsetHeight","e","target","matches","carouselManagement","keyCode","space","enter","preventDefault","stopPropagation","targetedCarouselItemId","dataset","carouselTargetId","targetedCarouselItem","index","Array","from","parentNode","children","indexOf","getOrCreateInstance","to","registerEventListeners"],"mappings":";;;;;;;;uJA8BMA,mBACQ,YADRA,2BAEgB,+BAFhBA,+BAGoB,8CAHpBA,qCAI0B,qDAJ1BA,yCAK8B,sEA2FrB,CACXC,KALS,KAjFkB,YACrBC,SAAWC,SAASC,cAAcJ,oBAClCK,iBAAmBF,SAASC,cAAcJ,4BAGhDE,SAASI,iBAAiB,qBAAqB,WACrCC,mBAAqBJ,SAASC,cAAcJ,sCAElDO,mBAAmBC,QAEnBN,SAASO,iBAAiBT,gCAAgCU,SAAQC,UAKzDA,QAAQC,UAAUC,SAAS,YAC5BF,QAAQG,MAAMC,MAAQR,mBAAmBS,YAAc,KACvDL,QAAQG,MAAMG,OAASV,mBAAmBW,aAAe,YAMrEhB,SAASI,iBAAiB,SAAUa,IAG5BA,EAAEC,OAAOC,QAAQrB,2CACjBsB,mBAAmBH,MAI3BjB,SAASI,iBAAiB,WAAWa,IAE5BA,EAAEI,UAAYC,kBACfL,EAAEI,UAAYE,mBACdN,EAAEC,OAAOC,QAAQrB,4CACjBmB,EAAEO,iBACFJ,mBAAmBH,aASrBG,mBAAqBH,IAKvBA,EAAEQ,kBACFR,EAAEO,uBAGIE,uBAAyBT,EAAEC,OAAOS,QAAQC,iBAC1CC,qBAAuB7B,SAASE,cAAc,IAAMwB,wBAEpDI,MAAQC,MAAMC,KAAKH,qBAAqBI,WAAWC,UAAUC,QAAQN,wCAElEO,oBAAoBjC,kBAAkBkC,GAAGP,QAItD9B,SAASI,iBAAiB,oBAAoB,uBAGjCgC,oBAAoBjC,kBAAkBkC,GAAG,MAItDlC,MAAAA,kBAAAA,iBAAkBC,iBAAiB,oBAAoB,KACxBJ,SAASE,cAAcJ,sCAE/BQ,YAQvBgC"} \ No newline at end of file diff --git a/public/lib/amd/src/usermenu.js b/public/lib/amd/src/usermenu.js index 4cb4e497c25..7b876d27bf9 100644 --- a/public/lib/amd/src/usermenu.js +++ b/public/lib/amd/src/usermenu.js @@ -91,6 +91,8 @@ const registerEventListeners = () => { // user menu. Therefore, we need to prevent the propagation of this event and then manually call the // carousel transition. e.stopPropagation(); + e.preventDefault(); + // The id of the targeted carousel item. const targetedCarouselItemId = e.target.dataset.carouselTargetId; const targetedCarouselItem = userMenu.querySelector('#' + targetedCarouselItemId);