mirror of
https://seed.radicle.dev/rad:z4D5UCArafTzTQpDZNQRuqswh3ury.git
synced 2026-11-11 06:10:37 +08:00
The file tree was the one list left un-virtualized, so wide directories mounted every entry and re-rendered all rows on selection. Render entries through VirtualList like the other lists.
93 lines
2.4 KiB
Svelte
93 lines
2.4 KiB
Svelte
<script lang="ts">
|
|
import type { Tree } from "@bindings/source/Tree";
|
|
|
|
import FileTreeFile from "@app/components/FileTreeFile.svelte";
|
|
import FileTreeFolder from "@app/components/FileTreeFolder.svelte";
|
|
import Icon from "@app/components/Icon.svelte";
|
|
|
|
interface Props {
|
|
fetchTree: (path: string) => Promise<Tree>;
|
|
fetchBlob: (path: string) => Promise<void>;
|
|
currentPath: string;
|
|
name: string;
|
|
prefix: string;
|
|
// Expansion state lives in Tree.svelte so it survives the virtualizer
|
|
// unmounting off-screen rows.
|
|
isExpanded: (prefix: string) => boolean;
|
|
toggleExpanded: (prefix: string) => void;
|
|
indent?: number;
|
|
}
|
|
|
|
const {
|
|
name,
|
|
fetchBlob,
|
|
currentPath,
|
|
prefix,
|
|
isExpanded,
|
|
toggleExpanded,
|
|
fetchTree,
|
|
indent = 0.5,
|
|
}: Props = $props();
|
|
const expanded = $derived(isExpanded(prefix));
|
|
|
|
const treePromise = $derived(
|
|
expanded ? fetchTree(prefix) : Promise.resolve(undefined),
|
|
);
|
|
</script>
|
|
|
|
<style>
|
|
.folder {
|
|
cursor: pointer;
|
|
width: 100%;
|
|
border-radius: var(--border-radius-sm);
|
|
}
|
|
.folder:hover {
|
|
background-color: var(--color-surface-subtle);
|
|
}
|
|
</style>
|
|
|
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
<div
|
|
class="folder"
|
|
style:padding-left="{indent}rem"
|
|
style:padding-right="0.5rem"
|
|
onclick={() => toggleExpanded(prefix)}>
|
|
<div class="global-flex txt-body-m-regular" style:padding="0.25rem 0">
|
|
<div class:txt-missing={!expanded}>
|
|
<Icon name={expanded ? "folder-open" : "folder"} />
|
|
</div>
|
|
{name}
|
|
</div>
|
|
</div>
|
|
{#if expanded}
|
|
{#await treePromise then tree}
|
|
{#if tree}
|
|
<div
|
|
style:display="flex"
|
|
style:flex-direction="column"
|
|
style:gap="0.25rem">
|
|
{#each tree.entries as entry (entry.path)}
|
|
{#if entry.kind === "tree"}
|
|
<FileTreeFolder
|
|
{fetchTree}
|
|
{fetchBlob}
|
|
name={entry.name}
|
|
{currentPath}
|
|
{isExpanded}
|
|
{toggleExpanded}
|
|
prefix={`${entry.path}/`}
|
|
indent={indent + 1.5} />
|
|
{:else if entry.kind === "blob"}
|
|
<FileTreeFile
|
|
name={entry.name}
|
|
fetchBlob={() => fetchBlob(entry.path)}
|
|
active={entry.path === currentPath}
|
|
indent={indent + 1.5} />
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
{/await}
|
|
{/if}
|