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

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 { class: className, count, item, itemHeight, placeholder }: Props = $props();
let element: HTMLElement | undefined = $state();
let scrollMargin = $state(0);
const virtualizer = $derived( const virtualizer = $derived(
createWindowVirtualizer({ createWindowVirtualizer({
count, count,
estimateSize: itemHeight, estimateSize: itemHeight,
scrollMargin,
}), }),
); );
const measureItem = (node: HTMLElement) => { const measureItem = (node: HTMLElement) => {
$effect(() => $virtualizer.measureElement(node)); $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> </script>
<div class={["relative", className]}> <div bind:this={element} class={["relative", className]}>
<div style:height="{$virtualizer.getTotalSize()}px"> <div style:height="{$virtualizer.getTotalSize()}px">
{#each $virtualizer.getVirtualItems() as virtualItem (virtualItem.key)} {#each $virtualizer.getVirtualItems() as virtualItem (virtualItem.key)}
<div <div
class="absolute left-0 top-0 w-full" 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} data-index={virtualItem.index}
use:measureItem use:measureItem
> >

View File

@@ -145,7 +145,9 @@
</button> </button>
<TopBarMenu <TopBarMenu
bind:isOpen={isMenuOpen} 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} {fileBlob}
filename={$info?.name} filename={$info?.name}
/> />

View File

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

View File

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