feat: rework des preferredSources
This commit is contained in:
parent
4dc6e5cfab
commit
4848a1736f
@@ -35,7 +35,7 @@
|
||||
</DialogTitle>
|
||||
<div class="mt-2">
|
||||
<p class="text-sm text-gray-500">
|
||||
Configurez l'ordre de priorité des sources pour ce manga. Les sources en haut de la liste seront privilégiées.
|
||||
Configurez l'ordre de priorité des sources pour ce manga. Glissez-déposez les sources pour les réorganiser.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,36 +56,78 @@
|
||||
<div v-if="localSources.length === 0" class="text-center py-8 text-gray-500">
|
||||
Aucune source disponible
|
||||
</div>
|
||||
<div v-else class="space-y-2">
|
||||
<div v-else class="space-y-3">
|
||||
<div
|
||||
v-for="(source, index) in localSources"
|
||||
:key="source.id"
|
||||
class="flex items-center p-3 bg-gray-50 rounded-lg border border-gray-200"
|
||||
:class="[
|
||||
'group relative flex items-center p-4 rounded-lg border-2 transition-all duration-200 cursor-grab active:cursor-grabbing select-none',
|
||||
{
|
||||
'bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-300 shadow-md': index === 0,
|
||||
'bg-gradient-to-r from-green-50 to-emerald-50 border-green-300': index === 1,
|
||||
'bg-gradient-to-r from-yellow-50 to-amber-50 border-yellow-300': index === 2,
|
||||
'bg-gray-50 border-gray-200': index > 2,
|
||||
'scale-105 shadow-lg border-blue-400': draggedIndex === index,
|
||||
'opacity-50': dragOverIndex === index && draggedIndex !== index,
|
||||
'scale-95 active:scale-95': isPressed === index
|
||||
}
|
||||
]"
|
||||
draggable="true"
|
||||
@dragstart="handleDragStart(index, $event)"
|
||||
@dragover="handleDragOver(index, $event)"
|
||||
@dragleave="handleDragLeave"
|
||||
@drop="handleDrop(index, $event)"
|
||||
@dragend="handleDragEnd"
|
||||
@mousedown="handleMouseDown(index)"
|
||||
@mouseup="handleMouseUp"
|
||||
@mouseleave="handleMouseUp"
|
||||
>
|
||||
<div class="flex items-center space-x-2 mr-3">
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 text-gray-400 hover:text-gray-600 disabled:opacity-50"
|
||||
:disabled="index === 0"
|
||||
@click="moveUp(index)"
|
||||
>
|
||||
<ChevronUpIcon class="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 text-gray-400 hover:text-gray-600 disabled:opacity-50"
|
||||
:disabled="index === localSources.length - 1"
|
||||
@click="moveDown(index)"
|
||||
>
|
||||
<ChevronDownIcon class="h-4 w-4" />
|
||||
</button>
|
||||
<!-- Badge de priorité -->
|
||||
<div class="absolute -top-2 -left-2 z-10">
|
||||
<div :class="[
|
||||
'inline-flex items-center justify-center w-8 h-8 rounded-full text-sm font-bold text-white shadow-lg',
|
||||
{
|
||||
'bg-gradient-to-r from-blue-500 to-indigo-600': index === 0,
|
||||
'bg-gradient-to-r from-green-500 to-emerald-600': index === 1,
|
||||
'bg-gradient-to-r from-yellow-500 to-amber-600': index === 2,
|
||||
'bg-gradient-to-r from-gray-500 to-slate-600': index > 2
|
||||
}
|
||||
]">
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-gray-900">{{ source.name }}</div>
|
||||
<div class="text-sm text-gray-500">{{ source.description || source.baseUrl }}</div>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400">
|
||||
Priorité {{ index + 1 }}
|
||||
|
||||
<!-- Indicateur de priorité -->
|
||||
<div class="mr-4">
|
||||
<div :class="[
|
||||
'flex items-center space-x-1 px-3 py-1 rounded-full text-xs font-semibold',
|
||||
{
|
||||
'bg-blue-100 text-blue-800': index === 0,
|
||||
'bg-green-100 text-green-800': index === 1,
|
||||
'bg-yellow-100 text-yellow-800': index === 2,
|
||||
'bg-gray-100 text-gray-600': index > 2
|
||||
}
|
||||
]">
|
||||
<span v-if="index === 0">🥇 Priorité haute</span>
|
||||
<span v-else-if="index === 1">🥈 Priorité moyenne</span>
|
||||
<span v-else-if="index === 2">🥉 Priorité basse</span>
|
||||
<span v-else>Priorité {{ index + 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informations de la source -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-semibold text-gray-900 truncate">{{ source.name }}</div>
|
||||
<div class="text-sm text-gray-600 truncate">
|
||||
<a :href="source.baseUrl" target="_blank" class="hover:text-blue-600 hover:underline">{{ source.baseUrl }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicateur de drag -->
|
||||
<div class="ml-4 text-gray-400 group-hover:text-gray-600 transition-colors duration-200">
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9h8M8 15h8" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -123,7 +165,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue';
|
||||
import { ChevronDownIcon, ChevronUpIcon, Cog6ToothIcon } from '@heroicons/vue/24/outline';
|
||||
import { Cog6ToothIcon } from '@heroicons/vue/24/outline';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -154,6 +196,13 @@ const emit = defineEmits(['close', 'save']);
|
||||
// Copie locale des sources pour le drag & drop
|
||||
const localSources = ref([]);
|
||||
|
||||
// États pour le drag & drop
|
||||
const draggedIndex = ref(null);
|
||||
const dragOverIndex = ref(null);
|
||||
|
||||
// État pour l'effet de clic
|
||||
const isPressed = ref(null);
|
||||
|
||||
// Watcher pour mettre à jour la copie locale quand les props changent
|
||||
watch(
|
||||
() => props.sources,
|
||||
@@ -167,20 +216,57 @@ const closeModal = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const moveUp = (index) => {
|
||||
if (index > 0) {
|
||||
const sources = [...localSources.value];
|
||||
[sources[index - 1], sources[index]] = [sources[index], sources[index - 1]];
|
||||
localSources.value = sources;
|
||||
}
|
||||
// Fonctions pour l'effet de clic
|
||||
const handleMouseDown = (index) => {
|
||||
isPressed.value = index;
|
||||
};
|
||||
|
||||
const moveDown = (index) => {
|
||||
if (index < localSources.value.length - 1) {
|
||||
const handleMouseUp = () => {
|
||||
isPressed.value = null;
|
||||
};
|
||||
|
||||
// Fonctions pour le drag & drop
|
||||
const handleDragStart = (index, event) => {
|
||||
draggedIndex.value = index;
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.dataTransfer.setData('text/html', event.target);
|
||||
};
|
||||
|
||||
const handleDragOver = (index, event) => {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
dragOverIndex.value = index;
|
||||
};
|
||||
|
||||
const handleDragLeave = () => {
|
||||
dragOverIndex.value = null;
|
||||
};
|
||||
|
||||
const handleDrop = (dropIndex, event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (draggedIndex.value !== null && draggedIndex.value !== dropIndex) {
|
||||
const sources = [...localSources.value];
|
||||
[sources[index], sources[index + 1]] = [sources[index + 1], sources[index]];
|
||||
const draggedItem = sources[draggedIndex.value];
|
||||
|
||||
// Supprimer l'élément de sa position actuelle
|
||||
sources.splice(draggedIndex.value, 1);
|
||||
|
||||
// L'insérer à la nouvelle position
|
||||
// Si on drop après l'élément original, on doit ajuster l'index
|
||||
const insertIndex = draggedIndex.value < dropIndex ? dropIndex - 1 : dropIndex;
|
||||
sources.splice(insertIndex, 0, draggedItem);
|
||||
|
||||
localSources.value = sources;
|
||||
}
|
||||
|
||||
draggedIndex.value = null;
|
||||
dragOverIndex.value = null;
|
||||
};
|
||||
|
||||
const handleDragEnd = () => {
|
||||
draggedIndex.value = null;
|
||||
dragOverIndex.value = null;
|
||||
};
|
||||
|
||||
const saveChanges = () => {
|
||||
|
||||
Reference in New Issue
Block a user