From dde609a8386bf611a2ae9562da9f16123a8a7507 Mon Sep 17 00:00:00 2001 From: xO-Tx Date: Fri, 1 Oct 2021 14:32:12 +0000 Subject: [PATCH] [IMP] web, website: remove website menu loading effect The goal of this commit is to remove the menu fade-in effect on page load since we can now call the initAutoMoreMenu in non-lazy JS code. When 'initAutoMoreMenu' called, images won't be loaded yet, that's why we add a class: '.o_menu_image_placeholder' to set a default size on images and once all images are loaded, we reupdate the menu. task-2618929 X-original-commit: 6be166df24dedbe4729e1e7bfe6a302713932b74 Part-of: odoo/odoo#77843 --- addons/web/static/src/legacy/js/core/menu.js | 30 ++++++++++++ .../static/src/js/content/auto_hide_menu.js | 46 ++++++++----------- addons/website/static/src/scss/website.scss | 13 +++--- 3 files changed, 56 insertions(+), 33 deletions(-) diff --git a/addons/web/static/src/legacy/js/core/menu.js b/addons/web/static/src/legacy/js/core/menu.js index 8a8870f6626..5c2a996e7f3 100644 --- a/addons/web/static/src/legacy/js/core/menu.js +++ b/addons/web/static/src/legacy/js/core/menu.js @@ -11,6 +11,9 @@ * can take => the overflowing (foldable) items are added in the dropdown-menu. * @param {string} [options.minSize] the menu auto-hide option will be disabled * if viewport is smaller than minSize. + * @param {Array} [options.images=[]] images to wait for before menu update. + * @param {Array} [options.loadingStyleClasses=[]] list of CSS classes to add while + * updating the menu. */ export async function initAutoMoreMenu(el, options) { if (!el) { @@ -20,6 +23,8 @@ export async function initAutoMoreMenu(el, options) { unfoldable: 'none', maxWidth: false, minSize: '767', + images: [], + loadingStyleClasses: [], }, options || {}); const isUserNavbar = el.parentElement.classList.contains('o_main_navbar'); @@ -29,6 +34,11 @@ export async function initAutoMoreMenu(el, options) { var extraItemsToggle = null; let debounce; + if (options.images.length) { + await _afterImagesLoading(options.images); + _adapt(); + } + const debouncedAdapt = () => { clearTimeout(debounce); debounce = setTimeout(_adapt, 250); @@ -173,6 +183,26 @@ export async function initAutoMoreMenu(el, options) { el.removeEventListener('dom:autoMoreMenu:adapt', _adapt); } + function _afterImagesLoading(images) { + const defs = images.map((image) => { + if (image.complete || !image.getClientRects().length) { + return null; + } + return new Promise(function (resolve, reject) { + if (!image.width) { + // The purpose of the 'o_menu_image_placeholder' class is to add a default + // size to non loaded images (on the first update) to prevent flickering. + image.classList.add('o_menu_image_placeholder'); + } + image.addEventListener('load', () => { + image.classList.remove('o_menu_image_placeholder'); + resolve(); + }); + }); + }); + return Promise.all(defs); + } + function _endAutoMoreMenu() { el.classList.remove(...options.loadingStyleClasses); } diff --git a/addons/website/static/src/js/content/auto_hide_menu.js b/addons/website/static/src/js/content/auto_hide_menu.js index 16588b4fecf..f592b9213e7 100644 --- a/addons/website/static/src/js/content/auto_hide_menu.js +++ b/addons/website/static/src/js/content/auto_hide_menu.js @@ -7,33 +7,27 @@ import { initAutoMoreMenu } from '@web/legacy/js/core/menu'; */ document.addEventListener('DOMContentLoaded', async () => { const header = document.querySelector('header#top'); - if (!header || header.classList.contains('o_no_autohide_menu')) { - return; - } - const topMenu = header.querySelector('#top_menu'); - const unfoldable = '.divider, .divider ~ li, .o_no_autohide_item, .js_language_selector'; - const excludedImagesSelector = '.o_mega_menu, .o_offcanvas_logo_container, .o_lang_flag'; - const excludedImages = [...header.querySelectorAll(excludedImagesSelector)]; - const images = [...header.querySelectorAll('img')].filter((img) => { - excludedImages.forEach(node => { - if (node.contains(img)) { - return false; - } - }); - return img.matches && !img.matches(excludedImagesSelector); - }); - const defs = images.map((image) => { - if (image.complete) { - return null; + if (header) { + const topMenu = header.querySelector('#top_menu'); + if (header.classList.contains('o_no_autohide_menu')) { + topMenu.classList.remove('o_menu_loading'); + return; } - return new Promise(function (resolve, reject) { - image.addEventListener('load', () => { - resolve(); + const unfoldable = '.divider, .divider ~ li, .o_no_autohide_item, .js_language_selector'; + const excludedImagesSelector = '.o_mega_menu, .o_offcanvas_logo_container, .o_lang_flag'; + const excludedImages = [...header.querySelectorAll(excludedImagesSelector)]; + const images = [...header.querySelectorAll('img')].filter((img) => { + excludedImages.forEach(node => { + if (node.contains(img)) { + return false; + } }); + return img.matches && !img.matches(excludedImagesSelector); }); - }); - - await Promise.all(defs); - initAutoMoreMenu(topMenu, {unfoldable: unfoldable}); - topMenu.classList.remove('o_menu_loading'); + initAutoMoreMenu(topMenu, { + unfoldable: unfoldable, + images: images, + loadingStyleClasses: ['o_menu_loading'] + }); + } }); diff --git a/addons/website/static/src/scss/website.scss b/addons/website/static/src/scss/website.scss index 73a5ff9a5ae..af7f2f1f52c 100644 --- a/addons/website/static/src/scss/website.scss +++ b/addons/website/static/src/scss/website.scss @@ -207,20 +207,19 @@ body:not(.o_fullscreen) { .navbar-collapse { min-width: 0; // Allows it to shrink during loading } - .nav-item { - transition: opacity 1000ms ease 0s; - } .btn { // This was a default bootstrap style before but it was removed from // the library at some point. It seems important in the header so that // the header does not flicker during loading. white-space: nowrap; } - .o_menu_loading { + .o_menu_image_placeholder { + width: 80px !important; + } + #top_menu { flex-wrap: nowrap !important; - overflow: hidden !important; - - .nav-item { + &.o_menu_loading { + overflow: hidden !important; opacity: 0 !important; } }