aethex-studio/components/FileTree.tsx

91 lines
3.3 KiB
TypeScript

"use client";
import React from 'react';
import { ChevronRight, ChevronDown, FileIcon, FolderIcon, Plus, FolderPlus } from 'lucide-react';
import { useEditorStore, FileNode } from '@/store/editor-store';
import { cn, getFileIcon, getPlatformIcon } from '@/lib/utils';
export function FileTree() {
const { files, openFile, moveFile } = useEditorStore();
const [expandedFolders, setExpandedFolders] = React.useState<Set<string>>(
new Set(['roblox', 'web', 'mobile', 'desktop', 'shared'])
);
const toggleFolder = (folderId: string) => {
setExpandedFolders(prev => {
const next = new Set(prev);
if (next.has(folderId)) {
next.delete(folderId);
} else {
next.add(folderId);
}
return next;
});
};
const renderNode = (node: FileNode, depth: number = 0) => {
const isExpanded = expandedFolders.has(node.id);
const isFolder = node.type === 'folder';
return (
<div key={node.id} draggable onDragStart={e => e.dataTransfer.setData('fileId', node.id)} onDrop={e => {
e.preventDefault();
const fileId = e.dataTransfer.getData('fileId');
if (fileId && fileId !== node.id && isFolder) {
moveFile(fileId, node.id);
}
}} onDragOver={e => isFolder && e.preventDefault()}>
<div
className={cn(
"flex items-center gap-3 px-2 py-1 cursor-pointer hover:bg-surface/50 transition-colors",
"text-sm"
)}
style={{ paddingLeft: `${depth * 14 + 8}px` }}
onClick={() => isFolder ? toggleFolder(node.id) : openFile(node)}
>
{isFolder && (
<span className="text-gray-400">
{isExpanded ? <ChevronDown className="w-4 h-4" /> : <ChevronRight className="w-4 h-4" />}
</span>
)}
{!isFolder && <span className="w-4" />}
<span className="text-lg mr-1">
{isFolder ? (isExpanded ? '📂' : '📁') : getFileIcon(node.name)}
</span>
<span className="flex-1 truncate text-gray-200">{node.name}</span>
{node.platform && (
<span className="text-xs opacity-50 ml-1">
{getPlatformIcon(node.platform)}
</span>
)}
</div>
{isFolder && isExpanded && node.children && (
<div>
{node.children.map(child => renderNode(child, depth + 1))}
</div>
)}
</div>
);
};
return (
<div className="h-full flex flex-col bg-gray-900 text-white">
<div className="flex items-center justify-between px-3 py-2 border-b border-gray-800">
<span className="text-xs font-semibold uppercase tracking-wider text-gray-400">
Explorer
</span>
<div className="flex gap-1">
<button className="p-1 hover:bg-surface rounded transition-colors" title="New File">
<Plus className="w-4 h-4 text-gray-400" />
</button>
<button className="p-1 hover:bg-surface rounded transition-colors" title="New Folder">
<FolderPlus className="w-4 h-4 text-gray-400" />
</button>
</div>
</div>
<div className="px-3 pt-2 pb-1 text-xs text-gray-500 font-semibold">PROJECT</div>
<div className="flex-1 overflow-y-auto">
{files.map((node: any) => renderNode(node))}
</div>
</div>
);
}