documentation/extensions/odoo_theme/static/js/menu.js
Antoine Vandevenne (anv) bcd752f01c [IMP] odoo_theme: scroll the active menu entry into view
In large and deeply nested TOCs like finance/accounting/fiscal_localizations/localizations/...
the user had to constantly scroll the menu to the menu entry they just
clicked because the expanded menu would not fit entirely on the page.
With this commit, the active menu entry is scrolled into view each time
the page is reloaded.

The previous behavior was also the reason for expanding the top menu
entries on only the homepage. Thanks to the scroll, they now always
expand.

Part-of: odoo/documentation#3251
2023-01-02 16:11:04 +01:00

118 lines
4.9 KiB
JavaScript

/* global _prepareAccordion */ //see utils.js
(function ($) {
document.addEventListener('DOMContentLoaded', () => {
const navigationMenu = document.getElementById('o_main_toctree');
// Allow to automatically collapse and expand TOC entries
_prepareAccordion(navigationMenu);
// Allow to respectively highlight and expand the TOC entries and their related TOC entry
// list whose page is displayed.
const deepestActiveTocEntries = _flagActiveTocEntriesAndLists(navigationMenu);
// Expand the top-level menu items.
_expandTopMenus(navigationMenu);
// Show hidden menu when the css classes have been properly specified
navigationMenu.removeAttribute('hidden');
// Scroll the menu to the deepest active TOC entry.
_scrollToDeepestActiveTocEntry(deepestActiveTocEntries);
});
/**
* Add the relevant classes on the TOC entries (and lists) whose page is displayed.
*
* TOC entries (<li> elements) that are on the path of the displayed page receive the
* `o_active_toc_entry` class, and their related (parent) TOC entry list (<ul> elements) receive
* the `show` (Bootstrap) class. Also, the deepest active TOC entries of their respective branch
* receive the `o_deepest_active_toc_entry` class, and their child TOC entry lists receive the
* `show` class.
*
* @param {HTMLElement} navigationMenu - The navigation menu.
* @return {Array} - The deepest active TOC entries of their respective branch.
*/
const _flagActiveTocEntriesAndLists = navigationMenu => {
const regexLayer = /\btoctree-l(?<layer>\d+)\b/;
let lastLayer = undefined;
let lastTocEntry = undefined;
const deepestActiveTocEntries = [];
navigationMenu.querySelectorAll('.current').forEach(element => {
if (element.tagName === 'UL') {
element.classList.add('show'); // Expand all related <ul>
} else if (element.tagName === 'LI') {
element.classList.add('o_active_toc_entry'); // Highlight all active <li>
let match = regexLayer.exec(element.className);
let currentLayer = parseInt(match.groups.layer, 10);
if (lastLayer && currentLayer <= lastLayer) { // Multiple lists are open.
// Flag the last active TOC entry as the deepest of its branch before moving to
// the next active branch.
deepestActiveTocEntries.push(lastTocEntry);
}
lastLayer = currentLayer;
lastTocEntry = element;
}
});
if (lastTocEntry) {
// The last active TOC entry is the deepest of its branch.
deepestActiveTocEntries.push(lastTocEntry);
}
deepestActiveTocEntries.forEach(deepestTocEntry => {
let tocEntryToHighlight;
const childTocEntryList = deepestTocEntry.querySelector('ul');
if (childTocEntryList) { // The TOC entry has an associated TOC entry list.
tocEntryToHighlight = deepestTocEntry;
childTocEntryList.classList.add('show');
} else { // The TOC entry is at the last level of its branch.
tocEntryToHighlight = deepestTocEntry.parentElement.parentElement;
}
tocEntryToHighlight.classList.add('o_deepest_active_toc_entry');
});
return deepestActiveTocEntries;
};
/**
* Add the `show` class on top-level menus.
*
* @param {HTMLElement} navigationMenu - The navigation menu.
*/
const _expandTopMenus = navigationMenu => {
navigationMenu.querySelectorAll('.toctree-l1').forEach(element => {
element.querySelector('ul').classList.add('show'); // Expand the top-level menus.
});
};
/**
* Scroll the active TOC entry into view.
*
* Note: this method must be called *after* the `_expandTopMenus` method and showing the menu.
*
* @param {Array} deepestActiveTocEntries - The deepest active TOC entries of their respective
* branch.
*/
const _scrollToDeepestActiveTocEntry = deepestActiveTocEntries => {
if (deepestActiveTocEntries.length > 0) {
deepestActiveTocEntries[0].scrollIntoView({block: 'center'});
}
};
document.addEventListener('scroll', () => {
// Allow to hide the searchbar when the page is scrolled in mobile.
_flagHeaderWithScrollPosition();
});
/**
* Add/Remove the class `o_header_scrolled` on the header according to the scroll position.
*/
const _flagHeaderWithScrollPosition = () => {
const header = document.querySelector('.o_main_header');
if (this.scrollY > 0) {
header.classList.add('o_header_scrolled');
} else {
header.classList.remove('o_header_scrolled');
}
};
})();