From 1c017ad8fb6a3e4425ca23d1632fecc5781f899e Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Thu, 10 Jun 2021 10:17:34 +0800 Subject: [PATCH] MDL-71680 navigation: Update templates/js to force nodes into more menu - Part of: MDL-69588 --- lib/amd/build/moremenu.min.js | 2 +- lib/amd/build/moremenu.min.js.map | 2 +- lib/amd/src/moremenu.js | 151 +++++++++++++++-------- lib/templates/moremenu_children.mustache | 4 +- 4 files changed, 105 insertions(+), 54 deletions(-) diff --git a/lib/amd/build/moremenu.min.js b/lib/amd/build/moremenu.min.js index e327b536683..7a0fbf83cb3 100644 --- a/lib/amd/build/moremenu.min.js +++ b/lib/amd/build/moremenu.min.js @@ -1,2 +1,2 @@ -define ("core/moremenu",["exports","jquery"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);var c={regions:{moredropdown:"[data-region=\"moredropdown\"]",morebutton:"[data-region=\"morebutton\"]"},classes:{dropdownitem:"dropdown-item",dropdownmoremenu:"dropdownmoremenu",dropdowntoggle:"dropdown-toggle",hidden:"d-none",active:"active",nav:"nav",navlink:"nav-link",observed:"observed"},attributes:{menu:"[role=\"menu\"]"}},d=6,e=function(a){var b=a.parentNode.offsetHeight+1,f=a.querySelector(c.regions.moredropdown),g=a.querySelector(c.regions.morebutton),h=a.querySelector("."+c.classes.dropdowntoggle);if(a.offsetHeight>b||a.children.length>d){g.classList.remove(c.classes.hidden);var i=Array.from(a.children).reverse();i.forEach(function(e){if(!e.classList.contains(c.classes.dropdownmoremenu)){if(a.offsetHeight>b||a.children.length>d){var g=a.removeChild(e),i=g.querySelector("."+c.classes.navlink);if(i&&!i.hasAttribute("role")){i.setAttribute("role","menuitem")}if(i.classList.contains(c.classes.active)){h.classList.add(c.classes.active)}i.classList.remove(c.classes.navlink);i.classList.add(c.classes.dropdownitem);f.prepend(g)}}})}else{if("children"in f){var j=Array.from(f.children);j.forEach(function(e){if(a.offsetHeightb){e(a)}}a.parentNode.classList.add(c.classes.observed)},f=function(a){e(a);window.addEventListener("resize",function(){e(a)});var d=function(a){var b=a.target.parentNode.querySelector(c.attributes.menu);if(b){b.classList.toggle("show")}a.stopPropagation()};(0,b.default)("."+c.classes.dropdownmoremenu).on("show.bs.dropdown",function(){var b=a.querySelector(c.regions.moredropdown);b.querySelectorAll(".dropdown").forEach(function(a){a.removeEventListener("click",d,!0);a.addEventListener("click",d,!0)})})};a.default=f;return a.default}); +define ("core/moremenu",["exports","jquery"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);var c={regions:{moredropdown:"[data-region=\"moredropdown\"]",morebutton:"[data-region=\"morebutton\"]"},classes:{dropdownitem:"dropdown-item",dropdownmoremenu:"dropdownmoremenu",dropdowntoggle:"dropdown-toggle",hidden:"d-none",active:"active",nav:"nav",navlink:"nav-link",observed:"observed"},attributes:{menu:"[role=\"menu\"]"}},d=function(a){var b=a.parentNode.offsetHeight+1,g=a.querySelector(c.regions.moredropdown),h=a.querySelector(c.regions.morebutton);if(a.offsetHeight>b){h.classList.remove(c.classes.hidden);var i=Array.from(a.children).reverse();i.forEach(function(d){if(!d.classList.contains(c.classes.dropdownmoremenu)){if(a.offsetHeight>b){var f=a.removeChild(d);e(a,f,!0)}}})}else{if("children"in g){var j=Array.from(g.children);j.forEach(function(c){if(a.offsetHeightb){d(a)}}a.parentNode.classList.add(c.classes.observed)},e=function(a,b){var d=2.\n\n/**\n * Moves wrapping navigation items into a more menu.\n *\n * @module core/moremenu\n * @package core\n * @copyright 2021 Moodle\n * @author Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\n/**\n * Moremenu selectors.\n */\nconst Selectors = {\n regions: {\n moredropdown: '[data-region=\"moredropdown\"]',\n morebutton: '[data-region=\"morebutton\"]'\n },\n classes: {\n dropdownitem: 'dropdown-item',\n dropdownmoremenu: 'dropdownmoremenu',\n dropdowntoggle: 'dropdown-toggle',\n hidden: 'd-none',\n active: 'active',\n nav: 'nav',\n navlink: 'nav-link',\n observed: 'observed',\n },\n attributes: {\n menu: '[role=\"menu\"]'\n }\n};\n\nconst maxMenuItems = 6;\n/**\n * Auto Collapse navigation items that wrap into a dropdown menu.\n *\n * @param {HTMLElement} menu The navbar container.\n */\nconst autoCollapse = menu => {\n\n const maxHeight = menu.parentNode.offsetHeight + 1;\n\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n\n const dropdownToggle = menu.querySelector('.' + Selectors.classes.dropdowntoggle);\n\n // If the menuitems wrap and the menu height is larger than the height of the\n // parent. Or if the number if menuitems is larger than the maximum menu items\n // allowed then start pushing navlinks into the moreDropdown.\n if (menu.offsetHeight > maxHeight || menu.children.length > maxMenuItems) {\n\n moreButton.classList.remove(Selectors.classes.hidden);\n\n const menuNodes = Array.from(menu.children).reverse();\n menuNodes.forEach(item => {\n if (!item.classList.contains(Selectors.classes.dropdownmoremenu)) {\n // After moving the menuitems into the moreDropdown check again\n // if the menuheight is still larger then the height of the parent or if the\n // menu still has more items than maxMenuItems.\n if (menu.offsetHeight > maxHeight || menu.children.length > maxMenuItems) {\n const lastNode = menu.removeChild(item);\n const navLink = lastNode.querySelector('.' + Selectors.classes.navlink);\n if (navLink && !navLink.hasAttribute('role')) {\n // Adding the menuitem role so the dropdown includes the\n // Accessibility improvements from theme/boost/amd/src/aria.js\n navLink.setAttribute('role', 'menuitem');\n }\n\n // If there are navLinks that contain an active link in the moreDropdown\n // make the dropdownToggle in the moreButton active.\n if (navLink.classList.contains(Selectors.classes.active)) {\n dropdownToggle.classList.add(Selectors.classes.active);\n }\n\n // Change the styling of the navLink to a dropdownitem and push it into\n // the moreDropdown.\n navLink.classList.remove(Selectors.classes.navlink);\n navLink.classList.add(Selectors.classes.dropdownitem);\n moreDropdown.prepend(lastNode);\n }\n }\n });\n } else {\n // If the the menu height is smaller than the height of the parent and there are\n // less than the maximum items in the menu, then try returning navlinks to the menu.\n\n if ('children' in moreDropdown) {\n const menuNodes = Array.from(moreDropdown.children);\n menuNodes.forEach(item => {\n\n if (menu.offsetHeight < maxHeight && menu.children.length < maxMenuItems) {\n const lastNode = moreDropdown.removeChild(item);\n const navLink = lastNode.querySelector('.' + Selectors.classes.dropdownitem);\n if (navLink) {\n const currentAttribute = navLink.getAttribute('role');\n if (currentAttribute === 'menuitem') {\n navLink.removeAttribute('role');\n }\n }\n\n // Stop displaying the active state on the dropdownToggle if\n // the active navlink is removed.\n if (navLink.classList.contains(Selectors.classes.active)) {\n dropdownToggle.classList.remove(Selectors.classes.active);\n }\n navLink.classList.remove(Selectors.classes.dropdownitem);\n navLink.classList.add(Selectors.classes.navlink);\n menu.insertBefore(lastNode, moreButton);\n }\n });\n\n // If there are no more menuNodes in the dropdown we can hide the moreButton.\n if (menuNodes.length === 0) {\n moreButton.classList.add(Selectors.classes.hidden);\n }\n }\n\n if (menu.offsetHeight > maxHeight) {\n autoCollapse(menu);\n }\n }\n menu.parentNode.classList.add(Selectors.classes.observed);\n};\n\n/**\n * Initialise the more menus.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n */\nexport default menu => {\n autoCollapse(menu);\n\n // When the screen size changes make sure the menu still fits.\n window.addEventListener('resize', () => {\n autoCollapse(menu);\n });\n\n const toggledropdown = e => {\n const innerMenu = e.target.parentNode.querySelector(Selectors.attributes.menu);\n if (innerMenu) {\n innerMenu.classList.toggle('show');\n }\n e.stopPropagation();\n };\n\n // If there are dropdowns in the MoreMenu, add a new\n // eventlistener to show the contents on click and prevent the\n // moreMenu from closing.\n $('.' + Selectors.classes.dropdownmoremenu).on('show.bs.dropdown', function() {\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n moreDropdown.querySelectorAll('.dropdown').forEach((dropdown) => {\n dropdown.removeEventListener('click', toggledropdown, true);\n dropdown.addEventListener('click', toggledropdown, true);\n });\n });\n};\n"],"file":"moremenu.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/moremenu.js"],"names":["Selectors","regions","moredropdown","morebutton","classes","dropdownitem","dropdownmoremenu","dropdowntoggle","hidden","active","nav","navlink","observed","attributes","menu","autoCollapse","maxHeight","parentNode","offsetHeight","moreDropdown","querySelector","moreButton","classList","remove","menuNodes","Array","from","children","reverse","forEach","item","contains","lastNode","removeChild","moveIntoMoreDropdown","dataset","forceintomoremenu","moveOutOfMoreDropdown","length","add","navNode","prepend","dropdownToggle","navLink","hasAttribute","setAttribute","append","currentAttribute","getAttribute","removeAttribute","insertBefore","window","addEventListener","toggledropdown","e","innerMenu","target","toggle","stopPropagation","on","querySelectorAll","dropdown","removeEventListener"],"mappings":"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","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 * Moves wrapping navigation items into a more menu.\n *\n * @module core/moremenu\n * @package core\n * @copyright 2021 Moodle\n * @author Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\n/**\n * Moremenu selectors.\n */\nconst Selectors = {\n regions: {\n moredropdown: '[data-region=\"moredropdown\"]',\n morebutton: '[data-region=\"morebutton\"]'\n },\n classes: {\n dropdownitem: 'dropdown-item',\n dropdownmoremenu: 'dropdownmoremenu',\n dropdowntoggle: 'dropdown-toggle',\n hidden: 'd-none',\n active: 'active',\n nav: 'nav',\n navlink: 'nav-link',\n observed: 'observed',\n },\n attributes: {\n menu: '[role=\"menu\"]'\n }\n};\n\n/**\n * Auto Collapse navigation items that wrap into a dropdown menu.\n *\n * @param {HTMLElement} menu The navbar container.\n */\nconst autoCollapse = menu => {\n\n const maxHeight = menu.parentNode.offsetHeight + 1;\n\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n\n // If the menu items wrap and the menu height is larger than the height of the\n // parent then start pushing navlinks into the moreDropdown.\n if (menu.offsetHeight > maxHeight) {\n\n moreButton.classList.remove(Selectors.classes.hidden);\n\n const menuNodes = Array.from(menu.children).reverse();\n menuNodes.forEach(item => {\n if (!item.classList.contains(Selectors.classes.dropdownmoremenu)) {\n // After moving the menu items into the moreDropdown check again\n // if the menu height is still larger then the height of the parent.\n if (menu.offsetHeight > maxHeight) {\n const lastNode = menu.removeChild(item);\n // Move this node into the more dropdown menu.\n moveIntoMoreDropdown(menu, lastNode, true);\n }\n }\n });\n } else {\n // If the menu height is smaller than the height of the parent, then try returning navlinks to the menu.\n\n if ('children' in moreDropdown) {\n const menuNodes = Array.from(moreDropdown.children);\n menuNodes.forEach(item => {\n // Don't move the node to the more menu if it is explicitly defined that\n // this node should be displayed in the more dropdown menu at all times.\n if (menu.offsetHeight < maxHeight && item.dataset.forceintomoremenu !== 'true') {\n const lastNode = moreDropdown.removeChild(item);\n // Move this node from the more dropdown menu into the main section of the menu.\n moveOutOfMoreDropdown(menu, lastNode);\n }\n });\n\n // If there are no more menuNodes in the dropdown we can hide the moreButton.\n if (menuNodes.length === 0) {\n moreButton.classList.add(Selectors.classes.hidden);\n }\n }\n\n if (menu.offsetHeight > maxHeight) {\n autoCollapse(menu);\n }\n }\n menu.parentNode.classList.add(Selectors.classes.observed);\n};\n\n/**\n * Move a node into the \"more\" dropdown menu.\n *\n * This method forces a given navigation node to be added and displayed within the \"more\" dropdown menu.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n * @param {HTMLElement} navNode The navigation node.\n * @param {boolean} prepend Whether to prepend or append the node to the content in the more dropdown menu.\n */\nconst moveIntoMoreDropdown = (menu, navNode, prepend = false) => {\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n const dropdownToggle = menu.querySelector('.' + Selectors.classes.dropdowntoggle);\n\n const navLink = navNode.querySelector('.' + Selectors.classes.navlink);\n if (navLink && !navLink.hasAttribute('role')) {\n // Adding the menuitem role so the dropdown includes the\n // accessibility improvements from theme/boost/amd/src/aria.js\n navLink.setAttribute('role', 'menuitem');\n }\n\n // If there are navLinks that contain an active link in the moreDropdown\n // make the dropdownToggle in the moreButton active.\n if (navLink.classList.contains(Selectors.classes.active)) {\n dropdownToggle.classList.add(Selectors.classes.active);\n }\n\n // Change the styling of the navLink to a dropdownitem and push it into\n // the moreDropdown.\n navLink.classList.remove(Selectors.classes.navlink);\n navLink.classList.add(Selectors.classes.dropdownitem);\n if (prepend) {\n moreDropdown.prepend(navNode);\n } else {\n moreDropdown.append(navNode);\n }\n};\n\n/**\n * Move a node out of the \"more\" dropdown menu.\n *\n * This method forces a given node from the \"more\" dropdown menu to be displayed in the main section of the menu.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n * @param {HTMLElement} navNode The navigation node.\n */\nconst moveOutOfMoreDropdown = (menu, navNode) => {\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n const dropdownToggle = menu.querySelector('.' + Selectors.classes.dropdowntoggle);\n const navLink = navNode.querySelector('.' + Selectors.classes.dropdownitem);\n\n if (navLink) {\n const currentAttribute = navLink.getAttribute('role');\n if (currentAttribute === 'menuitem') {\n navLink.removeAttribute('role');\n }\n }\n\n // Stop displaying the active state on the dropdownToggle if\n // the active navlink is removed.\n if (navLink.classList.contains(Selectors.classes.active)) {\n dropdownToggle.classList.remove(Selectors.classes.active);\n }\n navLink.classList.remove(Selectors.classes.dropdownitem);\n navLink.classList.add(Selectors.classes.navlink);\n menu.insertBefore(navNode, moreButton);\n};\n\n/**\n * Initialise the more menus.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n */\nexport default menu => {\n // Pre-populate the \"more\" dropdown menu with navigation nodes which are set to be displayed in this menu\n // by default at all times.\n if ('children' in menu) {\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n const menuNodes = Array.from(menu.children);\n menuNodes.forEach((item) => {\n if (!item.classList.contains(Selectors.classes.dropdownmoremenu) &&\n item.dataset.forceintomoremenu === 'true') {\n // Append this node into the more dropdown menu.\n moveIntoMoreDropdown(menu, item, false);\n // After adding the node into the more dropdown menu, make sure that the more dropdown menu button\n // is displayed.\n if (moreButton.classList.contains(Selectors.classes.hidden)) {\n moreButton.classList.remove(Selectors.classes.hidden);\n }\n }\n });\n }\n // Populate the more dropdown menu with additional nodes if necessary, depending on the current screen size.\n autoCollapse(menu);\n\n // When the screen size changes make sure the menu still fits.\n window.addEventListener('resize', () => {\n autoCollapse(menu);\n });\n\n const toggledropdown = e => {\n const innerMenu = e.target.parentNode.querySelector(Selectors.attributes.menu);\n if (innerMenu) {\n innerMenu.classList.toggle('show');\n }\n e.stopPropagation();\n };\n\n // If there are dropdowns in the MoreMenu, add a new\n // event listener to show the contents on click and prevent the\n // moreMenu from closing.\n $('.' + Selectors.classes.dropdownmoremenu).on('show.bs.dropdown', function() {\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n moreDropdown.querySelectorAll('.dropdown').forEach((dropdown) => {\n dropdown.removeEventListener('click', toggledropdown, true);\n dropdown.addEventListener('click', toggledropdown, true);\n });\n });\n};\n"],"file":"moremenu.min.js"} \ No newline at end of file diff --git a/lib/amd/src/moremenu.js b/lib/amd/src/moremenu.js index 57b283fb254..804edfa78ba 100644 --- a/lib/amd/src/moremenu.js +++ b/lib/amd/src/moremenu.js @@ -47,7 +47,6 @@ const Selectors = { } }; -const maxMenuItems = 6; /** * Auto Collapse navigation items that wrap into a dropdown menu. * @@ -60,70 +59,36 @@ const autoCollapse = menu => { const moreDropdown = menu.querySelector(Selectors.regions.moredropdown); const moreButton = menu.querySelector(Selectors.regions.morebutton); - const dropdownToggle = menu.querySelector('.' + Selectors.classes.dropdowntoggle); - - // If the menuitems wrap and the menu height is larger than the height of the - // parent. Or if the number if menuitems is larger than the maximum menu items - // allowed then start pushing navlinks into the moreDropdown. - if (menu.offsetHeight > maxHeight || menu.children.length > maxMenuItems) { + // If the menu items wrap and the menu height is larger than the height of the + // parent then start pushing navlinks into the moreDropdown. + if (menu.offsetHeight > maxHeight) { moreButton.classList.remove(Selectors.classes.hidden); const menuNodes = Array.from(menu.children).reverse(); menuNodes.forEach(item => { if (!item.classList.contains(Selectors.classes.dropdownmoremenu)) { - // After moving the menuitems into the moreDropdown check again - // if the menuheight is still larger then the height of the parent or if the - // menu still has more items than maxMenuItems. - if (menu.offsetHeight > maxHeight || menu.children.length > maxMenuItems) { + // After moving the menu items into the moreDropdown check again + // if the menu height is still larger then the height of the parent. + if (menu.offsetHeight > maxHeight) { const lastNode = menu.removeChild(item); - const navLink = lastNode.querySelector('.' + Selectors.classes.navlink); - if (navLink && !navLink.hasAttribute('role')) { - // Adding the menuitem role so the dropdown includes the - // Accessibility improvements from theme/boost/amd/src/aria.js - navLink.setAttribute('role', 'menuitem'); - } - - // If there are navLinks that contain an active link in the moreDropdown - // make the dropdownToggle in the moreButton active. - if (navLink.classList.contains(Selectors.classes.active)) { - dropdownToggle.classList.add(Selectors.classes.active); - } - - // Change the styling of the navLink to a dropdownitem and push it into - // the moreDropdown. - navLink.classList.remove(Selectors.classes.navlink); - navLink.classList.add(Selectors.classes.dropdownitem); - moreDropdown.prepend(lastNode); + // Move this node into the more dropdown menu. + moveIntoMoreDropdown(menu, lastNode, true); } } }); } else { - // If the the menu height is smaller than the height of the parent and there are - // less than the maximum items in the menu, then try returning navlinks to the menu. + // If the menu height is smaller than the height of the parent, then try returning navlinks to the menu. if ('children' in moreDropdown) { const menuNodes = Array.from(moreDropdown.children); menuNodes.forEach(item => { - - if (menu.offsetHeight < maxHeight && menu.children.length < maxMenuItems) { + // Don't move the node to the more menu if it is explicitly defined that + // this node should be displayed in the more dropdown menu at all times. + if (menu.offsetHeight < maxHeight && item.dataset.forceintomoremenu !== 'true') { const lastNode = moreDropdown.removeChild(item); - const navLink = lastNode.querySelector('.' + Selectors.classes.dropdownitem); - if (navLink) { - const currentAttribute = navLink.getAttribute('role'); - if (currentAttribute === 'menuitem') { - navLink.removeAttribute('role'); - } - } - - // Stop displaying the active state on the dropdownToggle if - // the active navlink is removed. - if (navLink.classList.contains(Selectors.classes.active)) { - dropdownToggle.classList.remove(Selectors.classes.active); - } - navLink.classList.remove(Selectors.classes.dropdownitem); - navLink.classList.add(Selectors.classes.navlink); - menu.insertBefore(lastNode, moreButton); + // Move this node from the more dropdown menu into the main section of the menu. + moveOutOfMoreDropdown(menu, lastNode); } }); @@ -140,12 +105,98 @@ const autoCollapse = menu => { menu.parentNode.classList.add(Selectors.classes.observed); }; +/** + * Move a node into the "more" dropdown menu. + * + * This method forces a given navigation node to be added and displayed within the "more" dropdown menu. + * + * @param {HTMLElement} menu The navbar moremenu. + * @param {HTMLElement} navNode The navigation node. + * @param {boolean} prepend Whether to prepend or append the node to the content in the more dropdown menu. + */ +const moveIntoMoreDropdown = (menu, navNode, prepend = false) => { + const moreDropdown = menu.querySelector(Selectors.regions.moredropdown); + const dropdownToggle = menu.querySelector('.' + Selectors.classes.dropdowntoggle); + + const navLink = navNode.querySelector('.' + Selectors.classes.navlink); + if (navLink && !navLink.hasAttribute('role')) { + // Adding the menuitem role so the dropdown includes the + // accessibility improvements from theme/boost/amd/src/aria.js + navLink.setAttribute('role', 'menuitem'); + } + + // If there are navLinks that contain an active link in the moreDropdown + // make the dropdownToggle in the moreButton active. + if (navLink.classList.contains(Selectors.classes.active)) { + dropdownToggle.classList.add(Selectors.classes.active); + } + + // Change the styling of the navLink to a dropdownitem and push it into + // the moreDropdown. + navLink.classList.remove(Selectors.classes.navlink); + navLink.classList.add(Selectors.classes.dropdownitem); + if (prepend) { + moreDropdown.prepend(navNode); + } else { + moreDropdown.append(navNode); + } +}; + +/** + * Move a node out of the "more" dropdown menu. + * + * This method forces a given node from the "more" dropdown menu to be displayed in the main section of the menu. + * + * @param {HTMLElement} menu The navbar moremenu. + * @param {HTMLElement} navNode The navigation node. + */ +const moveOutOfMoreDropdown = (menu, navNode) => { + const moreButton = menu.querySelector(Selectors.regions.morebutton); + const dropdownToggle = menu.querySelector('.' + Selectors.classes.dropdowntoggle); + const navLink = navNode.querySelector('.' + Selectors.classes.dropdownitem); + + if (navLink) { + const currentAttribute = navLink.getAttribute('role'); + if (currentAttribute === 'menuitem') { + navLink.removeAttribute('role'); + } + } + + // Stop displaying the active state on the dropdownToggle if + // the active navlink is removed. + if (navLink.classList.contains(Selectors.classes.active)) { + dropdownToggle.classList.remove(Selectors.classes.active); + } + navLink.classList.remove(Selectors.classes.dropdownitem); + navLink.classList.add(Selectors.classes.navlink); + menu.insertBefore(navNode, moreButton); +}; + /** * Initialise the more menus. * * @param {HTMLElement} menu The navbar moremenu. */ export default menu => { + // Pre-populate the "more" dropdown menu with navigation nodes which are set to be displayed in this menu + // by default at all times. + if ('children' in menu) { + const moreButton = menu.querySelector(Selectors.regions.morebutton); + const menuNodes = Array.from(menu.children); + menuNodes.forEach((item) => { + if (!item.classList.contains(Selectors.classes.dropdownmoremenu) && + item.dataset.forceintomoremenu === 'true') { + // Append this node into the more dropdown menu. + moveIntoMoreDropdown(menu, item, false); + // After adding the node into the more dropdown menu, make sure that the more dropdown menu button + // is displayed. + if (moreButton.classList.contains(Selectors.classes.hidden)) { + moreButton.classList.remove(Selectors.classes.hidden); + } + } + }); + } + // Populate the more dropdown menu with additional nodes if necessary, depending on the current screen size. autoCollapse(menu); // When the screen size changes make sure the menu still fits. @@ -162,7 +213,7 @@ export default menu => { }; // If there are dropdowns in the MoreMenu, add a new - // eventlistener to show the contents on click and prevent the + // event listener to show the contents on click and prevent the // moreMenu from closing. $('.' + Selectors.classes.dropdownmoremenu).on('show.bs.dropdown', function() { const moreDropdown = menu.querySelector(Selectors.regions.moredropdown); diff --git a/lib/templates/moremenu_children.mustache b/lib/templates/moremenu_children.mustache index bce9cd5e37c..eff177d6ef5 100644 --- a/lib/templates/moremenu_children.mustache +++ b/lib/templates/moremenu_children.mustache @@ -31,7 +31,7 @@ } }} {{#haschildren}} - {{/haschildren}} {{^haschildren}} -