perf(frontend): lazy-load ShareDialog and MoveDialog

ShareDialog (~15 KB JS) and MoveDialog (~5 KB JS) were statically imported by the
files, favorites, recent and shared routes, so they downloaded on every visit
even if the user never opened a share/move dialog. Convert them to the existing
lazyComponent pattern (as already used for FileViewer/WopiEditor): the chunk is
fetched the first time the dialog is opened.

The Vite manifest confirms both flip from static to isDynamicEntry. This defers
~26 KB raw / ~9.5 KB gzipped (JS + CSS) off the initial load of those four routes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DioCrafts
2026-06-21 19:17:10 +02:00
parent eef0ef5522
commit a3602e53bb
4 changed files with 77 additions and 43 deletions
+10 -2
View File
@@ -19,8 +19,6 @@
import { renameFolder, deleteFolder } from '$lib/api/endpoints/folders'; import { renameFolder, deleteFolder } from '$lib/api/endpoints/folders';
import type { FileItem } from '$lib/api/types'; import type { FileItem } from '$lib/api/types';
import { lazyComponent } from '$lib/composables/lazyComponent.svelte'; import { lazyComponent } from '$lib/composables/lazyComponent.svelte';
import MoveDialog from '$lib/components/MoveDialog.svelte';
import ShareDialog from '$lib/components/ShareDialog.svelte';
import ResourceList, { import ResourceList, {
type ContextAction, type ContextAction,
type GroupByDef, type GroupByDef,
@@ -127,8 +125,12 @@
// The file preview is loaded the first time a file is opened, keeping its // The file preview is loaded the first time a file is opened, keeping its
// module out of this route's initial chunk. // module out of this route's initial chunk.
const fileViewer = lazyComponent(() => import('$lib/components/FileViewer.svelte')); const fileViewer = lazyComponent(() => import('$lib/components/FileViewer.svelte'));
const moveDialog = lazyComponent(() => import('$lib/components/MoveDialog.svelte'));
const shareDialog = lazyComponent(() => import('$lib/components/ShareDialog.svelte'));
$effect(() => { $effect(() => {
if (viewerOpen) void fileViewer.load(); if (viewerOpen) void fileViewer.load();
if (moveOpen) void moveDialog.load();
if (shareOpen) void shareDialog.load();
}); });
function open(entry: ResourceEntry) { function open(entry: ResourceEntry) {
@@ -317,6 +319,8 @@
{@const FileViewer = fileViewer.component} {@const FileViewer = fileViewer.component}
<FileViewer bind:open={viewerOpen} file={viewerFile} /> <FileViewer bind:open={viewerOpen} file={viewerFile} />
{/if} {/if}
{#if moveDialog.component}
{@const MoveDialog = moveDialog.component}
<MoveDialog <MoveDialog
bind:open={moveOpen} bind:open={moveOpen}
item={moveTarget} item={moveTarget}
@@ -326,4 +330,8 @@
load(true, orderByForGroup()); load(true, orderByForGroup());
}} }}
/> />
{/if}
{#if shareDialog.component}
{@const ShareDialog = shareDialog.component}
<ShareDialog bind:open={shareOpen} item={shareTarget} /> <ShareDialog bind:open={shareOpen} item={shareTarget} />
{/if}
@@ -44,8 +44,6 @@
import type { FileItem, FolderItem, ItemType } from '$lib/api/types'; import type { FileItem, FolderItem, ItemType } from '$lib/api/types';
import ListToolbar from '$lib/components/ListToolbar.svelte'; import ListToolbar from '$lib/components/ListToolbar.svelte';
import VirtualList from '$lib/components/VirtualList.svelte'; import VirtualList from '$lib/components/VirtualList.svelte';
import MoveDialog from '$lib/components/MoveDialog.svelte';
import ShareDialog from '$lib/components/ShareDialog.svelte';
import { lazyComponent } from '$lib/composables/lazyComponent.svelte'; import { lazyComponent } from '$lib/composables/lazyComponent.svelte';
import { t } from '$lib/i18n/index.svelte'; import { t } from '$lib/i18n/index.svelte';
import { confirmDialog, promptDialog } from '$lib/stores/dialogs.svelte'; import { confirmDialog, promptDialog } from '$lib/stores/dialogs.svelte';
@@ -69,6 +67,8 @@
// call `.load()` when `viewerOpen` / `wopiOpen` flip true). // call `.load()` when `viewerOpen` / `wopiOpen` flip true).
const fileViewer = lazyComponent(() => import('$lib/components/FileViewer.svelte')); const fileViewer = lazyComponent(() => import('$lib/components/FileViewer.svelte'));
const wopiEditor = lazyComponent(() => import('$lib/components/WopiEditor.svelte')); const wopiEditor = lazyComponent(() => import('$lib/components/WopiEditor.svelte'));
const moveDialog = lazyComponent(() => import('$lib/components/MoveDialog.svelte'));
const shareDialog = lazyComponent(() => import('$lib/components/ShareDialog.svelte'));
// The URL rest param is the trail of folder ids from home's children down. // The URL rest param is the trail of folder ids from home's children down.
// /files → home root; /files/a/b → folder b inside a inside home. // /files → home root; /files/a/b → folder b inside a inside home.
@@ -1083,6 +1083,8 @@
$effect(() => { $effect(() => {
if (viewerOpen) void fileViewer.load(); if (viewerOpen) void fileViewer.load();
if (wopiOpen) void wopiEditor.load(); if (wopiOpen) void wopiEditor.load();
if (moveOpen) void moveDialog.load();
if (shareOpen) void shareDialog.load();
}); });
// Editability of the current context-menu target file, resolved async. // Editability of the current context-menu target file, resolved async.
let ctxCanEditWopi = $state(false); let ctxCanEditWopi = $state(false);
@@ -1936,6 +1938,8 @@
</div> </div>
{/snippet} {/snippet}
{#if moveDialog.component}
{@const MoveDialog = moveDialog.component}
<MoveDialog <MoveDialog
bind:open={moveOpen} bind:open={moveOpen}
item={actionTarget} item={actionTarget}
@@ -1946,11 +1950,15 @@
void reload(); void reload();
}} }}
/> />
{/if}
{#if shareDialog.component}
{@const ShareDialog = shareDialog.component}
<ShareDialog <ShareDialog
bind:open={shareOpen} bind:open={shareOpen}
item={actionTarget} item={actionTarget}
onshared={(id) => (sharedIds = new SvelteSet(sharedIds).add(id))} onshared={(id) => (sharedIds = new SvelteSet(sharedIds).add(id))}
/> />
{/if}
{#if fileViewer.component} {#if fileViewer.component}
{@const FileViewer = fileViewer.component} {@const FileViewer = fileViewer.component}
<FileViewer bind:open={viewerOpen} file={viewerFile} /> <FileViewer bind:open={viewerOpen} file={viewerFile} />
+10 -2
View File
@@ -20,8 +20,6 @@
import { renameFolder, deleteFolder } from '$lib/api/endpoints/folders'; import { renameFolder, deleteFolder } from '$lib/api/endpoints/folders';
import type { FileItem, ItemType } from '$lib/api/types'; import type { FileItem, ItemType } from '$lib/api/types';
import { lazyComponent } from '$lib/composables/lazyComponent.svelte'; import { lazyComponent } from '$lib/composables/lazyComponent.svelte';
import MoveDialog from '$lib/components/MoveDialog.svelte';
import ShareDialog from '$lib/components/ShareDialog.svelte';
import ResourceList, { import ResourceList, {
type ContextAction, type ContextAction,
type GroupByDef, type GroupByDef,
@@ -139,8 +137,12 @@
// The file preview is loaded the first time a file is opened, keeping its // The file preview is loaded the first time a file is opened, keeping its
// module out of this route's initial chunk. // module out of this route's initial chunk.
const fileViewer = lazyComponent(() => import('$lib/components/FileViewer.svelte')); const fileViewer = lazyComponent(() => import('$lib/components/FileViewer.svelte'));
const moveDialog = lazyComponent(() => import('$lib/components/MoveDialog.svelte'));
const shareDialog = lazyComponent(() => import('$lib/components/ShareDialog.svelte'));
$effect(() => { $effect(() => {
if (viewerOpen) void fileViewer.load(); if (viewerOpen) void fileViewer.load();
if (moveOpen) void moveDialog.load();
if (shareOpen) void shareDialog.load();
}); });
function open(entry: ResourceEntry) { function open(entry: ResourceEntry) {
@@ -362,6 +364,8 @@
{@const FileViewer = fileViewer.component} {@const FileViewer = fileViewer.component}
<FileViewer bind:open={viewerOpen} file={viewerFile} /> <FileViewer bind:open={viewerOpen} file={viewerFile} />
{/if} {/if}
{#if moveDialog.component}
{@const MoveDialog = moveDialog.component}
<MoveDialog <MoveDialog
bind:open={moveOpen} bind:open={moveOpen}
item={moveTarget} item={moveTarget}
@@ -371,4 +375,8 @@
load(true, orderByForGroup()); load(true, orderByForGroup());
}} }}
/> />
{/if}
{#if shareDialog.component}
{@const ShareDialog = shareDialog.component}
<ShareDialog bind:open={shareOpen} item={shareTarget} /> <ShareDialog bind:open={shareOpen} item={shareTarget} />
{/if}
+11 -1
View File
@@ -22,7 +22,7 @@
import type { FileItem, FolderItem, ItemType } from '$lib/api/types'; import type { FileItem, FolderItem, ItemType } from '$lib/api/types';
import Icon from '$lib/icons/Icon.svelte'; import Icon from '$lib/icons/Icon.svelte';
import ListToolbar from '$lib/components/ListToolbar.svelte'; import ListToolbar from '$lib/components/ListToolbar.svelte';
import ShareDialog from '$lib/components/ShareDialog.svelte'; import { lazyComponent } from '$lib/composables/lazyComponent.svelte';
import UserVignette from '$lib/components/UserVignette.svelte'; import UserVignette from '$lib/components/UserVignette.svelte';
import { t } from '$lib/i18n/index.svelte'; import { t } from '$lib/i18n/index.svelte';
import { ui } from '$lib/stores/ui.svelte'; import { ui } from '$lib/stores/ui.svelte';
@@ -55,6 +55,13 @@
let dialogOpen = $state(false); let dialogOpen = $state(false);
let dialogItem = $state<{ id: string; name: string; kind: ItemType } | null>(null); let dialogItem = $state<{ id: string; name: string; kind: ItemType } | null>(null);
// ShareDialog is heavy and only opens on demand — keep it out of this route's
// initial chunk and load it the first time the dialog is opened.
const shareDialog = lazyComponent(() => import('$lib/components/ShareDialog.svelte'));
$effect(() => {
if (dialogOpen) void shareDialog.load();
});
// Open kebab menu, keyed by grant id. // Open kebab menu, keyed by grant id.
let menuFor = $state<string | null>(null); let menuFor = $state<string | null>(null);
@@ -580,7 +587,10 @@
</div> </div>
{/if} {/if}
{#if shareDialog.component}
{@const ShareDialog = shareDialog.component}
<ShareDialog bind:open={dialogOpen} item={dialogItem} /> <ShareDialog bind:open={dialogOpen} item={dialogItem} />
{/if}
<style> <style>
.ms-lanes { .ms-lanes {