Skip to content

Commit 2c311a2

Browse files
committed
fix: improved drag drop
1 parent ffa472b commit 2c311a2

File tree

1 file changed

+61
-30
lines changed

1 file changed

+61
-30
lines changed

src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,12 @@ function RemoteFunctions(config = {}) {
595595
* @returns {Boolean} true if the nesting is valid
596596
*/
597597
function _isValidNesting(sourceElement, targetElement) {
598+
if (!sourceElement || !targetElement) {
599+
return false;
600+
}
601+
if (!sourceElement.tagName || !targetElement.tagName) {
602+
return false;
603+
}
598604
const sourceTag = sourceElement.tagName.toUpperCase();
599605
const targetTag = targetElement.tagName.toUpperCase();
600606

@@ -1110,44 +1116,69 @@ function RemoteFunctions(config = {}) {
11101116

11111117
/**
11121118
* this function is for finding the best target element on where to drop the dragged element
1113-
* for ex: div > image...here both the div and image are of the exact same size, then when user is dragging some
1114-
* other element, then almost everytime they want to drop it before/after the div and not like div>newEle+img
11151119
* @param {Element} target - The current target element
1116-
* @returns {Element|null} - The outermost parent with all edges aligned, or null
1120+
* @returns {Element} - The outermost parent with all edges aligned
11171121
*/
1118-
function _findBestParentTarget(target) {
1119-
if (!target) {
1120-
return null;
1122+
function _findBestParentTarget(target, sourceElement) {
1123+
if (!target) { return null; }
1124+
1125+
let el = target;
1126+
1127+
const SEMANTIC_CONTAINERS = new Set([
1128+
"DIV", "SECTION", "ARTICLE", "ASIDE", "MAIN",
1129+
"HEADER", "FOOTER", "NAV",
1130+
"UL", "OL", "LI"
1131+
]);
1132+
1133+
function isSemanticContainer(element) {
1134+
return element &&
1135+
element.nodeType === 1 &&
1136+
SEMANTIC_CONTAINERS.has(element.tagName);
11211137
}
11221138

1123-
const tolerance = 1; // 1px is considered same
1124-
let bestParent = null;
1125-
let currentElement = target;
1126-
let parent = currentElement.parentElement;
1139+
function geometrySuggestsUpgrade(child, parent) {
1140+
const tolerance = 2;
1141+
const c = child.getBoundingClientRect();
1142+
const p = parent.getBoundingClientRect();
11271143

1128-
while (parent) {
1129-
if (parent.hasAttribute(GLOBALS.DATA_BRACKETS_ID_ATTR) && isElementEditable(parent)) {
1130-
const currentRect = currentElement.getBoundingClientRect();
1131-
const parentRect = parent.getBoundingClientRect();
1144+
const topAligned = Math.abs(c.top - p.top) <= tolerance;
1145+
const bottomAligned = Math.abs(c.bottom - p.bottom) <= tolerance;
11321146

1133-
// check if all the edges are same
1134-
const topAligned = Math.abs(currentRect.top - parentRect.top) <= tolerance;
1135-
const bottomAligned = Math.abs(currentRect.bottom - parentRect.bottom) <= tolerance;
1136-
const leftAligned = Math.abs(currentRect.left - parentRect.left) <= tolerance;
1137-
const rightAligned = Math.abs(currentRect.right - parentRect.right) <= tolerance;
1147+
const fullyInside =
1148+
c.top >= p.top && c.bottom <= p.bottom &&
1149+
c.left >= p.left && c.right <= p.right;
11381150

1139-
if (topAligned && bottomAligned && leftAligned && rightAligned) {
1140-
// all edges match, we prefer the parent element
1141-
bestParent = parent;
1142-
currentElement = parent;
1143-
} else {
1144-
break;
1151+
return topAligned || bottomAligned || fullyInside;
1152+
}
1153+
1154+
while (el && el !== document.body) {
1155+
const parent = el.parentElement;
1156+
1157+
// hit element is semantic container
1158+
if (isSemanticContainer(el)) {
1159+
return el;
1160+
}
1161+
1162+
// child is valid sibling → allow
1163+
if (
1164+
parent &&
1165+
_isValidNesting(sourceElement, parent) &&
1166+
_canAcceptChildren(parent)
1167+
) {
1168+
return el;
1169+
}
1170+
1171+
// climb if parent is semantic and geometry suggests it
1172+
if (parent && isSemanticContainer(parent)) {
1173+
if (geometrySuggestsUpgrade(el, parent)) {
1174+
return parent;
11451175
}
11461176
}
1147-
parent = parent.parentElement;
1177+
1178+
el = parent;
11481179
}
11491180

1150-
return bestParent;
1181+
return null;
11511182
}
11521183

11531184
/**
@@ -1197,7 +1228,7 @@ function RemoteFunctions(config = {}) {
11971228
}
11981229

11991230
/**
1200-
* Handle dragover events on the document (throttled version)
1231+
* Handle dragover events on the document
12011232
* Shows drop markers on valid drop targets
12021233
* @param {Event} event - The dragover event
12031234
*/
@@ -1229,7 +1260,7 @@ function RemoteFunctions(config = {}) {
12291260
}
12301261

12311262
// Check if we should prefer a parent when all edges are aligned
1232-
const bestParent = _findBestParentTarget(target);
1263+
const bestParent = _findBestParentTarget(target, window._currentDraggedElement);
12331264
if (bestParent) {
12341265
target = bestParent;
12351266
}
@@ -1306,7 +1337,7 @@ function RemoteFunctions(config = {}) {
13061337
}
13071338

13081339
// Check if we should prefer a parent when all edges are aligned
1309-
const bestParent = _findBestParentTarget(target);
1340+
const bestParent = _findBestParentTarget(target, window._currentDraggedElement);
13101341
if (bestParent) {
13111342
target = bestParent;
13121343
}

0 commit comments

Comments
 (0)