refactor: standardize design
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -3,8 +3,8 @@ import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useModuleStore } from '@KTXC'
|
||||
import { useFileManager, useFileSelection, useFileUpload } from '@/composables'
|
||||
import type { ViewMode, SortField, SortOrder, BreadcrumbItem } from '@/types'
|
||||
import { FileCollectionObject } from '@FilesManager/models/collection'
|
||||
import { FileEntityObject } from '@FilesManager/models/entity'
|
||||
import { CollectionObject } from '@DocumentsManager/models/collection'
|
||||
import { EntityObject } from '@DocumentsManager/models/entity'
|
||||
|
||||
// Components
|
||||
import {
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
FilesGridView,
|
||||
FilesListView,
|
||||
FilesDetailsView,
|
||||
FileViewerDialog,
|
||||
NewFolderDialog,
|
||||
RenameDialog,
|
||||
DeleteConfirmDialog,
|
||||
@@ -26,7 +27,7 @@ import {
|
||||
// Check if file manager is available
|
||||
const moduleStore = useModuleStore()
|
||||
const isFileManagerAvailable = computed(() => {
|
||||
return moduleStore.has('file_manager') || moduleStore.has('FileManager')
|
||||
return moduleStore.has('documents_manager') || moduleStore.has('DocumentsManager')
|
||||
})
|
||||
|
||||
// Active provider/service (will be selectable later)
|
||||
@@ -65,16 +66,54 @@ const showNewFolderDialog = ref(false)
|
||||
const showRenameDialog = ref(false)
|
||||
const showDeleteDialog = ref(false)
|
||||
const showUploadDialog = ref(false)
|
||||
const nodeToRename = ref<FileCollectionObject | FileEntityObject | null>(null)
|
||||
const nodesToDelete = ref<(FileCollectionObject | FileEntityObject)[]>([])
|
||||
const nodeToRename = ref<CollectionObject | EntityObject | null>(null)
|
||||
const nodesToDelete = ref<(CollectionObject | EntityObject)[]>([])
|
||||
|
||||
// Drag and drop state
|
||||
const isDragOver = ref(false)
|
||||
|
||||
// Viewer state
|
||||
const viewerEntity = ref<EntityObject | null>(null)
|
||||
const showViewer = ref(false)
|
||||
|
||||
function handleOpenItem(item: CollectionObject | EntityObject) {
|
||||
if (item instanceof EntityObject) {
|
||||
viewerEntity.value = item
|
||||
showViewer.value = true
|
||||
} else {
|
||||
// Folders: navigate into them when opened via double-click / action
|
||||
selection.clear()
|
||||
handleFolderOpen(item as CollectionObject)
|
||||
}
|
||||
}
|
||||
|
||||
function handleViewerNavigate(entity: EntityObject) {
|
||||
viewerEntity.value = entity
|
||||
}
|
||||
|
||||
// Hidden file inputs
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null)
|
||||
const folderInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
function nodeId(node: CollectionObject | EntityObject): string {
|
||||
return String(node.identifier ?? '')
|
||||
}
|
||||
|
||||
function nodeLabel(node: CollectionObject | EntityObject): string {
|
||||
if (node instanceof EntityObject) {
|
||||
return node.properties.label || nodeId(node)
|
||||
}
|
||||
return node.properties.label || nodeId(node)
|
||||
}
|
||||
|
||||
function renameCurrentName(node: CollectionObject | EntityObject | null): string {
|
||||
if (!node) return ''
|
||||
if (node instanceof EntityObject) {
|
||||
return node.properties.label || nodeId(node)
|
||||
}
|
||||
return node.properties.label || nodeId(node)
|
||||
}
|
||||
|
||||
// Computed
|
||||
const breadcrumbs = computed<BreadcrumbItem[]>(() => {
|
||||
const items: BreadcrumbItem[] = [
|
||||
@@ -83,8 +122,8 @@ const breadcrumbs = computed<BreadcrumbItem[]>(() => {
|
||||
|
||||
for (const node of fileManager.breadcrumbs.value) {
|
||||
items.push({
|
||||
id: node.id,
|
||||
label: node.label,
|
||||
id: nodeId(node),
|
||||
label: nodeLabel(node),
|
||||
isRoot: false,
|
||||
})
|
||||
}
|
||||
@@ -98,24 +137,59 @@ const sortedItems = computed(() => {
|
||||
|
||||
// Sort collections
|
||||
collections.sort((a, b) => {
|
||||
const aVal = a[sortField.value as keyof FileCollectionObject] ?? ''
|
||||
const bVal = b[sortField.value as keyof FileCollectionObject] ?? ''
|
||||
const aVal = sortField.value === 'label'
|
||||
? (a.properties.label || nodeId(a))
|
||||
: sortField.value === 'modifiedOn'
|
||||
? (a.modified?.getTime() ?? 0)
|
||||
: sortField.value === 'createdOn'
|
||||
? (a.created?.getTime() ?? 0)
|
||||
: ''
|
||||
const bVal = sortField.value === 'label'
|
||||
? (b.properties.label || nodeId(b))
|
||||
: sortField.value === 'modifiedOn'
|
||||
? (b.modified?.getTime() ?? 0)
|
||||
: sortField.value === 'createdOn'
|
||||
? (b.created?.getTime() ?? 0)
|
||||
: ''
|
||||
const cmp = String(aVal).localeCompare(String(bVal))
|
||||
return sortOrder.value === 'asc' ? cmp : -cmp
|
||||
})
|
||||
|
||||
// Sort entities
|
||||
entities.sort((a, b) => {
|
||||
const aVal = a[sortField.value as keyof FileEntityObject] ?? ''
|
||||
const bVal = b[sortField.value as keyof FileEntityObject] ?? ''
|
||||
const aVal = sortField.value === 'label'
|
||||
? (a.properties.label || nodeId(a))
|
||||
: sortField.value === 'mime'
|
||||
? (a.properties.mime || '')
|
||||
: sortField.value === 'size'
|
||||
? a.properties.size
|
||||
: sortField.value === 'modifiedOn'
|
||||
? (a.modified?.getTime() ?? 0)
|
||||
: sortField.value === 'createdOn'
|
||||
? (a.created?.getTime() ?? 0)
|
||||
: ''
|
||||
const bVal = sortField.value === 'label'
|
||||
? (b.properties.label || nodeId(b))
|
||||
: sortField.value === 'mime'
|
||||
? (b.properties.mime || '')
|
||||
: sortField.value === 'size'
|
||||
? b.properties.size
|
||||
: sortField.value === 'modifiedOn'
|
||||
? (b.modified?.getTime() ?? 0)
|
||||
: sortField.value === 'createdOn'
|
||||
? (b.created?.getTime() ?? 0)
|
||||
: ''
|
||||
const cmp = String(aVal).localeCompare(String(bVal))
|
||||
return sortOrder.value === 'asc' ? cmp : -cmp
|
||||
})
|
||||
|
||||
// Filter by search
|
||||
const filterFn = (item: FileCollectionObject | FileEntityObject) => {
|
||||
const filterFn = (item: CollectionObject | EntityObject) => {
|
||||
if (!searchQuery.value) return true
|
||||
return item.label.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
const label = item instanceof EntityObject
|
||||
? (item.properties.label || nodeId(item))
|
||||
: (item.properties.label || nodeId(item))
|
||||
return label.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -136,13 +210,13 @@ async function handleBreadcrumbNavigate(item: BreadcrumbItem) {
|
||||
await fileManager.navigateTo(item.isRoot ? null : item.id)
|
||||
}
|
||||
|
||||
async function handleFolderOpen(folder: FileCollectionObject) {
|
||||
async function handleFolderOpen(folder: CollectionObject) {
|
||||
selection.clear()
|
||||
await fileManager.navigateTo(folder.id)
|
||||
await fileManager.navigateTo(nodeId(folder))
|
||||
}
|
||||
|
||||
// Item interaction methods
|
||||
function handleItemClick(item: FileCollectionObject | FileEntityObject, event: MouseEvent | KeyboardEvent) {
|
||||
function handleItemClick(item: CollectionObject | EntityObject, event: MouseEvent | KeyboardEvent) {
|
||||
const hasCtrl = event.ctrlKey || event.metaKey
|
||||
const hasShift = event.shiftKey
|
||||
|
||||
@@ -154,10 +228,10 @@ function handleItemClick(item: FileCollectionObject | FileEntityObject, event: M
|
||||
selection.select(item)
|
||||
} else {
|
||||
// Single click behavior depends on item type
|
||||
if (item['@type'] === 'files.collection') {
|
||||
if (item instanceof CollectionObject) {
|
||||
// Folders: navigate into them
|
||||
selection.clear()
|
||||
handleFolderOpen(item as FileCollectionObject)
|
||||
handleFolderOpen(item as CollectionObject)
|
||||
} else {
|
||||
// Files: show info panel
|
||||
selection.clear()
|
||||
@@ -167,7 +241,7 @@ function handleItemClick(item: FileCollectionObject | FileEntityObject, event: M
|
||||
}
|
||||
|
||||
// Show details panel for an item
|
||||
function handleShowDetails(item: FileCollectionObject | FileEntityObject) {
|
||||
function handleShowDetails(item: CollectionObject | EntityObject) {
|
||||
selection.clear()
|
||||
selection.select(item)
|
||||
}
|
||||
@@ -183,7 +257,7 @@ async function handleCreateFolder(name: string) {
|
||||
}
|
||||
|
||||
// Rename operations - for specific item
|
||||
function handleRenameItem(item: FileCollectionObject | FileEntityObject) {
|
||||
function handleRenameItem(item: CollectionObject | EntityObject) {
|
||||
nodeToRename.value = item
|
||||
showRenameDialog.value = true
|
||||
}
|
||||
@@ -192,7 +266,7 @@ async function handleRename(newName: string) {
|
||||
if (!nodeToRename.value) return
|
||||
|
||||
try {
|
||||
await fileManager.renameNode(nodeToRename.value.id, newName)
|
||||
await fileManager.renameNode(nodeId(nodeToRename.value), newName)
|
||||
showRenameDialog.value = false
|
||||
nodeToRename.value = null
|
||||
} catch (error) {
|
||||
@@ -201,7 +275,7 @@ async function handleRename(newName: string) {
|
||||
}
|
||||
|
||||
// Delete operations - for specific item
|
||||
function handleDeleteItem(item: FileCollectionObject | FileEntityObject) {
|
||||
function handleDeleteItem(item: CollectionObject | EntityObject) {
|
||||
nodesToDelete.value = [item]
|
||||
showDeleteDialog.value = true
|
||||
}
|
||||
@@ -209,7 +283,7 @@ function handleDeleteItem(item: FileCollectionObject | FileEntityObject) {
|
||||
async function handleDelete() {
|
||||
try {
|
||||
for (const node of nodesToDelete.value) {
|
||||
await fileManager.deleteNode(node.id)
|
||||
await fileManager.deleteNode(nodeId(node))
|
||||
}
|
||||
selection.clear()
|
||||
showDeleteDialog.value = false
|
||||
@@ -220,13 +294,13 @@ async function handleDelete() {
|
||||
}
|
||||
|
||||
// Download operation
|
||||
function handleDownloadItem(item: FileCollectionObject | FileEntityObject) {
|
||||
if (item['@type'] === 'files.entity') {
|
||||
function handleDownloadItem(item: CollectionObject | EntityObject) {
|
||||
if (item instanceof EntityObject) {
|
||||
// Download single file
|
||||
fileManager.downloadEntity(item.id, item.in)
|
||||
} else if (item['@type'] === 'files.collection') {
|
||||
fileManager.downloadEntity(nodeId(item), String(item.collection ?? fileManager.ROOT_ID))
|
||||
} else if (item instanceof CollectionObject) {
|
||||
// Download folder as ZIP
|
||||
fileManager.downloadCollection(item.id)
|
||||
fileManager.downloadCollection(nodeId(item))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,6 +522,7 @@ onMounted(async () => {
|
||||
:entities="sortedItems.entities"
|
||||
:selected-ids="selectedIds"
|
||||
@item-click="handleItemClick"
|
||||
@open="handleOpenItem"
|
||||
@rename="handleRenameItem"
|
||||
@delete="handleDeleteItem"
|
||||
@download="handleDownloadItem"
|
||||
@@ -461,6 +536,7 @@ onMounted(async () => {
|
||||
:entities="sortedItems.entities"
|
||||
:selected-ids="selectedIds"
|
||||
@item-click="handleItemClick"
|
||||
@open="handleOpenItem"
|
||||
@rename="handleRenameItem"
|
||||
@delete="handleDeleteItem"
|
||||
@download="handleDownloadItem"
|
||||
@@ -474,6 +550,7 @@ onMounted(async () => {
|
||||
:entities="sortedItems.entities"
|
||||
:selected-ids="selectedIds"
|
||||
@item-click="handleItemClick"
|
||||
@open="handleOpenItem"
|
||||
@rename="handleRenameItem"
|
||||
@delete="handleDeleteItem"
|
||||
@download="handleDownloadItem"
|
||||
@@ -506,7 +583,7 @@ onMounted(async () => {
|
||||
|
||||
<RenameDialog
|
||||
v-model="showRenameDialog"
|
||||
:current-name="nodeToRename?.label ?? ''"
|
||||
:current-name="renameCurrentName(nodeToRename)"
|
||||
@rename="handleRename"
|
||||
/>
|
||||
|
||||
@@ -535,6 +612,16 @@ onMounted(async () => {
|
||||
:selected-items="selection.selectedNodeArray.value"
|
||||
@close="selection.clear()"
|
||||
/>
|
||||
|
||||
<!-- File Viewer -->
|
||||
<FileViewerDialog
|
||||
v-model="showViewer"
|
||||
:entity="viewerEntity"
|
||||
:all-entities="sortedItems.entities"
|
||||
:get-url="fileManager.getEntityUrl"
|
||||
:download-entity="fileManager.downloadEntity"
|
||||
@navigate="handleViewerNavigate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user