summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorqsm-odoo <qsm@odoo.com>2020-09-17 10:41:10 +0000
committerqsm-odoo <qsm@odoo.com>2020-09-22 08:29:11 +0000
commit4e7be69825163c0a0ff41c882a196fc7f3158fb3 (patch)
tree517a928cddbfa019033af95f2f284cab4e82496d /addons
parent416b125803d043badef8e4b022c100e33d1dba04 (diff)
[IMP] website, *: move page scrollbar to #wrapwrap
*: web, web_editor, portal, account, website_blog, website_forum This is probably not ideal to move the scrolling behavior out of the html element but this will be needed for the editor to not have a double scrollbar on the right. This is actually also useful for the scrollbar to appear under the purple odoo menu instead of its right. This commit, if to be reverted in the future, however contains many improvements to make the transition easier that should be kept: jQuery and Odoo functions to be able to scrollTo a specific element, get the top scrollable element, check if an element is scrollable, etc. Also, some magic was done: if some code tries to scroll to a specific scroll position via jQuery's scrollTop or animate functions on the html/body element, those will still work as intended (obviously, using the new utils to do that is however recommended). This commit also reviews our scroll behaviors to make them more robust and share the code. Part of https://github.com/odoo/odoo/pull/57975 task-2344227
Diffstat (limited to 'addons')
-rw-r--r--addons/account/static/src/js/account_portal_sidebar.js3
-rw-r--r--addons/portal/static/src/js/portal_chatter.js21
-rw-r--r--addons/web/static/src/js/core/dom.js101
-rw-r--r--addons/web/static/src/js/libs/bootstrap.js14
-rw-r--r--addons/web/static/src/js/libs/jquery.js87
-rw-r--r--addons/web/static/src/scss/base_frontend.scss12
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.editor.js41
-rw-r--r--addons/website/static/src/js/content/menu.js12
-rw-r--r--addons/website/static/src/js/content/snippets.animation.js41
-rw-r--r--addons/website/static/src/js/editor/snippets.editor.js1
-rw-r--r--addons/website/static/src/js/editor/snippets.options.js12
-rw-r--r--addons/website/static/src/snippets/s_table_of_content/000.js36
-rw-r--r--addons/website/views/website_navbar_templates.xml2
-rw-r--r--addons/website_blog/static/src/js/contentshare.js14
-rw-r--r--addons/website_blog/static/src/js/website_blog.js8
-rw-r--r--addons/website_forum/static/src/js/website_forum.js9
16 files changed, 293 insertions, 121 deletions
diff --git a/addons/account/static/src/js/account_portal_sidebar.js b/addons/account/static/src/js/account_portal_sidebar.js
index 0b48c0a9647..58114e00a56 100644
--- a/addons/account/static/src/js/account_portal_sidebar.js
+++ b/addons/account/static/src/js/account_portal_sidebar.js
@@ -1,6 +1,7 @@
odoo.define('account.AccountPortalSidebar', function (require) {
'use strict';
+const dom = require('web.dom');
var publicWidget = require('web.public.widget');
var PortalSidebar = require('portal.PortalSidebar');
var utils = require('web.utils');
@@ -57,7 +58,7 @@ publicWidget.registry.AccountPortalSidebar = PortalSidebar.extend({
if (!$target.length) {
return;
}
- $('html, body').scrollTop($target.offset().top);
+ dom.scrollTo($target[0], {duration: 0});
},
/**
* @private
diff --git a/addons/portal/static/src/js/portal_chatter.js b/addons/portal/static/src/js/portal_chatter.js
index a0a761f4d8c..134efbf6097 100644
--- a/addons/portal/static/src/js/portal_chatter.js
+++ b/addons/portal/static/src/js/portal_chatter.js
@@ -2,6 +2,7 @@ odoo.define('portal.chatter', function (require) {
'use strict';
var core = require('web.core');
+const dom = require('web.dom');
var publicWidget = require('web.public.widget');
var time = require('web.time');
var portalComposer = require('portal.composer');
@@ -292,17 +293,15 @@ publicWidget.registry.portalChatter = publicWidget.Widget.extend({
/**
* @override
*/
- start: function () {
- var self = this;
- var defs = [this._super.apply(this, arguments)];
- var chatter = new PortalChatter(this, this.$el.data());
- defs.push(chatter.appendTo(this.$el));
- return Promise.all(defs).then(function () {
- // scroll to the right place after chatter loaded
- if (window.location.hash === '#' + self.$el.attr('id')) {
- $('html, body').scrollTop(self.$el.offset().top);
- }
- });
+ async start() {
+ const proms = [this._super.apply(this, arguments)];
+ const chatter = new PortalChatter(this, this.$el.data());
+ proms.push(chatter.appendTo(this.$el));
+ await Promise.all(proms);
+ // scroll to the right place after chatter loaded
+ if (window.location.hash === `#${this.el.id}`) {
+ dom.scrollTo(this.el, {duration: 0});
+ }
},
});
diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js
index 716593eb558..f4aba50be8a 100644
--- a/addons/web/static/src/js/core/dom.js
+++ b/addons/web/static/src/js/core/dom.js
@@ -142,6 +142,12 @@ var dom = {
}
},
/**
+ * @return {HTMLElement}
+ */
+ closestScrollable(el) {
+ return $(el).closestScrollable()[0];
+ },
+ /**
* jQuery find function behavior is::
*
* $('A').find('A B') <=> $('A A B')
@@ -229,6 +235,26 @@ var dom = {
return position;
},
/**
+ * @returns {HTMLElement}
+ */
+ getScrollingElement() {
+ return $().getScrollingElement()[0];
+ },
+ /**
+ * @param {HTMLElement} el
+ * @returns {boolean}
+ */
+ hasScrollableContent(el) {
+ return $(el).hasScrollableContent();
+ },
+ /**
+ * @param {HTMLElement} el
+ * @returns {boolean}
+ */
+ isScrollable(el) {
+ return $(el).isScrollable();
+ },
+ /**
* Protects a function which is to be used as a handler by preventing its
* execution for the duration of a previous call to it (including async
* parts of that call).
@@ -455,6 +481,81 @@ var dom = {
}
},
/**
+ * Computes the size by which a scrolling point should be decreased so that
+ * the top fixed elements of the page appear above that scrolling point.
+ *
+ * @returns {number}
+ */
+ scrollFixedOffset() {
+ let size = 0;
+ for (const el of $('.o_top_fixed_element')) {
+ size += $(el).outerHeight();
+ }
+ return size;
+ },
+ /**
+ * @param {HTMLElement} el - the element to stroll to
+ * @param {number} [options] - same as animate of jQuery
+ * @param {number} [options.extraOffset=0]
+ * extra offset to add on top of the automatic one (the automatic one
+ * being computed based on fixed header sizes)
+ * @param {number} [options.forcedOffset]
+ * offset used instead of the automatic one (extraOffset will be
+ * ignored too)
+ * @return {Promise}
+ */
+ scrollTo(el, options = {}) {
+ const $el = $(el);
+ const $scrollable = $el.parent().closestScrollable();
+ const $topLevelScrollable = $().getScrollingElement();
+ const isTopScroll = $scrollable.is($topLevelScrollable);
+
+ function _computeScrollTop() {
+ const elPosition = $scrollable[0].scrollTop + ($el.offset().top - $scrollable.offset().top);
+ let offset = options.forcedOffset;
+ if (offset === undefined) {
+ offset = (isTopScroll ? dom.scrollFixedOffset() : 0) + (options.extraOffset || 0);
+ }
+ return Math.max(0, elPosition - offset);
+ }
+
+ const originalScrollTop = _computeScrollTop();
+
+ return new Promise(resolve => {
+ const clonedOptions = Object.assign({}, options);
+
+ // During the animation, detect any change needed for the scroll
+ // offset. If any occurs, stop the animation and continuing it to
+ // the new scroll point for the remaining time.
+ // Note: limitation, the animation won't be as fluid as possible if
+ // the easing mode is different of 'linear'.
+ clonedOptions.progress = function (a, b, remainingMs) {
+ if (options.progress) {
+ options.progress.apply(this, ...arguments);
+ }
+ const newScrollTop = _computeScrollTop();
+ if (Math.abs(newScrollTop - originalScrollTop) <= 1.0) {
+ return;
+ }
+ $scrollable.stop();
+ dom.scrollTo(el, Object.assign({}, options, {
+ duration: remainingMs,
+ })).then(() => resolve());
+ };
+
+ // Detect the end of the animation to be able to indicate it to
+ // the caller via the returned Promise.
+ clonedOptions.complete = function () {
+ if (options.complete) {
+ options.complete.apply(this, ...arguments);
+ }
+ resolve();
+ };
+
+ $scrollable.animate({scrollTop: originalScrollTop}, clonedOptions);
+ });
+ },
+ /**
* Creates an automatic 'more' dropdown-menu for a set of navbar items.
*
* @param {jQuery} $el
diff --git a/addons/web/static/src/js/libs/bootstrap.js b/addons/web/static/src/js/libs/bootstrap.js
index 8f200794fa8..07e04c64a39 100644
--- a/addons/web/static/src/js/libs/bootstrap.js
+++ b/addons/web/static/src/js/libs/bootstrap.js
@@ -82,6 +82,20 @@ $.fn.tooltip.Constructor.prototype.show = function () {
return bootstrapShowFunction.call(this);
};
+/* Bootstrap scrollspy fix for non-body to spy */
+
+const bootstrapSpyRefreshFunction = $.fn.scrollspy.Constructor.prototype.refresh;
+$.fn.scrollspy.Constructor.prototype.refresh = function () {
+ bootstrapSpyRefreshFunction.apply(this, arguments);
+ if (this._scrollElement === window || this._config.method !== 'offset') {
+ return;
+ }
+ const baseScrollTop = this._getScrollTop();
+ for (let i = 0; i < this._offsets.length; i++) {
+ this._offsets[i] += baseScrollTop;
+ }
+};
+
return {
makeExtendedSanitizeWhiteList: makeExtendedSanitizeWhiteList,
};
diff --git a/addons/web/static/src/js/libs/jquery.js b/addons/web/static/src/js/libs/jquery.js
index 1a33d43c2e5..6921b004019 100644
--- a/addons/web/static/src/js/libs/jquery.js
+++ b/addons/web/static/src/js/libs/jquery.js
@@ -122,5 +122,92 @@ $.fn.extend({
});
});
},
+ /**
+ * @return {jQuery}
+ */
+ closestScrollable() {
+ let $el = this;
+ while ($el[0] !== document.scrollingElement) {
+ if ($el.isScrollable()) {
+ return $el;
+ }
+ $el = $el.parent();
+ }
+ return $el;
+ },
+ /**
+ * @returns {jQuery}
+ */
+ getScrollingElement() {
+ const $baseScrollingElement = $(document.scrollingElement);
+ if ($baseScrollingElement.isScrollable()
+ && $baseScrollingElement.hasScrollableContent()) {
+ return $baseScrollingElement;
+ }
+ const bodyHeight = $(document.body).height();
+ for (const el of document.body.children) {
+ // Search for a body child which is at least as tall as the body
+ // and which has the ability to scroll if enough content in it. If
+ // found, suppose this is the top scrolling element.
+ if (el.scrollHeight < bodyHeight) {
+ continue;
+ }
+ const $el = $(el);
+ if ($el.isScrollable()) {
+ return $el;
+ }
+ }
+ return $baseScrollingElement;
+ },
+ /**
+ * @return {boolean}
+ */
+ hasScrollableContent() {
+ return this[0].scrollHeight > this[0].clientHeight;
+ },
+ /**
+ * @returns {boolean}
+ */
+ isScrollable() {
+ const overflow = this.css('overflow-y');
+ return overflow === 'auto' || overflow === 'scroll'
+ || (overflow === 'visible' && this === document.scrollingElement);
+ },
});
+
+// jQuery functions monkey-patching
+
+// Some magic to ensure scrolltop and animate on html/body animate the top level
+// scrollable element even if not html or body.
+const originalScrollTop = $.fn.scrollTop;
+$.fn.scrollTop = function (value) {
+ if (value !== undefined && this.filter('html, body').length) {
+ // The caller wants to scroll a set of elements including html and/or
+ // body to a specific point -> do that but make sure to add the real
+ // top level element to that set of elements if any different is found.
+ originalScrollTop.apply(this.not('html, body').add($().getScrollingElement()), arguments);
+ return this;
+ } else if (value === undefined && this.eq(0).is('html, body')) {
+ // The caller wants to get the scroll point of a set of elements, jQuery
+ // will return the scroll point of the first one, if it is html or body
+ // return the scroll point of the real top level element.
+ return originalScrollTop.apply($().getScrollingElement(), arguments);
+ }
+ return originalScrollTop.apply(this, arguments);
+};
+const originalAnimate = $.fn.animate;
+$.fn.animate = function (properties, ...rest) {
+ const props = Object.assign({}, properties);
+ if ('scrollTop' in props && this.filter('html, body').length) {
+ // The caller wants to scroll a set of elements including html and/or
+ // body to a specific point -> do that but make sure to add the real
+ // top level element to that set of elements if any different is found.
+ originalAnimate.call(this.not('html, body').add($().getScrollingElement()), {'scrollTop': props['scrollTop']}, ...rest);
+ delete props['scrollTop'];
+ }
+ if (!Object.keys(props).length) {
+ return this;
+ }
+ return originalAnimate.call(this, props, ...rest);
+};
});
diff --git a/addons/web/static/src/scss/base_frontend.scss b/addons/web/static/src/scss/base_frontend.scss
index f1c341a89f9..585ce965ec5 100644
--- a/addons/web/static/src/scss/base_frontend.scss
+++ b/addons/web/static/src/scss/base_frontend.scss
@@ -1,4 +1,9 @@
// Frontend general
+html, body, #wrapwrap {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
#wrapwrap {
// The z-index is useful to prevent that children with a negative z-index
// go behind the wrapwrap (we create a new stacking context).
@@ -6,8 +11,11 @@
position: relative;
display: flex;
flex-flow: column nowrap;
- width: 100%;
- min-height: 100%;
+ // ... we delegate the scroll to that top element instead of the window/body
+ // This is at least needed for the edit mode to not have a double scrollbar
+ // due to the right editor panel (and since we want to minimize the style
+ // difference between edit mode and non-edit mode (wysiwyg)...).
+ overflow: auto;
> * {
flex: 0 0 auto;
diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index aa5bdd867e9..ecebf152f9d 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -100,7 +100,7 @@ var SnippetEditor = Widget.extend({
},
},
});
- this.draggableComponent = new SmoothScrollOnDrag(this, this.$el, $('html'), smoothScrollOptions);
+ this.draggableComponent = new SmoothScrollOnDrag(this, this.$el, $().getScrollingElement(), smoothScrollOptions);
} else {
this.$('.o_overlay_move_options').addClass('d-none');
$customize.find('.oe_snippet_clone').addClass('d-none');
@@ -997,8 +997,8 @@ var SnippetsMenu = Widget.extend({
// Hide the active overlay when scrolling.
// Show it again and recompute all the overlays after the scroll.
- // TODO: current limitation, suppose that this is the body which scrolls
- this.$document.on('scroll.snippets_menu', _.throttle(() => {
+ this.$scrollingElement = $().getScrollingElement();
+ this.$scrollingElement.on('scroll.snippets_menu', _.throttle(() => {
for (const editor of this.snippetEditors) {
editor.toggleOverlayVisibility(false);
}
@@ -1054,6 +1054,7 @@ var SnippetsMenu = Widget.extend({
this.$snippetEditorArea.remove();
this.$window.off('.snippets_menu');
this.$document.off('.snippets_menu');
+ this.$scrollingElement.off('.snippets_menu');
}
core.bus.off('deactivate_snippet', this, this._onDeactivateSnippet);
delete this.cacheSnippetTemplate[this.options.snippets];
@@ -1778,13 +1779,6 @@ var SnippetsMenu = Widget.extend({
*/
_getScrollOptions(options = {}) {
return Object.assign({}, options, {
- offsetElements: Object.assign({
- $top: $('#web_editor-top-edit'), // TODO should ideally be retrieved another way
- $left: this.$el,
- }, options.offsetElements),
- scrollBoundaries: Object.assign({
- left: false,
- }, options.scrollBoundaries),
jQueryDraggableOptions: Object.assign({
appendTo: this.$body,
cursor: 'move',
@@ -1954,7 +1948,7 @@ var SnippetsMenu = Widget.extend({
},
},
});
- this.draggableComponent = new SmoothScrollOnDrag(this, $snippets, $('html'), smoothScrollOptions);
+ this.draggableComponent = new SmoothScrollOnDrag(this, $snippets, $().getScrollingElement(), smoothScrollOptions);
},
/**
* Adds the 'o_default_snippet_text' class on nodes which contain only
@@ -2005,29 +1999,14 @@ var SnippetsMenu = Widget.extend({
.prop('disabled', tab !== this.tabs.OPTIONS);
},
/**
- * Scroll to the dropped snippet.
+ * Scrolls to given snippet.
*
* @private
- * @param {jQuery} [$el] - dropped snippet
- * @param {integer} [scrollValue] - scrollValue
+ * @param {jQuery} $el - snippet to scroll to
+ * @return {Promise}
*/
- _scrollToSnippet: function ($el, scrollValue) {
- // return if it's an inner snippet or if it's dropped in a modal
- if ($el.get(0).tagName.toLowerCase() !== 'section' || $el.parent().find("[class^='modal-']").length) {
- return Promise.resolve();
- }
- let headerHeight = 0;
- _.each($('.o_top_fixed_element'), el => headerHeight += $(el).outerHeight());
- return new Promise(resolve => {
- $('html, body').animate(
- {
- scrollTop: scrollValue - headerHeight - 50,
- },
- 700,
- 'swing',
- resolve,
- );
- });
+ async _scrollToSnippet($el) {
+ return dom.scrollTo($el[0], {extraOffset: 50});
},
/**
* @private
diff --git a/addons/website/static/src/js/content/menu.js b/addons/website/static/src/js/content/menu.js
index 27000cfd48d..bcf9ebbaf78 100644
--- a/addons/website/static/src/js/content/menu.js
+++ b/addons/website/static/src/js/content/menu.js
@@ -76,10 +76,8 @@ const BaseAnimatedHeader = animations.Animation.extend({
this._updateMainPaddingTop();
this.el.classList.toggle('o_top_fixed_element', this.fixedHeader && this._isShown());
- const bottom = this.el.getBoundingClientRect().bottom
- || (this.el.nextElementSibling.getBoundingClientRect().top + window.scrollY);
for (const callback of extraMenuUpdateCallbacks) {
- callback(bottom);
+ callback();
}
},
/**
@@ -130,6 +128,14 @@ const BaseAnimatedHeader = animations.Animation.extend({
_toggleFixedHeader: function (useFixed = true) {
this.fixedHeader = useFixed;
this.el.classList.toggle('o_header_affixed', useFixed);
+ // Compensate scrollbar
+ if (useFixed) {
+ const scrollableEl = this.$el.parent().closestScrollable()[0];
+ const style = window.getComputedStyle(this.el);
+ this.el.style.setProperty('right', `${parseInt(style['right']) + scrollableEl.offsetWidth - scrollableEl.clientWidth}px`, 'important');
+ } else {
+ this.el.style.removeProperty('right');
+ }
this._adaptToHeaderChange();
},
/**
diff --git a/addons/website/static/src/js/content/snippets.animation.js b/addons/website/static/src/js/content/snippets.animation.js
index 81640cde197..368be2e810b 100644
--- a/addons/website/static/src/js/content/snippets.animation.js
+++ b/addons/website/static/src/js/content/snippets.animation.js
@@ -8,6 +8,7 @@ odoo.define('website.content.snippets.animation', function (require) {
var Class = require('web.Class');
var config = require('web.config');
var core = require('web.core');
+const dom = require('web.dom');
var mixins = require('web.mixins');
var publicWidget = require('web.public.widget');
var utils = require('web.utils');
@@ -123,12 +124,13 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
// Initialize the animation startEvents, startTarget, endEvents, endTarget and callbacks
this._updateCallback = updateCallback;
this.startEvents = startEvents || 'scroll';
- this.$startTarget = $($startTarget || window);
+ this.$startTarget = $($startTarget ? $startTarget : this.startEvents === 'scroll' ? $().getScrollingElement()[0] : window);
if (options.getStateCallback) {
this._getStateCallback = options.getStateCallback;
- } else if (this.startEvents === 'scroll' && this.$startTarget[0] === window) {
+ } else if (this.startEvents === 'scroll' && this.$startTarget[0] === $().getScrollingElement()[0]) {
+ const $scrollable = this.$startTarget;
this._getStateCallback = function () {
- return window.pageYOffset;
+ return $scrollable.scrollTop();
};
} else if (this.startEvents === 'resize' && this.$startTarget[0] === window) {
this._getStateCallback = function () {
@@ -898,38 +900,19 @@ registry.anchorSlide = publicWidget.Widget.extend({
* @private
* @param {jQuery} $el the element to scroll to.
* @param {string} [scrollValue='true'] scroll value
+ * @returns {Promise}
*/
- _scrollTo: function ($el, scrollValue = 'true', forcedDelay = undefined) {
- const _computeScrollTo = () => {
- const headerHeight = this._computeHeaderHeight();
- const offset = $el.css('position') !== 'fixed' ? $el.offset().top : $el.position().top;
- return offset - headerHeight;
- };
- const originalScrollTo = _computeScrollTo();
- return new Promise(resolve => {
- $('html, body').animate({
- scrollTop: originalScrollTo,
- }, {
- duration: forcedDelay || (scrollValue === 'true' ? 500 : 0),
- progress: (a, b, remainingMs) => {
- const scrollTo = _computeScrollTo();
- if (Math.abs(scrollTo - originalScrollTo) >= 1.0) {
- $('html, body').stop();
- this._scrollTo($el, scrollValue, remainingMs).then(() => resolve());
- }
- },
- complete: () => resolve(),
- });
+ async _scrollTo($el, scrollValue = 'true') {
+ return dom.scrollTo($el[0], {
+ duration: scrollValue === 'true' ? 500 : 0,
+ extraOffset: this._computeExtraOffset(),
});
},
/**
* @private
*/
- _computeHeaderHeight: function () {
- let headerHeight = 0;
- const $navbarFixed = $('.o_top_fixed_element');
- _.each($navbarFixed, el => headerHeight += $(el).outerHeight());
- return headerHeight;
+ _computeExtraOffset() {
+ return 0;
},
//--------------------------------------------------------------------------
diff --git a/addons/website/static/src/js/editor/snippets.editor.js b/addons/website/static/src/js/editor/snippets.editor.js
index e226fd70543..40f999f836c 100644
--- a/addons/website/static/src/js/editor/snippets.editor.js
+++ b/addons/website/static/src/js/editor/snippets.editor.js
@@ -100,6 +100,7 @@ weSnippetEditor.Class.include({
if (!options.offsetElements || !options.offsetElements.$top) {
const $header = $('#top');
if ($header.length) {
+ finalOptions.offsetElements = finalOptions.offsetElements || {};
finalOptions.offsetElements.$top = $header;
}
}
diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js
index 5819402c93a..3b53a594bd7 100644
--- a/addons/website/static/src/js/editor/snippets.options.js
+++ b/addons/website/static/src/js/editor/snippets.options.js
@@ -5,6 +5,7 @@ const {ColorpickerWidget} = require('web.Colorpicker');
const config = require('web.config');
var core = require('web.core');
var Dialog = require('web.Dialog');
+const dom = require('web.dom');
const weUtils = require('web_editor.utils');
var options = require('web_editor.snippets.options');
const wUtils = require('website.utils');
@@ -2504,13 +2505,10 @@ options.registry.SnippetMove = options.Class.extend({
break;
}
if (params.name === 'move_up_opt' || params.name === 'move_down_opt') {
- $('html, body').animate(
- {
- scrollTop: this.$target.offset().top - $(window).height() / 2,
- },
- 500,
- 'linear',
- );
+ dom.scrollTo(this.$target[0], {
+ extraOffset: 50,
+ easing: 'linear',
+ });
}
},
});
diff --git a/addons/website/static/src/snippets/s_table_of_content/000.js b/addons/website/static/src/snippets/s_table_of_content/000.js
index ac610304e48..68250bc88fa 100644
--- a/addons/website/static/src/snippets/s_table_of_content/000.js
+++ b/addons/website/static/src/snippets/s_table_of_content/000.js
@@ -11,18 +11,18 @@ const TableOfContent = publicWidget.Widget.extend({
/**
* @override
*/
- start: function () {
- this._initializeNavbarTopPosition();
+ async start() {
+ await this._super(...arguments);
+ this._updateTableOfContentNavbarPosition();
extraMenuUpdateCallbacks.push(this._updateTableOfContentNavbarPosition.bind(this));
- return this._super.apply(this, arguments);
},
/**
* @override
*/
- destroy: function () {
+ destroy() {
this.$target.css('top', '');
this.$target.find('.s_table_of_content_navbar').css('top', '');
- this._super.apply(this, arguments);
+ this._super(...arguments);
},
//--------------------------------------------------------------------------
@@ -30,26 +30,16 @@ const TableOfContent = publicWidget.Widget.extend({
//--------------------------------------------------------------------------
/**
- * Initialize the top position of the snippet navbar according to the height
- * of the headers of the page.
- *
* @private
*/
- _initializeNavbarTopPosition: function () {
- let headerHeight = 0;
+ _updateTableOfContentNavbarPosition() {
+ let position = 0;
const $fixedElements = $('.o_top_fixed_element');
- _.each($fixedElements, el => headerHeight += $(el).outerHeight());
- this._updateTableOfContentNavbarPosition(headerHeight);
- },
- /**
- * @private
- * @param {number} position
- */
- _updateTableOfContentNavbarPosition: function (position) {
+ _.each($fixedElements, el => position += $(el).outerHeight());
const isHorizontalNavbar = this.$target.hasClass('s_table_of_content_horizontal_navbar');
this.$target.css('top', isHorizontalNavbar ? position : '');
this.$target.find('.s_table_of_content_navbar').css('top', isHorizontalNavbar ? '' : position + 20);
- $('body').scrollspy({target: '.s_table_of_content_navbar', offset: position + 100});
+ $().getScrollingElement().scrollspy({target: '.s_table_of_content_navbar', method: 'offset', offset: position + 100});
},
});
@@ -66,15 +56,15 @@ publicWidget.registry.anchorSlide.include({
* @override
* @private
*/
- _computeHeaderHeight: function () {
- let headerHeight = this._super(...arguments);
+ _computeExtraOffset() {
+ let extraOffset = this._super(...arguments);
if (this.$el.hasClass('table_of_content_link')) {
const tableOfContentNavbarEl = this.$el.closest('.s_table_of_content_navbar_sticky.s_table_of_content_horizontal_navbar');
if (tableOfContentNavbarEl.length > 0) {
- headerHeight += $(tableOfContentNavbarEl).outerHeight();
+ extraOffset += $(tableOfContentNavbarEl).outerHeight();
}
}
- return headerHeight;
+ return extraOffset;
},
});
diff --git a/addons/website/views/website_navbar_templates.xml b/addons/website/views/website_navbar_templates.xml
index 6537ed5e646..1126a8aaf5a 100644
--- a/addons/website/views/website_navbar_templates.xml
+++ b/addons/website/views/website_navbar_templates.xml
@@ -20,7 +20,7 @@
<t t-set="body_classname" t-value="(body_classname if body_classname else '') + (' o_connected_user' if env['ir.ui.view'].user_has_groups('base.group_user') else '')"/>
</xpath>
<xpath expr="//div[@id='wrapwrap']" position="before">
- <nav groups="base.group_user" t-if="website" id="oe_main_menu_navbar" class="o_main_navbar o_top_fixed_element">
+ <nav groups="base.group_user" t-if="website" id="oe_main_menu_navbar" class="o_main_navbar">
<ul id="oe_applications">
<li class="dropdown active">
<a class="dropdown-toggle full" data-toggle="dropdown" href="#" accesskey="h">
diff --git a/addons/website_blog/static/src/js/contentshare.js b/addons/website_blog/static/src/js/contentshare.js
index 8f8d40ae6e4..2e6c9cd45dd 100644
--- a/addons/website_blog/static/src/js/contentshare.js
+++ b/addons/website_blog/static/src/js/contentshare.js
@@ -1,6 +1,8 @@
odoo.define('website_blog.contentshare', function (require) {
'use strict';
+const dom = require('web.dom');
+
$.fn.share = function (options) {
var option = $.extend($.fn.share.defaults, options);
var selected_text = "";
@@ -39,13 +41,13 @@ $.fn.share = function (options) {
return $popover_content;
},
commentEdition: function () {
- var positionComment = ($('#o_wblog_post_comments').position()).top - 50;
$(".o_portal_chatter_composer_form textarea").val('"' + selected_text + '" ').focus();
- $('html, body').stop().animate({
- 'scrollTop': positionComment
- }, 500, 'swing', function () {
- window.location.hash = 'blog_post_comment_quote';
- });
+ const commentsEl = $('#o_wblog_post_comments')[0];
+ if (commentsEl) {
+ dom.scrollTo(commentsEl).then(() => {
+ window.location.hash = 'blog_post_comment_quote';
+ });
+ }
},
getSelection: function (share) {
if (window.getSelection) {
diff --git a/addons/website_blog/static/src/js/website_blog.js b/addons/website_blog/static/src/js/website_blog.js
index 3dfeb6b2f3d..3286f553121 100644
--- a/addons/website_blog/static/src/js/website_blog.js
+++ b/addons/website_blog/static/src/js/website_blog.js
@@ -1,10 +1,10 @@
odoo.define('website_blog.website_blog', function (require) {
'use strict';
-var publicWidget = require('web.public.widget');
-require('website.content.snippets.animation');
+const dom = require('web.dom');
+const publicWidget = require('web.public.widget');
-publicWidget.registry.websiteBlog = publicWidget.registry.anchorSlide.extend({
+publicWidget.registry.websiteBlog = publicWidget.Widget.extend({
selector: '.website_blog',
events: {
'click #o_wblog_next_container': '_onNextBlogClick',
@@ -93,7 +93,7 @@ publicWidget.registry.websiteBlog = publicWidget.registry.anchorSlide.extend({
* @param {Function} callback - to be executed after the scroll is performed
*/
_forumScrollAction: function ($el, duration, callback) {
- this._scrollTo($el, 'true', duration).then(() => callback());
+ dom.scrollTo($el[0], {duration: duration}).then(() => callback());
},
});
});
diff --git a/addons/website_forum/static/src/js/website_forum.js b/addons/website_forum/static/src/js/website_forum.js
index 65325a17c60..2158b720461 100644
--- a/addons/website_forum/static/src/js/website_forum.js
+++ b/addons/website_forum/static/src/js/website_forum.js
@@ -1,6 +1,7 @@
odoo.define('website_forum.website_forum', function (require) {
'use strict';
+const dom = require('web.dom');
var core = require('web.core');
var weDefaultOptions = require('web_editor.wysiwyg.default_options');
var wysiwygLoader = require('web_editor.loader');
@@ -166,9 +167,11 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
});
this.$('#post_reply').on('shown.bs.collapse', function (e) {
- $('html').animate({
- scrollTop: $('#post_reply').offset().top - ($(window).innerHeight() - $('#post_reply').innerHeight())
- }, 500);
+ const replyEl = document.querySelector('#post_reply');
+ const scrollingElement = dom.closestScrollable(replyEl.parentNode);
+ dom.scrollTo(replyEl, {
+ forcedOffset: $(scrollingElement).innerHeight() - $(replyEl).innerHeight(),
+ });
});
return this._super.apply(this, arguments);