✨ File component added
This commit is contained in:
parent
abae860b58
commit
16f0617df2
213
src/components/file/VFile.vue
Normal file
213
src/components/file/VFile.vue
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import VBadge from '../badge/VBadge.vue';
|
||||||
|
import VButton from '../button/VButton.vue';
|
||||||
|
import VProgressBar from '../progressbar/VProgressBar.vue';
|
||||||
|
import { usePrimeVue } from 'primevue';
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import styles from '@visua/typography.module.css';
|
||||||
|
|
||||||
|
export interface IVFile {
|
||||||
|
file: File
|
||||||
|
advanced?: boolean
|
||||||
|
index?: number
|
||||||
|
removeFileCallback?: (index: number) => void
|
||||||
|
status?: 'pending' | 'completed' | 'error'
|
||||||
|
progress?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<IVFile>(), {
|
||||||
|
advanced: false,
|
||||||
|
index: 0,
|
||||||
|
removeFileCallback: () => {},
|
||||||
|
status: 'pending',
|
||||||
|
progress: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeType = computed(() => {
|
||||||
|
switch (props.status) {
|
||||||
|
case 'pending': return 'warning'
|
||||||
|
case 'completed': return 'success'
|
||||||
|
case 'error': return 'error'
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeLabel = computed(() => {
|
||||||
|
switch (props.status) {
|
||||||
|
case 'pending': return 'En attente'
|
||||||
|
case 'completed': return 'Terminé'
|
||||||
|
case 'error': return 'Annulé'
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const $primevue = usePrimeVue();
|
||||||
|
const totalSize = ref(0);
|
||||||
|
const totalSizePercent = ref(0);
|
||||||
|
|
||||||
|
const formatSize = (bytes: number) => {
|
||||||
|
const k = 1024;
|
||||||
|
const dm = 3;
|
||||||
|
const sizes = $primevue.config.locale?.fileSizeTypes ?? ['B', 'KB', 'MB', 'GB', 'TB'];;
|
||||||
|
|
||||||
|
if (bytes === 0) {
|
||||||
|
return `0 ${ sizes[0]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
const formattedSize = parseFloat((bytes / k ** i).toFixed(dm));
|
||||||
|
|
||||||
|
return `${formattedSize} ${sizes[i]}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRemoveTemplatingFile = (file: File, removeFileCallback: (index: number) => void, index: number) => {
|
||||||
|
removeFileCallback(index);
|
||||||
|
totalSize.value -= parseInt(formatSize(file.size));
|
||||||
|
totalSizePercent.value = totalSize.value / 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFileIconClass = (file: File) => {
|
||||||
|
const type = file.name.toLowerCase();
|
||||||
|
if (type.endsWith('.pdf')) return 'ri-file-pdf-2-line';
|
||||||
|
if (type.endsWith('.doc') || type.endsWith('.docx')) return 'ri-file-word-2-line';
|
||||||
|
if (type.endsWith('.xls') || type.endsWith('.xlsx')) return 'ri-file-excel-2-line';
|
||||||
|
if (type.endsWith('.txt')) return 'ri-file-text-line';
|
||||||
|
if (type.endsWith('.zip') || type.endsWith('.rar')) return 'ri-file-zip-line';
|
||||||
|
return 'ri-file-line'; // icône générique
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFileIconColor = (file: File) => {
|
||||||
|
const type = file.name.toLowerCase();
|
||||||
|
if (type.endsWith('.pdf')) return 'var(--border-plain-error)'; // rouge
|
||||||
|
if (type.endsWith('.doc') || type.endsWith('.docx')) return 'var(--border-plain-blue-france)'; // bleu
|
||||||
|
if (type.endsWith('.xls') || type.endsWith('.xlsx')) return 'var(--border-plain-success)'; // vert
|
||||||
|
if (type.endsWith('.txt')) return 'var(--border-plain-grey)'; // gris
|
||||||
|
if (type.endsWith('.zip') || type.endsWith('.rar')) return 'var(--illustration-color-850-tournesol-default)'; // jaune
|
||||||
|
return 'var(--border-plain-blue-france)'; // bleu marine
|
||||||
|
};
|
||||||
|
|
||||||
|
const borderColor = computed(() => {
|
||||||
|
switch (props.status) {
|
||||||
|
case 'pending': return '2px solid var(--system-color-950-warning-active)';
|
||||||
|
case 'completed': return '2px solid var(--system-color-950-success-active)';
|
||||||
|
case 'error': return '2px solid var(--system-color-950-success-active)';
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getImagePreview = (file: File) => {
|
||||||
|
return URL.createObjectURL(file);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="props.advanced"
|
||||||
|
class="advanced-file"
|
||||||
|
:title="`Fichier : ${props.file.name}`"
|
||||||
|
:style="{border: borderColor}"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
v-if="file.type.startsWith('image/')"
|
||||||
|
role="presentation"
|
||||||
|
:alt="props.file.name"
|
||||||
|
:src="getImagePreview(props.file)"
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
v-else
|
||||||
|
:class="getFileIconClass(props.file)"
|
||||||
|
:style="{color: getFileIconColor(props.file), fontSize: '6.25rem'}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span :title="props.file.name" class="file-name">
|
||||||
|
{{ props.file.name }}
|
||||||
|
</span>
|
||||||
|
<div>{{ formatSize(props.file.size) }}</div>
|
||||||
|
<VBadge
|
||||||
|
v-if="advanced"
|
||||||
|
:label="badgeLabel"
|
||||||
|
:type="badgeType"
|
||||||
|
small
|
||||||
|
no-icon
|
||||||
|
/>
|
||||||
|
<VProgressBar
|
||||||
|
v-if="advanced && status === 'pending'"
|
||||||
|
:value="props.progress"
|
||||||
|
:show-value="false"
|
||||||
|
style="width: 100%; margin-top: 0.5rem;"
|
||||||
|
/>
|
||||||
|
<VButton
|
||||||
|
tertiary
|
||||||
|
noOutline
|
||||||
|
size="sm"
|
||||||
|
icon="ri-close-line"
|
||||||
|
@click="onRemoveTemplatingFile(props.file, props.removeFileCallback, props.index)"
|
||||||
|
title='Spprimer le fichier'
|
||||||
|
ref="clearfile"
|
||||||
|
aria-controls="clear-file"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- Simple file -->
|
||||||
|
<div v-else class="simple-file" >
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
v-if="file.type.startsWith('image/')"
|
||||||
|
role="presentation"
|
||||||
|
:alt="props.file.name"
|
||||||
|
:src="getImagePreview(props.file)"
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
v-else
|
||||||
|
:class="getFileIconClass(props.file)"
|
||||||
|
:style="{color: getFileIconColor(props.file), fontSize: '2.5rem', padding: '0px'}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="file-info" :class="[styles['text-body-SM-detail-text-Regular']]">
|
||||||
|
<span>{{ props.file.name }}</span>
|
||||||
|
<span>{{ formatSize(props.file.size) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
img{
|
||||||
|
width: 2.5rem;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-file{
|
||||||
|
border-radius: 3px;
|
||||||
|
width: fit-content;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-file{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: calc(var(--p-fileupload-content-gap) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name{
|
||||||
|
max-width: 7.5rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: start;
|
||||||
|
gap: 0.025rem;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user