import { Accessor, Component, createContext, createSignal, For, JSX, Show, useContext } from "solid-js"; import { AiFillFile, AiFillFolder, AiFillFolderOpen } from "solid-icons/ai"; import { SelectionProvider, selectable } from "~/features/selectable"; import css from "./filetree.module.css"; import { debounce } from "~/utilities"; selectable; export interface FileEntry { name: string; id: string; kind: 'file'; meta: File; handle: FileSystemFileHandle; directory: FileSystemDirectoryHandle; } export interface FolderEntry { name: string; id: string; kind: 'folder'; entries: Entry[]; } export type Entry = FileEntry | FolderEntry; export const emptyFolder: FolderEntry = { name: '', id: '', kind: 'folder', entries: [] } as const; export async function* walk(directory: FileSystemDirectoryHandle, filters: RegExp[] = [], depth = 0): AsyncGenerator { if (depth === 10) { return; } for await (const handle of directory.values()) { if (filters.some(f => f.test(handle.name))) { continue; } const id = await handle.getUniqueId(); if (handle.kind === 'file') { yield { name: handle.name, id, kind: 'file', meta: await handle.getFile(), handle, directory }; } else { yield { name: handle.name, id, kind: 'folder', entries: await Array.fromAsync(walk(handle, filters, depth + 1)) }; } } } interface TreeContextType { open(file: File): void; } const TreeContext = createContext(); export const Tree: Component<{ entries: Entry[], children: (file: Accessor) => JSX.Element, open?: TreeContextType['open'] }> = (props) => { const [selection, setSelection] = createSignal([]); const context = { open: props.open ?? (() => { }), }; return
<_Tree entries={props.entries} children={props.children} />
; } const _Tree: Component<{ entries: Entry[], children: (file: Accessor) => JSX.Element }> = (props) => { const context = useContext(TreeContext); return { entry => <> { folder => } { file => context?.open(file().meta)}> {props.children(file)} } } } const Folder: Component<{ folder: FolderEntry, children: (file: Accessor) => JSX.Element }> = (props) => { const [open, setOpen] = createSignal(true); return
debounce(() => setOpen(o => !o), 1)}> }> {props.folder.name} <_Tree entries={props.folder.entries} children={props.children} />
; }; const sort_by = (key: string) => (objA: Record, objB: Record) => { const a = objA[key]; const b = objB[key]; return Number(a < b) - Number(b < a); };