Initial commit

This commit is contained in:
root
2025-12-21 09:57:09 -05:00
committed by Sebastian Krupinski
commit 8ac20d8b45
38 changed files with 4677 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
/**
* File selection composable
* Provides reactive selection management for file manager
*/
import { ref, computed } from 'vue'
import type { Ref, ComputedRef } from 'vue'
import { FileCollectionObject } from '@FileManager/models/collection'
import { FileEntityObject } from '@FileManager/models/entity'
type NodeRecord = FileCollectionObject | FileEntityObject
export interface UseFileSelectionOptions {
multiple?: boolean
allowFolders?: boolean
allowFiles?: boolean
}
export function useFileSelection(options: UseFileSelectionOptions = {}) {
const {
multiple = true,
allowFolders = true,
allowFiles = true
} = options
const selectedIds: Ref<Set<string>> = ref(new Set())
const selectedNodes: Ref<Map<string, NodeRecord>> = ref(new Map())
// Get selected count
const count: ComputedRef<number> = computed(() => selectedIds.value.size)
// Check if any selected
const hasSelection: ComputedRef<boolean> = computed(() => selectedIds.value.size > 0)
// Get selected IDs as array
const selectedIdArray: ComputedRef<string[]> = computed(() =>
Array.from(selectedIds.value)
)
// Get selected nodes as array
const selectedNodeArray: ComputedRef<NodeRecord[]> = computed(() =>
Array.from(selectedNodes.value.values())
)
// Get selected collections only
const selectedCollections: ComputedRef<FileCollectionObject[]> = computed(() =>
selectedNodeArray.value.filter(
(node): node is FileCollectionObject => node['@type'] === 'files.collection'
)
)
// Get selected entities only
const selectedEntities: ComputedRef<FileEntityObject[]> = computed(() =>
selectedNodeArray.value.filter(
(node): node is FileEntityObject => node['@type'] === 'files.entity'
)
)
// Check if a node is selected
const isSelected = (nodeId: string): boolean => {
return selectedIds.value.has(nodeId)
}
// Check if node type is allowed
const isTypeAllowed = (node: NodeRecord): boolean => {
if (node['@type'] === 'files.collection' && !allowFolders) {
return false
}
if (node['@type'] === 'files.entity' && !allowFiles) {
return false
}
return true
}
// Select a node
const select = (node: NodeRecord) => {
if (!isTypeAllowed(node)) {
return
}
if (!multiple) {
// Clear previous selection for single select
selectedIds.value.clear()
selectedNodes.value.clear()
}
selectedIds.value.add(node.id)
selectedNodes.value.set(node.id, node)
}
// Deselect a node
const deselect = (nodeId: string) => {
selectedIds.value.delete(nodeId)
selectedNodes.value.delete(nodeId)
}
// Toggle selection
const toggle = (node: NodeRecord) => {
if (isSelected(node.id)) {
deselect(node.id)
} else {
select(node)
}
}
// Select multiple nodes
const selectMultiple = (nodes: NodeRecord[]) => {
if (!multiple) {
// For single select, only select the last one
const lastNode = nodes[nodes.length - 1]
if (lastNode && isTypeAllowed(lastNode)) {
selectedIds.value.clear()
selectedNodes.value.clear()
selectedIds.value.add(lastNode.id)
selectedNodes.value.set(lastNode.id, lastNode)
}
return
}
for (const node of nodes) {
if (isTypeAllowed(node)) {
selectedIds.value.add(node.id)
selectedNodes.value.set(node.id, node)
}
}
}
// Select all from a list
const selectAll = (nodes: NodeRecord[]) => {
if (!multiple) {
return
}
selectMultiple(nodes)
}
// Clear selection
const clear = () => {
selectedIds.value.clear()
selectedNodes.value.clear()
}
// Set selection (replace current)
const setSelection = (nodes: NodeRecord[]) => {
clear()
selectMultiple(nodes)
}
return {
// State
selectedIds,
selectedNodes,
// Computed
count,
hasSelection,
selectedIdArray,
selectedNodeArray,
selectedCollections,
selectedEntities,
// Methods
isSelected,
select,
deselect,
toggle,
selectMultiple,
selectAll,
clear,
setSelection,
}
}
export default useFileSelection