diff --git a/addons/web/static/src/legacy/js/core/dom.js b/addons/web/static/src/legacy/js/core/dom.js index bfadd57bdd4..77d2313d1ce 100644 --- a/addons/web/static/src/legacy/js/core/dom.js +++ b/addons/web/static/src/legacy/js/core/dom.js @@ -161,6 +161,13 @@ var dom = { closestScrollable(el) { return $(el).closestScrollable()[0] || null; }, + /** + * @param {HTMLElement} el + * @see $.compensateScrollbar + */ + compensateScrollbar(el, ...rest) { + $(el).compensateScrollbar(...rest); + }, /** * jQuery find function behavior is:: * diff --git a/addons/web/static/src/legacy/js/libs/jquery.js b/addons/web/static/src/legacy/js/libs/jquery.js index 93404f98994..38944b4bf2e 100644 --- a/addons/web/static/src/legacy/js/libs/jquery.js +++ b/addons/web/static/src/legacy/js/libs/jquery.js @@ -139,6 +139,31 @@ $.fn.extend({ } return $el; }, + /** + * Adapt the given css property by adding the size of a scrollbar if any. + * Limitation: only works if the given css property is not already used as + * inline style for another reason. + * + * @param {boolean} [add=true] + * @param {boolean} [isScrollElement=true] + * @param {string} [cssProperty='padding-right'] + */ + compensateScrollbar(add = true, isScrollElement = true, cssProperty = 'padding-right') { + for (const el of this) { + // Compensate scrollbar + el.style.removeProperty(cssProperty); + if (!add) { + return; + } + const scrollableEl = isScrollElement ? el : $(el).parent().closestScrollable()[0]; + const style = window.getComputedStyle(el); + const borderLeftWidth = parseInt(style.borderLeftWidth.replace('px', '')); + const borderRightWidth = parseInt(style.borderRightWidth.replace('px', '')); + const bordersWidth = borderLeftWidth + borderRightWidth; + const newValue = parseInt(style[cssProperty]) + scrollableEl.offsetWidth - scrollableEl.clientWidth - bordersWidth; + el.style.setProperty(cssProperty, `${newValue}px`, 'important'); + } + }, /** * @returns {jQuery} */ diff --git a/addons/website/static/src/js/content/menu.js b/addons/website/static/src/js/content/menu.js index 3a5a7054bfc..c66f74f351a 100644 --- a/addons/website/static/src/js/content/menu.js +++ b/addons/website/static/src/js/content/menu.js @@ -2,6 +2,7 @@ odoo.define('website.content.menu', function (require) { 'use strict'; const config = require('web.config'); +var dom = require('web.dom'); var publicWidget = require('web.public.widget'); var animations = require('website.content.snippets.animation'); const extraMenuUpdateCallbacks = []; @@ -31,6 +32,7 @@ const BaseAnimatedHeader = animations.Animation.extend({ */ start: function () { this.$main = this.$el.next('main'); + this.isOverlayHeader = !!this.$el.closest('.o_header_overlay, .o_header_overlay_theme').length; this.$dropdowns = this.$el.find('.dropdown, .dropdown-menu'); this.$navbarCollapses = this.$el.find('.navbar-collapse'); @@ -69,13 +71,18 @@ const BaseAnimatedHeader = animations.Animation.extend({ // Private //-------------------------------------------------------------------------- + /** + * @private + */ + _adaptFixedHeaderPosition() { + dom.compensateScrollbar(this.el, this.fixedHeader, false, 'right'); + }, /** * @private */ _adaptToHeaderChange: function () { this.options.wysiwyg && this.options.wysiwyg.odooEditor.observerUnactive(); - this.headerHeight = this.$el.outerHeight(); - this.topGap = this._computeTopGap(); + this._updateMainPaddingTop(); // Take menu into account when `dom.scrollTo()` is used whenever it is // visible - be it floating, fully displayed or partially hidden. this.el.classList.toggle('o_top_fixed_element', this._isShown()); @@ -135,6 +142,19 @@ const BaseAnimatedHeader = animations.Animation.extend({ this.fixedHeader = useFixed; this._adaptToHeaderChange(); this.el.classList.toggle('o_header_affixed', useFixed); + this._adaptFixedHeaderPosition(); + }, + /** + * @private + */ + _updateMainPaddingTop: function () { + this.headerHeight = this.$el.outerHeight(); + this.topGap = this._computeTopGap(); + + if (this.isOverlayHeader) { + return; + } + this.$main.css('padding-top', this.fixedHeader ? this.headerHeight : ''); }, //-------------------------------------------------------------------------- @@ -178,6 +198,7 @@ const BaseAnimatedHeader = animations.Animation.extend({ * @private */ _updateHeaderOnResize: function () { + this._adaptFixedHeaderPosition(); if (document.body.classList.contains('overflow-hidden') && config.device.size_class > config.device.SIZES.SM) { document.body.classList.remove('overflow-hidden'); diff --git a/addons/website/static/src/scss/website.scss b/addons/website/static/src/scss/website.scss index b642bc072af..c26425acf8f 100644 --- a/addons/website/static/src/scss/website.scss +++ b/addons/website/static/src/scss/website.scss @@ -746,7 +746,7 @@ $-transition-duration: 200ms; .o_header_affixed { display: block; @include o-position-absolute(0, 0, auto, 0); - position: sticky; + position: fixed; &:not(.o_header_no_transition) { transition: transform $-transition-duration;