[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
This commit is contained in:
Younn Olivier
2022-06-24 10:28:06 +02:00
committed by qsm-odoo
parent 3588155534
commit 457e655ff7
2 changed files with 41 additions and 0 deletions
@@ -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 <html>).
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);
@@ -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;
};
}
});
},
/**