230 lines
6.0 KiB
Vue
230 lines
6.0 KiB
Vue
<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}"
|
|
:class="[styles['text-body-XS-mention-text-Medium']]"
|
|
>
|
|
<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"
|
|
class="button"
|
|
/>
|
|
<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: '4rem', fontWeight: 500}"
|
|
/>
|
|
</div>
|
|
<span :title="props.file?.name" class="file-name">
|
|
{{ props.file?.name }}
|
|
</span>
|
|
<span>{{ formatSize(props.file?.size ?? 0) }}</span>
|
|
<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: 6rem; margin-top: 0.5rem;"
|
|
/>
|
|
</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 ?? 0) }}</span>
|
|
</div>
|
|
<VBadge
|
|
:label="badgeLabel"
|
|
:type="badgeType"
|
|
small
|
|
no-icon
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="css" scoped>
|
|
img{
|
|
width: 2.5rem;
|
|
height: auto;
|
|
}
|
|
|
|
.button{
|
|
display: flex;
|
|
align-self: self-end;
|
|
}
|
|
|
|
.advanced-file{
|
|
border-radius: 3px;
|
|
width: fit-content;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding-bottom: 0.25rem;
|
|
}
|
|
|
|
.simple-file{
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: calc(var(--p-fileupload-content-gap) / 4);
|
|
}
|
|
|
|
.file-name{
|
|
width: 6rem;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
margin: 0rem 0.5rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.file-info{
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: start;
|
|
gap: 0.025rem;
|
|
}
|
|
</style>
|