From 457e655ff7f4651f185bd7544b8384e2d0ee8ef8 Mon Sep 17 00:00:00 2001 From: Younn Olivier Date: Fri, 8 Apr 2022 11:04:00 +0200 Subject: [PATCH] [FIX] web, web_editor: adapt drag and drop to iframe snippets SmoothScrollOnDrag and jquery-ui, the libraries used to handle the drag and drop of the snippets, are using the HTMLElement getBoundingClientRect method on the draggable/droppable elements to get their position, listen to mouseover events and trigger scroll while dragging. With the WebsiteEditorClientAction's iframe, the getBoundingClientRect method of elements inside the iframe was not taking into account a possible offset of the iframe. For example, in the "mobile preview" edition mode, the iframe was reduced in size and centered in the screen. This commit adds this offset to these libraries: - in SmoothScrollOnDrag, when the drag is started, to correctly trigger the scroll inside the iframe. - the insertion zones' getBoundingClientRect (used for the jquery-ui droppable) is patched. - the SnippetEditor.cover method is adapted to correctly display the handles over the snippets. See merge commit for more information. task-2687506 --- .../legacy/js/core/smooth_scroll_on_drag.js | 10 ++++++ .../static/src/js/editor/snippets.editor.js | 31 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/addons/web/static/src/legacy/js/core/smooth_scroll_on_drag.js b/addons/web/static/src/legacy/js/core/smooth_scroll_on_drag.js index 37aae5f1f44..1e9eeb20f6f 100644 --- a/addons/web/static/src/legacy/js/core/smooth_scroll_on_drag.js +++ b/addons/web/static/src/legacy/js/core/smooth_scroll_on_drag.js @@ -254,6 +254,14 @@ const SmoothScrollOnDrag = Class.extend(mixins.ParentedMixin, { - this.options.offsetElementsManager.left(), }; + if (this.iframeOffset) { + const { x, y } = this.iframeOffset; + visibleOffset.left -= x; + visibleOffset.top -= y; + visibleOffset.bottom += y; + visibleOffset.right += x; + } + // If this.$scrollTarget is the html tag, we need to take the scroll position in to account // as offsets positions are calculated relative to the document (thus ). if (this.scrollTargetIsDocument) { @@ -361,6 +369,8 @@ const SmoothScrollOnDrag = Class.extend(mixins.ParentedMixin, { */ _onSmoothDragStart(ev, ui, onDragStartCallBack) { this.scrollTargetIsDocument = this.$scrollTarget.is('html'); + const iframeEl = this.$scrollTarget[0].ownerDocument.defaultView.frameElement; + this.iframeOffset = iframeEl && iframeEl.getBoundingClientRect(); this.scrollTargetIsParent = this.$scrollTarget.get(0).contains(this.$element.get(0)); this._updatePositionOptions(ui); this._startSmoothScroll(ui); 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 a324b4be5f3..cf5eb31e0b7 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -368,6 +368,16 @@ var SnippetEditor = Widget.extend({ // Now cover the element const offset = $target.offset(); + + // If the target is in an iframe, we need the iframe offset. + const targetWindow = $target[0].ownerDocument.defaultView; + const editorWindow = this.$el[0].ownerDocument.defaultView; + if (targetWindow.frameElement && targetWindow !== editorWindow) { + const { x, y } = targetWindow.frameElement.getBoundingClientRect(); + offset.left += x; + offset.top += y; + } + var manipulatorOffset = this.$el.parent().offset(); offset.top -= manipulatorOffset.top; offset.left -= manipulatorOffset.left; @@ -1778,6 +1788,13 @@ var SnippetsMenu = Widget.extend({ // Cleaning consecutive zone and up zones placed between floating or // inline elements. We do not like these kind of zones. $zones = this.getEditableArea().find('.oe_drop_zone:not(.oe_vertical)'); + + let iframeOffset; + const bodyWindow = this.$body[0].ownerDocument.defaultView; + if (bodyWindow.frameElement && bodyWindow !== this.ownerDocument.defaultView) { + iframeOffset = bodyWindow.frameElement.getBoundingClientRect(); + } + $zones.each(function () { var zone = $(this); var prev = zone.prev(); @@ -1798,6 +1815,20 @@ var SnippetsMenu = Widget.extend({ && dispPrev.indexOf('inline') >= 0 && dispNext.indexOf('inline') >= 0) { zone.remove(); } + + // In the case of the SnippetsMenu being instanciated in the global + // document, with its editable content in an iframe, we want to + // take the iframe's offset into account to compute the dropzones. + if (iframeOffset) { + this.oldGetBoundingClientRect = this.getBoundingClientRect; + this.getBoundingClientRect = () => { + const rect = this.oldGetBoundingClientRect(); + const { x, y } = iframeOffset; + rect.x += x; + rect.y += y; + return rect; + }; + } }); }, /**