215 lines
5.5 KiB
Vue
215 lines
5.5 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'
|
|
: 'border-gray-300 hover:border-gray-400'
|
|
]"
|
|
>
|
|
<!-- 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">
|
|
{{ isDragOver ? 'Déposez votre fichier ici' : 'Sélectionnez un fichier CBR ou CBZ' }}
|
|
</h3>
|
|
<p class="text-sm text-gray-500">
|
|
Glissez-déposez votre fichier ou cliquez pour le sélectionner
|
|
</p>
|
|
<p class="text-xs text-gray-400">
|
|
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 rounded-lg">
|
|
<div class="flex items-center space-x-3">
|
|
<DocumentIcon class="w-8 h-8 text-gray-600" />
|
|
<div class="flex-1 min-w-0">
|
|
<p class="text-sm font-medium text-gray-900 truncate">
|
|
{{ selectedFile.name }}
|
|
</p>
|
|
<p class="text-sm text-gray-500">
|
|
{{ formatFileSize(selectedFile.size) }}
|
|
</p>
|
|
</div>
|
|
<button
|
|
@click="clearFile"
|
|
class="p-1 text-gray-400 hover:text-gray-600 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>
|