Skip to content

Commit 65282b5

Browse files
feat(drag and drop for groups): add drag and drop support for grouping monitors
Implements Issue #6196. Allows monitors to be reorganized into groups using drag-and-drop. Includes socket sync and local state updates with fallback handling for failed emits.
1 parent 9394560 commit 65282b5

File tree

1 file changed

+112
-1
lines changed

1 file changed

+112
-1
lines changed

src/components/MonitorListItem.vue

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
<template>
22
<div>
3-
<div :style="depthMargin">
3+
<div
4+
:style="depthMargin"
5+
:draggable="monitor.type !== 'group'"
6+
:class="{ 'drag-over': dragOver }"
7+
@dragstart="onDragStart"
8+
@dragend="onDragEnd"
9+
@dragenter.prevent="onDragEnter"
10+
@dragleave.prevent="onDragLeave"
11+
@dragover.prevent
12+
@drop.prevent="onDrop"
13+
>
414
<!-- Checkbox -->
515
<div v-if="isSelectMode" class="select-input-wrapper">
616
<input
@@ -116,6 +126,8 @@ export default {
116126
data() {
117127
return {
118128
isCollapsed: true,
129+
dragging: false,
130+
dragOver: false,
119131
};
120132
},
121133
computed: {
@@ -187,6 +199,101 @@ export default {
187199
188200
window.localStorage.setItem("monitorCollapsed", JSON.stringify(storageObject));
189201
},
202+
/**
203+
* Initializes the drag operation if the monitor is draggable.
204+
* Prevents dragging of group monitors.
205+
* @param {DragEvent} event - The dragstart event triggered by the browser.
206+
* @returns {void} This method does not return anything.
207+
*/
208+
onDragStart(event) {
209+
// Only allow dragging non-group monitors (so groups act as drop targets)
210+
if (this.monitor.type === "group") {
211+
event.preventDefault();
212+
return;
213+
}
214+
215+
try {
216+
event.dataTransfer.setData("text/monitor-id", String(this.monitor.id));
217+
event.dataTransfer.effectAllowed = "move";
218+
this.dragging = true;
219+
} catch (e) {
220+
// ignore
221+
}
222+
},
223+
224+
onDragEnd() {
225+
this.dragging = false;
226+
},
227+
228+
onDragEnter(event) {
229+
// show visual feedback if this item is a group
230+
if (this.monitor.type === "group") {
231+
this.dragOver = true;
232+
}
233+
},
234+
235+
onDragLeave(event) {
236+
if (this.monitor.type === "group") {
237+
this.dragOver = false;
238+
}
239+
},
240+
241+
async onDrop(event) {
242+
event.preventDefault();
243+
this.dragOver = false;
244+
245+
// Only groups accept drops
246+
if (this.monitor.type !== "group") {
247+
return;
248+
}
249+
250+
const draggedId = event.dataTransfer.getData("text/monitor-id");
251+
if (!draggedId) {
252+
return;
253+
}
254+
255+
const draggedMonitorId = parseInt(draggedId);
256+
if (isNaN(draggedMonitorId) || draggedMonitorId === this.monitor.id) {
257+
return;
258+
}
259+
260+
const draggedMonitor = this.$root.monitorList[draggedMonitorId];
261+
if (!draggedMonitor) {
262+
return;
263+
}
264+
265+
// Save original parent so we can revert locally if server returns error
266+
const originalParent = draggedMonitor.parent;
267+
268+
// Prepare a full monitor object (clone) and set new parent
269+
const monitorToSave = JSON.parse(JSON.stringify(draggedMonitor));
270+
monitorToSave.parent = this.monitor.id;
271+
272+
// Optimistically update local state so UI updates immediately
273+
this.$root.monitorList[draggedMonitorId].parent = this.monitor.id;
274+
275+
// Send updated monitor state via socket
276+
try {
277+
this.$root.getSocket().emit("editMonitor", monitorToSave, (res) => {
278+
if (!res || !res.ok) {
279+
// Revert local change on error
280+
if (this.$root.monitorList[draggedMonitorId]) {
281+
this.$root.monitorList[draggedMonitorId].parent = originalParent;
282+
}
283+
if (res && res.msg) {
284+
this.$root.toastError(res.msg);
285+
}
286+
} else {
287+
this.$root.toastRes(res);
288+
}
289+
});
290+
} catch (e) {
291+
// revert on exception
292+
if (this.$root.monitorList[draggedMonitorId]) {
293+
this.$root.monitorList[draggedMonitorId].parent = originalParent;
294+
}
295+
}
296+
},
190297
/**
191298
* Get URL of monitor
192299
* @param {number} id ID of monitor
@@ -253,4 +360,8 @@ export default {
253360
z-index: 15;
254361
}
255362
363+
.drag-over {
364+
background-color: rgba(6, 182, 212, 0.04);
365+
}
366+
256367
</style>

0 commit comments

Comments
 (0)