From ea2bcfc83cf69f75a4de2257667dab92b976ac33 Mon Sep 17 00:00:00 2001 From: Rodolpho Lima Date: Fri, 26 May 2023 17:35:38 +0200 Subject: [PATCH] [FIX] web_editor: toolbar position update on scroll Before this commit the floating toolbar position was not being updated on scroll events in certain scenarios. The problem resulted from adding the event listener to the incorrect scroll container, which was calculated only once in the Wysiwyg life cycle, on start. The calculated scrollable container could be incorrect due to: - changes in the web client resulting from the swich from read to edit mode taking place after the Wysiwyg was started, - changes in the web client resulting from a window resize (particularly when, in the form view, the chatter switches position from right to bottom and vice-versa), - the editor being mounted inside an iframe and the scroll container being an element in the top document. - the editor being mounter inside an iframe and the scroll container being the iframe's root element. This commit solves the issue by detecting scroll events anywhere in the document (and in the iframe's document when it applies) and updating the toolbar position when the editable is a descendant of the scrolled element. task-3263463 X-original-commit: 5382c995eecfe2fcea39d9456860b48704645c99 Part-of: odoo/odoo#133459 --- .../static/src/js/wysiwyg/wysiwyg.js | 23 +++++------ .../static/src/js/wysiwyg/wysiwyg_iframe.js | 41 +++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js index b36f2b5e70b..63853bb2f28 100644 --- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js +++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js @@ -273,6 +273,7 @@ export class Wysiwyg extends Component { this.colorpickers = {}; this._onDocumentMousedown = this._onDocumentMousedown.bind(this); this._onBlur = this._onBlur.bind(this); + this._onScroll = this._onScroll.bind(this); this.customizableLinksSelector = 'a' + ':not([data-bs-toggle="tab"])' + ':not([data-bs-toggle="collapse"])' @@ -947,6 +948,7 @@ export class Wysiwyg extends Component { for (const timeout of this.tooltipTimeouts) { clearTimeout(timeout); } + document.removeEventListener('scroll', this._onScroll, true); } /** * @override @@ -1919,18 +1921,10 @@ export class Wysiwyg extends Component { } this._updateFaResizeButtons(); }); - // we need the Timeout to be sure the editable content is loaded - // before calculating the scrollParent() element. - setTimeout(() => { - const scrollableContainer = this.$el.scrollParent(); - if (!options.snippets && scrollableContainer.length) { - this.odooEditor.addDomListener( - scrollableContainer[0], - 'scroll', - this.odooEditor.updateToolbarPosition.bind(this.odooEditor), - ); - } - }, 0); + if (!options.snippets) { + // Scroll event does not bubble. + document.addEventListener('scroll', this._onScroll, true); + } } /** * @private @@ -2764,6 +2758,11 @@ export class Wysiwyg extends Component { this.options.onWysiwygBlur && this.options.onWysiwygBlur(); } } + _onScroll(ev) { + if (ev.target.contains(this.$editable[0])) { + this.odooEditor.updateToolbarPosition(); + } + } _signalOffline() { if (!this._isOnline) { return; diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js index 0bb692ac1c1..f6eef88e415 100644 --- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js +++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js @@ -38,6 +38,16 @@ patch(Wysiwyg.prototype, { } }, + /** + * @override + **/ + destroy() { + if (this.options.inIframe) { + this.$iframe?.[0].contentDocument.removeEventListener('scroll', this._onScroll, true); + } + super.destroy(); + }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -180,6 +190,37 @@ patch(Wysiwyg.prototype, { this.$iframe[0].contentWindow.addEventListener('blur', this._onBlur); } }, + + /** + * When the editable is inside an iframe, we want to update the toolbar + * position in 2 scenarios: + * 1. scroll event in the top document, if the iframe is a descendant of + * the scroll container. + * 2. scroll event in the iframe's document. + * + * @override + */ + _onScroll(ev) { + if (this.options.inIframe) { + const iframeDocument = this.$iframe[0].contentDocument; + const scrollInIframe = ev.target === iframeDocument || ev.target.ownerDocument === iframeDocument; + if (ev.target.contains(this.$iframe[0]) || scrollInIframe) { + this.odooEditor.updateToolbarPosition(); + } + } else { + return super._onScroll(...arguments); + } + }, + + /** + * @override + */ + _configureToolbar(options) { + super._configureToolbar(...arguments); + if (this.options.inIframe && !options.snippets) { + this.$iframe[0].contentDocument.addEventListener('scroll', this._onScroll, true); + } + }, }); function getWysiwygIframeContent(params) {