Skip to content

Commit 08ee786

Browse files
authored
feat: drag-and-drop reordering for URLs on the dashboard (#105)
* re-order URLs on the dashboard * create dataTransfer variable
1 parent 8bc30d6 commit 08ee786

File tree

1 file changed

+121
-1
lines changed

1 file changed

+121
-1
lines changed

web/index.html

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,59 @@
1919
border-color: #ffc107 !important;
2020
box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5) !important;
2121
}
22+
23+
/* --- Drag & Drop Styles --- */
24+
.drag-handle {
25+
cursor: grab;
26+
user-select: none;
27+
display: flex;
28+
align-items: center;
29+
padding: 0 0.75rem 0 0;
30+
color: #6c757d;
31+
}
32+
.drag-handle:active {
33+
cursor: grabbing;
34+
}
35+
.drag-handle svg {
36+
width: 18px;
37+
height: 18px;
38+
fill: currentColor;
39+
}
40+
41+
#urls ul.list-group {
42+
user-select: none;
43+
position: relative;
44+
}
45+
#urls li.list-group-item {
46+
cursor: default;
47+
transition: background 0.2s, transform 0.2s;
48+
position: relative;
49+
display: flex;
50+
align-items: center;
51+
}
52+
#urls li.list-group-item:hover {
53+
background-color: rgba(220, 53, 69, 0.1);
54+
}
55+
#urls li.list-group-item.dragging {
56+
opacity: 0.6;
57+
transform: scale(0.95);
58+
cursor: grabbing;
59+
z-index: 1000;
60+
background-color: rgba(220, 53, 69, 0.15);
61+
}
62+
#urls li.list-group-item.over::before {
63+
content: "";
64+
position: absolute;
65+
top: 0;
66+
left: 12px;
67+
right: 12px;
68+
height: 3px;
69+
background-color: #dc3545;
70+
border-radius: 2px;
71+
}
72+
#urls li.list-group-item a {
73+
user-select: text;
74+
}
2275
</style>
2376
</head>
2477
<body>
@@ -47,7 +100,17 @@ <h1 class="my-1">Pi<span class="bg-danger mx-1 px-1 rounded">OSK</span></h1>
47100

48101
<template id="template-url">
49102
<li class="list-group-item fs-3">
50-
<div class="d-flex justify-content-between align-items-center">
103+
<span class="drag-handle" draggable="true" title="Drag to reorder">
104+
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
105+
<circle cx="9" cy="5" r="2"></circle>
106+
<circle cx="9" cy="12" r="2"></circle>
107+
<circle cx="9" cy="19" r="2"></circle>
108+
<circle cx="15" cy="5" r="2"></circle>
109+
<circle cx="15" cy="12" r="2"></circle>
110+
<circle cx="15" cy="19" r="2"></circle>
111+
</svg>
112+
</span>
113+
<div class="d-flex justify-content-between align-items-center flex-grow-1">
51114
<div class="d-flex align-items-center flex-grow-1">
52115
<button type="button" class="btn btn-close mx-3" aria-label="Remove"></button>
53116
<a class="link-underline-dark text-truncate"></a>
@@ -76,6 +139,8 @@ <h1 class="my-1">Pi<span class="bg-danger mx-1 px-1 rounded">OSK</span></h1>
76139

77140
<script>
78141
$(document).ready(function() {
142+
const $urlList = $("#urls ul.list-group");
143+
79144
// Use event delegation to highlight inputs when they are changed.
80145
$('#urls').on('change', '.duration-input, .cycles-input', function() {
81146
$(this).addClass('input-changed');
@@ -86,6 +151,61 @@ <h1 class="my-1">Pi<span class="bg-danger mx-1 px-1 rounded">OSK</span></h1>
86151
$('#execute').on('click', function() {
87152
$('.input-changed').removeClass('input-changed');
88153
});
154+
155+
// --- DRAG & DROP REORDER LOGIC ---
156+
let draggingItem = null;
157+
158+
// Start dragging from the handle
159+
$urlList.on("dragstart", ".drag-handle", function (e) {
160+
draggingItem = $(this).closest("li")[0];
161+
draggingItem.classList.add("dragging");
162+
const dataTransfer = e.originalEvent.dataTransfer;
163+
dataTransfer.effectAllowed = "move";
164+
dataTransfer.setData("text/plain", "");
165+
});
166+
167+
// End dragging
168+
$urlList.on("dragend", ".drag-handle", function () {
169+
if (draggingItem) {
170+
draggingItem.classList.remove("dragging");
171+
}
172+
$urlList.find("li.list-group-item").removeClass("over");
173+
draggingItem = null;
174+
});
175+
176+
// Handle dragging over the list
177+
$urlList.on("dragover", function (e) {
178+
e.preventDefault();
179+
if (!draggingItem) return;
180+
181+
const afterElement = getDragAfterElement(this, e.clientY);
182+
$urlList.find("li.list-group-item").removeClass("over");
183+
184+
if (afterElement) {
185+
$(afterElement).addClass("over");
186+
this.insertBefore(draggingItem, afterElement);
187+
} else {
188+
this.appendChild(draggingItem);
189+
}
190+
});
191+
192+
// Helper function to find the element to drop before
193+
function getDragAfterElement(container, y) {
194+
const draggableElements = [...container.querySelectorAll("li.list-group-item:not(.dragging)")];
195+
196+
return draggableElements.reduce(
197+
(closest, child) => {
198+
const box = child.getBoundingClientRect();
199+
const offset = y - box.top - box.height / 2;
200+
if (offset < 0 && offset > closest.offset) {
201+
return { offset: offset, element: child };
202+
} else {
203+
return closest;
204+
}
205+
},
206+
{ offset: Number.NEGATIVE_INFINITY }
207+
).element;
208+
}
89209
});
90210
</script>
91211

0 commit comments

Comments
 (0)