mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-02-04 08:06:56 +00:00
디렉터리 페이지에서 하위 디렉터리도 가상 리스트로 표시하도록 개선
This commit is contained in:
@@ -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
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
<ActionEntryButton class="h-14" onclick={onParentClick}>
|
count={entries.length}
|
||||||
<DirectoryEntryLabel type="parent-directory" name=".." />
|
itemHeight={(index) => 56 + (index + 1 < entries.length ? 4 : 0)}
|
||||||
</ActionEntryButton>
|
>
|
||||||
{/if}
|
{#snippet item(index)}
|
||||||
{#each subDirectories as subDirectory}
|
{@const entry = entries[index]!}
|
||||||
<SubDirectory info={subDirectory} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
<div class={index + 1 < entries.length ? "pb-1" : ""}>
|
||||||
{/each}
|
{#if entry.type === "parent"}
|
||||||
{#if files.length > 0}
|
<ActionEntryButton class="h-14" onclick={onParentClick}>
|
||||||
<RowVirtualizer
|
<DirectoryEntryLabel type="parent-directory" name=".." />
|
||||||
count={files.length}
|
</ActionEntryButton>
|
||||||
itemHeight={(index) => 56 + (index + 1 < files.length ? 4 : 0)}
|
{:else if entry.type === "directory"}
|
||||||
>
|
<SubDirectory
|
||||||
{#snippet item(index)}
|
info={entry.details}
|
||||||
{@const file = files[index]!}
|
onclick={onEntryClick}
|
||||||
<div class={index + 1 < files.length ? "pb-1" : ""}>
|
onOpenMenuClick={onEntryMenuClick}
|
||||||
{#if file.type === "file"}
|
/>
|
||||||
<File info={file.details} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
{:else if entry.type === "file"}
|
||||||
{:else}
|
<File info={entry.details} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
||||||
<UploadingFile state={file.details} />
|
{:else}
|
||||||
{/if}
|
<UploadingFile state={entry.details} />
|
||||||
</div>
|
{/if}
|
||||||
{/snippet}
|
</div>
|
||||||
</RowVirtualizer>
|
{/snippet}
|
||||||
{/if}
|
</RowVirtualizer>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-grow items-center justify-center">
|
<div class="flex flex-grow items-center justify-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user