/** * 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> = ref(new Set()) const selectedNodes: Ref> = ref(new Map()) // Get selected count const count: ComputedRef = computed(() => selectedIds.value.size) // Check if any selected const hasSelection: ComputedRef = computed(() => selectedIds.value.size > 0) // Get selected IDs as array const selectedIdArray: ComputedRef = computed(() => Array.from(selectedIds.value) ) // Get selected nodes as array const selectedNodeArray: ComputedRef = computed(() => Array.from(selectedNodes.value.values()) ) // Get selected collections only const selectedCollections: ComputedRef = computed(() => selectedNodeArray.value.filter( (node): node is FileCollectionObject => node['@type'] === 'files.collection' ) ) // Get selected entities only const selectedEntities: ComputedRef = 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