2626 <div class =" text-xs text-muted font-medium uppercase tracking-wide" >Saved themes</div >
2727
2828 <div
29- v-for =" theme in themes"
29+ v-for =" ( theme, index) in themes"
3030 :key =" theme.name"
31- class =" flex items-center gap-2 px-3 py-2 rounded-lg border border-default bg-elevated hover:bg-accented transition-colors"
31+ draggable =" true"
32+ class =" flex items-center gap-2.5 px-3 py-2 rounded-lg border border-default bg-elevated hover:bg-accented transition-colors cursor-move"
33+ :class =" {
34+ 'opacity-50': draggedIndex === index,
35+ 'border-primary': dropTargetIndex === index
36+ }"
37+ @dragstart =" handleDragStart(index, $event)"
38+ @dragend =" handleDragEnd"
39+ @dragover =" handleDragOver(index, $event)"
40+ @dragleave =" handleDragLeave"
41+ @drop =" handleDrop(index, $event)"
3242 >
3343 <!-- Color dot -->
3444 <div
3545 class =" w-3 h-3 rounded-full shrink-0 ring-1 ring-default"
3646 :style =" { backgroundColor: theme.shades['500'] }"
3747 />
3848
39- <!-- Name -->
40- <span class =" flex-1 text-sm font-mono truncate" >{{ theme.name }}</span >
49+ <!-- Name with tooltip -->
50+ <UTooltip :text =" theme .name " class="flex-1 min-w-0">
51+ <span class =" block text-sm font-mono truncate" >{{ theme.name }}</span >
52+ </UTooltip >
4153
4254 <!-- Apply to picker -->
4355 <UTooltip text="Apply to color picker">
@@ -125,6 +137,7 @@ const {
125137 removeTheme,
126138 setDefault,
127139 setColor,
140+ reorderThemes,
128141 exportThemes,
129142 importThemes
130143} = useThemeStore ();
@@ -133,6 +146,48 @@ const fileInput = ref<HTMLInputElement | null>(null);
133146const importMessage = ref (' ' );
134147const importError = ref (false );
135148
149+ // Drag and drop state
150+ const draggedIndex = ref <number | null >(null );
151+ const dropTargetIndex = ref <number | null >(null );
152+
153+ function handleDragStart(index : number , event : DragEvent ) {
154+ draggedIndex .value = index ;
155+ if (event .dataTransfer ) {
156+ event .dataTransfer .effectAllowed = ' move' ;
157+ event .dataTransfer .setData (' text/plain' , index .toString ());
158+ }
159+ }
160+
161+ function handleDragEnd() {
162+ draggedIndex .value = null ;
163+ dropTargetIndex .value = null ;
164+ }
165+
166+ function handleDragOver(index : number , event : DragEvent ) {
167+ event .preventDefault ();
168+ if (event .dataTransfer ) {
169+ event .dataTransfer .dropEffect = ' move' ;
170+ }
171+ if (draggedIndex .value !== null && draggedIndex .value !== index ) {
172+ dropTargetIndex .value = index ;
173+ }
174+ }
175+
176+ function handleDragLeave() {
177+ dropTargetIndex .value = null ;
178+ }
179+
180+ function handleDrop(toIndex : number , event : DragEvent ) {
181+ event .preventDefault ();
182+
183+ if (draggedIndex .value !== null && draggedIndex .value !== toIndex ) {
184+ reorderThemes (draggedIndex .value , toIndex );
185+ }
186+
187+ draggedIndex .value = null ;
188+ dropTargetIndex .value = null ;
189+ }
190+
136191function applyTheme(theme : ThemeEntry ) {
137192 // Apply the theme's base color (500 shade) to the color picker
138193 const baseColor = theme .shades [' 500' ];
0 commit comments