forked from Gerome-Elassaad/CodingIT
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsandbox-file-tree.tsx
More file actions
137 lines (129 loc) · 3.44 KB
/
Copy pathsandbox-file-tree.tsx
File metadata and controls
137 lines (129 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { useState } from 'react'
import {
ChevronDown,
ChevronRight,
File as FileIcon,
Folder,
RefreshCw,
} from 'lucide-react'
import { Button } from './ui/button'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from './ui/tooltip'
export interface FileSystemNode {
name: string
isDirectory: boolean
path?: string
children?: FileSystemNode[]
}
interface SandboxFileTreeProps {
files: FileSystemNode[]
onSelectFile: (path: string) => void
onRefresh?: () => void
isLoading?: boolean
}
export function SandboxFileTree({
files,
onSelectFile,
onRefresh,
isLoading = false
}: SandboxFileTreeProps) {
return (
<div className="p-2">
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium text-muted-foreground">Sandbox Files</span>
{onRefresh && (
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
onClick={onRefresh}
disabled={isLoading}
>
<RefreshCw className={`h-3 w-3 ${isLoading ? 'animate-spin' : ''}`} />
</Button>
</TooltipTrigger>
<TooltipContent>Refresh files</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
{Array.isArray(files) && files.length > 0 ? (
files.map(file => (
<FileTreeNode
key={file.name}
node={file}
onSelectFile={onSelectFile}
/>
))
) : (
<div className="text-sm text-muted-foreground p-2">
{isLoading ? 'Loading files...' : 'No files in sandbox'}
</div>
)}
</div>
)
}
interface FileTreeNodeProps {
node: FileSystemNode
onSelectFile: (path: string) => void
level?: number
}
function FileTreeNode({
node,
onSelectFile,
level = 0,
path = '',
}: FileTreeNodeProps & { path?: string }) {
const [isOpen, setIsOpen] = useState(false)
const isDirectory = node.isDirectory
const hasChildren = node.children && node.children.length > 0
const newPath = path ? `${path}/${node.name}` : node.name
const handleToggle = () => {
if (isDirectory) {
setIsOpen(!isOpen)
} else {
onSelectFile(node.path || newPath)
}
}
return (
<div>
<div
className="flex items-center cursor-pointer hover:bg-primary/5 dark:hover:bg-muted/50 rounded-sm p-1 group"
style={{ paddingLeft: level * 16 + 4 }}
onClick={handleToggle}
>
{isDirectory ? (
<>
{isOpen ? (
<ChevronDown size={16} className="mr-1 text-muted-foreground" />
) : (
<ChevronRight size={16} className="mr-1 text-muted-foreground" />
)}
<Folder size={16} className="mr-2 text-blue-500" />
</>
) : (
<FileIcon size={16} className="mr-2 ml-4 text-muted-foreground" />
)}
<span className="text-sm truncate flex-1">{node.name}</span>
</div>
{isOpen &&
hasChildren &&
node.children?.map(child => (
<FileTreeNode
key={child.name}
node={child}
onSelectFile={onSelectFile}
level={level + 1}
path={newPath}
/>
))}
</div>
)
}