feat: implement common file preview component

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-08-29 19:13:51 -04:00
parent 0ceb6808c4
commit c5a6461f5c
6 changed files with 108 additions and 47 deletions
+4 -39
View File
@@ -1,9 +1,9 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { CollectionObject } from '@DocumentsManager/models/collection'
import { EntityObject } from '@DocumentsManager/models/entity'
import { getFileIcon, formatSize } from '@/utils/fileHelpers'
import { formatSize } from '@/utils/fileHelpers'
import FileActionsMenu from '@/components/FileActionsMenu.vue'
import FilePreview from '@/components/FilePreview.vue'
import FilePreviewPopover from '@/components/viewer/FilePreviewPopover.vue'
type PreviewSource = (item: { id: string; meta?: Record<string, unknown> }) => string | Blob | Promise<string | Blob>
@@ -13,25 +13,9 @@ const props = defineProps<{
entities: EntityObject[]
selectedIds: Set<string>
previewSource?: PreviewSource
thumbnailSource?: PreviewSource
}>()
// Thumbnails only support the plain-URL form of PreviewSource (what Documents
// actually passes); Blob/Promise results just fall back to the file icon.
const thumbnailErrors = reactive(new Set<string>())
function thumbnailUrl(entity: EntityObject): string | null {
if (!props.previewSource) return null
const result = props.previewSource({
id: String(entity.identifier ?? ''),
meta: { collection: entity.collection ? String(entity.collection) : null },
})
return typeof result === 'string' ? result : null
}
function markThumbnailFailed(id: string) {
thumbnailErrors.add(id)
}
const emit = defineEmits<{
'item-click': [item: CollectionObject | EntityObject, event: MouseEvent | KeyboardEvent]
'open': [item: CollectionObject | EntityObject]
@@ -93,21 +77,7 @@ const emit = defineEmits<{
/>
</div>
<div class="files-grid-thumbnail">
<v-img
v-if="thumbnailUrl(entity) && !thumbnailErrors.has(String(entity.identifier))"
:src="thumbnailUrl(entity)!"
:alt="entity.properties.label || 'File preview'"
cover
class="files-grid-thumbnail-img"
@error="markThumbnailFailed(String(entity.identifier))"
>
<template #placeholder>
<div class="d-flex fill-height align-center justify-center">
<v-progress-circular indeterminate color="primary" size="20" width="2" />
</div>
</template>
</v-img>
<v-icon v-else size="48" color="grey">{{ getFileIcon(entity) }}</v-icon>
<FilePreview :entity="entity" :source="thumbnailSource" :size="64" />
</div>
<div class="files-grid-label">{{ entity.properties.label || String(entity.identifier ?? '') }}</div>
<div class="files-grid-size">{{ formatSize(entity.properties.size) }}</div>
@@ -181,11 +151,6 @@ const emit = defineEmits<{
background-color: rgb(var(--v-theme-surface-variant), 0.4);
}
.files-grid-thumbnail-img {
width: 100%;
height: 100%;
}
.files-grid-label {
margin-top: 8px;
font-size: 13px;