export class BaseFileList extends HTMLElement { constructor() { super(); this.files = []; this.folders = []; this.selectedFiles = new Set(); this.selectedFolders = new Set(); this.boundHandleClick = this.handleClick.bind(this); this.boundHandleDblClick = this.handleDblClick.bind(this); this.boundHandleChange = this.handleChange.bind(this); } connectedCallback() { this.addEventListener('click', this.boundHandleClick); this.addEventListener('dblclick', this.boundHandleDblClick); this.addEventListener('change', this.boundHandleChange); } disconnectedCallback() { this.removeEventListener('click', this.boundHandleClick); this.removeEventListener('dblclick', this.boundHandleDblClick); this.removeEventListener('change', this.boundHandleChange); } isEditableFile(filename, mimeType) { if (mimeType && mimeType.startsWith('text/')) return true; const editableExtensions = [ 'txt', 'md', 'log', 'json', 'js', 'py', 'html', 'css', 'xml', 'yaml', 'yml', 'sh', 'bat', 'ini', 'conf', 'cfg' ]; const extension = filename.split('.').pop().toLowerCase(); return editableExtensions.includes(extension); } getFileIcon(mimeType) { if (mimeType.startsWith('image/')) return '📷'; if (mimeType.startsWith('video/')) return '🎥'; if (mimeType.startsWith('audio/')) return '🎵'; if (mimeType.includes('pdf')) return '📄'; if (mimeType.includes('text')) return '📄'; return '📄'; } formatFileSize(bytes) { if (bytes < 1024) return bytes + ' B'; if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'; if (bytes < 1073741824) return (bytes / 1048576).toFixed(1) + ' MB'; return (bytes / 1073741824).toFixed(1) + ' GB'; } renderFolder(folder) { const isSelected = this.selectedFolders.has(folder.id); const starIcon = folder.is_starred ? '★' : '☆'; const starAction = folder.is_starred ? 'unstar-folder' : 'star-folder'; const actions = this.getFolderActions(folder); return `
📁
${folder.name}
${actions}
`; } renderFile(file) { const isSelected = this.selectedFiles.has(file.id); const icon = this.getFileIcon(file.mime_type); const size = this.formatFileSize(file.size); const starIcon = file.is_starred ? '★' : '☆'; const starAction = file.is_starred ? 'unstar-file' : 'star-file'; const actions = this.getFileActions(file); return `
${icon}
${file.name}
${size}
${actions}
`; } getFolderActions(folder) { return ''; } getFileActions(file) { return ''; } handleClick(e) { const target = e.target; if (target.id === 'clear-selection-btn') { this.clearSelection(); return; } if (target.classList.contains('action-btn')) { e.stopPropagation(); const action = target.dataset.action; const id = parseInt(target.dataset.id); this.handleAction(action, id); return; } if (target.classList.contains('select-item')) { e.stopPropagation(); return; } const fileItem = target.closest('.file-item:not(.folder-item)'); if (fileItem) { const fileId = parseInt(fileItem.dataset.fileId); const file = this.files.find(f => f.id === fileId); if (this.isEditableFile(file.name, file.mime_type)) { this.dispatchEvent(new CustomEvent('edit-file', { detail: { file: file }, bubbles: true })); } else { this.dispatchEvent(new CustomEvent('photo-click', { detail: { photo: file }, bubbles: true })); } } } handleDblClick(e) { const folderItem = e.target.closest('.folder-item'); if (folderItem) { const folderId = parseInt(folderItem.dataset.folderId); this.dispatchEvent(new CustomEvent('folder-open', { detail: { folderId } })); } } handleChange(e) { const target = e.target; if (target.id === 'select-all') { this.toggleSelectAll(target.checked); return; } if (target.classList.contains('select-item')) { const type = target.dataset.type; const id = parseInt(target.dataset.id); this.toggleSelectItem(type, id, target.checked); } } toggleSelectItem(type, id, checked) { if (type === 'file') { if (checked) { this.selectedFiles.add(id); } else { this.selectedFiles.delete(id); } } else if (type === 'folder') { if (checked) { this.selectedFolders.add(id); } else { this.selectedFolders.delete(id); } } const item = this.querySelector(`[data-${type}-id="${id}"]`); if (item) { if (checked) { item.classList.add('selected'); } else { item.classList.remove('selected'); } } this.updateSelectionUI(); } toggleSelectAll(checked) { this.selectedFiles.clear(); this.selectedFolders.clear(); if (checked) { this.files.forEach(file => this.selectedFiles.add(file.id)); this.folders.forEach(folder => this.selectedFolders.add(folder.id)); } this.querySelectorAll('.select-item').forEach(checkbox => { checkbox.checked = checked; }); this.querySelectorAll('.file-item').forEach(item => { if (checked) { item.classList.add('selected'); } else { item.classList.remove('selected'); } }); this.updateSelectionUI(); } clearSelection() { this.selectedFiles.clear(); this.selectedFolders.clear(); this.querySelectorAll('.select-item').forEach(checkbox => { checkbox.checked = false; }); this.querySelectorAll('.file-item').forEach(item => { item.classList.remove('selected'); }); this.updateSelectionUI(); } updateSelectionUI() { const hasSelected = this.selectedFiles.size > 0 || this.selectedFolders.size > 0; const totalItems = this.files.length + this.folders.length; const totalSelected = this.selectedFiles.size + this.selectedFolders.size; const allSelected = totalItems > 0 && totalSelected === totalItems; const selectAllCheckbox = this.querySelector('#select-all'); const selectAllLabel = this.querySelector('label[for="select-all"]'); const batchActionsDiv = this.querySelector('.batch-actions'); if (selectAllCheckbox) { selectAllCheckbox.checked = allSelected; this.updateIndeterminateState(); } if (selectAllLabel) { selectAllLabel.textContent = hasSelected ? `${totalSelected} selected` : 'Select all'; } if (hasSelected && !batchActionsDiv) { this.createBatchActionsBar(); } else if (!hasSelected && batchActionsDiv) { batchActionsDiv.remove(); } } createBatchActionsBar() { } updateIndeterminateState() { const selectAllCheckbox = this.querySelector('#select-all'); if (selectAllCheckbox) { const totalItems = this.files.length + this.folders.length; const totalSelected = this.selectedFiles.size + this.selectedFolders.size; const hasSelected = totalSelected > 0; const allSelected = totalItems > 0 && totalSelected === totalItems; selectAllCheckbox.indeterminate = hasSelected && !allSelected; } } async handleAction(action, id) { } }