From b49a5d1b017f790e69b1e666e1deade35f9aed4c Mon Sep 17 00:00:00 2001 From: Romeo Fragomeli Date: Tue, 10 Oct 2023 17:19:39 +0200 Subject: [PATCH] [FIX] web: improve useSortable when no placeholder is not a clone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sortable uses the "enter" and "leave" events to handle the move of the placeholder in the container. When the initial drag element is cloned, the algorithm is more simple as the placeholder takes the same size as the original one. When we use a custom placeholder (in our case, a small line) and the placeholder change position, it triggers an "enter" and "leave" events in loop due to the smaller size of the placeholder. This commit ensures the placeholder to move only one time per pointer event move ("enter" and "leave") and avoid flickering when we move one element on the first/last element. Note: a better solution to avoid this complexity is to use pointer "move" event instead of "enter" and "leave". closes odoo/odoo#138350 Signed-off-by: Adrien Dieudonné (adr) --- addons/web/static/src/core/utils/sortable.js | 33 +++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/addons/web/static/src/core/utils/sortable.js b/addons/web/static/src/core/utils/sortable.js index 5dc98abd8a1..f5d8e4dec5d 100644 --- a/addons/web/static/src/core/utils/sortable.js +++ b/addons/web/static/src/core/utils/sortable.js @@ -141,6 +141,9 @@ const hookParams = { }; const onElementComplexPointerEnter = (ev) => { + if (ctx.haveAlreadyChanged) { + return; + } const element = ev.currentTarget; const siblingArray = [...element.parentElement.children].filter( @@ -160,14 +163,18 @@ const hookParams = { if (isDirectSibling) { if (pos === Node.DOCUMENT_POSITION_PRECEDING) { element.before(current.placeHolder); + ctx.haveAlreadyChanged = true; } else if (pos === Node.DOCUMENT_POSITION_FOLLOWING) { element.after(current.placeHolder); + ctx.haveAlreadyChanged = true; } } else { if (pos === Node.DOCUMENT_POSITION_FOLLOWING) { element.before(current.placeHolder); + ctx.haveAlreadyChanged = true; } else if (pos === Node.DOCUMENT_POSITION_PRECEDING) { element.after(current.placeHolder); + ctx.haveAlreadyChanged = true; } } } @@ -179,21 +186,34 @@ const hookParams = { * @param {PointerEvent} ev */ const onElementComplexPointerLeave = (ev) => { + if (ctx.haveAlreadyChanged) { + return; + } const element = ev.currentTarget; + const elementRect = element.getBoundingClientRect(); + + const relatedElement = ev.relatedTarget; + const relatedElementRect = element.getBoundingClientRect(); const siblingArray = [...element.parentElement.children].filter( (el) => el === current.placeHolder || (el.matches(elementSelector) && !el.classList.contains(DRAGGED_CLASS)) ); + const pointerOnSiblings = siblingArray.indexOf(relatedElement) > -1; const elementIndex = siblingArray.indexOf(element); - const isAtEdge = elementIndex === 0 || elementIndex === siblingArray.length - 1; - if (isAtEdge && (!groupSelector || current.group === element.closest(groupSelector))) { - const pos = current.placeHolder.compareDocumentPosition(element); - if (pos === Node.DOCUMENT_POSITION_PRECEDING) { + const isFirst = elementIndex === 0; + const isAbove = relatedElementRect.top <= elementRect.top; + const isLast = elementIndex === siblingArray.length - 1; + const isBelow = relatedElementRect.bottom >= elementRect.bottom; + const pos = current.placeHolder.compareDocumentPosition(element); + if (!pointerOnSiblings) { + if (isFirst && isAbove && pos === Node.DOCUMENT_POSITION_PRECEDING) { element.before(current.placeHolder); - } else if (pos === Node.DOCUMENT_POSITION_FOLLOWING) { + ctx.haveAlreadyChanged = true; + } else if (isLast && isBelow && pos === Node.DOCUMENT_POSITION_FOLLOWING) { element.after(current.placeHolder); + ctx.haveAlreadyChanged = true; } } callHandler("onElementLeave", { element }); @@ -258,6 +278,9 @@ const hookParams = { return pick(current, "element", "group"); }, + onDrag({ ctx }) { + ctx.haveAlreadyChanged = false; + }, onDragEnd({ ctx }) { return pick(ctx.current, "element", "group"); },