Skip to content

Commit

Permalink
Fix: More menu "keep outside of more menu" doesn't show any effect, r…
Browse files Browse the repository at this point in the history
…esolves #461 (#580)
  • Loading branch information
prasanna-lmsace authored May 15, 2024
1 parent 06a606a commit 087af02
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 59 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Changes

### v4.3-r13

* 2024-05-13 - Bugfix: Make the "More menu behavior" setting in smart menus more stable, resolves #461.
* 2024-05-11 - Improvement: Enhance smart menu restrictions for authenticated user role, guest roles and visitor role, resolves #571
* 2024-05-11 - Improvement: Smart menu "locations" must be filled with a value, resolves #404
* 2024-05-10 - Bugfix: Do not show empty smart menus to users, resolves #405
Expand Down
2 changes: 1 addition & 1 deletion amd/build/smartmenu.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion amd/build/smartmenu.min.js.map

Large diffs are not rendered by default.

113 changes: 103 additions & 10 deletions amd/src/smartmenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
*/

define(["jquery", "core/moremenu"], function($) {

const Selectors = {
dropDownMenu: "dropdownmoremenu",
forceOut: "force-menu-out",
navLink: "nav-link",
dropDownItem: "dropdown-item",
classes: {
dropDownMenuList: ".dropdownmoremenu ul.dropdown-menu",
forceOut: ".dropdownmoremenu .force-menu-out"
}
};

/**
* 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.
Expand Down Expand Up @@ -160,17 +172,77 @@ define(["jquery", "core/moremenu"], function($) {
*/
const autoCollapse = () => {
var primaryNav = document.querySelector('.primary-navigation ul.more-nav');
moveOutMoreMenu(primaryNav);
if (primaryNav != undefined) {
setOutMenuPositions(primaryNav); // Create a data flag to maintain the original position of the menus.
moveOutMoreMenu(primaryNav);
}


var menuBar = document.querySelector('nav.menubar ul.more-nav');
moveOutMoreMenu(menuBar);
if (menuBar != undefined) {
setOutMenuPositions(menuBar);
moveOutMoreMenu(menuBar);
}

window.onresize = (e) => {
// Verify the event is original by browser resize.
if (e.isTrusted) {
moveOutMoreMenu(primaryNav);
moveOutMoreMenu(menuBar);
}
};
};

/**
* Finds and sets the positions of all menus before moving them,
* helping to maintain the positions of the menus after being moved out from the moremenu.
*
* @param {HTMLElement} navMenu The navbar container.
*/
const setOutMenuPositions = (navMenu) => {

if (navMenu === undefined || navMenu === null) {
return;
}

// Find all menu items excluding the dropdownmoremenu class.
var li = Array.from(navMenu.children).filter((e) => !e.classList.contains(Selectors.dropDownMenu));

// Initialize the position variable.
var position = 0;

// Loop through each menu item and set its original position.
li.forEach((menu) => {
position = li.indexOf(menu);
menu.dataset.orgposition = position; // Store the original position in the menu's dataset.
});

// Maintain the positions of the menus inside the moremenu from the last position of the outside menus.
var moreMenu = navMenu.querySelector(Selectors.classes.dropDownMenuList);
Array.from(moreMenu.children).forEach((menu) => {
menu.dataset.orgposition = position++;
});
};

/**
* Rearranges the menus placed outside the more menu based on their original positions.
*
* @param {HTMLElement} navMenu The navbar container.
*/
const reArrangeMenuOrgPositions = (navMenu) => {
// Retrieve all menu items and sort them based on their original positions.
var li = Array.from(navMenu.children).sort((a, b) => a.dataset.orgposition - b.dataset.orgposition);
// Append the sorted menu items back to the navbar container.
li.forEach((menu) => navMenu.appendChild(menu));
};

/**
* Move the items from more menu, items which is set to force outside more menu.
* Remove those items from more menu and insert the menu before the last normal item.
* Find the length and children's length to insert the out menus in that positions.
* Move the non forced more menu to moremenu to make the menu alignment.
* Rerun the more menu it will more the other normal menus into more menu to fix the alignmenu issue.
* After the menus are move out, rearrange menus to its original positions.
*
* @param {HTMLElement} navMenu The navbar container.
*/
Expand All @@ -180,27 +252,48 @@ define(["jquery", "core/moremenu"], function($) {
return;
}

var outMenus = navMenu.querySelectorAll('.dropdownmoremenu .force-menu-out');
// Filter the available menus to move inside of more menu.
var li = Array.from(navMenu.children).reverse().filter(
(e) => !e.classList.contains(Selectors.forceOut) && !e.classList.contains(Selectors.dropDownMenu));

// Alternate menus are not available for move to moremenu, stop make the menus move to outside.
if (li.length < 1) {
return;
}

var outMenus = navMenu.querySelectorAll(Selectors.classes.forceOut);
var menuslist = [];

if (outMenus === null) {
return;
}

outMenus.forEach((menu) => {
menu.querySelector('a').classList.remove('dropdown-item');
menu.querySelector('a').classList.add('nav-link');
menu.querySelector('a').classList.remove(Selectors.dropDownItem);
menu.querySelector('a').classList.add(Selectors.navLink);

menuslist.push(menu);
menu.parentNode.removeChild(menu);
});
// Find the length and children's length to insert the out menus in that positions.
var length = menuslist.length;
var navLength = navMenu.children.length - 1; // Remove more menu.
var newPosition = navLength - length || 0;

// Insert the stored menus before the more menu.
menuslist.forEach((menu) => navMenu.insertBefore(menu, navMenu.children[newPosition]));
var moveMenus = [];
menuslist.forEach((menu) => {
if (navMenu.insertBefore(menu, navMenu.lastElementChild) && li.length > 0) {
// Instead of move into moremenu, place the menus before the moremenu will moved to moremenu by moremenu.js.
moveMenus.push(li.shift());
}
});

// Move the non forced more menu before the moremenu to make the menu alignment.
moveMenus.forEach((menu) => {
navMenu.insertBefore(menu, navMenu.lastElementChild);
});

window.dispatchEvent(new Event('resize')); // Dispatch the resize event to create more menu.

// After the menus are move out, rearrange menus to its original positions.
reArrangeMenuOrgPositions(navMenu);
};

return {
Expand Down
2 changes: 1 addition & 1 deletion lang/en/theme_boost_union.php
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,7 @@
$string['smartmenusmenumode'] = 'Menu mode';
$string['smartmenusmenumode_help'] = '<p>Select the mode how the menu\'s items should be displayed.</p><ul><li>Submenu: The menu items is displayed as a submenu with the menu\'s title as parent node. This is the default option.</li><li>Inline: The menu\'s items are displayed directly in the navigation, one after another. Please note that this option is not supported for card type menus.</li></ul>';
$string['smartmenusmenumoremenubehavior'] = 'More menu behavior';
$string['smartmenusmenumoremenubehavior_help'] = '<p>Select what should happen if there are too many menus to fit in the menu location.</p><ul><li>Do not change anything: No particular behaviour will be enforced, excess menus will be moved into the \'More\' menu automatically.</li><li>Force into more menu: This mode moves the menu directly into the \'More\' menu even if there would still be space.</li><li>Keep outside of more menu: This mode keeps the menu outside of the \'More\' menu as long as possible.</li></ul>';
$string['smartmenusmenumoremenubehavior_help'] = '<p>Select what should happen if there are too many menus to fit in the menu location.</p><ul><li>Do not change anything: No particular behaviour will be enforced, excess menus will be moved into the \'More\' menu automatically.</li><li>Force into more menu: This mode moves the menu directly into the \'More\' menu even if there would still be space.</li><li>Keep outside of more menu: This mode keeps the menu outside of the \'More\' menu as long as possible – moving other subsequent menus to the more menu instead if needed.</li></ul><p>Please note that this setting only affects menus which are located in the main navigation or in the menu bar area.</p>';
$string['smartmenusmenumoremenubehaviorforceinto'] = 'Force into more menu';
$string['smartmenusmenumoremenubehaviorkeepoutside'] = 'Keep outside of more menu';
$string['smartmenusmenunothingtodisplay'] = 'There aren\'t any smart menus created yet. Please create your first smart menu to get things going.';
Expand Down
2 changes: 1 addition & 1 deletion templates/smartmenus-cardmenu-children.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
</li>
{{/haschildren}}
{{^haschildren}}
<li data-key="{{key}}" class="nav-item" role="none" data-forceintomoremenu="{{#forceintomoremenu}}true{{/forceintomoremenu}}{{^forceintomoremenu}}false{{/forceintomoremenu}}">
<li data-key="{{key}}" class="nav-item {{#menuclasses}}{{.}} {{/menuclasses}}" role="none" data-forceintomoremenu="{{#forceintomoremenu}}true{{/forceintomoremenu}}{{^forceintomoremenu}}false{{/forceintomoremenu}}">
{{#istablist}}
{{^is_action_link}}
<a role="tab" class="nav-link {{#isactive}}active{{/isactive}} {{#classes}}{{.}} {{/classes}}"
Expand Down
Loading

0 comments on commit 087af02

Please sign in to comment.