a23146e7f0
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
166 lines
4.6 KiB
Vue
166 lines
4.6 KiB
Vue
<script setup lang="ts">
|
|
import { CollectionObject } from '@DocumentsManager/models/collection'
|
|
import { EntityObject } from '@DocumentsManager/models/entity'
|
|
import { getFileIcon, formatSize } from '@/utils/fileHelpers'
|
|
import FileActionsMenu from '@/components/FileActionsMenu.vue'
|
|
import FilePreviewPopover from '@/components/viewer/FilePreviewPopover.vue'
|
|
|
|
type PreviewSource = (item: { id: string; meta?: Record<string, unknown> }) => string | Blob | Promise<string | Blob>
|
|
|
|
defineProps<{
|
|
collections: CollectionObject[]
|
|
entities: EntityObject[]
|
|
selectedIds: Set<string>
|
|
previewSource?: PreviewSource
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'item-click': [item: CollectionObject | EntityObject, event: MouseEvent | KeyboardEvent]
|
|
'open': [item: CollectionObject | EntityObject]
|
|
'edit': [item: CollectionObject | EntityObject]
|
|
'rename': [item: CollectionObject | EntityObject]
|
|
'delete': [item: CollectionObject | EntityObject]
|
|
'download': [item: CollectionObject | EntityObject]
|
|
'show-details': [item: CollectionObject | EntityObject]
|
|
}>()
|
|
</script>
|
|
|
|
<template>
|
|
<div class="files-grid pa-4">
|
|
<!-- Folders -->
|
|
<div
|
|
v-for="folder in collections"
|
|
:key="String(folder.identifier)"
|
|
class="files-grid-item"
|
|
:class="{ selected: selectedIds.has(String(folder.identifier)) }"
|
|
@click="emit('item-click', folder, $event)"
|
|
>
|
|
<div class="files-grid-item-actions">
|
|
<FileActionsMenu
|
|
:item="folder"
|
|
size="x-small"
|
|
button-class="action-btn"
|
|
@open="emit('open', $event)"
|
|
@edit="emit('edit', $event)"
|
|
@rename="emit('rename', $event)"
|
|
@delete="emit('delete', $event)"
|
|
@download="emit('download', $event)"
|
|
@show-details="emit('show-details', $event)"
|
|
/>
|
|
</div>
|
|
<v-icon size="48" color="amber-darken-2">mdi-folder</v-icon>
|
|
<div class="files-grid-label">{{ folder.properties.label || String(folder.identifier ?? '') }}</div>
|
|
</div>
|
|
|
|
<!-- Files -->
|
|
<div
|
|
v-for="entity in entities"
|
|
:key="String(entity.identifier)"
|
|
class="files-grid-item"
|
|
:class="{ selected: selectedIds.has(String(entity.identifier)) }"
|
|
@click="emit('item-click', entity, $event)"
|
|
@dblclick.stop="emit('open', entity)"
|
|
>
|
|
<div class="files-grid-item-actions">
|
|
<FileActionsMenu
|
|
:item="entity"
|
|
size="x-small"
|
|
button-class="action-btn"
|
|
@open="emit('open', $event)"
|
|
@edit="emit('edit', $event)"
|
|
@rename="emit('rename', $event)"
|
|
@delete="emit('delete', $event)"
|
|
@download="emit('download', $event)"
|
|
@show-details="emit('show-details', $event)"
|
|
/>
|
|
</div>
|
|
<v-icon size="48" color="grey">{{ getFileIcon(entity) }}</v-icon>
|
|
<div class="files-grid-label">{{ entity.properties.label || String(entity.identifier ?? '') }}</div>
|
|
<div class="files-grid-size">{{ formatSize(entity.properties.size) }}</div>
|
|
|
|
<FilePreviewPopover
|
|
v-if="previewSource"
|
|
:entity="entity"
|
|
:source="previewSource"
|
|
@expand="emit('open', $event)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.files-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
gap: 16px;
|
|
content-visibility: auto;
|
|
contain-intrinsic-size: auto 500px;
|
|
}
|
|
|
|
.files-grid-item {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 16px 8px;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
content-visibility: auto;
|
|
contain-intrinsic-size: 120px 120px;
|
|
}
|
|
|
|
.files-grid-item:hover {
|
|
background-color: rgb(var(--v-theme-surface-variant), 0.5);
|
|
}
|
|
|
|
.files-grid-item.selected {
|
|
background-color: rgb(var(--v-theme-primary), 0.12);
|
|
}
|
|
|
|
.files-grid-item-actions {
|
|
position: absolute;
|
|
top: 4px;
|
|
right: 4px;
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.files-grid-item:hover .files-grid-item-actions,
|
|
.files-grid-item.selected .files-grid-item-actions {
|
|
opacity: 1;
|
|
}
|
|
|
|
.action-btn {
|
|
background-color: rgb(var(--v-theme-surface));
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.files-grid-label {
|
|
margin-top: 8px;
|
|
font-size: 13px;
|
|
text-align: center;
|
|
word-break: break-word;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
}
|
|
|
|
.files-grid-size {
|
|
font-size: 11px;
|
|
color: rgb(var(--v-theme-on-surface-variant));
|
|
margin-top: 2px;
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.files-grid {
|
|
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
|
gap: 8px;
|
|
}
|
|
}
|
|
</style>
|