Skip to main content
Glama
FileTree.tsx3.3 kB
'use client'; import { ChevronRight } from 'lucide-react'; import { type FC, useState } from 'react'; import { cn } from '../../utils/cn'; import { MaxHeightSmoother } from '../MaxHeightSmoother'; import { createFileTree, type FilePath } from './createFileTree'; type FileTreeProps = { filesPaths: string[]; activeFile?: string; onClickFile?: (filePath: string) => void; prePaths?: string[]; }; const concatFilePath = (paths: string[]) => paths.join('/'); type FileItemProps = { filesPaths: string[]; subPath?: FilePath[]; path: string; onClickFile?: (title: string) => void; activeFile?: string; prePaths: string[]; isFile: boolean; }; const FileItem: FC<FileItemProps> = ({ filesPaths, path, subPath, onClickFile, activeFile, prePaths, isFile, }) => { const [subPathOpen, setSubPathOpen] = useState(true); const level = prePaths.length + 1; const currentPath = concatFilePath([ ...prePaths.slice(level - 1, level), path, ]); const totalPath = concatFilePath([...prePaths, path]); const filteredFilePaths = filesPaths .map( (path) => path.replace(/^\/?/, '') // This regex matches the first slash, if it exists, at the start of the string) ) .filter((filePath) => filePath.startsWith(currentPath)); const newPath = filteredFilePaths.map((path) => path.replace(currentPath, '').replace(/^\/?/, '') ); const isActive = totalPath === activeFile; const indentation = new Array(level).fill(' ').join(''); return ( <> <button className={cn( 'flex w-full items-start justify-start whitespace-pre text-nowrap px-2 py-1 text-xs transition', isActive ? 'bg-neutral-200 dark:bg-neutral-700' : 'cursor-pointer hover:bg-neutral-300 dark:hover:bg-neutral-900' )} key={path} onClick={() => { if (isFile) { onClickFile?.(totalPath); } else { setSubPathOpen(!subPathOpen); } }} > <span className={cn('whitespace-pre', isFile && 'ml-2')}> {indentation} </span> {!isFile && ( <ChevronRight className={cn(`transition`, subPathOpen && `rotate-90 transform`)} size={16} /> )} {path} </button> {subPath && ( <MaxHeightSmoother isHidden={!subPathOpen} className="overflow-x-hidden" > <FileTree filesPaths={newPath} activeFile={activeFile} onClickFile={onClickFile} prePaths={[...prePaths, path]} /> </MaxHeightSmoother> )} </> ); }; export const FileTree: FC<FileTreeProps> = ({ filesPaths, activeFile, onClickFile, prePaths = [], }) => { const fileTree = createFileTree(filesPaths); return ( <div className="flex size-full flex-col items-start justify-start py-1 text-neutral"> {fileTree.map(({ path, subPath, isFile }) => ( <FileItem key={path} isFile={isFile} subPath={subPath} path={path} onClickFile={onClickFile} activeFile={activeFile} prePaths={prePaths} filesPaths={filesPaths} /> ))} </div> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server