19
19
border-color : # ffc107 !important ;
20
20
box-shadow : 0 0 0 0.2rem rgba (255 , 193 , 7 , 0.5 ) !important ;
21
21
}
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
+ }
22
75
</ style >
23
76
</ head >
24
77
< body >
@@ -47,7 +100,17 @@ <h1 class="my-1">Pi<span class="bg-danger mx-1 px-1 rounded">OSK</span></h1>
47
100
48
101
< template id ="template-url ">
49
102
< 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 ">
51
114
< div class ="d-flex align-items-center flex-grow-1 ">
52
115
< button type ="button " class ="btn btn-close mx-3 " aria-label ="Remove "> </ button >
53
116
< 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>
76
139
77
140
< script >
78
141
$ ( document ) . ready ( function ( ) {
142
+ const $urlList = $ ( "#urls ul.list-group" ) ;
143
+
79
144
// Use event delegation to highlight inputs when they are changed.
80
145
$ ( '#urls' ) . on ( 'change' , '.duration-input, .cycles-input' , function ( ) {
81
146
$ ( 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>
86
151
$ ( '#execute' ) . on ( 'click' , function ( ) {
87
152
$ ( '.input-changed' ) . removeClass ( 'input-changed' ) ;
88
153
} ) ;
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
+ }
89
209
} ) ;
90
210
</ script >
91
211
0 commit comments