[MERGE] website, *: remove menu loading effect
*: web, website_sale See sub-commits for details. In short: the menu was loading with an effect partly for technical reasons: potentially folding menu items in a "+" dropdown instead of making it go over multiple lines. Since the JS lazy loading, the menu made even more time to appear. All of that is now removed: the menu is there as soon as the page appears to the user, without effect or delay. task-2618929 closes odoo/odoo#77843 X-original-commit: 0681678c4d028f9f2bda419996bf5b14e771c132 Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
@@ -61,6 +61,7 @@ This module provides the core of the Odoo Web Client.
|
||||
'web/static/src/boot.js',
|
||||
'web/static/src/session.js',
|
||||
'web/static/src/legacy/js/core/cookie_utils.js',
|
||||
'web/static/src/legacy/js/core/menu.js',
|
||||
],
|
||||
'web.assets_common': [
|
||||
('include', 'web._assets_helpers'),
|
||||
@@ -79,6 +80,7 @@ This module provides the core of the Odoo Web Client.
|
||||
('remove', 'web/static/src/boot.js'),
|
||||
('remove', 'web/static/src/session.js'),
|
||||
('remove', 'web/static/src/legacy/js/core/cookie_utils.js'),
|
||||
('remove', 'web/static/src/legacy/js/core/menu.js'),
|
||||
],
|
||||
'web.assets_backend': [
|
||||
('include', 'web._assets_helpers'),
|
||||
|
||||
@@ -616,131 +616,6 @@ var dom = {
|
||||
$scrollable.animate({scrollTop: originalScrollTop}, clonedOptions);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Creates an automatic 'more' dropdown-menu for a set of navbar items.
|
||||
*
|
||||
* @param {jQuery} $el
|
||||
* @param {Object} [options]
|
||||
* @param {string} [options.unfoldable='none']
|
||||
* @param {function} [options.maxWidth]
|
||||
* @param {string} [options.sizeClass='SM']
|
||||
*/
|
||||
initAutoMoreMenu: function ($el, options) {
|
||||
options = _.extend({
|
||||
unfoldable: 'none',
|
||||
maxWidth: false,
|
||||
sizeClass: 'SM',
|
||||
}, options || {});
|
||||
|
||||
var autoMarginLeftRegex = /\bm[lx]?(?:-(?:sm|md|lg|xl))?-auto\b/;
|
||||
var autoMarginRightRegex = /\bm[rx]?(?:-(?:sm|md|lg|xl))?-auto\b/;
|
||||
|
||||
var $extraItemsToggle = null;
|
||||
|
||||
var debouncedAdapt = _.debounce(_adapt, 250);
|
||||
core.bus.on('resize', null, debouncedAdapt);
|
||||
_adapt();
|
||||
|
||||
$el.data('dom:autoMoreMenu:adapt', _adapt);
|
||||
$el.data('dom:autoMoreMenu:destroy', function () {
|
||||
_restore();
|
||||
core.bus.off('resize', null, debouncedAdapt);
|
||||
$el.removeData(['dom:autoMoreMenu:destroy', 'dom:autoMoreMenu:adapt']);
|
||||
});
|
||||
|
||||
function _restore() {
|
||||
if ($extraItemsToggle === null) {
|
||||
return;
|
||||
}
|
||||
var $items = $extraItemsToggle.children('.dropdown-menu').children();
|
||||
$items.addClass('nav-item');
|
||||
$items.children('.dropdown-item, a').removeClass('dropdown-item').addClass('nav-link');
|
||||
$items.insertBefore($extraItemsToggle);
|
||||
$extraItemsToggle.remove();
|
||||
$extraItemsToggle = null;
|
||||
}
|
||||
|
||||
function _adapt() {
|
||||
_restore();
|
||||
|
||||
if (!$el.is(':visible') || $el.closest('.show').length) {
|
||||
// Never transform the menu when it is not visible yet or if
|
||||
// it is a toggleable one.
|
||||
return;
|
||||
}
|
||||
if (config.device.size_class <= config.device.SIZES[options.sizeClass]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $allItems = $el.children();
|
||||
var $unfoldableItems = $allItems.filter(options.unfoldable);
|
||||
var $items = $allItems.not($unfoldableItems);
|
||||
|
||||
var maxWidth = 0;
|
||||
if (options.maxWidth) {
|
||||
maxWidth = options.maxWidth();
|
||||
} else {
|
||||
maxWidth = computeFloatOuterWidthWithMargins($el[0], true, true, true);
|
||||
var style = window.getComputedStyle($el[0]);
|
||||
maxWidth -= (parseFloat(style.paddingLeft) + parseFloat(style.paddingRight) + parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth));
|
||||
maxWidth -= _.reduce($unfoldableItems, function (sum, el) {
|
||||
return sum + computeFloatOuterWidthWithMargins(el, true, true, false);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
var nbItems = $items.length;
|
||||
var menuItemsWidth = _.reduce($items, function (sum, el) {
|
||||
return sum + computeFloatOuterWidthWithMargins(el, true, true, false);
|
||||
}, 0);
|
||||
|
||||
if (maxWidth - menuItemsWidth >= -0.001) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $dropdownMenu = $('<ul/>', {class: 'dropdown-menu'});
|
||||
$extraItemsToggle = $('<li/>', {class: 'nav-item dropdown o_extra_menu_items'})
|
||||
.append($('<a/>', {role: 'button', href: '#', class: 'nav-link dropdown-toggle o-no-caret', 'data-toggle': 'dropdown', 'aria-expanded': false})
|
||||
.append($('<i/>', {class: 'fa fa-plus'})))
|
||||
.append($dropdownMenu);
|
||||
$extraItemsToggle.insertAfter($items.last());
|
||||
|
||||
menuItemsWidth += computeFloatOuterWidthWithMargins($extraItemsToggle[0], true, true, false);
|
||||
do {
|
||||
menuItemsWidth -= computeFloatOuterWidthWithMargins($items.eq(--nbItems)[0], true, true, false);
|
||||
} while (!(maxWidth - menuItemsWidth >= -0.001) && (nbItems > 0));
|
||||
|
||||
var $extraItems = $items.slice(nbItems).detach();
|
||||
$extraItems.removeClass('nav-item');
|
||||
$extraItems.children('.nav-link, a').removeClass('nav-link').addClass('dropdown-item');
|
||||
$dropdownMenu.append($extraItems);
|
||||
$extraItemsToggle.find('.nav-link').toggleClass('active', $extraItems.children().hasClass('active'));
|
||||
}
|
||||
|
||||
function computeFloatOuterWidthWithMargins(el, mLeft, mRight, considerAutoMargins) {
|
||||
var rect = el.getBoundingClientRect();
|
||||
var style = window.getComputedStyle(el);
|
||||
var outerWidth = rect.right - rect.left;
|
||||
if (mLeft !== false && (considerAutoMargins || !autoMarginLeftRegex.test(el.getAttribute('class')))) {
|
||||
outerWidth += parseFloat(style.marginLeft);
|
||||
}
|
||||
if (mRight !== false && (considerAutoMargins || !autoMarginRightRegex.test(el.getAttribute('class')))) {
|
||||
outerWidth += parseFloat(style.marginRight);
|
||||
}
|
||||
// Would be NaN for invisible elements for example
|
||||
return isNaN(outerWidth) ? 0 : outerWidth;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Cleans what has been done by ``initAutoMoreMenu``.
|
||||
*
|
||||
* @param {jQuery} $el
|
||||
*/
|
||||
destroyAutoMoreMenu: function ($el) {
|
||||
var destroyFunc = $el.data('dom:autoMoreMenu:destroy');
|
||||
if (destroyFunc) {
|
||||
destroyFunc.call(null);
|
||||
}
|
||||
},
|
||||
};
|
||||
return dom;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
/**
|
||||
* Creates an automatic 'more' dropdown-menu for a set of navbar items.
|
||||
*
|
||||
* @param {HTMLElement} el
|
||||
* @param {Object} [options]
|
||||
* @param {string} [options.unfoldable='none'] selector for items that do not
|
||||
* need to be added to dropdown-menu.
|
||||
* @param {float} [options.maxWidth] The max width value that menu content
|
||||
* 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) {
|
||||
return;
|
||||
}
|
||||
options = Object.assign({
|
||||
unfoldable: 'none',
|
||||
maxWidth: false,
|
||||
minSize: '767',
|
||||
images: [],
|
||||
loadingStyleClasses: [],
|
||||
}, options || {});
|
||||
|
||||
const isUserNavbar = el.parentElement.classList.contains('o_main_navbar');
|
||||
const dropdownSubMenuClasses = ['show', 'border', 'position-static'];
|
||||
var autoMarginLeftRegex = /\bm[lx]?(?:-(?:sm|md|lg|xl))?-auto\b/;
|
||||
var autoMarginRightRegex = /\bm[rx]?(?:-(?:sm|md|lg|xl))?-auto\b/;
|
||||
var extraItemsToggle = null;
|
||||
let debounce;
|
||||
|
||||
if (options.images.length) {
|
||||
await _afterImagesLoading(options.images);
|
||||
_adapt();
|
||||
}
|
||||
|
||||
const debouncedAdapt = () => {
|
||||
clearTimeout(debounce);
|
||||
debounce = setTimeout(_adapt, 250);
|
||||
};
|
||||
window.addEventListener('resize', debouncedAdapt);
|
||||
_adapt();
|
||||
|
||||
el.addEventListener('dom:autoMoreMenu:adapt', _adapt);
|
||||
el.addEventListener('dom:autoMoreMenu:destroy', destroy, {once: true});
|
||||
|
||||
function _restore() {
|
||||
if (!extraItemsToggle) {
|
||||
return;
|
||||
}
|
||||
// Move extra menu items from dropdown-menu to menu element in the same order.
|
||||
[...extraItemsToggle.querySelector('.dropdown-menu').children].forEach((item) => {
|
||||
if (!isUserNavbar) {
|
||||
item.classList.add('nav-item');
|
||||
const itemLink = item.querySelector('.dropdown-item');
|
||||
itemLink.classList.remove('dropdown-item');
|
||||
itemLink.classList.add('nav-link');
|
||||
} else {
|
||||
item.classList.remove('dropdown-item');
|
||||
const dropdownSubMenu = item.querySelector('.dropdown-menu');
|
||||
if (dropdownSubMenu) {
|
||||
dropdownSubMenu.classList.remove(...dropdownSubMenuClasses);
|
||||
}
|
||||
}
|
||||
el.insertBefore(item, extraItemsToggle);
|
||||
});
|
||||
extraItemsToggle.remove();
|
||||
extraItemsToggle = null;
|
||||
}
|
||||
|
||||
function _adapt() {
|
||||
if (options.loadingStyleClasses.length) {
|
||||
el.classList.add(...options.loadingStyleClasses);
|
||||
}
|
||||
_restore();
|
||||
|
||||
// Ignore invisible/toggleable top menu element & small viewports.
|
||||
if (!el.getClientRects().length || el.closest('.show')
|
||||
|| window.matchMedia(`(max-width: ${options.minSize}px)`).matches) {
|
||||
return _endAutoMoreMenu();
|
||||
}
|
||||
|
||||
let unfoldableItems = [];
|
||||
const items = [...el.children].filter((node) => {
|
||||
if (node.matches && !node.matches(options.unfoldable)) {
|
||||
return true;
|
||||
}
|
||||
unfoldableItems.push(node);
|
||||
return false;
|
||||
});
|
||||
var nbItems = items.length;
|
||||
var menuItemsWidth = items.reduce((sum, el) => sum + computeFloatOuterWidthWithMargins(el, true, true, false), 0);
|
||||
let maxWidth = 0;
|
||||
|
||||
if (options.maxWidth) {
|
||||
maxWidth = options.maxWidth();
|
||||
}
|
||||
if (!maxWidth) {
|
||||
maxWidth = computeFloatOuterWidthWithMargins(el, true, true, true);
|
||||
var style = window.getComputedStyle(el);
|
||||
maxWidth -= (parseFloat(style.paddingLeft) + parseFloat(style.paddingRight) + parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth));
|
||||
maxWidth -= unfoldableItems.reduce((sum, el) => sum + computeFloatOuterWidthWithMargins(el, true, true, false), 0);
|
||||
}
|
||||
// Ignore if there is no overflow.
|
||||
if (maxWidth - menuItemsWidth >= -0.001) {
|
||||
return _endAutoMoreMenu();
|
||||
}
|
||||
|
||||
const dropdownMenu = _addExtraItemsButton(items[nbItems - 1].nextElementSibling);
|
||||
menuItemsWidth += computeFloatOuterWidthWithMargins(extraItemsToggle, true, true, false);
|
||||
do {
|
||||
menuItemsWidth -= computeFloatOuterWidthWithMargins(items[--nbItems], true, true, false);
|
||||
} while (!(maxWidth - menuItemsWidth >= -0.001) && (nbItems > 0));
|
||||
|
||||
const extraItems = items.slice(nbItems);
|
||||
extraItems.forEach((el) => {
|
||||
if (!isUserNavbar) {
|
||||
const navLink = el.querySelector('.nav-link, a');
|
||||
el.classList.remove('nav-item');
|
||||
navLink.classList.remove('nav-link');
|
||||
navLink.classList.add('dropdown-item');
|
||||
navLink.classList.toggle('active', el.classList.contains('active'));
|
||||
} else {
|
||||
const dropdownSubMenu = el.querySelector('.dropdown-menu');
|
||||
el.classList.add('dropdown-item');
|
||||
if (dropdownSubMenu) {
|
||||
dropdownSubMenu.classList.add(...dropdownSubMenuClasses);
|
||||
}
|
||||
}
|
||||
dropdownMenu.appendChild(el);
|
||||
});
|
||||
_endAutoMoreMenu();
|
||||
}
|
||||
|
||||
function computeFloatOuterWidthWithMargins(el, mLeft, mRight, considerAutoMargins) {
|
||||
var rect = el.getBoundingClientRect();
|
||||
var style = window.getComputedStyle(el);
|
||||
var outerWidth = rect.right - rect.left;
|
||||
if (mLeft !== false && (considerAutoMargins || !autoMarginLeftRegex.test(el.getAttribute('class')))) {
|
||||
outerWidth += parseFloat(style.marginLeft);
|
||||
}
|
||||
if (mRight !== false && (considerAutoMargins || !autoMarginRightRegex.test(el.getAttribute('class')))) {
|
||||
outerWidth += parseFloat(style.marginRight);
|
||||
}
|
||||
// Would be NaN for invisible elements for example
|
||||
return isNaN(outerWidth) ? 0 : outerWidth;
|
||||
}
|
||||
|
||||
function _addExtraItemsButton(target) {
|
||||
let dropdownMenu = document.createElement('ul');
|
||||
extraItemsToggle = document.createElement('li');
|
||||
const extraItemsToggleIcon = document.createElement('i');
|
||||
const extraItemsToggleLink = document.createElement('a');
|
||||
|
||||
dropdownMenu.className = 'dropdown-menu';
|
||||
extraItemsToggle.className = 'nav-item dropdown o_extra_menu_items';
|
||||
extraItemsToggleIcon.className = 'fa fa-plus';
|
||||
Object.entries({
|
||||
role: 'button',
|
||||
href: '#',
|
||||
class: 'nav-link dropdown-toggle o-no-caret',
|
||||
'data-toggle': 'dropdown',
|
||||
'aria-expanded': false,
|
||||
}).forEach(([key, value]) => {
|
||||
extraItemsToggleLink.setAttribute(key, value);
|
||||
});
|
||||
|
||||
extraItemsToggleLink.appendChild(extraItemsToggleIcon);
|
||||
extraItemsToggle.appendChild(extraItemsToggleLink);
|
||||
extraItemsToggle.appendChild(dropdownMenu);
|
||||
el.insertBefore(extraItemsToggle, target);
|
||||
return dropdownMenu;
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
_restore();
|
||||
window.removeEventListener('resize', debouncedAdapt);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans what has been done by ``initAutoMoreMenu``.
|
||||
*
|
||||
* @param {HTMLElement} el
|
||||
*/
|
||||
export function destroyAutoMoreMenu(el) {
|
||||
el.dispatchEvent(new Event('dom:autoMoreMenu:destroy'));
|
||||
}
|
||||
@@ -133,10 +133,12 @@
|
||||
],
|
||||
'web.assets_frontend_minimal': [
|
||||
'website/static/src/js/content/inject_dom.js',
|
||||
'website/static/src/js/content/auto_hide_menu.js',
|
||||
],
|
||||
'web.assets_frontend_lazy': [
|
||||
# Remove assets_frontend_minimal
|
||||
('remove', 'website/static/src/js/content/inject_dom.js'),
|
||||
('remove', 'website/static/src/js/content/auto_hide_menu.js'),
|
||||
],
|
||||
'web._assets_primary_variables': [
|
||||
'website/static/src/scss/primary_variables.scss',
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { initAutoMoreMenu } from '@web/legacy/js/core/menu';
|
||||
|
||||
/**
|
||||
* Auto adapt the header layout so that elements are not wrapped on a new line.
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const header = document.querySelector('header#top');
|
||||
if (header) {
|
||||
const topMenu = header.querySelector('#top_menu');
|
||||
if (header.classList.contains('o_no_autohide_menu')) {
|
||||
topMenu.classList.remove('o_menu_loading');
|
||||
return;
|
||||
}
|
||||
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);
|
||||
});
|
||||
initAutoMoreMenu(topMenu, {
|
||||
unfoldable: unfoldable,
|
||||
images: images,
|
||||
loadingStyleClasses: ['o_menu_loading']
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -4,9 +4,7 @@ odoo.define('website.content.menu', function (require) {
|
||||
const config = require('web.config');
|
||||
var dom = require('web.dom');
|
||||
var publicWidget = require('web.public.widget');
|
||||
var wUtils = require('website.utils');
|
||||
var animations = require('website.content.snippets.animation');
|
||||
|
||||
const extraMenuUpdateCallbacks = [];
|
||||
|
||||
const BaseAnimatedHeader = animations.Animation.extend({
|
||||
@@ -413,51 +411,6 @@ publicWidget.registry.FadeOutHeader = BaseDisappearingHeader.extend({
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Auto adapt the header layout so that elements are not wrapped on a new line.
|
||||
*/
|
||||
publicWidget.registry.autohideMenu = publicWidget.Widget.extend({
|
||||
selector: 'header#top',
|
||||
disabledInEditableMode: false,
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async start() {
|
||||
await this._super(...arguments);
|
||||
this.$topMenu = this.$('#top_menu');
|
||||
this.noAutohide = this.$el.is('.o_no_autohide_menu');
|
||||
if (!this.noAutohide) {
|
||||
await wUtils.onceAllImagesLoaded(this.$('.navbar'), this.$('.o_mega_menu, .o_offcanvas_logo_container, .dropdown-menu .o_lang_flag'));
|
||||
|
||||
// The previous code will make sure we wait for images to be fully
|
||||
// loaded before initializing the auto more menu. But in some cases,
|
||||
// it is not enough, we also have to wait for fonts or even extra
|
||||
// scripts. Those will have no impact on the feature in most cases
|
||||
// though, so we will only update the auto more menu at that time,
|
||||
// no wait for it to initialize the feature.
|
||||
var $window = $(window);
|
||||
$window.on('load.autohideMenu', function () {
|
||||
$window.trigger('resize');
|
||||
});
|
||||
|
||||
dom.initAutoMoreMenu(this.$topMenu, {unfoldable: '.divider, .divider ~ li, .o_no_autohide_item'});
|
||||
}
|
||||
this.$topMenu.removeClass('o_menu_loading');
|
||||
this.$topMenu.trigger('menu_loaded');
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy() {
|
||||
this._super(...arguments);
|
||||
if (!this.noAutohide && this.$topMenu) {
|
||||
$(window).off('.autohideMenu');
|
||||
dom.destroyAutoMoreMenu(this.$topMenu);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Note: this works well with the affixMenu... by chance (menuDirection is
|
||||
* called after alphabetically).
|
||||
|
||||
@@ -5,7 +5,7 @@ import dom from 'web.dom';
|
||||
import publicWidget from 'web.public.widget';
|
||||
import concurrency from 'web.concurrency';
|
||||
import Widget from 'web.Widget';
|
||||
|
||||
import { initAutoMoreMenu } from '@web/legacy/js/core/menu';
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
var WebsiteNavbar = publicWidget.RootWidget.extend({
|
||||
@@ -40,7 +40,7 @@ var WebsiteNavbar = publicWidget.RootWidget.extend({
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
dom.initAutoMoreMenu(this.$('ul.o_menu_sections'), {
|
||||
initAutoMoreMenu(this.el.querySelector('ul.o_menu_sections'), {
|
||||
maxWidth: function () {
|
||||
// The navbar contains different elements in community and
|
||||
// enterprise, so we check for both of them here only
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<t t-set="website_sale_order" t-value="website.sale_get_order()" />
|
||||
<t t-set="show_cart" t-value="true"/>
|
||||
<li t-attf-class="#{_item_class} divider d-none"/> <!-- Make sure the cart and related menus are not folded (see autohideMenu) -->
|
||||
<li t-attf-class="o_wsale_my_cart #{not show_cart and 'd-none'} #{_item_class}">
|
||||
<li t-attf-class="o_wsale_my_cart align-self-md-start #{not show_cart and 'd-none'} #{_item_class}">
|
||||
<a href="/shop/cart" t-attf-class="#{_link_class}">
|
||||
<i t-if="_icon" class="fa fa-shopping-cart"/>
|
||||
<span t-if="_text">My Cart</span>
|
||||
@@ -124,7 +124,7 @@
|
||||
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
||||
<t t-call="website_sale.header_cart_link">
|
||||
<t t-set="_text" t-value="True"/>
|
||||
<t t-set="_item_class" t-value="'nav-item'"/>
|
||||
<t t-set="_item_class" t-value="'nav-item ml-md-auto'"/>
|
||||
<t t-set="_link_class" t-value="'nav-link'"/>
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user