master
 1---
 2import { fileIcon } from '@utils/file-icon';
 3import { Icon } from 'astro-icon/components';
 4
 5type FileNode =
 6  | {
 7      name: string;
 8      children?: FileNode[];
 9    }
10  | string;
11
12interface Props {
13  files: FileNode[];
14  open?: boolean;
15}
16
17const { files, open } = Astro.props;
18---
19
20<ul class="menu menu-sm rounded-box border-base-content/25 w-full border">
21  {
22    (() => {
23      function renderer(node: FileNode) {
24        if (typeof node === 'string') {
25          return (
26            <li>
27              <span class="cursor-auto">
28                <Icon name={fileIcon(node)} />
29                {node}
30              </span>
31            </li>
32          );
33        } else {
34          return (
35            <li>
36              <details {...(open ? { open: true } : {})}>
37                <summary>
38                  <Icon name="mdi:folder" />
39                  {node.name}
40                </summary>
41                {node.children && <ul>{node.children.map((child) => renderer(child))}</ul>}
42              </details>
43            </li>
44          );
45        }
46      }
47      return files.map((node) => renderer(node));
48    })()
49  }
50</ul>