diff --git a/amd/build/smartmenu.min.js b/amd/build/smartmenu.min.js index 6eca61394f2..86dc478dbe1 100644 --- a/amd/build/smartmenu.min.js +++ b/amd/build/smartmenu.min.js @@ -5,6 +5,6 @@ * @copyright 2023 bdecent GmbH * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("theme_boost_union/smartmenu",["jquery","core/moremenu"],(function($){const hideSubmenus=target=>{var visibleMenu=document.querySelectorAll("nav.moremenu .dropdown-submenu.show");null!==visibleMenu&&visibleMenu.forEach((el=>{el!=target&&el.classList.remove("show")}))},moveOutMoreMenu=navMenu=>{if(null!==navMenu){var outMenus=navMenu.querySelectorAll(".dropdownmoremenu .force-menu-out"),menuslist=[];if(null!==outMenus){outMenus.forEach((menu=>{menu.querySelector("a").classList.remove("dropdown-item"),menu.querySelector("a").classList.add("nav-link"),menuslist.push(menu),menu.parentNode.removeChild(menu)}));var length=menuslist.length,newPosition=navMenu.children.length-1-length||0;menuslist.forEach((menu=>navMenu.insertBefore(menu,navMenu.children[newPosition]))),window.dispatchEvent(new Event("resize"))}}};return{init:()=>{(()=>{var submenu=document.querySelectorAll("nav.moremenu .dropdown-submenu");null!==submenu&&submenu.forEach((item=>{item.addEventListener("click",(e=>{var target=e.currentTarget;hideSubmenus(target),target.classList.toggle("show"),e.stopPropagation()}))})),$(document).on("hidden.bs.dropdown",(e=>{var submenus=e.relatedTarget.parentNode.querySelectorAll(".dropdown-submenu.show");null!==submenus&&submenus.forEach((e=>e.classList.remove("show")))})),document.addEventListener("click",(e=>{var dropdown=e.target.closest(".dropdownmoremenu"),subMenu=e.target.closest(".dropdown-submenu");dropdown&&null!==subMenu&&(dropdown.querySelectorAll(".dropdown-submenu.show").forEach((menu=>{menu.classList.remove("show")})),subMenu.classList.toggle("show"));var dropdownMenu=e.target.parentNode.classList.contains("dropdown");dropdown&&dropdownMenu&&dropdown.querySelectorAll(".dropdown-menu.show").forEach((menu=>{menu!=e.target.closest(".dropdown-menu")&&menu.classList.remove("show")}))}),!0);var helpIcon=document.querySelectorAll(".moremenu .dropdown .menu-helpicon");null!==helpIcon&&helpIcon.forEach((icon=>{icon.addEventListener("click",(e=>{e.stopPropagation()}))}))})(),(()=>{var cards=document.querySelectorAll(".card-dropdown.card-overflow-no-wrap");if(null!==cards){var scrollStart,scrollMoved;let startPos,scrollPos;cards.forEach((card=>{var scrollElement=card.querySelector(".dropdown-menu");scrollElement.addEventListener("mousedown",(e=>{scrollStart=!0;var target=e.currentTarget.querySelector(".card-block-wrapper");startPos=e.pageX,scrollPos=target.scrollLeft})),scrollElement.addEventListener("mousemove",(e=>{if(e.preventDefault(),!scrollStart)return;scrollMoved=!0;var target=e.currentTarget.querySelector(".card-block-wrapper");const scroll=e.pageX-startPos;target.scrollLeft=scrollPos-scroll})),scrollElement.addEventListener("click",(e=>{scrollMoved&&(e.preventDefault(),scrollMoved=!1),e.stopPropagation()})),scrollElement.addEventListener("mouseleave",(()=>{scrollStart=!1,scrollMoved=!1})),scrollElement.addEventListener("mouseup",(()=>{scrollStart=!1}))}))}})(),(()=>{var primaryNav=document.querySelector(".primary-navigation ul.more-nav");moveOutMoreMenu(primaryNav);var menuBar=document.querySelector("nav.menubar ul.more-nav");moveOutMoreMenu(menuBar)})()}}})); +define("theme_boost_union/smartmenu",["jquery","core/moremenu","theme_boost_union/submenu"],(function($,MoreMenu,SubMenu){const moveOutMoreMenu=navMenu=>{if(null!==navMenu){var outMenus=navMenu.querySelectorAll(".dropdownmoremenu .force-menu-out"),menuslist=[];if(null!==outMenus){outMenus.forEach((menu=>{menu.querySelector("a").classList.remove("dropdown-item"),menu.querySelector("a").classList.add("nav-link"),menuslist.push(menu),menu.parentNode.removeChild(menu)}));var length=menuslist.length,newPosition=navMenu.children.length-1-length||0;menuslist.forEach((menu=>navMenu.insertBefore(menu,navMenu.children[newPosition]))),window.dispatchEvent(new Event("resize"))}}};return{init:()=>{SubMenu.init(),(()=>{var cards=document.querySelectorAll(".card-dropdown.card-overflow-no-wrap");if(null!==cards){var scrollStart,scrollMoved;let startPos,scrollPos;cards.forEach((card=>{var scrollElement=card.querySelector(".dropdown-menu");scrollElement.addEventListener("mousedown",(e=>{scrollStart=!0;var target=e.currentTarget.querySelector(".card-block-wrapper");startPos=e.pageX,scrollPos=target.scrollLeft})),scrollElement.addEventListener("mousemove",(e=>{if(e.preventDefault(),!scrollStart)return;scrollMoved=!0;var target=e.currentTarget.querySelector(".card-block-wrapper");const scroll=e.pageX-startPos;target.scrollLeft=scrollPos-scroll})),scrollElement.addEventListener("click",(e=>{scrollMoved&&(e.preventDefault(),scrollMoved=!1),e.stopPropagation()})),scrollElement.addEventListener("mouseleave",(()=>{scrollStart=!1,scrollMoved=!1})),scrollElement.addEventListener("mouseup",(()=>{scrollStart=!1}))}))}})(),(()=>{var primaryNav=document.querySelector(".primary-navigation ul.more-nav");moveOutMoreMenu(primaryNav);var menuBar=document.querySelector("nav.menubar ul.more-nav");moveOutMoreMenu(menuBar)})()}}})); //# sourceMappingURL=smartmenu.min.js.map \ No newline at end of file diff --git a/amd/build/smartmenu.min.js.map b/amd/build/smartmenu.min.js.map index 6877d2a276f..b8462cba710 100644 --- a/amd/build/smartmenu.min.js.map +++ b/amd/build/smartmenu.min.js.map @@ -1 +1 @@ -{"version":3,"file":"smartmenu.min.js","sources":["../src/smartmenu.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 * Theme Boost Union - JS for smart menu to realize the third level submenu support.\n *\n * @module theme_boost_union/smartmenu\n * @copyright 2023 bdecent GmbH \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine([\"jquery\", \"core/moremenu\"], function($) {\n /**\n * Implement the second level of submenu support.\n * Find the submenus inside the dropdown, add an event listener for click event which - on the click - shows the submenu list.\n */\n const addSubmenu = () => {\n // Fetch the list of submenus from moremenu.\n var submenu = document.querySelectorAll('nav.moremenu .dropdown-submenu');\n if (submenu !== null) {\n submenu.forEach((item) => {\n // Add event listener to show the submenu on click.\n item.addEventListener('click', (e) => {\n var target = e.currentTarget;\n // Hide the shown menu.\n hideSubmenus(target);\n target.classList.toggle('show');\n // Prevent hiding the parent menu.\n e.stopPropagation();\n });\n });\n }\n\n // Hide the submenus when its parent dropdown is hidden.\n $(document).on('hidden.bs.dropdown', e => {\n var target = e.relatedTarget.parentNode;\n var submenus = target.querySelectorAll('.dropdown-submenu.show');\n if (submenus !== null) {\n submenus.forEach((e) => e.classList.remove('show'));\n }\n });\n\n // Provide the third level menu support inside the more menu.\n // StopPropagation used in the toggledropdown method on Moremenu.js, It prevents the opening of the third level menus.\n // Used the document delegation method to fetch the click on moremenu and submenu.\n document.addEventListener('click', (e) => {\n var dropdown = e.target.closest('.dropdownmoremenu');\n var subMenu = e.target.closest('.dropdown-submenu');\n if (dropdown && subMenu !== null) {\n // Hide the previously opend submenus. before open the new one.\n dropdown.querySelectorAll('.dropdown-submenu.show').forEach((menu) => {\n menu.classList.remove('show');\n });\n subMenu.classList.toggle('show');\n }\n\n // Hide the opened menus before open the other menus.\n var dropdownMenu = e.target.parentNode.classList.contains('dropdown');\n if (dropdown && dropdownMenu) {\n dropdown.querySelectorAll('.dropdown-menu.show').forEach((menu) => {\n // Hide the opened menus in more menu.\n if (menu != e.target.closest('.dropdown-menu')) {\n menu.classList.remove('show');\n }\n });\n }\n\n }, true);\n\n // Prevent the closing of dropdown during the click on help icon.\n var helpIcon = document.querySelectorAll('.moremenu .dropdown .menu-helpicon');\n if (helpIcon !== null) {\n helpIcon.forEach((icon) => {\n icon.addEventListener('click', (e) => {\n e.stopPropagation();\n });\n });\n }\n };\n\n /**\n * Hide visible submenus before display new submenu.\n *\n * @param {Selector} target\n */\n const hideSubmenus = (target) => {\n var visibleMenu = document.querySelectorAll('nav.moremenu .dropdown-submenu.show');\n if (visibleMenu !== null) {\n visibleMenu.forEach((el) => {\n if (el != target) {\n el.classList.remove('show');\n }\n });\n }\n };\n\n /**\n * Make the no wrapped card menus scroll using swipe or drag.\n */\n const cardScroll = () => {\n var cards = document.querySelectorAll('.card-dropdown.card-overflow-no-wrap');\n if (cards !== null) {\n var scrollStart; // Verify the mouse is clicked and still in click not released.\n var scrollMoved; // Prevent the click on scrolling.\n let startPos, scrollPos;\n\n cards.forEach((card) => {\n var scrollElement = card.querySelector('.dropdown-menu');\n\n scrollElement.addEventListener('mousedown', (e) => {\n scrollStart = true;\n var target = e.currentTarget.querySelector('.card-block-wrapper');\n startPos = e.pageX;\n scrollPos = target.scrollLeft;\n });\n\n scrollElement.addEventListener('mousemove', (e) => {\n e.preventDefault();\n if (!scrollStart) {\n return;\n }\n scrollMoved = true;\n var target = e.currentTarget.querySelector('.card-block-wrapper');\n const scroll = e.pageX - startPos;\n target.scrollLeft = scrollPos - scroll;\n });\n\n scrollElement.addEventListener('click', (e) => {\n if (scrollMoved) {\n e.preventDefault();\n scrollMoved = false;\n }\n e.stopPropagation();\n });\n scrollElement.addEventListener('mouseleave', () => {\n scrollStart = false;\n scrollMoved = false;\n });\n scrollElement.addEventListener('mouseup', () => {\n scrollStart = false;\n });\n });\n }\n };\n\n /**\n * Move the menubar and primary navigation menu items from more menu.\n */\n const autoCollapse = () => {\n var primaryNav = document.querySelector('.primary-navigation ul.more-nav');\n moveOutMoreMenu(primaryNav);\n\n var menuBar = document.querySelector('nav.menubar ul.more-nav');\n moveOutMoreMenu(menuBar);\n };\n\n /**\n * Move the items from more menu, items which is set to force outside more menu.\n * Remove those items from more menu and insert the menu before the last normal item.\n * Find the length and children's length to insert the out menus in that positions.\n * Rerun the more menu it will more the other normal menus into more menu to fix the alignmenu issue.\n *\n * @param {HTMLElement} navMenu The navbar container.\n */\n const moveOutMoreMenu = (navMenu) => {\n\n if (navMenu === null) {\n return;\n }\n\n var outMenus = navMenu.querySelectorAll('.dropdownmoremenu .force-menu-out');\n var menuslist = [];\n\n if (outMenus === null) {\n return;\n }\n\n outMenus.forEach((menu) => {\n menu.querySelector('a').classList.remove('dropdown-item');\n menu.querySelector('a').classList.add('nav-link');\n\n menuslist.push(menu);\n menu.parentNode.removeChild(menu);\n });\n // Find the length and children's length to insert the out menus in that positions.\n var length = menuslist.length;\n var navLength = navMenu.children.length - 1; // Remove more menu.\n var newPosition = navLength - length || 0;\n // Insert the stored menus before the more menu.\n menuslist.forEach((menu) => navMenu.insertBefore(menu, navMenu.children[newPosition]));\n window.dispatchEvent(new Event('resize')); // Dispatch the resize event to create more menu.\n };\n\n return {\n init: () => {\n addSubmenu();\n cardScroll();\n autoCollapse();\n }\n };\n});\n"],"names":["define","$","hideSubmenus","target","visibleMenu","document","querySelectorAll","forEach","el","classList","remove","moveOutMoreMenu","navMenu","outMenus","menuslist","menu","querySelector","add","push","parentNode","removeChild","length","newPosition","children","insertBefore","window","dispatchEvent","Event","init","submenu","item","addEventListener","e","currentTarget","toggle","stopPropagation","on","submenus","relatedTarget","dropdown","closest","subMenu","dropdownMenu","contains","helpIcon","icon","addSubmenu","cards","scrollStart","scrollMoved","startPos","scrollPos","card","scrollElement","pageX","scrollLeft","preventDefault","scroll","cardScroll","primaryNav","menuBar","autoCollapse"],"mappings":";;;;;;;AAuBAA,qCAAO,CAAC,SAAU,kBAAkB,SAASC,SA0EnCC,aAAgBC,aACdC,YAAcC,SAASC,iBAAiB,uCACxB,OAAhBF,aACAA,YAAYG,SAASC,KACbA,IAAML,QACNK,GAAGC,UAAUC,OAAO,YA0E9BC,gBAAmBC,aAEL,OAAZA,aAIAC,SAAWD,QAAQN,iBAAiB,qCACpCQ,UAAY,MAEC,OAAbD,UAIJA,SAASN,SAASQ,OACdA,KAAKC,cAAc,KAAKP,UAAUC,OAAO,iBACzCK,KAAKC,cAAc,KAAKP,UAAUQ,IAAI,YAEtCH,UAAUI,KAAKH,MACfA,KAAKI,WAAWC,YAAYL,aAG5BM,OAASP,UAAUO,OAEnBC,YADYV,QAAQW,SAASF,OAAS,EACZA,QAAU,EAExCP,UAAUP,SAASQ,MAASH,QAAQY,aAAaT,KAAMH,QAAQW,SAASD,gBACxEG,OAAOC,cAAc,IAAIC,MAAM,oBAG5B,CACHC,KAAM,KAlLS,UAEXC,QAAUxB,SAASC,iBAAiB,kCACxB,OAAZuB,SACAA,QAAQtB,SAASuB,OAEbA,KAAKC,iBAAiB,SAAUC,QACxB7B,OAAS6B,EAAEC,cAEf/B,aAAaC,QACbA,OAAOM,UAAUyB,OAAO,QAExBF,EAAEG,wBAMdlC,EAAEI,UAAU+B,GAAG,sBAAsBJ,QAE7BK,SADSL,EAAEM,cAAcnB,WACPb,iBAAiB,0BACtB,OAAb+B,UACAA,SAAS9B,SAASyB,GAAMA,EAAEvB,UAAUC,OAAO,aAOnDL,SAAS0B,iBAAiB,SAAUC,QAC5BO,SAAWP,EAAE7B,OAAOqC,QAAQ,qBAC5BC,QAAUT,EAAE7B,OAAOqC,QAAQ,qBAC3BD,UAAwB,OAAZE,UAEZF,SAASjC,iBAAiB,0BAA0BC,SAASQ,OACzDA,KAAKN,UAAUC,OAAO,WAE1B+B,QAAQhC,UAAUyB,OAAO,aAIzBQ,aAAeV,EAAE7B,OAAOgB,WAAWV,UAAUkC,SAAS,YACtDJ,UAAYG,cACZH,SAASjC,iBAAiB,uBAAuBC,SAASQ,OAElDA,MAAQiB,EAAE7B,OAAOqC,QAAQ,mBACzBzB,KAAKN,UAAUC,OAAO,cAKnC,OAGCkC,SAAWvC,SAASC,iBAAiB,sCACxB,OAAbsC,UACAA,SAASrC,SAASsC,OACdA,KAAKd,iBAAiB,SAAUC,IAC5BA,EAAEG,yBAyHVW,GAhGW,UACXC,MAAQ1C,SAASC,iBAAiB,2CACxB,OAAVyC,MAAgB,KACZC,YACAC,gBACAC,SAAUC,UAEdJ,MAAMxC,SAAS6C,WACPC,cAAgBD,KAAKpC,cAAc,kBAEvCqC,cAActB,iBAAiB,aAAcC,IACzCgB,aAAc,MACV7C,OAAS6B,EAAEC,cAAcjB,cAAc,uBAC3CkC,SAAWlB,EAAEsB,MACbH,UAAYhD,OAAOoD,cAGvBF,cAActB,iBAAiB,aAAcC,OACzCA,EAAEwB,kBACGR,mBAGLC,aAAc,MACV9C,OAAS6B,EAAEC,cAAcjB,cAAc,6BACrCyC,OAASzB,EAAEsB,MAAQJ,SACzB/C,OAAOoD,WAAaJ,UAAYM,UAGpCJ,cAActB,iBAAiB,SAAUC,IACjCiB,cACAjB,EAAEwB,iBACFP,aAAc,GAElBjB,EAAEG,qBAENkB,cAActB,iBAAiB,cAAc,KACzCiB,aAAc,EACdC,aAAc,KAElBI,cAActB,iBAAiB,WAAW,KACtCiB,aAAc,UAyDtBU,GAhDa,UACbC,WAAatD,SAASW,cAAc,mCACxCL,gBAAgBgD,gBAEZC,QAAUvD,SAASW,cAAc,2BACrCL,gBAAgBiD,UA4CZC"} \ No newline at end of file +{"version":3,"file":"smartmenu.min.js","sources":["../src/smartmenu.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\r\n//\r\n// Moodle is free software: you can redistribute it and/or modify\r\n// it under the terms of the GNU General Public License as published by\r\n// the Free Software Foundation, either version 3 of the License, or\r\n// (at your option) any later version.\r\n//\r\n// Moodle is distributed in the hope that it will be useful,\r\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\n// GNU General Public License for more details.\r\n//\r\n// You should have received a copy of the GNU General Public License\r\n// along with Moodle. If not, see .\r\n\r\n/**\r\n * Theme Boost Union - JS for smart menu to realize the third level submenu support.\r\n *\r\n * @module theme_boost_union/smartmenu\r\n * @copyright 2023 bdecent GmbH \r\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\r\n */\r\n\r\ndefine([\"jquery\", \"core/moremenu\", \"theme_boost_union/submenu\"], function($, MoreMenu, SubMenu) {\r\n\r\n /**\r\n * Make the no wrapped card menus scroll using swipe or drag.\r\n */\r\n const cardScroll = () => {\r\n var cards = document.querySelectorAll('.card-dropdown.card-overflow-no-wrap');\r\n if (cards !== null) {\r\n var scrollStart; // Verify the mouse is clicked and still in click not released.\r\n var scrollMoved; // Prevent the click on scrolling.\r\n let startPos, scrollPos;\r\n\r\n cards.forEach((card) => {\r\n var scrollElement = card.querySelector('.dropdown-menu');\r\n\r\n scrollElement.addEventListener('mousedown', (e) => {\r\n scrollStart = true;\r\n var target = e.currentTarget.querySelector('.card-block-wrapper');\r\n startPos = e.pageX;\r\n scrollPos = target.scrollLeft;\r\n });\r\n\r\n scrollElement.addEventListener('mousemove', (e) => {\r\n e.preventDefault();\r\n if (!scrollStart) {\r\n return;\r\n }\r\n scrollMoved = true;\r\n var target = e.currentTarget.querySelector('.card-block-wrapper');\r\n const scroll = e.pageX - startPos;\r\n target.scrollLeft = scrollPos - scroll;\r\n });\r\n\r\n scrollElement.addEventListener('click', (e) => {\r\n if (scrollMoved) {\r\n e.preventDefault();\r\n scrollMoved = false;\r\n }\r\n e.stopPropagation();\r\n });\r\n scrollElement.addEventListener('mouseleave', () => {\r\n scrollStart = false;\r\n scrollMoved = false;\r\n });\r\n scrollElement.addEventListener('mouseup', () => {\r\n scrollStart = false;\r\n });\r\n });\r\n }\r\n };\r\n\r\n /**\r\n * Move the menubar and primary navigation menu items from more menu.\r\n */\r\n const autoCollapse = () => {\r\n var primaryNav = document.querySelector('.primary-navigation ul.more-nav');\r\n moveOutMoreMenu(primaryNav);\r\n\r\n var menuBar = document.querySelector('nav.menubar ul.more-nav');\r\n moveOutMoreMenu(menuBar);\r\n };\r\n\r\n /**\r\n * Move the items from more menu, items which is set to force outside more menu.\r\n * Remove those items from more menu and insert the menu before the last normal item.\r\n * Find the length and children's length to insert the out menus in that positions.\r\n * Rerun the more menu it will more the other normal menus into more menu to fix the alignmenu issue.\r\n *\r\n * @param {HTMLElement} navMenu The navbar container.\r\n */\r\n const moveOutMoreMenu = (navMenu) => {\r\n\r\n if (navMenu === null) {\r\n return;\r\n }\r\n\r\n var outMenus = navMenu.querySelectorAll('.dropdownmoremenu .force-menu-out');\r\n var menuslist = [];\r\n\r\n if (outMenus === null) {\r\n return;\r\n }\r\n\r\n outMenus.forEach((menu) => {\r\n menu.querySelector('a').classList.remove('dropdown-item');\r\n menu.querySelector('a').classList.add('nav-link');\r\n\r\n menuslist.push(menu);\r\n menu.parentNode.removeChild(menu);\r\n });\r\n // Find the length and children's length to insert the out menus in that positions.\r\n var length = menuslist.length;\r\n var navLength = navMenu.children.length - 1; // Remove more menu.\r\n var newPosition = navLength - length || 0;\r\n // Insert the stored menus before the more menu.\r\n menuslist.forEach((menu) => navMenu.insertBefore(menu, navMenu.children[newPosition]));\r\n window.dispatchEvent(new Event('resize')); // Dispatch the resize event to create more menu.\r\n };\r\n\r\n return {\r\n init: () => {\r\n SubMenu.init();\r\n cardScroll();\r\n autoCollapse();\r\n }\r\n };\r\n});\r\n"],"names":["define","$","MoreMenu","SubMenu","moveOutMoreMenu","navMenu","outMenus","querySelectorAll","menuslist","forEach","menu","querySelector","classList","remove","add","push","parentNode","removeChild","length","newPosition","children","insertBefore","window","dispatchEvent","Event","init","cards","document","scrollStart","scrollMoved","startPos","scrollPos","card","scrollElement","addEventListener","e","target","currentTarget","pageX","scrollLeft","preventDefault","scroll","stopPropagation","cardScroll","primaryNav","menuBar","autoCollapse"],"mappings":";;;;;;;AAuBAA,qCAAO,CAAC,SAAU,gBAAiB,8BAA8B,SAASC,EAAGC,SAAUC,eAsE7EC,gBAAmBC,aAEL,OAAZA,aAIAC,SAAWD,QAAQE,iBAAiB,qCACpCC,UAAY,MAEC,OAAbF,UAIJA,SAASG,SAASC,OACdA,KAAKC,cAAc,KAAKC,UAAUC,OAAO,iBACzCH,KAAKC,cAAc,KAAKC,UAAUE,IAAI,YAEtCN,UAAUO,KAAKL,MACfA,KAAKM,WAAWC,YAAYP,aAG5BQ,OAASV,UAAUU,OAEnBC,YADYd,QAAQe,SAASF,OAAS,EACZA,QAAU,EAExCV,UAAUC,SAASC,MAASL,QAAQgB,aAAaX,KAAML,QAAQe,SAASD,gBACxEG,OAAOC,cAAc,IAAIC,MAAM,oBAG5B,CACHC,KAAM,KACFtB,QAAQsB,OAhGG,UACXC,MAAQC,SAASpB,iBAAiB,2CACxB,OAAVmB,MAAgB,KACZE,YACAC,gBACAC,SAAUC,UAEdL,MAAMjB,SAASuB,WACPC,cAAgBD,KAAKrB,cAAc,kBAEvCsB,cAAcC,iBAAiB,aAAcC,IACzCP,aAAc,MACVQ,OAASD,EAAEE,cAAc1B,cAAc,uBAC3CmB,SAAWK,EAAEG,MACbP,UAAYK,OAAOG,cAGvBN,cAAcC,iBAAiB,aAAcC,OACzCA,EAAEK,kBACGZ,mBAGLC,aAAc,MACVO,OAASD,EAAEE,cAAc1B,cAAc,6BACrC8B,OAASN,EAAEG,MAAQR,SACzBM,OAAOG,WAAaR,UAAYU,UAGpCR,cAAcC,iBAAiB,SAAUC,IACjCN,cACAM,EAAEK,iBACFX,aAAc,GAElBM,EAAEO,qBAENT,cAAcC,iBAAiB,cAAc,KACzCN,aAAc,EACdC,aAAc,KAElBI,cAAcC,iBAAiB,WAAW,KACtCN,aAAc,UAyDtBe,GAhDa,UACbC,WAAajB,SAAShB,cAAc,mCACxCP,gBAAgBwC,gBAEZC,QAAUlB,SAAShB,cAAc,2BACrCP,gBAAgByC,UA4CZC"} \ No newline at end of file diff --git a/amd/build/submenu.min.js b/amd/build/submenu.min.js new file mode 100644 index 00000000000..d7faa05c5d2 --- /dev/null +++ b/amd/build/submenu.min.js @@ -0,0 +1,11 @@ +define("theme_boost_union/submenu",["exports","jquery","core/key_codes"],(function(_exports,_jquery,_key_codes){var obj; +/** + * Initializes and handles events in the user menu. + * + * @module core/smartMenu + * @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,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const selectors_smartMenu=".theme-boost-union-smartmenu-carousel",selectors_smartMenuCarousel="#smartmenu-carousel",selectors_smartMenuCarouselItemActive="#smartmenu-carousel .carousel-item.active",selectors_smartMenuCarouselNavigationLink="#smartmenu-carousel .carousel-navigation-link";var _default={init:()=>{const smartMenus=document.querySelectorAll(selectors_smartMenu);void 0!==smartMenus&&null!==smartMenus.length&&smartMenus.forEach((e=>(smartMenu=>{var smartMenuDropDown=smartMenu.querySelector(".dropdown-menu");new MutationObserver((function(){if(smartMenuDropDown.classList.contains("show")){smartMenu.querySelector(selectors_smartMenuCarouselItemActive).focus();var element=smartMenu.querySelector(selectors_smartMenuCarousel);void 0===element&&null===element.length||(element.style.minWidth=element.offsetWidth+"px",element.style.minHeight=element.offsetHeight+"px")}}),!0).observe(smartMenuDropDown,{attributes:!0,childList:!0}),smartMenu.addEventListener("click",(e=>{e.target.matches(selectors_smartMenuCarouselNavigationLink)&&carouselManagement(e)}),!0),smartMenu.addEventListener("keydown",(e=>{e.keyCode!==_key_codes.space&&e.keyCode!==_key_codes.enter||!e.target.matches(selectors_smartMenuCarouselNavigationLink)||(e.preventDefault(),carouselManagement(e))}),!0);const carouselManagement=e=>{e.stopPropagation();const targetedCarouselItemId=e.target.dataset.carouselTargetId,targetedCarouselItem=smartMenu.querySelector("#"+targetedCarouselItemId),index=Array.from(targetedCarouselItem.parentNode.children).indexOf(targetedCarouselItem);(0,_jquery.default)(smartMenu.querySelector(selectors_smartMenuCarousel)).carousel(index)};(0,_jquery.default)(selectors_smartMenu).on("hide.bs.dropdown",(()=>{(0,_jquery.default)(smartMenu.querySelector(selectors_smartMenuCarousel)).carousel(0)})),(0,_jquery.default)(selectors_smartMenuCarousel).on("slid.bs.carousel",(()=>{smartMenu.querySelector(selectors_smartMenuCarouselItemActive).focus()}))})(e)))}};return _exports.default=_default,_exports.default})); + +//# sourceMappingURL=submenu.min.js.map \ No newline at end of file diff --git a/amd/build/submenu.min.js.map b/amd/build/submenu.min.js.map new file mode 100644 index 00000000000..3a0d3c0d942 --- /dev/null +++ b/amd/build/submenu.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"submenu.min.js","sources":["../src/submenu.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\r\n//\r\n// Moodle is free software: you can redistribute it and/or modify\r\n// it under the terms of the GNU General Public License as published by\r\n// the Free Software Foundation, either version 3 of the License, or\r\n// (at your option) any later version.\r\n//\r\n// Moodle is distributed in the hope that it will be useful,\r\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\n// GNU General Public License for more details.\r\n//\r\n// You should have received a copy of the GNU General Public License\r\n// along with Moodle. If not, see .\r\n\r\n/**\r\n * Initializes and handles events in the user menu.\r\n *\r\n * @module core/smartMenu\r\n * @copyright 2021 Moodle\r\n * @author Mihail Geshoski \r\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\r\n */\r\n\r\nimport $ from 'jquery';\r\nimport {space, enter} from 'core/key_codes';\r\n\r\n/**\r\n * User menu constants.\r\n */\r\nconst selectors = {\r\n smartMenu: '.theme-boost-union-smartmenu-carousel',\r\n smartMenuCarousel: '#smartmenu-carousel',\r\n smartMenuCarouselItem: '#smartmenu-carousel .carousel-item',\r\n smartMenuCarouselItemActive: '#smartmenu-carousel .carousel-item.active',\r\n smartMenuCarouselNavigationLink: '#smartmenu-carousel .carousel-navigation-link',\r\n};\r\n\r\n/**\r\n * Register event listeners.\r\n */\r\nconst registerEventListeners = (smartMenu) => {\r\n\r\n var smartMenuDropDown = smartMenu.querySelector('.dropdown-menu');\r\n var observer = new MutationObserver(function() {\r\n if (!smartMenuDropDown.classList.contains('show')) {\r\n return;\r\n }\r\n const activeCarouselItem = smartMenu.querySelector(selectors.smartMenuCarouselItemActive)\r\n // Set the focus on the active carousel item.\r\n activeCarouselItem.focus();\r\n\r\n var element = smartMenu.querySelector(selectors.smartMenuCarousel);\r\n if (element !== undefined || element.length !== null) {\r\n // Resize all non-active carousel items to match the height and width of the current active (main)\r\n // carousel item to avoid sizing inconsistencies. This has to be done once the dropdown menu is fully\r\n // displayed ('shown.bs.dropdown') as the offsetWidth and offsetHeight cannot be obtained when the\r\n // element is hidden.\r\n\r\n element.style.minWidth = element.offsetWidth + 'px';\r\n element.style.minHeight = element.offsetHeight + 'px';\r\n }\r\n }, true);\r\n observer.observe(smartMenuDropDown, { attributes: true, childList: true });\r\n\r\n // Handle click events in the user menu.\r\n smartMenu.addEventListener('click', (e) => {\r\n\r\n // Handle click event on the carousel navigation (control) links in the user menu.\r\n if (e.target.matches(selectors.smartMenuCarouselNavigationLink)) {\r\n carouselManagement(e);\r\n }\r\n\r\n }, true);\r\n\r\n smartMenu.addEventListener('keydown', e => {\r\n // Handle keydown event on the carousel navigation (control) links in the user menu.\r\n if ((e.keyCode === space ||\r\n e.keyCode === enter) &&\r\n e.target.matches(selectors.smartMenuCarouselNavigationLink)) {\r\n e.preventDefault();\r\n carouselManagement(e);\r\n }\r\n }, true);\r\n\r\n /**\r\n * We do the same actions here even if the caller was a click or button press.\r\n *\r\n * @param {Event} e The triggering element and key presses etc.\r\n */\r\n const carouselManagement = e => {\r\n\r\n // By default the user menu dropdown element closes on a click event. This behaviour is not desirable\r\n // as we need to be able to navigate through the carousel items (submenus of the user menu) within the\r\n // user menu. Therefore, we need to prevent the propagation of this event and then manually call the\r\n // carousel transition.\r\n e.stopPropagation();\r\n // The id of the targeted carousel item.\r\n const targetedCarouselItemId = e.target.dataset.carouselTargetId;\r\n const targetedCarouselItem = smartMenu.querySelector('#' + targetedCarouselItemId);\r\n // Get the position (index) of the targeted carousel item within the parent container element.\r\n const index = Array.from(targetedCarouselItem.parentNode.children).indexOf(targetedCarouselItem);\r\n // Navigate to the targeted carousel item.\r\n $(smartMenu.querySelector(selectors.smartMenuCarousel)).carousel(index);\r\n\r\n };\r\n\r\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\r\n $(selectors.smartMenu).on('hide.bs.dropdown', () => {\r\n // Reset the state once the user menu dropdown is closed and return back to the first (main) carousel item\r\n // if necessary.\r\n $(smartMenu.querySelector(selectors.smartMenuCarousel)).carousel(0);\r\n });\r\n\r\n // Handle the 'slid.bs.carousel' event (Fired when the carousel has completed its slide transition).\r\n $(selectors.smartMenuCarousel).on('slid.bs.carousel', () => {\r\n const activeCarouselItem = smartMenu.querySelector(selectors.smartMenuCarouselItemActive);\r\n // Set the focus on the newly activated carousel item.\r\n activeCarouselItem.focus();\r\n });\r\n};\r\n\r\n/**\r\n * Initialize the user menu.\r\n */\r\nconst init = () => {\r\n const smartMenus = document.querySelectorAll(selectors.smartMenu);\r\n if (smartMenus !== undefined && smartMenus.length !== null) {\r\n smartMenus.forEach((e) => registerEventListeners(e));\r\n }\r\n};\r\n\r\nexport default {\r\n init: init,\r\n};\r\n"],"names":["selectors","init","smartMenus","document","querySelectorAll","undefined","length","forEach","e","smartMenu","smartMenuDropDown","querySelector","MutationObserver","classList","contains","focus","element","style","minWidth","offsetWidth","minHeight","offsetHeight","observe","attributes","childList","addEventListener","target","matches","carouselManagement","keyCode","space","enter","preventDefault","stopPropagation","targetedCarouselItemId","dataset","carouselTargetId","targetedCarouselItem","index","Array","from","parentNode","children","indexOf","carousel","on","registerEventListeners"],"mappings":";;;;;;;;mJA8BMA,oBACS,wCADTA,4BAEiB,sBAFjBA,sCAI2B,4CAJ3BA,0CAK+B,6DAiGtB,CACXC,KARS,WACHC,WAAaC,SAASC,iBAAiBJ,0BAC1BK,IAAfH,YAAkD,OAAtBA,WAAWI,QACvCJ,WAAWK,SAASC,GAvFIC,CAAAA,gBAExBC,kBAAoBD,UAAUE,cAAc,kBACjC,IAAIC,kBAAiB,cAC3BF,kBAAkBG,UAAUC,SAAS,SAGfL,UAAUE,cAAcX,uCAEhCe,YAEfC,QAAUP,UAAUE,cAAcX,kCACtBK,IAAZW,SAA4C,OAAnBA,QAAQV,SAMjCU,QAAQC,MAAMC,SAAWF,QAAQG,YAAc,KAC/CH,QAAQC,MAAMG,UAAYJ,QAAQK,aAAe,UAEtD,GACMC,QAAQZ,kBAAmB,CAAEa,YAAY,EAAMC,WAAW,IAGnEf,UAAUgB,iBAAiB,SAAUjB,IAG7BA,EAAEkB,OAAOC,QAAQ3B,4CACjB4B,mBAAmBpB,MAGxB,GAEHC,UAAUgB,iBAAiB,WAAWjB,IAE7BA,EAAEqB,UAAYC,kBACftB,EAAEqB,UAAYE,mBACdvB,EAAEkB,OAAOC,QAAQ3B,6CACjBQ,EAAEwB,iBACFJ,mBAAmBpB,OAExB,SAOGoB,mBAAqBpB,IAMvBA,EAAEyB,wBAEIC,uBAAyB1B,EAAEkB,OAAOS,QAAQC,iBAC1CC,qBAAuB5B,UAAUE,cAAc,IAAMuB,wBAErDI,MAAQC,MAAMC,KAAKH,qBAAqBI,WAAWC,UAAUC,QAAQN,0CAEzE5B,UAAUE,cAAcX,8BAA8B4C,SAASN,4BAKnEtC,qBAAqB6C,GAAG,oBAAoB,yBAGxCpC,UAAUE,cAAcX,8BAA8B4C,SAAS,0BAInE5C,6BAA6B6C,GAAG,oBAAoB,KACvBpC,UAAUE,cAAcX,uCAEhCe,YAUO+B,CAAuBtC"} \ No newline at end of file diff --git a/amd/src/smartmenu.js b/amd/src/smartmenu.js index 892b105dc9b..ca03e01955a 100644 --- a/amd/src/smartmenu.js +++ b/amd/src/smartmenu.js @@ -21,90 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(["jquery", "core/moremenu"], function($) { - /** - * Implement the second level of submenu support. - * Find the submenus inside the dropdown, add an event listener for click event which - on the click - shows the submenu list. - */ - const addSubmenu = () => { - // Fetch the list of submenus from moremenu. - var submenu = document.querySelectorAll('nav.moremenu .dropdown-submenu'); - if (submenu !== null) { - submenu.forEach((item) => { - // Add event listener to show the submenu on click. - item.addEventListener('click', (e) => { - var target = e.currentTarget; - // Hide the shown menu. - hideSubmenus(target); - target.classList.toggle('show'); - // Prevent hiding the parent menu. - e.stopPropagation(); - }); - }); - } - - // Hide the submenus when its parent dropdown is hidden. - $(document).on('hidden.bs.dropdown', e => { - var target = e.relatedTarget.parentNode; - var submenus = target.querySelectorAll('.dropdown-submenu.show'); - if (submenus !== null) { - submenus.forEach((e) => e.classList.remove('show')); - } - }); - - // Provide the third level menu support inside the more menu. - // StopPropagation used in the toggledropdown method on Moremenu.js, It prevents the opening of the third level menus. - // Used the document delegation method to fetch the click on moremenu and submenu. - document.addEventListener('click', (e) => { - var dropdown = e.target.closest('.dropdownmoremenu'); - var subMenu = e.target.closest('.dropdown-submenu'); - if (dropdown && subMenu !== null) { - // Hide the previously opend submenus. before open the new one. - dropdown.querySelectorAll('.dropdown-submenu.show').forEach((menu) => { - menu.classList.remove('show'); - }); - subMenu.classList.toggle('show'); - } - - // Hide the opened menus before open the other menus. - var dropdownMenu = e.target.parentNode.classList.contains('dropdown'); - if (dropdown && dropdownMenu) { - dropdown.querySelectorAll('.dropdown-menu.show').forEach((menu) => { - // Hide the opened menus in more menu. - if (menu != e.target.closest('.dropdown-menu')) { - menu.classList.remove('show'); - } - }); - } - - }, true); - - // Prevent the closing of dropdown during the click on help icon. - var helpIcon = document.querySelectorAll('.moremenu .dropdown .menu-helpicon'); - if (helpIcon !== null) { - helpIcon.forEach((icon) => { - icon.addEventListener('click', (e) => { - e.stopPropagation(); - }); - }); - } - }; - - /** - * Hide visible submenus before display new submenu. - * - * @param {Selector} target - */ - const hideSubmenus = (target) => { - var visibleMenu = document.querySelectorAll('nav.moremenu .dropdown-submenu.show'); - if (visibleMenu !== null) { - visibleMenu.forEach((el) => { - if (el != target) { - el.classList.remove('show'); - } - }); - } - }; +define(["jquery", "core/moremenu", "theme_boost_union/submenu"], function($, MoreMenu, SubMenu) { /** * Make the no wrapped card menus scroll using swipe or drag. @@ -205,7 +122,7 @@ define(["jquery", "core/moremenu"], function($) { return { init: () => { - addSubmenu(); + SubMenu.init(); cardScroll(); autoCollapse(); } diff --git a/amd/src/submenu.js b/amd/src/submenu.js new file mode 100644 index 00000000000..d0130980990 --- /dev/null +++ b/amd/src/submenu.js @@ -0,0 +1,135 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Initializes and handles events in the user menu. + * + * @module core/smartMenu + * @copyright 2021 Moodle + * @author Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import $ from 'jquery'; +import {space, enter} from 'core/key_codes'; + +/** + * User menu constants. + */ +const selectors = { + smartMenu: '.theme-boost-union-smartmenu-carousel', + smartMenuCarousel: '#smartmenu-carousel', + smartMenuCarouselItem: '#smartmenu-carousel .carousel-item', + smartMenuCarouselItemActive: '#smartmenu-carousel .carousel-item.active', + smartMenuCarouselNavigationLink: '#smartmenu-carousel .carousel-navigation-link', +}; + +/** + * Register event listeners. + */ +const registerEventListeners = (smartMenu) => { + + var smartMenuDropDown = smartMenu.querySelector('.dropdown-menu'); + var observer = new MutationObserver(function() { + if (!smartMenuDropDown.classList.contains('show')) { + return; + } + const activeCarouselItem = smartMenu.querySelector(selectors.smartMenuCarouselItemActive) + // Set the focus on the active carousel item. + activeCarouselItem.focus(); + + var element = smartMenu.querySelector(selectors.smartMenuCarousel); + if (element !== undefined || element.length !== null) { + // Resize all non-active carousel items to match the height and width of the current active (main) + // carousel item to avoid sizing inconsistencies. This has to be done once the dropdown menu is fully + // displayed ('shown.bs.dropdown') as the offsetWidth and offsetHeight cannot be obtained when the + // element is hidden. + + element.style.minWidth = element.offsetWidth + 'px'; + element.style.minHeight = element.offsetHeight + 'px'; + } + }, true); + observer.observe(smartMenuDropDown, { attributes: true, childList: true }); + + // Handle click events in the user menu. + smartMenu.addEventListener('click', (e) => { + + // Handle click event on the carousel navigation (control) links in the user menu. + if (e.target.matches(selectors.smartMenuCarouselNavigationLink)) { + carouselManagement(e); + } + + }, true); + + smartMenu.addEventListener('keydown', e => { + // Handle keydown event on the carousel navigation (control) links in the user menu. + if ((e.keyCode === space || + e.keyCode === enter) && + e.target.matches(selectors.smartMenuCarouselNavigationLink)) { + e.preventDefault(); + carouselManagement(e); + } + }, true); + + /** + * We do the same actions here even if the caller was a click or button press. + * + * @param {Event} e The triggering element and key presses etc. + */ + const carouselManagement = e => { + + // By default the user menu dropdown element closes on a click event. This behaviour is not desirable + // as we need to be able to navigate through the carousel items (submenus of the user menu) within the + // user menu. Therefore, we need to prevent the propagation of this event and then manually call the + // carousel transition. + e.stopPropagation(); + // The id of the targeted carousel item. + const targetedCarouselItemId = e.target.dataset.carouselTargetId; + const targetedCarouselItem = smartMenu.querySelector('#' + targetedCarouselItemId); + // Get the position (index) of the targeted carousel item within the parent container element. + const index = Array.from(targetedCarouselItem.parentNode.children).indexOf(targetedCarouselItem); + // Navigate to the targeted carousel item. + $(smartMenu.querySelector(selectors.smartMenuCarousel)).carousel(index); + + }; + + // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed). + $(selectors.smartMenu).on('hide.bs.dropdown', () => { + // Reset the state once the user menu dropdown is closed and return back to the first (main) carousel item + // if necessary. + $(smartMenu.querySelector(selectors.smartMenuCarousel)).carousel(0); + }); + + // Handle the 'slid.bs.carousel' event (Fired when the carousel has completed its slide transition). + $(selectors.smartMenuCarousel).on('slid.bs.carousel', () => { + const activeCarouselItem = smartMenu.querySelector(selectors.smartMenuCarouselItemActive); + // Set the focus on the newly activated carousel item. + activeCarouselItem.focus(); + }); +}; + +/** + * Initialize the user menu. + */ +const init = () => { + const smartMenus = document.querySelectorAll(selectors.smartMenu); + if (smartMenus !== undefined && smartMenus.length !== null) { + smartMenus.forEach((e) => registerEventListeners(e)); + } +}; + +export default { + init: init, +}; diff --git a/classes/output/navigation/primary.php b/classes/output/navigation/primary.php index 1f8021a7a0c..c34d53f7c5b 100644 --- a/classes/output/navigation/primary.php +++ b/classes/output/navigation/primary.php @@ -96,11 +96,18 @@ public function export_for_template(?renderer_base $output = null): array { } // Get the menus for the main menu. + $mainmenu = []; $mainmenu = smartmenu::get_menus_forlocation(smartmenu::LOCATION_MAIN, $smartmenus); + // Convert the children menu items into submenus. + $primarymenu = $this->convert_submenus($mainmenu); + // Separate the menus for the menubar. $menubarmenus = smartmenu::get_menus_forlocation(smartmenu::LOCATION_MENU, $smartmenus); + // Convert the children menu items into submenus. + $menubarmenus = $this->convert_submenus($menubarmenus); + // Separate the menus for the user menus. $locationusermenus = smartmenu::get_menus_forlocation(smartmenu::LOCATION_USER, $smartmenus); @@ -108,7 +115,7 @@ public function export_for_template(?renderer_base $output = null): array { $locationbottom = smartmenu::get_menus_forlocation(smartmenu::LOCATION_BOTTOM, $smartmenus); // Merge the smart menu nodes which contain the main menu location with the primary and custom menu nodes. - $menudata = array_merge($this->get_primary_nav(), $this->get_custom_menu($output), $mainmenu); + $menudata = array_merge($this->get_primary_nav(), $this->get_custom_menu($output), $primarymenu); $moremenu = new \core\navigation\output\more_menu((object) $menudata, 'navbar-nav', false); // Menubar. @@ -120,9 +127,10 @@ public function export_for_template(?renderer_base $output = null): array { // Bottom bar. // Include the menu navigation menus to the mobile menu when the bottom bar doesn't have any menus. + $mobilemenudata = array_merge($this->get_primary_nav(), $this->get_custom_menu($output), $mainmenu); $mobileprimarynav = (!empty($locationbottom)) ? array_merge($this->get_primary_nav(), $this->get_custom_menu($output), $locationbottom) - : $mobileprimarynav = $menudata; + : $mobileprimarynav = $mobilemenudata; if (!empty($mobileprimarynav)) { $bottombar = new \core\navigation\output\more_menu((object) $mobileprimarynav, 'navbar-nav-bottom-bar', false); @@ -219,9 +227,10 @@ public function get_user_menu(renderer_base $output): array { * * @param array $usermenu * @param array $menus + * @param bool $forusermenu If false the divider and logout nodes are unchanged. * @return void */ - public function build_usermenus(&$usermenu, $menus) { + public function build_usermenus(&$usermenu, $menus, $forusermenu=true) { if (empty($menus)) { return []; @@ -229,8 +238,12 @@ public function build_usermenus(&$usermenu, $menus) { $logout = !empty($usermenu['items']) ? array_pop($usermenu['items']) : ''; foreach ($menus as $menu) { + + $menu = !is_object($menu) ? (object) $menu : $menu; // Menu with empty childrens. if (!isset($menu->children)) { + $menu->submenulink = false; + $menu->link = !(isset($menu->divider) && $menu->divider); $usermenu['items'][] = $menu; continue; } @@ -239,6 +252,9 @@ public function build_usermenus(&$usermenu, $menus) { if (isset($menu->submenuid)) { $children = $menu->children; + $menu->link = false; + $menu->submenulink = true; + // Add the second level menus list before the course list to the user menu. // This will have the effect that, when opening the third level submenus, the transition will go to the right. $submenu = [ @@ -253,7 +269,8 @@ public function build_usermenus(&$usermenu, $menus) { array_walk($children, function(&$value) use (&$usermenu, $menu) { if (isset($value['divider'])) { $value['itemtype'] = 'divider'; - $value['link'] = ''; + $value['link'] = false; + $value['divider'] = true; } // Children is submenu item, add third level submenu. @@ -284,18 +301,68 @@ public function build_usermenus(&$usermenu, $menus) { } } - // Include the divider after smart menus items to make difference from logout. - $divider = [ - 'title' => '####', - 'itemtype' => 'divider', - 'divider' => 1, - 'link' => '', - ]; - array_push($usermenu['items'], $divider); + if ($forusermenu) { + // Include the divider after smart menus items to make difference from logout. + $divider = [ + 'title' => '####', + 'itemtype' => 'divider', + 'divider' => 1, + 'link' => '', + ]; + array_push($usermenu['items'], $divider); + + // Update the logout menu at end of menus. + if (!empty($logout)) { + array_push($usermenu['items'], $logout); + } + } + } + + /** + * Converts the second level children of moremenu into submenu format, similar to usermenu. + * + * Updates the ID of first-level submenus as the value of 'sort', where 'sort' contains unique IDs. + * Splits the children of first-level submenus into 'items' and 'submenus', where 'items' contain the first-level main menus + * and 'submenus' contain their children. + * + * @param array $menus The array of menus to build submenus for. + * @return array The updated array of menus with submenus built. + */ + protected function convert_submenus($menus) { + + // Verify the empty entries. + if (empty($menus)) { + return $menus; + } + + // Create a deep clone of menus, direct use of menus mismatch with the usermenus format. + $primarymenu = array_map(function($item) { + return clone $item; + }, $menus); + + foreach ($primarymenu as $key => $parentmenu) { + // Menu doesn't contain any children menus, continue to the next menu. + if (!$parentmenu->haschildren) { + continue; + } + + $submenu = []; + // Children menus of this menu. + $children = $parentmenu->children; - // Update the logout menu at end of menus. - if (!empty($logout)) { - array_push($usermenu['items'], $logout); + // Updates the ID of first-level submenus as the value of 'sort', where 'sort' contains unique IDs. + array_walk($children, function(&$val) { + $val['submenuid'] = $val['sort']; + }); + + // Update the format of children menus into submenus, similar to usermenu. + $this->build_usermenus($submenu, (object) $children, false); + + // Splits the children of first-level submenus into 'items' and 'submenus'. + $primarymenu[$key]->children = ['items' => $submenu['items'] ?? []]; + $primarymenu[$key]->submenus = $submenu['submenus'] ?? []; } + + return $primarymenu; } } diff --git a/scss/boost_union/post.scss b/scss/boost_union/post.scss index f1f37eb5bdc..0307298a9af 100644 --- a/scss/boost_union/post.scss +++ b/scss/boost_union/post.scss @@ -1383,8 +1383,8 @@ body.theme_boost-union-footerbuttonnone.jsenabled { } /* The positions of the dropdown menu added */ .dropdown-menu { - left: auto; - right: 0; + /* left: auto; + right: 0; */ li { width: 100%; } @@ -2635,3 +2635,34 @@ body.dir-rtl { } } } + +// Add icon for the submenu. +.navbar.fixed-top .boost-union-moremenu.dropdown-menu .dropdown-item.carousel-navigation-link::after { + content: "\f0da"; + font-family: var(--fa-style-family, "Font Awesome 6 Free", fontawesome); + font-weight: var(--fa-style, 900); + font-size: 1rem; + right: 0.75rem; + position: absolute; +} + +// Prevent the carousel navigation link child elements pointer events. +.navbar.fixed-top .boost-union-moremenu.dropdown-menu .carousel-navigation-link>* { + pointer-events: none; +} +.navbar.fixed-top .boost-union-moremenu.dropdown-menu .carousel-navigation-link>* img { + margin: 0; +} + +// Allign the carousel naviagtion link arrow icon and put some padding. +.navbar.fixed-top .usermenu .dropdown-menu .submenu .header, +.navbar.fixed-top .boost-union-moremenu.dropdown-menu .submenu .header { + padding: 0.25rem 0.75rem; + font-size: .975rem; + display: flex; + align-items: center; +} + +.navbar.fixed-top .boost-union-moremenu.dropdown-menu { + min-width: 235px; +} diff --git a/templates/smartmenus-moremenu-children.mustache b/templates/smartmenus-moremenu-children.mustache index 8d4d5eab6c3..7b4daf2be5d 100644 --- a/templates/smartmenus-moremenu-children.mustache +++ b/templates/smartmenus-moremenu-children.mustache @@ -32,7 +32,7 @@ } }} {{#haschildren}} - {{/haschildren}} diff --git a/version.php b/version.php index b0d29cf75a0..494394ae8f3 100644 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'theme_boost_union'; -$plugin->version = 2023102025; +$plugin->version = 2023102026; $plugin->release = 'v4.3-r8'; $plugin->requires = 2023100900; $plugin->supported = [403, 403];