refactor: standardize design

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-03-03 21:55:48 -05:00
parent 31a33d8758
commit a59dbff9f1
25 changed files with 994 additions and 306 deletions

View File

@@ -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>