1*a9f9618cSEvan Baconimport { FileCode01Icon, LayoutAlt01Icon, FolderIcon } from '@expo/styleguide-icons'; 2c7504584SBartosz Kaszubowskiimport { HTMLAttributes, ReactNode } from 'react'; 3c7504584SBartosz Kaszubowski 4c7504584SBartosz Kaszubowskitype FileTreeProps = HTMLAttributes<HTMLDivElement> & { 5c7504584SBartosz Kaszubowski files?: string[]; 6c7504584SBartosz Kaszubowski}; 7c7504584SBartosz Kaszubowski 8c7504584SBartosz Kaszubowskitype FileObject = { 9c7504584SBartosz Kaszubowski [key: string]: FileObject; 10c7504584SBartosz Kaszubowski}; 11c7504584SBartosz Kaszubowski 12c7504584SBartosz Kaszubowskiexport function FileTree({ files = [], ...rest }: FileTreeProps) { 13c7504584SBartosz Kaszubowski return ( 14c7504584SBartosz Kaszubowski <div className="text-xs border border-default rounded-md bg-default mb-4 p-2 pb-4" {...rest}> 15c7504584SBartosz Kaszubowski {renderStructure(generateStructure(files))} 16c7504584SBartosz Kaszubowski </div> 17c7504584SBartosz Kaszubowski ); 18c7504584SBartosz Kaszubowski} 19c7504584SBartosz Kaszubowski 20c7504584SBartosz Kaszubowskifunction generateStructure(files: string[]): FileObject { 21c7504584SBartosz Kaszubowski const structure = {}; 22c7504584SBartosz Kaszubowski files.forEach(path => 23c7504584SBartosz Kaszubowski path.split('/').reduce((acc: FileObject, key) => acc[key] ?? (acc[key] = {}), structure) 24c7504584SBartosz Kaszubowski ); 25c7504584SBartosz Kaszubowski return structure; 26c7504584SBartosz Kaszubowski} 27c7504584SBartosz Kaszubowski 28c7504584SBartosz Kaszubowskifunction renderStructure(structure: FileObject, level = 0): ReactNode { 29c7504584SBartosz Kaszubowski return Object.entries(structure).map(([key, value]) => { 30*a9f9618cSEvan Bacon const FileIcon = getIconForFile(key); 31c7504584SBartosz Kaszubowski return Object.keys(value).length ? ( 32c7504584SBartosz Kaszubowski <div className="mt-1 pt-1 px-2 rounded-sm flex flex-col"> 33c7504584SBartosz Kaszubowski <div className="flex items-center"> 34c7504584SBartosz Kaszubowski {' '.repeat(level)} 35c7504584SBartosz Kaszubowski <FolderIcon className="text-icon-tertiary mr-2 opacity-60" /> 36c7504584SBartosz Kaszubowski <code className="text-secondary">{key}</code> 37c7504584SBartosz Kaszubowski </div> 38c7504584SBartosz Kaszubowski {renderStructure(value, level + 1)} 39c7504584SBartosz Kaszubowski </div> 40c7504584SBartosz Kaszubowski ) : ( 41c7504584SBartosz Kaszubowski <div className="mt-1 pl-3 pt-1 px-2 rounded-sm flex items-center"> 42c7504584SBartosz Kaszubowski {' '.repeat(level - 1)} 43*a9f9618cSEvan Bacon <FileIcon className="text-icon-tertiary mr-2" /> 44c7504584SBartosz Kaszubowski <code className="text-default">{key}</code> 45c7504584SBartosz Kaszubowski </div> 46c7504584SBartosz Kaszubowski ); 47c7504584SBartosz Kaszubowski }); 48c7504584SBartosz Kaszubowski} 49*a9f9618cSEvan Bacon 50*a9f9618cSEvan Baconfunction getIconForFile(filename: string) { 51*a9f9618cSEvan Bacon if (/_layout\.[jt]sx?/.test(filename)) { 52*a9f9618cSEvan Bacon return LayoutAlt01Icon; 53*a9f9618cSEvan Bacon } 54*a9f9618cSEvan Bacon return FileCode01Icon; 55*a9f9618cSEvan Bacon} 56