From 3749d6d66c8cc49fd6613c8a8b6889ca5150fe99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Souk=C3=A9ina=20Bojabza?= Date: Tue, 20 Sep 2022 08:37:53 +0000 Subject: [PATCH] [FIX] web_editor: add support for unordered dropzone events When going from a dropzone to an other, and if it is done really fast or if the dropzones are really close (glued to each other) or if they are overlapping, the order of the "over" and "out" dropzone events is not the one expected. Indeed, instead of having over > out > over, we have over > over > out. This resulted in a traceback when using the grid as the listeners and properties were therefore not removed correctly when such a case happened. This commit adds the support for this situation by first detecting if two "over" are following each other and then making a virtual "out" if this is the case. task-2973198 X-original-commit: c6886410603a16b6a3c5fe459d273dd883c110c7 Part-of: odoo/odoo#102525 --- .../static/src/js/editor/snippets.editor.js | 100 ++++++++++++++---- 1 file changed, 78 insertions(+), 22 deletions(-) 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 58089c805be..bf1b1c25e8a 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -914,6 +914,42 @@ var SnippetEditor = Widget.extend({ } return result; }, + /** + * Called when an "over" dropzone event happens after an other "over" + * without an "out" between them. It escapes the previous dropzone. + * + * @private + * @param {Object} self + * the same `self` variable as when we are in `_onDragAndDropStart` + * @param {Element} currentDropzoneEl + * the dropzone over which we are currently dragging + */ + _outPreviousDropzone(self, currentDropzoneEl) { + const previousDropzoneEl = this; + const rowEl = previousDropzoneEl.parentNode; + + if (rowEl.classList.contains('o_grid_mode')) { + document.body.removeEventListener('mousemove', self.onDragMove, false); + const fromGridToGrid = currentDropzoneEl.classList.contains('oe_grid_zone'); + if (fromGridToGrid) { + // If we went from a grid dropzone to an other grid one. + rowEl.style.removeProperty('position'); + } else { + // If we went from a grid dropzone to a normal one. + gridUtils._gridCleanUp(rowEl, self.$target[0]); + self.$target[0].style.removeProperty('z-index'); + } + + // Removing the drag helper and the background grid and + // resizing the grid and the dropzone. + self.dragState.dragHelperEl.remove(); + self.dragState.backgroundGridEl.remove(); + gridUtils._resizeGrid(rowEl); + const rowCount = parseInt(rowEl.dataset.rowCount); + previousDropzoneEl.style.gridRowEnd = Math.max(rowCount + 1, 1); + } + previousDropzoneEl.classList.remove('invisible'); + }, //-------------------------------------------------------------------------- // Handlers @@ -977,7 +1013,7 @@ var SnippetEditor = Widget.extend({ // Storing the current grid and grid area to use them for the // history. - this.dragState.previousGrid = rowEl; + this.dragState.startingGrid = rowEl; this.dragState.prevGridArea = self.$target[0].style.gridArea; // Reload the images. @@ -1084,6 +1120,16 @@ var SnippetEditor = Widget.extend({ const $dropzone = $(this).first().after(self.$target); $dropzone.addClass('invisible'); + // Checking if the "out" event happened before this "over": if + // `self.dragState.currentDropzoneEl` exists, "out" didn't + // happen because it deletes it. We are therefore in the case + // of an "over" after an "over" and we need to escape the + // previous dropzone first. + if (self.dragState.currentDropzoneEl) { + self._outPreviousDropzone.apply(self.dragState.currentDropzoneEl, [self, $dropzone[0]]); + } + self.dragState.currentDropzoneEl = $dropzone[0]; + if ($dropzone[0].classList.contains('oe_grid_zone')) { // Case where the column we are dragging is over a grid // dropzone. @@ -1138,7 +1184,6 @@ var SnippetEditor = Widget.extend({ self.dragState.currentHeight = rowEl.clientHeight; self.dragState.dragHelperEl = dragHelperEl; self.dragState.backgroundGridEl = backgroundGridEl; - self.dragState.dropzoneEl = $dropzone[0]; self.onDragMove = self._onDragMove.bind(self); document.body.addEventListener('mousemove', self.onDragMove, false); } @@ -1146,26 +1191,37 @@ var SnippetEditor = Widget.extend({ out: function () { const dropzoneEl = this; const rowEl = dropzoneEl.parentNode; - if (rowEl.classList.contains('o_grid_mode')) { - // Removing the listener + cleaning. - document.body.removeEventListener('mousemove', self.onDragMove, false); - gridUtils._gridCleanUp(rowEl, self.$target[0]); - self.$target[0].style.removeProperty('z-index'); - // Removing the drag helper and the background grid and - // resizing the grid and the dropzone. - self.dragState.dragHelperEl.remove(); - self.dragState.backgroundGridEl.remove(); - gridUtils._resizeGrid(rowEl); - const rowCount = parseInt(rowEl.dataset.rowCount); - dropzoneEl.style.gridRowEnd = Math.max(rowCount + 1, 1); - } + // Checking if the "out" event happens right after the "over" + // of the same dropzone. If it is not the case, we don't do + // anything since the previous dropzone was already escaped (at + // the start of the over). + const sameDropzoneAsCurrent = self.dragState.currentDropzoneEl === dropzoneEl; - var prev = self.$target.prev(); - if (this === prev[0]) { - self.dropped = false; - self.$target.detach(); - $(this).removeClass('invisible'); + if (sameDropzoneAsCurrent) { + if (rowEl.classList.contains('o_grid_mode')) { + // Removing the listener + cleaning. + document.body.removeEventListener('mousemove', self.onDragMove, false); + gridUtils._gridCleanUp(rowEl, self.$target[0]); + self.$target[0].style.removeProperty('z-index'); + + // Removing the drag helper and the background grid and + // resizing the grid and the dropzone. + self.dragState.dragHelperEl.remove(); + self.dragState.backgroundGridEl.remove(); + gridUtils._resizeGrid(rowEl); + const rowCount = parseInt(rowEl.dataset.rowCount); + dropzoneEl.style.gridRowEnd = Math.max(rowCount + 1, 1); + } + + var prev = self.$target.prev(); + if (this === prev[0]) { + self.dropped = false; + self.$target.detach(); + $(this).removeClass('invisible'); + } + + delete self.dragState.currentDropzoneEl; } }, }); @@ -1317,7 +1373,7 @@ var SnippetEditor = Widget.extend({ }); this.draggableComponent.$scrollTarget.off('scroll.scrolling_element'); const samePositionAsStart = this.$target[0].classList.contains('o_grid_item') - ? (this.$target[0].parentNode === this.dragState.previousGrid + ? (this.$target[0].parentNode === this.dragState.startingGrid && this.$target[0].style.gridArea === this.dragState.prevGridArea) : this._dropSiblings.prev === this.$target.prev()[0] && this._dropSiblings.next === this.$target.next()[0]; if (!samePositionAsStart) { @@ -1534,7 +1590,7 @@ var SnippetEditor = Widget.extend({ const startingHeight = this.dragState.startingHeight; const currentHeight = this.dragState.currentHeight; const backgroundGridEl = this.dragState.backgroundGridEl; - const dropzoneEl = this.dragState.dropzoneEl; + const dropzoneEl = this.dragState.currentDropzoneEl; const rowOverflow = Math.round((bottom - currentHeight) / (gridProp.rowSize + gridProp.rowGap)); const updateRows = bottom > currentHeight || bottom <= currentHeight && bottom > startingHeight; const rowCount = Math.max(rowEl.dataset.rowCount, this.dragState.columnRowCount);