🐛 fix: File upload component updated
This commit is contained in:
parent
0d925df378
commit
fc64beda13
|
@ -11,17 +11,16 @@ import type { FileUploadErrorEvent, FileUploadProgressEvent, FileUploadRemoveEve
|
||||||
import { computed, useId, ref, onMounted } from 'vue';
|
import { computed, useId, ref, onMounted } from 'vue';
|
||||||
import styles from '@visua/typography.module.css';
|
import styles from '@visua/typography.module.css';
|
||||||
|
|
||||||
|
const fileUploadRef = ref();
|
||||||
|
const fileProgressMap = ref<Record<string, number>>({});
|
||||||
|
const fileSelected = ref(false);
|
||||||
|
const hasActiveError = ref(false);
|
||||||
|
const lastSelectedFile = ref<File | null>(null);
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const fileUploadRef = ref();
|
|
||||||
const fileProgressMap = ref<Record<string, number>>({});
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
upload: () => fileUploadRef.value.upload()
|
|
||||||
});
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVFileUpload>(), {
|
const props = withDefaults(defineProps<IVFileUpload>(), {
|
||||||
id: () => useId(),
|
id: () => useId(),
|
||||||
label: 'Ajouter un fichier',
|
label: 'Ajouter un fichier',
|
||||||
|
@ -43,7 +42,7 @@ const props = withDefaults(defineProps<IVFileUpload>(), {
|
||||||
cancelLabel: 'Annuler',
|
cancelLabel: 'Annuler',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
showUploadButton: true,
|
showUploadButton: true,
|
||||||
advanced: false,
|
advanced: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
|
@ -58,22 +57,21 @@ const emit = defineEmits([
|
||||||
'removeUploadFile',
|
'removeUploadFile',
|
||||||
'uploader',
|
'uploader',
|
||||||
]);
|
]);
|
||||||
const fileSelected = ref(false);
|
|
||||||
const hasActiveError = ref(false);
|
|
||||||
|
|
||||||
const lastSelectedFile = ref<File | null>(null);
|
defineExpose({
|
||||||
|
upload: () => fileUploadRef.value.upload()
|
||||||
|
});
|
||||||
|
|
||||||
const handleSelect = (event: FileUploadSelectEvent) => {
|
const handleSelect = (event: FileUploadSelectEvent) => {
|
||||||
emit('select', event);
|
emit('select', event);
|
||||||
|
|
||||||
if (!props.advanced && event.files.length > 0 && fileUploadRef.value) {
|
if (!props.advanced && event.files.length > 0 && fileUploadRef.value) {
|
||||||
fileUploadRef.value.clear();
|
fileUploadRef.value.files = [event.files.at(-1)];
|
||||||
fileUploadRef.value.files.push(event.files[event.files.length - 1]);
|
lastSelectedFile.value = event.files.at(-1);
|
||||||
|
|
||||||
}
|
}
|
||||||
fileSelected.value = true;
|
fileSelected.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
emit('clear');
|
emit('clear');
|
||||||
fileSelected.value = false;
|
fileSelected.value = false;
|
||||||
|
@ -82,13 +80,9 @@ const handleClear = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpload = (event: FileUploadUploadEvent) => {
|
const handleUpload = (event: FileUploadUploadEvent) => {
|
||||||
totalSize.value = 0;
|
|
||||||
totalSizePercent.value = 100;
|
|
||||||
emit('upload', event);
|
emit('upload', event);
|
||||||
hasActiveError.value = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleRemove = (event: FileUploadRemoveEvent) => {
|
const handleRemove = (event: FileUploadRemoveEvent) => {
|
||||||
emit('remove', event);
|
emit('remove', event);
|
||||||
fileSelected.value = false;
|
fileSelected.value = false;
|
||||||
|
@ -110,34 +104,19 @@ const handleError = (event: FileUploadErrorEvent) => {
|
||||||
hasActiveError.value = true;
|
hasActiveError.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const error = computed(() => hasActiveError.value);
|
const uploadEvent = (callback: () => void) => {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
const isSimpleAndEmpty = computed(() => {
|
|
||||||
return !props.advanced && !fileSelected.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const currentFiles = fileUploadRef.value?.files || [];
|
const currentFiles = fileUploadRef.value?.files || [];
|
||||||
fileSelected.value = currentFiles.length > 0;
|
fileSelected.value = currentFiles.length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalSize = ref(0);
|
|
||||||
const totalSizePercent = ref(0);
|
|
||||||
|
|
||||||
const uploadEvent = (callback: () => void) => {
|
|
||||||
totalSizePercent.value = totalSize.value / 10;
|
|
||||||
callback();
|
|
||||||
fileUploadRef.value.upload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const padding = computed(() => props.advanced ? '1.125rem' : '0rem')
|
const padding = computed(() => props.advanced ? '1.125rem' : '0rem')
|
||||||
const borderColor = computed(() => props.advanced ? 'var(--border-default-grey)' : 'transparent');
|
const borderColor = computed(() => props.advanced ? 'var(--border-default-grey)' : 'transparent');
|
||||||
|
|
||||||
const getLastFileSelected = (files: File[]) => {
|
const error = computed(() => hasActiveError.value);
|
||||||
const indexOfLastFileSelected = files.length - 1;
|
|
||||||
return files[indexOfLastFileSelected];
|
|
||||||
}
|
|
||||||
|
|
||||||
const labelState = computed(() => {
|
const labelState = computed(() => {
|
||||||
if(!error.value && !props.disabled) return 'default';
|
if(!error.value && !props.disabled) return 'default';
|
||||||
|
@ -163,7 +142,6 @@ const globalStatusMessage = computed<{
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -211,88 +189,101 @@ const globalStatusMessage = computed<{
|
||||||
class="p-fileupload"
|
class="p-fileupload"
|
||||||
>
|
>
|
||||||
<template #header="slotProps">
|
<template #header="slotProps">
|
||||||
<div v-if="props.advanced" class="advanced-fileupload-header">
|
<VButtonGroup
|
||||||
<VButtonGroup
|
v-if="props.advanced"
|
||||||
:buttons="[
|
:buttons="[
|
||||||
{
|
{
|
||||||
label: 'Parcourir...',
|
label: 'Parcourir...',
|
||||||
onClick: slotProps.chooseCallback,
|
onClick: slotProps.chooseCallback,
|
||||||
title: 'parcourir les fichiers',
|
title: 'parcourir les fichiers',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Téléverser',
|
label: 'Téléverser',
|
||||||
icon: 'ri-upload-cloud-line',
|
icon: 'ri-upload-cloud-line',
|
||||||
onClick: () => uploadEvent(slotProps.uploadCallback),
|
onClick: () => uploadEvent(slotProps.uploadCallback),
|
||||||
secondary: true,
|
secondary: true,
|
||||||
disabled: !slotProps.files || slotProps.files.length === 0,
|
disabled: !slotProps.files || slotProps.files.length === 0,
|
||||||
title: 'televerser les fichiers'
|
title: 'televerser les fichiers'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Supprimer',
|
label: 'Supprimer',
|
||||||
tertiary: true,
|
tertiary: true,
|
||||||
onClick: slotProps.clearCallback,
|
onClick: slotProps.clearCallback,
|
||||||
disabled: !slotProps.files || slotProps.files.length === 0,
|
disabled: !slotProps.files || slotProps.files.length === 0,
|
||||||
title: 'suprimer les fichiers'
|
title: 'suprimer les fichiers'
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
:disabled="props.disabled"
|
||||||
|
inlineLayoutWhen="always"
|
||||||
|
title="boutons de gestion des fichiers"
|
||||||
|
/>
|
||||||
|
<div v-else class="simple">
|
||||||
|
<VButton
|
||||||
|
label="Parcourir..."
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
inlineLayoutWhen="always"
|
size="sm"
|
||||||
title="boutons de gestion des fichiers"
|
@click="slotProps.chooseCallback"
|
||||||
|
title="parcourir les fichiers"
|
||||||
/>
|
/>
|
||||||
<VMessage
|
|
||||||
v-if="globalStatusMessage"
|
|
||||||
:type="globalStatusMessage.type"
|
|
||||||
:title="globalStatusMessage.title"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else class="simple-fileupload-header">
|
|
||||||
<VButton label="Parcourir..." :disabled="props.disabled" size="sm" @click="slotProps.chooseCallback" title="parcourir les fichiers"/>
|
|
||||||
<span
|
<span
|
||||||
v-if="isSimpleAndEmpty"
|
v-if="(!slotProps.files || slotProps.files.length === 0) && (!slotProps.uploadedFiles || slotProps.uploadedFiles.length === 0)"
|
||||||
:class="[styles['text-body-SM-detail-text-Regular']]"
|
:class="[styles['text-body-SM-detail-text-Regular']]"
|
||||||
>
|
>
|
||||||
Aucun fichier sélectionné
|
Aucun fichier sélectionné
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #content="slotProps" v-if="!isSimpleAndEmpty">
|
<template #empty>
|
||||||
<div v-if="props.advanced" class="advanced-container-content">
|
<div class="fileupload-empty" v-if="props.advanced" :class="[styles['text-body-SM-detail-text-Regular']]">
|
||||||
<div class="fileupload-content">
|
<i class="ri-upload-cloud-line upload-cloud-icon"></i>
|
||||||
<div v-if="slotProps.files.length > 0">
|
<p>Glissez-déposez les fichiers ici pour les téléverser.</p>
|
||||||
<div class="file-area">
|
</div>
|
||||||
<VFile
|
</template>
|
||||||
v-for="(file, index) in slotProps.files"
|
<template #content="slotProps">
|
||||||
:key="file.name + file.type + file.size"
|
<div v-if="props.advanced" style="margin-top: 0.5rem;">
|
||||||
:file="file"
|
<VMessage
|
||||||
:index="index"
|
v-if="globalStatusMessage"
|
||||||
:remove-file-callback="slotProps.removeFileCallback"
|
:type="globalStatusMessage.type"
|
||||||
:progress="fileProgressMap[file.name] || 0"
|
:title="globalStatusMessage.title"
|
||||||
status="pending"
|
/>
|
||||||
advanced
|
<div v-if="slotProps.files.length > 0" class="file-area">
|
||||||
/>
|
<VFile
|
||||||
</div>
|
v-for="(file, index) in slotProps.files"
|
||||||
</div>
|
:key="file.name + file.type + file.size"
|
||||||
<div v-if="slotProps.uploadedFiles.length > 0" class="fileupload-content">
|
:file="file"
|
||||||
<div class="file-area">
|
:index="index"
|
||||||
<VFile
|
:remove-file-callback="slotProps.removeFileCallback"
|
||||||
v-for="(file, index) in slotProps.uploadedFiles"
|
:progress="fileProgressMap[file.name] || 0"
|
||||||
:key="file.name + file.type + file.size"
|
status="pending"
|
||||||
:file="file"
|
advanced
|
||||||
:index="index"
|
/>
|
||||||
:remove-file-callback="slotProps.removeFileCallback"
|
</div>
|
||||||
:progress="100"
|
<div v-if="slotProps.uploadedFiles.length > 0" class="file-area">
|
||||||
status="completed"
|
<VFile
|
||||||
advanced
|
v-for="(file, index) in slotProps.uploadedFiles"
|
||||||
/>
|
:key="file.name + file.type + file.size"
|
||||||
</div>
|
:file="file"
|
||||||
</div>
|
:index="index"
|
||||||
|
:remove-file-callback="slotProps.removeFileCallback"
|
||||||
|
:progress="100"
|
||||||
|
status="completed"
|
||||||
|
advanced
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="slotProps.files.length > 0" class="simple-container-content">
|
<div v-else>
|
||||||
<VFile
|
<VFile
|
||||||
:file="getLastFileSelected(slotProps.files)"
|
v-if="lastSelectedFile"
|
||||||
|
:file="lastSelectedFile"
|
||||||
:remove-file-callback="slotProps.removeFileCallback"
|
:remove-file-callback="slotProps.removeFileCallback"
|
||||||
|
status="pending"
|
||||||
|
/>
|
||||||
|
<VFile
|
||||||
|
v-if="slotProps.uploadedFiles.length > 0 && slotProps.uploadedFiles.at(-1)"
|
||||||
|
:file="slotProps.uploadedFiles.at(-1)!"
|
||||||
|
:remove-file-callback="slotProps.removeFileCallback"
|
||||||
|
status="completed"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<VHint
|
<VHint
|
||||||
|
@ -303,12 +294,6 @@ const globalStatusMessage = computed<{
|
||||||
icon
|
icon
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #empty v-if="props.advanced">
|
|
||||||
<div class="fileupload-empty" v-if="props.advanced">
|
|
||||||
<i class="ri-upload-cloud-line upload-cloud-icon"></i>
|
|
||||||
<p>Glissez-déposez les fichiers ici pour les téléverser.</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</FileUpload>
|
</FileUpload>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -334,27 +319,19 @@ const globalStatusMessage = computed<{
|
||||||
padding: v-bind(padding);
|
padding: v-bind(padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.simple-fileupload-header{
|
.simple{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: var(--p-fileupload-basic-gap);
|
align-items: center;
|
||||||
align-items: baseline;
|
gap: var(--p-fileupload-file-gap);
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.advanced-fileupload-header{
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--p-fileupload-content-gap);
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileupload-empty{
|
.fileupload-empty{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-around;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-cloud-icon{
|
.upload-cloud-icon{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user