Skip to content

Commit 97e56ad

Browse files
committed
feat: allow dropping before as well as after the element
1 parent 5c5be80 commit 97e56ad

File tree

2 files changed

+49
-17
lines changed

2 files changed

+49
-17
lines changed

src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -329,29 +329,36 @@ function RemoteFunctions(config) {
329329
/**
330330
* This function creates a marker to indicate a valid drop position
331331
* @param {DOMElement} element - The element where the drop is possible
332+
* @param {Boolean} showAtBottom - Whether to show the marker at the bottom of the element
332333
*/
333-
function _createDropMarker(element) {
334+
function _createDropMarker(element, showAtBottom) {
334335
// clean any existing marker from that element
335336
_removeDropMarkerFromElement(element);
336337

337338
// create the marker element
338339
let marker = window.document.createElement("div");
339340
marker.className = DROP_MARKER_CLASSNAME;
340341

341-
// position the marker at the top of the element
342+
// position the marker at the top or bottom of the element
342343
let rect = element.getBoundingClientRect();
343344
let scrollTop = window.pageYOffset || document.documentElement.scrollTop;
344345
let scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
345346

346347
// marker styling
347348
marker.style.position = "absolute";
348-
marker.style.top = (rect.top + scrollTop - 5) + "px";
349349
marker.style.left = (rect.left + scrollLeft) + "px";
350350
marker.style.width = rect.width + "px";
351351
marker.style.height = "2px";
352352
marker.style.backgroundColor = "#4285F4";
353353
marker.style.zIndex = "2147483646";
354354

355+
// position the marker at the top or at the bottom of the element
356+
if (showAtBottom) {
357+
marker.style.top = (rect.bottom + scrollTop + 3) + "px";
358+
} else {
359+
marker.style.top = (rect.top + scrollTop - 5) + "px";
360+
}
361+
355362
element._dropMarker = marker; // we need this in the _removeDropMarkerFromElement function
356363
window.document.body.appendChild(marker);
357364
}
@@ -419,9 +426,14 @@ function RemoteFunctions(config) {
419426
return;
420427
}
421428

429+
// check if the cursor is in the top half or bottom half of the target element
430+
const rect = target.getBoundingClientRect();
431+
const middleY = rect.top + (rect.height / 2);
432+
const showAtBottom = event.clientY > middleY;
433+
422434
// before creating a drop marker, make sure that we clear all the drop markers
423435
_clearDropMarkers();
424-
_createDropMarker(target);
436+
_createDropMarker(target, showAtBottom);
425437
}
426438

427439
/**
@@ -455,6 +467,11 @@ function RemoteFunctions(config) {
455467
return;
456468
}
457469

470+
// check if the cursor is in the top half or bottom half of the target element
471+
const rect = target.getBoundingClientRect();
472+
const middleY = rect.top + (rect.height / 2);
473+
const insertAfter = event.clientY > middleY;
474+
458475
// IDs of the source and target elements
459476
const sourceId = window._currentDraggedElement.getAttribute("data-brackets-id");
460477
const targetId = target.getAttribute("data-brackets-id");
@@ -466,6 +483,7 @@ function RemoteFunctions(config) {
466483
targetElement: target,
467484
sourceId: Number(sourceId),
468485
targetId: Number(targetId),
486+
insertAfter: insertAfter,
469487
move: true
470488
});
471489

src/LiveDevelopment/LivePreviewEdit.js

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ define(function (require, exports, module) {
145145
* it is called when there is drag-drop in the live preview
146146
* @param {Number} sourceId - the data-brackets-id of the element being moved
147147
* @param {Number} targetId - the data-brackets-id of the target element where to move
148+
* @param {Boolean} insertAfter - whether to insert the source element after the target element
148149
*/
149-
function _moveElementInSource(sourceId, targetId) {
150+
function _moveElementInSource(sourceId, targetId, insertAfter) {
150151
// this is to get the currently live document that is being served in the live preview
151152
const currLiveDoc = LiveDevMultiBrowser.getCurrentLiveDoc();
152153
if (!currLiveDoc) {
@@ -160,32 +161,44 @@ define(function (require, exports, module) {
160161

161162
// position of source and target elements in the editor
162163
const sourceRange = HTMLInstrumentation.getPositionFromTagId(editor, sourceId);
164+
const targetRange = HTMLInstrumentation.getPositionFromTagId(editor, targetId);
163165

164-
if (!sourceRange) {
166+
if (!sourceRange || !targetRange) {
165167
return;
166168
}
167169

168170
const sourceText = editor.getTextBetween(sourceRange.from, sourceRange.to);
171+
const targetIndent = editor.getTextBetween({ line: targetRange.from.line, ch: 0 }, targetRange.from);
169172

170173
// creating a batch operation so that undo in live preview works fine
171174
editor.document.batchOperation(function () {
172175
// first, we need to remove the source code from its initial position
173176
editor.replaceRange("", sourceRange.from, sourceRange.to);
174177

175-
// get the target range, this is where we want to insert the text
176-
const targetRange = HTMLInstrumentation.getPositionFromTagId(editor, targetId);
177-
if(!targetRange) {
178+
// recalculate the target range, as the source text is not removed
179+
const updatedTargetRange = HTMLInstrumentation.getPositionFromTagId(editor, targetId);
180+
if (!updatedTargetRange) {
178181
return;
179182
}
180-
const targetText = editor.getTextBetween(targetRange.from, targetRange.to);
181-
const targetIndent = editor.getTextBetween({ line: targetRange.from.line, ch: 0 }, targetRange.from);
182183

183-
// to check if there is only indentation and no text before it
184-
if (targetIndent.trim() === "") {
185-
const finalText = sourceText + '\n' + targetIndent + targetText;
186-
editor.replaceRange(finalText, targetRange.from, targetRange.to);
184+
if (insertAfter) {
185+
const insertPos = {
186+
line: updatedTargetRange.to.line,
187+
ch: updatedTargetRange.to.ch
188+
};
189+
190+
editor.replaceRange("\n" + targetIndent + sourceText, insertPos);
187191
} else {
188-
editor.replaceRange(sourceText + targetText, targetRange.from, targetRange.to);
192+
// insert before
193+
const targetText = editor.getTextBetween(updatedTargetRange.from, updatedTargetRange.to);
194+
195+
// to check if there is only indentation and no text before it
196+
if (targetIndent.trim() === "") {
197+
const finalText = sourceText + '\n' + targetIndent + targetText;
198+
editor.replaceRange(finalText, updatedTargetRange.from, updatedTargetRange.to);
199+
} else {
200+
editor.replaceRange(sourceText + targetText, updatedTargetRange.from, updatedTargetRange.to);
201+
}
189202
}
190203
});
191204
}
@@ -206,14 +219,15 @@ define(function (require, exports, module) {
206219
207220
sourceId: sourceId, (these are for move (drag & drop))
208221
targetId: targetId,
222+
insertAfter: boolean, (whether to insert after the target element)
209223
move: true
210224
}
211225
* these are the main properties that are passed through the message
212226
*/
213227
function handleLivePreviewEditOperation(message) {
214228
// handle move(drag & drop)
215229
if (message.move && message.sourceId && message.targetId) {
216-
_moveElementInSource(message.sourceId, message.targetId);
230+
_moveElementInSource(message.sourceId, message.targetId, message.insertAfter);
217231
return;
218232
}
219233

0 commit comments

Comments
 (0)