diff options
| author | qsm-odoo <qsm@odoo.com> | 2020-09-17 10:41:10 +0000 |
|---|---|---|
| committer | qsm-odoo <qsm@odoo.com> | 2020-09-22 08:29:11 +0000 |
| commit | 4e7be69825163c0a0ff41c882a196fc7f3158fb3 (patch) | |
| tree | 517a928cddbfa019033af95f2f284cab4e82496d /addons/web/static/src/js/libs | |
| parent | 416b125803d043badef8e4b022c100e33d1dba04 (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/web/static/src/js/libs')
| -rw-r--r-- | addons/web/static/src/js/libs/bootstrap.js | 14 | ||||
| -rw-r--r-- | addons/web/static/src/js/libs/jquery.js | 87 |
2 files changed, 101 insertions, 0 deletions
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); +}; }); |
