Files
Mangarr/assets/vue/app/domain/conversion/presentation/components/FileUploadArea.vue
ext.jeremy.guillot@maxicoffee.domains ec1ef8fe68 feat: dark mode complet + préférences utilisateur
- Ajout du store userPreferencesStore (thème, vue, tri, pagination, lecteur)
- Page UserPreferencesPage pour configurer toutes les préférences
- Câblage des prefs dans HomePage (viewMode, sortBy, itemsPerPage), readerStore (fallback prefs), ChapterReader (autoHide, autoFullscreen, sync), useNotifications (toastDuration)
- Thème sombre (dark: Tailwind) sur tous les composants Vue : Layout, Pagination, NotificationToast, MangaCard, MangaVolume, MangaDetails, AddManga, HomePage, ActivityPage, JobItem, MangaDeleteModal, MangaEditModal, MangaPreferredSourcesModal, ManageChaptersModal, MangaChapterList, MangaChapter, ConversionPage, FileUploadArea, ConversionProgress, NewImportPage, FileImportCard, MangaMatchCard, StatusBadge, ImportResults
- i18n partiellement initialisé

Jeremy Guillot
2026-03-12 20:38:29 +01:00

215 lines
5.8 KiB
Vue

<template>
<div
class="relative"
@dragover.prevent="handleDragOver"
@dragenter.prevent="handleDragEnter"
@dragleave.prevent="handleDragLeave"
@drop.prevent="handleDrop"
>
<div
:class="[
'border-2 border-dashed rounded-lg p-8 text-center transition-all duration-200',
isDragOver
? 'border-green-400 bg-green-50 dark:bg-green-900/20'
: 'border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500'
]"
>
<!-- Zone d'upload -->
<div class="space-y-4">
<!-- Icône -->
<div class="flex justify-center">
<ArchiveBoxIcon
:class="[
'w-16 h-16 transition-colors duration-200',
isDragOver ? 'text-green-500' : 'text-gray-400'
]"
/>
</div>
<!-- Message principal -->
<div class="space-y-2">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ isDragOver ? 'Déposez votre fichier ici' : 'Sélectionnez un fichier CBR ou CBZ' }}
</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">
Glissez-déposez votre fichier ou cliquez pour le sélectionner
</p>
<p class="text-xs text-gray-400 dark:text-gray-500">
Fichiers supportés: .cbr, .cbz (max. 150MB)
</p>
</div>
<!-- Bouton de sélection -->
<div class="flex justify-center">
<label
for="file-upload"
:class="[
'relative cursor-pointer rounded-md px-4 py-2 font-medium text-white transition-colors duration-200',
isDragOver
? 'bg-green-500 hover:bg-green-600'
: 'bg-green-600 hover:bg-green-700'
]"
>
<span>Sélectionner un fichier</span>
<input
id="file-upload"
name="file-upload"
type="file"
class="sr-only"
accept=".cbr,.cbz"
@change="handleFileSelect"
/>
</label>
</div>
<!-- Informations du fichier sélectionné -->
<div v-if="selectedFile" class="mt-6 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<div class="flex items-center space-x-3">
<DocumentIcon class="w-8 h-8 text-gray-600 dark:text-gray-400" />
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">
{{ selectedFile.name }}
</p>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{ formatFileSize(selectedFile.size) }}
</p>
</div>
<button
@click="clearFile"
class="p-1 text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
title="Supprimer le fichier"
>
<XMarkIcon class="w-5 h-5" />
</button>
</div>
</div>
</div>
</div>
<!-- Overlay pendant le drag -->
<div
v-if="isDragOver"
class="absolute inset-0 bg-green-100 bg-opacity-50 rounded-lg flex items-center justify-center"
style="pointer-events: none;"
>
<div class="text-green-600 font-medium text-lg">
Déposez le fichier ici
</div>
</div>
</div>
</template>
<script>
import { ArchiveBoxIcon, DocumentIcon, XMarkIcon } from '@heroicons/vue/24/outline';
import { ref } from 'vue';
export default {
name: 'FileUploadArea',
components: {
ArchiveBoxIcon,
DocumentIcon,
XMarkIcon,
},
props: {
selectedFile: {
type: File,
default: null,
},
disabled: {
type: Boolean,
default: false,
},
},
emits: ['file-selected', 'file-cleared'],
setup(props, { emit }) {
const isDragOver = ref(false);
const dragCounter = ref(0);
// Handlers pour le drag & drop
const handleDragEnter = (event) => {
if (props.disabled) return;
event.preventDefault();
dragCounter.value++;
isDragOver.value = true;
};
const handleDragOver = (event) => {
if (props.disabled) return;
event.preventDefault();
};
const handleDragLeave = (event) => {
if (props.disabled) return;
event.preventDefault();
dragCounter.value--;
if (dragCounter.value === 0) {
isDragOver.value = false;
}
};
const handleDrop = (event) => {
if (props.disabled) return;
event.preventDefault();
isDragOver.value = false;
dragCounter.value = 0;
const files = event.dataTransfer.files;
if (files.length > 0) {
const file = files[0];
emit('file-selected', file);
}
};
// Handler pour la sélection de fichier via input
const handleFileSelect = (event) => {
if (props.disabled) return;
const files = event.target.files;
if (files.length > 0) {
const file = files[0];
emit('file-selected', file);
}
// Réinitialiser l'input pour permettre la sélection du même fichier
event.target.value = '';
};
// Supprimer le fichier sélectionné
const clearFile = () => {
emit('file-cleared');
};
// Formater la taille du fichier
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 octets';
const k = 1024;
const sizes = ['octets', 'Ko', 'Mo', 'Go'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
};
return {
isDragOver,
handleDragEnter,
handleDragOver,
handleDragLeave,
handleDrop,
handleFileSelect,
clearFile,
formatFileSize,
};
},
};
</script>