디렉터리 페이지에서 하위 디렉터리도 가상 리스트로 표시하도록 개선

This commit is contained in:
static
2025-12-30 18:44:46 +09:00
parent 409ae09f4f
commit 1e57941f4c
4 changed files with 60 additions and 43 deletions

View File

@@ -13,24 +13,38 @@
let { class: className, count, item, itemHeight, placeholder }: Props = $props();
let element: HTMLElement | undefined = $state();
let scrollMargin = $state(0);
const virtualizer = $derived(
createWindowVirtualizer({
count,
estimateSize: itemHeight,
scrollMargin,
}),
);
const measureItem = (node: HTMLElement) => {
$effect(() => $virtualizer.measureElement(node));
};
$effect(() => {
if (!element) return;
const observer = new ResizeObserver(() => {
scrollMargin = element!.getBoundingClientRect().top + window.scrollY;
});
observer.observe(element.parentElement!);
return () => observer.disconnect();
});
</script>
<div class={["relative", className]}>
<div bind:this={element} class={["relative", className]}>
<div style:height="{$virtualizer.getTotalSize()}px">
{#each $virtualizer.getVirtualItems() as virtualItem (virtualItem.key)}
<div
class="absolute left-0 top-0 w-full"
style:transform="translateY({virtualItem.start}px)"
style:transform="translateY({virtualItem.start - scrollMargin}px)"
data-index={virtualItem.index}
use:measureItem
>

View File

@@ -145,7 +145,9 @@
</button>
<TopBarMenu
bind:isOpen={isMenuOpen}
directoryId={page.url.searchParams.get("from") === "category" ? $info?.parentId : undefined}
directoryId={["category", "gallery"].includes(page.url.searchParams.get("from") ?? "")
? $info?.parentId
: undefined}
{fileBlob}
filename={$info?.name}
/>

View File

@@ -22,5 +22,5 @@
<TopBar title="사진 및 동영상" />
<FullscreenDiv>
<Gallery {files} onFileClick={({ id }) => goto(`/file/${id}`)} />
<Gallery {files} onFileClick={({ id }) => goto(`/file/${id}?from=gallery`)} />
</FullscreenDiv>

View File

@@ -25,56 +25,57 @@
showParentEntry = false,
}: Props = $props();
type FileEntry =
type Entry =
| { type: "parent" }
| { type: "directory"; name: string; details: (typeof info.subDirectories)[number] }
| { type: "file"; name: string; details: (typeof info.files)[number] }
| { type: "uploading-file"; name: string; details: LiveFileUploadState };
const toFileEntry =
<T extends FileEntry["type"]>(type: T) =>
(details: Extract<FileEntry, { type: T }>["details"]) => ({
const toEntry =
<T extends Exclude<Entry["type"], "parent">>(type: T) =>
(details: Extract<Entry, { type: T }>["details"]) => ({
type,
name: details.name,
details,
});
const subDirectories = $derived(
sortEntries(structuredClone($state.snapshot(info.subDirectories))),
);
const files = $derived(
sortEntries<FileEntry>([
...info.files.map(toFileEntry("file")),
...getUploadingFiles(info.id).map(toFileEntry("uploading-file")),
const entries = $derived([
...(showParentEntry ? ([{ type: "parent" }] as const) : []),
...sortEntries(info.subDirectories.map(toEntry("directory"))),
...sortEntries([
...info.files.map(toEntry("file")),
...getUploadingFiles(info.id).map(toEntry("uploading-file")),
]),
);
]);
</script>
{#if subDirectories.length + files.length > 0 || showParentEntry}
<div class="space-y-1 pb-[4.5rem]">
{#if showParentEntry}
<ActionEntryButton class="h-14" onclick={onParentClick}>
<DirectoryEntryLabel type="parent-directory" name=".." />
</ActionEntryButton>
{/if}
{#each subDirectories as subDirectory}
<SubDirectory info={subDirectory} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
{/each}
{#if files.length > 0}
<RowVirtualizer
count={files.length}
itemHeight={(index) => 56 + (index + 1 < files.length ? 4 : 0)}
>
{#snippet item(index)}
{@const file = files[index]!}
<div class={index + 1 < files.length ? "pb-1" : ""}>
{#if file.type === "file"}
<File info={file.details} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
{:else}
<UploadingFile state={file.details} />
{/if}
</div>
{/snippet}
</RowVirtualizer>
{/if}
{#if entries.length > 0}
<div class="pb-[4.5rem]">
<RowVirtualizer
count={entries.length}
itemHeight={(index) => 56 + (index + 1 < entries.length ? 4 : 0)}
>
{#snippet item(index)}
{@const entry = entries[index]!}
<div class={index + 1 < entries.length ? "pb-1" : ""}>
{#if entry.type === "parent"}
<ActionEntryButton class="h-14" onclick={onParentClick}>
<DirectoryEntryLabel type="parent-directory" name=".." />
</ActionEntryButton>
{:else if entry.type === "directory"}
<SubDirectory
info={entry.details}
onclick={onEntryClick}
onOpenMenuClick={onEntryMenuClick}
/>
{:else if entry.type === "file"}
<File info={entry.details} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
{:else}
<UploadingFile state={entry.details} />
{/if}
</div>
{/snippet}
</RowVirtualizer>
</div>
{:else}
<div class="flex flex-grow items-center justify-center">