mirror of
https://github.com/moodle/moodle.git
synced 2025-04-20 16:04:25 +02:00
Merge branch 'MDL-77721-master' of https://github.com/lameze/moodle
This commit is contained in:
commit
9ec55f3091
2
lib/amd/build/menu_navigation.min.js
vendored
2
lib/amd/build/menu_navigation.min.js
vendored
@ -7,6 +7,6 @@ define("core/menu_navigation",["exports"],(function(_exports){Object.definePrope
|
||||
* @author Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const SELECTORS_menuitem='[role="menuitem"]',SELECTORS_tab='[role="tab"]',SELECTORS_dropdowntoggle='[data-toggle="dropdown"]',SELECTORS_dropdownitemactive='.dropdown-item[aria-current="true"]';let openDropdownNode=null;const clickErrorHandler=(item,fallback)=>null!==item?item:fallback,menuItemHelper=src=>{let parent;if(!src.dataset.disableactive){if(src.classList.contains("dropdown-item")){parent=src.closest(".dropdown-menu");const dropDownToggle=document.getElementById(parent.getAttribute("aria-labelledby"));dropDownToggle.classList.add("active"),dropDownToggle.setAttribute("tabindex",0)}else{if(!src.matches("".concat(SELECTORS_tab,",").concat(SELECTORS_menuitem))||src.matches(SELECTORS_dropdowntoggle))return;parent=src.parentElement.parentElement.querySelector(".dropdown-menu")}Array.prototype.forEach.call(parent.children,(node=>{const menuItem=node.querySelector(SELECTORS_menuitem);null!==menuItem&&(menuItem.classList.remove("active"),menuItem.removeAttribute("aria-current"))})),"menuitem"===src.getAttribute("role")&&src.setAttribute("aria-current","true")}},keyboardListenerEvents=e=>{const src=e.srcElement,firstNode=e.currentTarget.firstElementChild,lastNode=findUsableLastNode(e.currentTarget);if(src.classList.contains("dropdown-item"))"ArrowRight"!=e.key&&"ArrowLeft"!=e.key||(e.preventDefault(),null!==openDropdownNode&&openDropdownNode.parentElement.click())," "!=e.key&&"Enter"!=e.key||(e.preventDefault(),menuItemHelper(src),src.parentElement.classList.contains("dropdown")||src.click());else{const rtl=window.right_to_left(),arrowNext=rtl?"ArrowLeft":"ArrowRight",arrowPrevious=rtl?"ArrowRight":"ArrowLeft";"menuitem"===src.getAttribute("role")&&(e.key==arrowNext&&(e.preventDefault(),setFocusNext(src,firstNode)),e.key==arrowPrevious&&(e.preventDefault(),setFocusPrev(src,lastNode)),"ArrowUp"!=e.key&&"ArrowDown"!=e.key||(openDropdownNode=src,e.preventDefault()),"Home"==e.key&&(e.preventDefault(),setFocusHomeEnd(firstNode)),"End"==e.key&&(e.preventDefault(),setFocusHomeEnd(lastNode)))," "!=e.key&&"Enter"!=e.key||(e.preventDefault(),src.parentElement.classList.contains("dropdown")||src.click())}},clickListenerEvents=e=>{const src=e.srcElement;menuItemHelper(src)};_exports.default=elementRoot=>{elementRoot.removeEventListener("keydown",keyboardListenerEvents),elementRoot.removeEventListener("click",clickListenerEvents),elementRoot.addEventListener("keydown",keyboardListenerEvents),elementRoot.addEventListener("click",clickListenerEvents)},window.addEventListener("pageshow",(function(){const items=document.querySelectorAll(SELECTORS_dropdownitemactive);null!==items&&items.length>1&&items.forEach((function(e){const href=e.getAttribute("href");href!==window.location.href&&href!==window.location.pathname&&href!==window.location.href+"/index.php"&&href!==window.location.pathname+"index.php"&&(e.classList.remove("active"),e.removeAttribute("aria-current"))}))}));const setFocusNext=(currentNode,firstNode)=>{const listElement=currentNode.parentElement,nextListItem=(el=>{do{el=el.nextElementSibling}while(el&&!el.offsetHeight);return el})(listElement),nodeToSelect=clickErrorHandler(nextListItem,firstNode),itemSelector="tablist"===listElement.parentElement.getAttribute("role")?SELECTORS_tab:SELECTORS_menuitem;nodeToSelect.querySelector(itemSelector).focus()},setFocusPrev=(currentNode,lastNode)=>{const listElement=currentNode.parentElement,nextListItem=(el=>{do{el=el.previousElementSibling}while(el&&!el.offsetHeight);return el})(listElement),nodeToSelect=clickErrorHandler(nextListItem,lastNode),itemSelector="tablist"===listElement.parentElement.getAttribute("role")?SELECTORS_tab:SELECTORS_menuitem;nodeToSelect.querySelector(itemSelector).focus()},setFocusHomeEnd=node=>{node.querySelector(SELECTORS_menuitem).focus()},findUsableLastNode=elementRoot=>{if(elementRoot.lastElementChild.classList.contains("d-none")){const nodesToUse=Array.prototype.map.call(elementRoot.children,(node=>node)).reverse().filter((node=>{if(!node.classList.contains("d-none"))return node}));return 0!==nodesToUse.length?nodesToUse[0]:elementRoot.firstElementChild}return elementRoot.lastElementChild};return _exports.default}));
|
||||
const SELECTORS_menuitem='[role="menuitem"]',SELECTORS_tab='[role="tab"]',SELECTORS_dropdowntoggle='[data-toggle="dropdown"]';let openDropdownNode=null;const clickErrorHandler=(item,fallback)=>null!==item?item:fallback,menuItemHelper=src=>{let parent;if(!src.dataset.disableactive){if(src.classList.contains("dropdown-item")){parent=src.closest(".dropdown-menu");const dropDownToggle=document.getElementById(parent.getAttribute("aria-labelledby"));dropDownToggle.classList.add("active"),dropDownToggle.setAttribute("tabindex",0)}else{if(!src.matches("".concat(SELECTORS_tab,",").concat(SELECTORS_menuitem))||src.matches(SELECTORS_dropdowntoggle))return;parent=src.parentElement.parentElement.querySelector(".dropdown-menu")}Array.prototype.forEach.call(parent.children,(node=>{const menuItem=node.querySelector(SELECTORS_menuitem);null!==menuItem&&(menuItem.classList.remove("active"),menuItem.removeAttribute("aria-current"))})),"menuitem"===src.getAttribute("role")&&src.setAttribute("aria-current","true")}},keyboardListenerEvents=e=>{const src=e.srcElement,firstNode=e.currentTarget.firstElementChild,lastNode=findUsableLastNode(e.currentTarget);if(src.classList.contains("dropdown-item"))"ArrowRight"!=e.key&&"ArrowLeft"!=e.key||(e.preventDefault(),null!==openDropdownNode&&openDropdownNode.parentElement.click())," "!=e.key&&"Enter"!=e.key||(e.preventDefault(),menuItemHelper(src),src.parentElement.classList.contains("dropdown")||src.click());else{const rtl=window.right_to_left(),arrowNext=rtl?"ArrowLeft":"ArrowRight",arrowPrevious=rtl?"ArrowRight":"ArrowLeft";"menuitem"===src.getAttribute("role")&&(e.key==arrowNext&&(e.preventDefault(),setFocusNext(src,firstNode)),e.key==arrowPrevious&&(e.preventDefault(),setFocusPrev(src,lastNode)),"ArrowUp"!=e.key&&"ArrowDown"!=e.key||(openDropdownNode=src,e.preventDefault()),"Home"==e.key&&(e.preventDefault(),setFocusHomeEnd(firstNode)),"End"==e.key&&(e.preventDefault(),setFocusHomeEnd(lastNode)))," "!=e.key&&"Enter"!=e.key||(e.preventDefault(),src.parentElement.classList.contains("dropdown")||src.click())}},clickListenerEvents=e=>{const src=e.srcElement;menuItemHelper(src)};_exports.default=elementRoot=>{elementRoot.removeEventListener("keydown",keyboardListenerEvents),elementRoot.removeEventListener("click",clickListenerEvents),elementRoot.addEventListener("keydown",keyboardListenerEvents),elementRoot.addEventListener("click",clickListenerEvents)};const setFocusNext=(currentNode,firstNode)=>{const listElement=currentNode.parentElement,nextListItem=(el=>{do{el=el.nextElementSibling}while(el&&!el.offsetHeight);return el})(listElement),nodeToSelect=clickErrorHandler(nextListItem,firstNode),itemSelector="tablist"===listElement.parentElement.getAttribute("role")?SELECTORS_tab:SELECTORS_menuitem;nodeToSelect.querySelector(itemSelector).focus()},setFocusPrev=(currentNode,lastNode)=>{const listElement=currentNode.parentElement,nextListItem=(el=>{do{el=el.previousElementSibling}while(el&&!el.offsetHeight);return el})(listElement),nodeToSelect=clickErrorHandler(nextListItem,lastNode),itemSelector="tablist"===listElement.parentElement.getAttribute("role")?SELECTORS_tab:SELECTORS_menuitem;nodeToSelect.querySelector(itemSelector).focus()},setFocusHomeEnd=node=>{node.querySelector(SELECTORS_menuitem).focus()},findUsableLastNode=elementRoot=>{if(elementRoot.lastElementChild.classList.contains("d-none")){const nodesToUse=Array.prototype.map.call(elementRoot.children,(node=>node)).reverse().filter((node=>{if(!node.classList.contains("d-none"))return node}));return 0!==nodesToUse.length?nodesToUse[0]:elementRoot.firstElementChild}return elementRoot.lastElementChild};return _exports.default}));
|
||||
|
||||
//# sourceMappingURL=menu_navigation.min.js.map
|
File diff suppressed because one or more lines are too long
@ -26,7 +26,6 @@ const SELECTORS = {
|
||||
'menuitem': '[role="menuitem"]',
|
||||
'tab': '[role="tab"]',
|
||||
'dropdowntoggle': '[data-toggle="dropdown"]',
|
||||
'dropdownitemactive': '.dropdown-item[aria-current="true"]',
|
||||
};
|
||||
|
||||
let openDropdownNode = null;
|
||||
@ -85,30 +84,6 @@ const menuItemHelper = src => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there are sub items in a dropdown menu. There can be one element active only. That is usually controlled
|
||||
* by the server. However, when you click, the newly clicked item gets the active state as well. This is no problem
|
||||
* because the user leaves the page and a new page load happens. When the user hits the back button, the old page dom
|
||||
* is restored from the cache, with both menu items active. If there is such a case, we need to uncheck the item that
|
||||
* was clicked when leaving this page.
|
||||
*
|
||||
*/
|
||||
const dropDownMenuActiveCheck = function() {
|
||||
const items = document.querySelectorAll(SELECTORS.dropdownitemactive);
|
||||
// Do the check only, if there is more than one subitem active.
|
||||
if (items !== null && items.length > 1) {
|
||||
items.forEach(function(e) {
|
||||
// Get the link target from the href attribute and compare it with the current url in the browser.
|
||||
const href = e.getAttribute('href');
|
||||
if (href !== window.location.href && href !== window.location.pathname
|
||||
&& href !== window.location.href + '/index.php' && href !== window.location.pathname + 'index.php') {
|
||||
e.classList.remove('active');
|
||||
e.removeAttribute('aria-current');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Defined keyboard event handling so we can remove listeners on nodes on resize etc.
|
||||
*
|
||||
@ -205,9 +180,6 @@ export default elementRoot => {
|
||||
elementRoot.addEventListener('click', clickListenerEvents);
|
||||
};
|
||||
|
||||
// We need this triggered only when the user hits the back button.
|
||||
window.addEventListener('pageshow', dropDownMenuActiveCheck);
|
||||
|
||||
/**
|
||||
* Handle the focusing to the next element in the dropdown.
|
||||
*
|
||||
|
@ -55,9 +55,9 @@ class primary implements renderable, templatable {
|
||||
$output = $this->page->get_renderer('core');
|
||||
}
|
||||
|
||||
$menudata = (object) $this->merge_primary_and_custom($this->get_primary_nav(), $this->get_custom_menu($output));
|
||||
$menudata = (object) array_merge($this->get_primary_nav(), $this->get_custom_menu($output));
|
||||
$moremenu = new \core\navigation\output\more_menu($menudata, 'navbar-nav', false);
|
||||
$mobileprimarynav = $this->merge_primary_and_custom($this->get_primary_nav(), $this->get_custom_menu($output), true);
|
||||
$mobileprimarynav = array_merge($this->get_primary_nav(), $this->get_custom_menu($output));
|
||||
|
||||
$languagemenu = new \core\output\language_menu($this->page);
|
||||
|
||||
@ -116,111 +116,6 @@ class primary implements renderable, templatable {
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* When defining custom menu items, the active flag is not obvserved correctly. Therefore, the merge of the primary
|
||||
* and custom navigation must be handled a bit smarter. Change the "isactive" flag of the nodes (this may set by
|
||||
* default in the primary nav nodes but is entirely missing in the custom nav nodes).
|
||||
* Set the $expandedmenu argument to true when the menu for the mobile template is build.
|
||||
*
|
||||
* @param array $primary
|
||||
* @param array $custom
|
||||
* @param bool $expandedmenu
|
||||
* @return array
|
||||
*/
|
||||
protected function merge_primary_and_custom(array $primary, array $custom, bool $expandedmenu = false): array {
|
||||
if (empty($custom)) {
|
||||
return $primary; // No custom nav, nothing to merge.
|
||||
}
|
||||
// Remember the amount of primary nodes and whether we changed the active flag in the custom menu nodes.
|
||||
$primarylen = count($primary);
|
||||
$changed = false;
|
||||
foreach (array_keys($custom) as $i) {
|
||||
if (!$changed) {
|
||||
if ($this->flag_active_nodes($custom[$i], $expandedmenu)) {
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
$primary[] = $custom[$i];
|
||||
}
|
||||
// In case some custom node is active, mark all primary nav elements as inactive.
|
||||
if ($changed) {
|
||||
for ($i = 0; $i < $primarylen; $i++) {
|
||||
$primary[$i]['isactive'] = false;
|
||||
}
|
||||
}
|
||||
return $primary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive checks if any of the children is active. If that's the case this node (the parent) is active as
|
||||
* well. If the node has no children, check if the node itself is active. Use pass by reference for the node
|
||||
* object because we actively change/set the "isactive" flag inside the method and this needs to be kept at the
|
||||
* callers side.
|
||||
* Set $expandedmenu to true, if the mobile menu is done, in this case the active flag gets the node that is
|
||||
* actually active, while the parent hierarchy of the active node gets the flag isopen.
|
||||
*
|
||||
* @param object $node
|
||||
* @param bool $expandedmenu
|
||||
* @return bool
|
||||
*/
|
||||
protected function flag_active_nodes(object $node, bool $expandedmenu = false): bool {
|
||||
global $FULLME;
|
||||
$active = false;
|
||||
foreach (array_keys($node->children ?? []) as $c) {
|
||||
if ($this->flag_active_nodes($node->children[$c], $expandedmenu)) {
|
||||
$active = true;
|
||||
}
|
||||
}
|
||||
// One of the children is active, so this node (the parent) is active as well.
|
||||
if ($active) {
|
||||
if ($expandedmenu) {
|
||||
$node->isopen = true;
|
||||
} else {
|
||||
$node->isactive = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// By default, the menu item node to check is not active.
|
||||
$node->isactive = false;
|
||||
|
||||
// Check if the node url matches the called url. The node url may omit the trailing index.php, therefore check
|
||||
// this as well.
|
||||
if (empty($node->url)) {
|
||||
// Current menu node has no url set, so it can't be active.
|
||||
return false;
|
||||
}
|
||||
$nodeurl = parse_url($node->url);
|
||||
$current = parse_url($FULLME ?? '');
|
||||
|
||||
$pathmatches = false;
|
||||
// Exact match of the path of node and current url.
|
||||
if ($nodeurl['path'] === $current['path']) {
|
||||
$pathmatches = true;
|
||||
}
|
||||
// The current url may be trailed by a index.php, otherwise it's the same as the node path.
|
||||
if (!$pathmatches && $nodeurl['path'] . 'index.php' === $current['path']) {
|
||||
$pathmatches = true;
|
||||
}
|
||||
// No path did match, so the node can't be active.
|
||||
if (!$pathmatches) {
|
||||
return false;
|
||||
}
|
||||
// We are here because the path matches, so now look at the query string.
|
||||
$nodequery = $nodeurl['query'] ?? '';
|
||||
$currentquery = $current['query'] ?? '';
|
||||
// If the node has no query string defined, then the patch match is sufficient.
|
||||
if (empty($nodeurl['query'])) {
|
||||
$node->isactive = true;
|
||||
return true;
|
||||
}
|
||||
// If the node contains a query string then also the current url must match this query.
|
||||
if ($nodequery === $currentquery) {
|
||||
$node->isactive = true;
|
||||
}
|
||||
return $node->isactive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/Generate the user menu.
|
||||
*
|
||||
|
@ -153,17 +153,6 @@ class primary_test extends \advanced_testcase {
|
||||
* @param array $expected
|
||||
*/
|
||||
public function test_get_custom_menu(string $config, array $expected) {
|
||||
$actual = $this->get_custom_menu($config);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the template data for the custommenuitem that is set here via parameter.
|
||||
* @param string $config
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function get_custom_menu(string $config): array {
|
||||
global $CFG, $PAGE;
|
||||
$CFG->custommenuitems = $config;
|
||||
$output = new primary($PAGE);
|
||||
@ -182,7 +171,8 @@ class primary_test extends \advanced_testcase {
|
||||
|
||||
$actual = $method->invoke($output, $renderer);
|
||||
$custommenufilter($actual);
|
||||
return $actual;
|
||||
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -311,126 +301,4 @@ class primary_test extends \advanced_testcase {
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the merge_primary_and_custom and the eval_is_active method. Merge primary and custom menu with different
|
||||
* page urls and check that the correct nodes are active and open, depending on the data for each menu.
|
||||
*
|
||||
* @covers \core\navigation\output\primary::merge_primary_and_custom
|
||||
* @covers \core\navigation\output\primary::flag_active_nodes
|
||||
* @return void
|
||||
* @throws \ReflectionException
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public function test_merge_primary_and_custom() {
|
||||
global $PAGE;
|
||||
|
||||
$menu = $this->merge_and_render_menus();
|
||||
|
||||
$this->assertEquals(4, count(\array_keys($menu)));
|
||||
$msg = 'No active nodes for page ' . $PAGE->url;
|
||||
$this->assertEmpty($this->get_menu_item_names_by_type($menu, 'isactive'), $msg);
|
||||
$this->assertEmpty($this->get_menu_item_names_by_type($menu, 'isopen'), str_replace('active', 'open', $msg));
|
||||
|
||||
$msg = 'Active nodes desktop for /course/search.php';
|
||||
$menu = $this->merge_and_render_menus('/course/search.php');
|
||||
$isactive = $this->get_menu_item_names_by_type($menu, 'isactive');
|
||||
$this->assertEquals(['Courses', 'Course search'], $isactive, $msg);
|
||||
$this->assertEmpty($this->get_menu_item_names_by_type($menu, 'isopem'), str_replace('Active', 'Open', $msg));
|
||||
|
||||
$msg = 'Active nodes mobile for /course/search.php';
|
||||
$menu = $this->merge_and_render_menus('/course/search.php', true);
|
||||
$isactive = $this->get_menu_item_names_by_type($menu, 'isactive');
|
||||
$this->assertEquals(['Course search'], $isactive, $msg);
|
||||
$isopen = $this->get_menu_item_names_by_type($menu, 'isopen');
|
||||
$this->assertEquals(['Courses'], $isopen, str_replace('Active', 'Open', $msg));
|
||||
|
||||
$msg = 'Active nodes desktop for /course/search.php?areaids=core_course-course&q=test';
|
||||
$menu = $this->merge_and_render_menus('/course/search.php?areaids=core_course-course&q=test');
|
||||
$isactive = $this->get_menu_item_names_by_type($menu, 'isactive');
|
||||
$this->assertEquals(['Courses', 'Course search'], $isactive, $msg);
|
||||
|
||||
$msg = 'Active nodes desktop for /?theme=boost';
|
||||
$menu = $this->merge_and_render_menus('/?theme=boost');
|
||||
$isactive = $this->get_menu_item_names_by_type($menu, 'isactive');
|
||||
$this->assertEquals(['Theme', 'Boost'], $isactive, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to get an array of top menu items from the primary and the custom menu. The latter is defined
|
||||
* in this function.
|
||||
* @param string|null $url
|
||||
* @param bool|null $ismobile
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function merge_and_render_menus(?string $url = null, ?bool $ismobile = false): array {
|
||||
global $PAGE, $FULLME;
|
||||
|
||||
if ($url !== null) {
|
||||
$PAGE->set_url($url);
|
||||
$FULLME = $PAGE->url->out();
|
||||
}
|
||||
$primary = new primary($PAGE);
|
||||
|
||||
$method = new ReflectionMethod('core\navigation\output\primary', 'get_primary_nav');
|
||||
$method->setAccessible(true);
|
||||
$dataprimary = $method->invoke($primary);
|
||||
|
||||
// Take this custom menu that would come from the setting custommenitems.
|
||||
$custommenuitems = <<< ENDMENU
|
||||
Theme
|
||||
-Boost|/?theme=boost
|
||||
-Classic|/?theme=classic
|
||||
-Purge Cache|/admin/purgecaches.php
|
||||
Courses
|
||||
-All courses|/course/
|
||||
-Course search|/course/search.php
|
||||
-###
|
||||
-FAQ|https://example.org/faq
|
||||
-My Important Course|/course/view.php?id=4
|
||||
Mobile app|https://example.org/app|Download our app
|
||||
ENDMENU;
|
||||
|
||||
$datacustom = $this->get_custom_menu($custommenuitems);
|
||||
$method = new ReflectionMethod('core\navigation\output\primary', 'merge_primary_and_custom');
|
||||
$method->setAccessible(true);
|
||||
$menucomplete = $method->invoke($primary, $dataprimary, $datacustom, $ismobile);
|
||||
return $menucomplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the menu array structure (all nodes recursively) and fetch the node texts from the menu nodes that are
|
||||
* active/open (determined via param $nodetype that can be "inactive" or "isopen"). The returned array contains a
|
||||
* list of nade names that match this criterion.
|
||||
* @param array $menu
|
||||
* @param string $nodetype
|
||||
* @return array
|
||||
*/
|
||||
protected function get_menu_item_names_by_type(array $menu, string $nodetype): array {
|
||||
$matchednodes = [];
|
||||
foreach ($menu as $menuitem) {
|
||||
// Either the node is an array.
|
||||
if (is_array($menuitem)) {
|
||||
if ($menuitem[$nodetype] ?? false) {
|
||||
$matchednodes[] = $menuitem['text'];
|
||||
}
|
||||
// Recursively move through child items.
|
||||
if (array_key_exists('children', $menuitem) && count($menuitem['children'])) {
|
||||
$matchednodes = array_merge($matchednodes, $this->get_menu_item_names_by_type($menuitem['children'], $nodetype));
|
||||
}
|
||||
} else {
|
||||
// Otherwise the node is a standard object.
|
||||
if (isset($menuitem->{$nodetype}) && $menuitem->{$nodetype} === true) {
|
||||
$matchednodes[] = $menuitem->text;
|
||||
}
|
||||
// Recursively move through child items.
|
||||
if (isset($menuitem->children) && is_array($menuitem->children) && !empty($menuitem->children)) {
|
||||
$matchednodes = array_merge($matchednodes, $this->get_menu_item_names_by_type($menuitem->children, $nodetype));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $matchednodes;
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@
|
||||
<div class="list-group">
|
||||
{{#mobileprimarynav}}
|
||||
{{#haschildren}}
|
||||
<a id="drop-down-{{sort}}" href="#" class="list-group-item list-group-item-action icons-collapse-expand {{^isopen}}collapsed {{/isopen}}d-flex" data-toggle="collapse" data-target="#drop-down-menu-{{sort}}" aria-expanded="{{#isopen}}true{{/isopen}}{{^isopen}}false{{/isopen}}" aria-controls="drop-down-menu-{{sort}}">
|
||||
<a id="drop-down-{{sort}}" href="#" class="list-group-item list-group-item-action icons-collapse-expand collapsed d-flex" data-toggle="collapse" data-target="#drop-down-menu-{{sort}}" aria-expanded="false" aria-controls="drop-down-menu-{{sort}}">
|
||||
{{{text}}}
|
||||
<span class="ml-auto expanded-icon icon-no-margin mx-2">
|
||||
{{#pix}} t/expanded, core {{/pix}}
|
||||
@ -77,10 +77,10 @@
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
<div class="collapse {{#isopen}}show {{/isopen}}list-group-item p-0 border-0" role="menu" id="drop-down-menu-{{sort}}" aria-labelledby="drop-down-{{sort}}">
|
||||
<div class="collapse list-group-item p-0 border-0" role="menu" id="drop-down-menu-{{sort}}" aria-labelledby="drop-down-{{sort}}">
|
||||
{{#children}}
|
||||
{{^divider}}
|
||||
<a href="{{{url}}}" class="pl-5 {{^isactive}}bg-light{{/isactive}}{{#isactive}}active{{/isactive}} list-group-item list-group-item-action">{{{text}}}</a>
|
||||
<a href="{{{url}}}" class="pl-5 bg-light list-group-item list-group-item-action">{{{text}}}</a>
|
||||
{{/divider}}
|
||||
{{/children}}
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user