xref: /expo/docs/ui/components/FileTree/index.tsx (revision a9f9618c)
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