diff --git a/addons/web/static/src/legacy/js/core/dom.js b/addons/web/static/src/legacy/js/core/dom.js index 4f1696b43ea..aa5b0ef4a74 100644 --- a/addons/web/static/src/legacy/js/core/dom.js +++ b/addons/web/static/src/legacy/js/core/dom.js @@ -59,6 +59,21 @@ var dom = { _notify(content, options.callbacks); } }, + /** + * Detects if 2 elements are colliding. + * + * @param {Element} el1 + * @param {Element} el2 + * @returns {boolean} + */ + areColliding(el1, el2) { + const el1Rect = el1.getBoundingClientRect(); + const el2Rect = el2.getBoundingClientRect(); + return el1Rect.bottom > el2Rect.top + && el1Rect.top < el2Rect.bottom + && el1Rect.right > el2Rect.left + && el1Rect.left < el2Rect.right; + }, /** * Autoresize a $textarea node, by recomputing its height when necessary * @param {number} [options.min_height] by default, 50. diff --git a/addons/website/static/src/js/content/snippets.animation.js b/addons/website/static/src/js/content/snippets.animation.js index 07cc9c3fbb5..5eb0863019d 100644 --- a/addons/website/static/src/js/content/snippets.animation.js +++ b/addons/website/static/src/js/content/snippets.animation.js @@ -1028,6 +1028,60 @@ registry.HeaderHamburgerFull = publicWidget.Widget.extend({ }, }); +registry.BottomFixedElement = publicWidget.Widget.extend({ + selector: '#wrapwrap', + + /** + * @override + */ + async start() { + this.$scrollingElement = $().getScrollingElement(); + this.__hideBottomFixedElements = _.debounce(() => this._hideBottomFixedElements(), 500); + this.$scrollingElement.on('scroll.bottom_fixed_element', this.__hideBottomFixedElements); + $(window).on('resize.bottom_fixed_element', this.__hideBottomFixedElements); + return this._super(...arguments); + }, + /** + * @override + */ + destroy() { + this._super(...arguments); + this.$scrollingElement.off('.bottom_fixed_element'); + $(window).off('.bottom_fixed_element'); + $('.o_bottom_fixed_element').removeClass('o_bottom_fixed_element_hidden'); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Hides the elements that are fixed at the bottom of the screen if the + * scroll reaches the bottom of the page and if the elements hide a button. + * + * @private + */ + _hideBottomFixedElements() { + // Note: check in the whole DOM instead of #wrapwrap as unfortunately + // some things are still put outside of the #wrapwrap (like the livechat + // button which is the main reason of this code). + const $bottomFixedElements = $('.o_bottom_fixed_element'); + if (!$bottomFixedElements.length) { + return; + } + + $bottomFixedElements.removeClass('o_bottom_fixed_element_hidden'); + if ((this.$scrollingElement[0].offsetHeight + this.$scrollingElement[0].scrollTop) >= (this.$scrollingElement[0].scrollHeight - 2)) { + const buttonEls = [...this.$('.btn:visible')]; + for (const el of $bottomFixedElements) { + if (buttonEls.some(button => dom.areColliding(button, el))) { + el.classList.add('o_bottom_fixed_element_hidden'); + } + } + } + }, +}); + return { Widget: publicWidget.Widget, Animation: Animation, diff --git a/addons/website/static/src/scss/website.scss b/addons/website/static/src/scss/website.scss index ae4bed0d74a..e23ce3f0ef0 100644 --- a/addons/website/static/src/scss/website.scss +++ b/addons/website/static/src/scss/website.scss @@ -1422,3 +1422,10 @@ ul.o_checklist > li.o_checked::after { left: - ($o-checklist-margin-left - $o-checklist-checkmark-width) - 1; top: 0; } + +// Bottom fixed element (e.g. livechat button) +.modal-open .o_bottom_fixed_element, .o_bottom_fixed_element_hidden { + // Prevent bottom fixed elements from hidding buttons and + // hide them if a modal is open. + display: none !important; +} diff --git a/addons/website_livechat/static/src/legacy/public_livechat.js b/addons/website_livechat/static/src/legacy/public_livechat.js index 6bb1573564d..3cd2cc1d41d 100644 --- a/addons/website_livechat/static/src/legacy/public_livechat.js +++ b/addons/website_livechat/static/src/legacy/public_livechat.js @@ -7,6 +7,7 @@ var LivechatButton = require('im_livechat.legacy.im_livechat.im_livechat').Livec LivechatButton.include({ + className: `${LivechatButton.prototype.className} o_bottom_fixed_element`, /** * @override @@ -22,6 +23,19 @@ LivechatButton.include({ } return this._super(); }, + /** + * @override + */ + start() { + // We trigger a resize to launch the event that checks if this element hides + // a button when the page is loaded. + $(window).trigger('resize'); + return this._super(...arguments); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- /** * @override diff --git a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js index 6ab1fe1e25d..f384b43ab80 100644 --- a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js +++ b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js @@ -59,6 +59,9 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, { return $('#comparelist .o_product_panel_content').html(); } }); + // We trigger a resize to launch the event that checks if this element hides + // a button when the page is loaded. + $(window).trigger('resize'); $(document.body).on('click.product_comparaison_widget', '.comparator-popover .o_comparelist_products .o_remove', function (ev) { ev.preventDefault(); diff --git a/addons/website_sale_comparison/static/src/xml/comparison.xml b/addons/website_sale_comparison/static/src/xml/comparison.xml index fe887dd3fa8..800cdddb04a 100644 --- a/addons/website_sale_comparison/static/src/xml/comparison.xml +++ b/addons/website_sale_comparison/static/src/xml/comparison.xml @@ -1,7 +1,7 @@ -
+