add img preview + fix navbar

This commit is contained in:
Matthieu
2025-10-16 10:26:36 +02:00
parent 8eada12438
commit 4ccc19505f
10 changed files with 479 additions and 344 deletions

View File

@@ -214,13 +214,21 @@
class="flex items-center justify-between rounded border border-base-200 bg-base-100 px-3 py-2"
>
<div class="flex items-center gap-3 text-sm">
<span class="text-xl" :class="documentIcon(document).colorClass">
<div class="h-14 w-14 flex-shrink-0 overflow-hidden rounded-md border border-base-200 bg-base-200/70 flex items-center justify-center">
<img
v-if="isImageDocument(document) && document.path"
:src="document.path"
class="h-full w-full object-cover"
:alt="`Aperçu de ${document.name}`"
>
<component
v-else
:is="documentIcon(document).component"
class="h-6 w-6"
:class="documentIcon(document).colorClass"
aria-hidden="true"
/>
</span>
</div>
<div>
<div class="font-medium">
{{ document.name }}
@@ -309,7 +317,7 @@ import DocumentUpload from './DocumentUpload.vue'
import ConstructeurSelect from './ConstructeurSelect.vue'
import { useDocuments } from '~/composables/useDocuments'
import { getFileIcon } from '~/utils/fileIcons'
import { canPreviewDocument } from '~/utils/documentPreview'
import { canPreviewDocument, isImageDocument } from '~/utils/documentPreview'
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
import IconLucideChevronRight from '~icons/lucide/chevron-right'
import { useCustomFields } from '~/composables/useCustomFields'

View File

@@ -37,12 +37,21 @@
<ul v-if="selectedFiles.length" class="mt-4 w-full space-y-2 text-left">
<li v-for="file in selectedFiles" :key="file.name" class="flex items-center justify-between text-sm">
<div class="flex items-center gap-3">
<component
:is="getIcon(file).component"
class="h-6 w-6"
:class="getIcon(file).colorClass"
aria-hidden="true"
/>
<div class="h-14 w-14 flex-shrink-0 overflow-hidden rounded-md border border-base-300 bg-base-200/70 flex items-center justify-center">
<img
v-if="isImageFile(file)"
:src="getFilePreview(file)"
class="h-full w-full object-cover"
:alt="`Aperçu de ${file.name}`"
>
<component
v-else
:is="getIcon(file).component"
class="h-6 w-6"
:class="getIcon(file).colorClass"
aria-hidden="true"
/>
</div>
<div class="flex flex-col">
<span class="font-medium">{{ file.name }}</span>
<span class="text-xs text-gray-500">{{ formatSize(file.size) }} {{ file.type || 'Type inconnu' }}</span>
@@ -58,7 +67,7 @@
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { ref, computed, watch, onBeforeUnmount } from 'vue'
import { useToast } from '~/composables/useToast'
import { getFileIcon } from '~/utils/fileIcons'
import IconLucideCloudUpload from '~icons/lucide/cloud-upload'
@@ -96,6 +105,30 @@ const dragActive = ref(false)
const fileInput = ref(null)
const internalFiles = ref([])
const { showError } = useToast()
const previewUrls = new Map()
const isImageFile = (file) => (file?.type || '').startsWith('image/')
const getFilePreview = (file) => {
if (!isImageFile(file)) { return null }
if (!previewUrls.has(file)) {
previewUrls.set(file, URL.createObjectURL(file))
}
return previewUrls.get(file)
}
const cleanupRemovedPreviews = (previousFiles = [], nextFiles = []) => {
const nextSet = new Set(nextFiles)
previousFiles.forEach((file) => {
if (!nextSet.has(file)) {
const url = previewUrls.get(file)
if (url) {
URL.revokeObjectURL(url)
previewUrls.delete(file)
}
}
})
}
const selectedFiles = computed(() => internalFiles.value)
@@ -103,6 +136,7 @@ watch(
() => props.modelValue,
(newValue) => {
if (Array.isArray(newValue)) {
cleanupRemovedPreviews(internalFiles.value, newValue)
internalFiles.value = [...newValue]
}
},
@@ -114,6 +148,7 @@ const triggerFileDialog = () => {
}
const emitFiles = (files) => {
cleanupRemovedPreviews(internalFiles.value, files)
internalFiles.value = files
emit('update:modelValue', files)
emit('files-added', files)
@@ -180,4 +215,11 @@ const formatSize = (size) => {
const getIcon = (file) => {
return getFileIcon({ name: file.name, mime: file.type })
}
onBeforeUnmount(() => {
previewUrls.forEach((url) => {
URL.revokeObjectURL(url)
})
previewUrls.clear()
})
</script>

View File

@@ -276,13 +276,21 @@
class="flex items-center justify-between rounded border border-base-200 bg-base-100 px-3 py-2"
>
<div class="flex items-center gap-3 text-sm">
<span class="text-xl" :class="documentIcon(document).colorClass">
<div class="h-14 w-14 flex-shrink-0 overflow-hidden rounded-md border border-base-200 bg-base-200/70 flex items-center justify-center">
<img
v-if="isImageDocument(document) && document.path"
:src="document.path"
class="h-full w-full object-cover"
:alt="`Aperçu de ${document.name}`"
>
<component
v-else
:is="documentIcon(document).component"
class="h-6 w-6"
:class="documentIcon(document).colorClass"
aria-hidden="true"
/>
</span>
</div>
<div>
<div class="font-medium">
{{ document.name }}
@@ -340,7 +348,7 @@ import { useCustomFields } from "~/composables/useCustomFields";
import { useToast } from "~/composables/useToast";
import { useDocuments } from "~/composables/useDocuments";
import { getFileIcon } from "~/utils/fileIcons";
import { canPreviewDocument } from "~/utils/documentPreview";
import { canPreviewDocument, isImageDocument } from "~/utils/documentPreview";
import DocumentUpload from "~/components/DocumentUpload.vue";
import DocumentPreviewModal from "~/components/DocumentPreviewModal.vue";
import IconLucidePackage from "~icons/lucide/package";

View File

@@ -53,9 +53,21 @@
class="flex items-center justify-between rounded border border-base-200 bg-base-100 px-3 py-2"
>
<div class="flex items-center gap-3 text-sm">
<span class="text-xl" :class="documentIcon(document).colorClass">
<component :is="documentIcon(document).component" class="h-6 w-6" aria-hidden="true" />
</span>
<div class="h-14 w-14 flex-shrink-0 overflow-hidden rounded-md border border-base-200 bg-base-200/70 flex items-center justify-center">
<img
v-if="isImageDocument(document) && document.path"
:src="document.path"
class="h-full w-full object-cover"
:alt="`Aperçu de ${document.name}`"
>
<component
v-else
:is="documentIcon(document).component"
class="h-6 w-6"
:class="documentIcon(document).colorClass"
aria-hidden="true"
/>
</div>
<div>
<div class="font-medium">
{{ document.name }}
@@ -103,6 +115,7 @@
<script setup>
import { computed, toRefs } from 'vue'
import { isImageDocument } from '~/utils/documentPreview'
import DocumentUpload from '~/components/DocumentUpload.vue'
import SiteContactFormFields from '~/components/sites/SiteContactFormFields.vue'