web: register sidebar snippets from layouts

This commit is contained in:
2026-07-23 20:01:54 +09:00
parent b3854004a1
commit fb4d3c3f1f
8 changed files with 137 additions and 40 deletions
+20 -13
View File
@@ -1,28 +1,35 @@
<script lang="ts">
import { page } from '$app/state';
import { setContext } from 'svelte';
import WorkspaceAlerts from '$lib/workspace/alerts/WorkspaceAlerts.svelte';
import GlobalSidebar from '$lib/workspace/sidebar/GlobalSidebar.svelte';
import WorkspaceSidebar from '$lib/workspace/sidebar/WorkspaceSidebar.svelte';
import { SIDEBAR_CONTEXT, type SidebarSnippet } from '$lib/workspace/sidebar/context';
import '../app.css';
import type { LayoutProps } from './$types';
let { data, children }: LayoutProps = $props();
let { children }: LayoutProps = $props();
let sidebar = $state<SidebarSnippet | null>(null);
let sidebarCollapsed = $state(false);
setContext(SIDEBAR_CONTEXT, {
setSidebar(snippet: SidebarSnippet) {
sidebar = snippet;
},
clearSidebar(snippet: SidebarSnippet) {
if (sidebar === snippet) sidebar = null;
sidebarCollapsed = false;
},
setCollapsed(collapsed: boolean) {
sidebarCollapsed = collapsed;
},
});
</script>
<WorkspaceAlerts />
<div class:sidebar-collapsed={data.workspaceScoped && sidebarCollapsed} class="workspace-layout">
{#if data.workspaceScoped}
<WorkspaceSidebar
workspace={data.workspace ?? null}
workspaceError={data.workspaceError ?? null}
repositories={data.repositories ?? null}
repositoriesError={data.repositoriesError ?? null}
currentPath={page.url.pathname}
collapsed={sidebarCollapsed}
onToggleCollapsed={() => (sidebarCollapsed = !sidebarCollapsed)}
/>
<div class:sidebar-collapsed={sidebarCollapsed} class="workspace-layout">
{#if sidebar}
{@render sidebar()}
{:else}
<GlobalSidebar currentPath={page.url.pathname} />
{/if}
+6 -18
View File
@@ -1,31 +1,19 @@
import { redirect } from "@sveltejs/kit";
import { loadJson, workspaceApiPath, workspaceRoute } from "$lib/workspace/api/http";
import type { RepositoryListResponse, WorkspaceResponse } from "$lib/workspace/sidebar/types";
import { loadJson, workspaceRoute } from "$lib/workspace/api/http";
import type { WorkspaceResponse } from "$lib/workspace/sidebar/types";
import type { LayoutLoad } from "./$types";
export const ssr = false;
export const prerender = false;
export const load: LayoutLoad = async ({ fetch, params, url }) => {
const workspaceId = params.workspaceId;
if (workspaceId) {
const apiPath = (path: string) => workspaceApiPath(workspaceId, path);
const [workspace, repositories] = await Promise.all([
loadJson<WorkspaceResponse>(fetch, apiPath("/workspace")),
loadJson<RepositoryListResponse>(fetch, apiPath("/repositories")),
]);
return {
workspaceScoped: true,
workspace: workspace.data,
workspaceError: workspace.error,
repositories: repositories.data,
repositoriesError: repositories.error,
};
if (params.workspaceId) {
return {};
}
const publicRoutes = new Set(["/account", "/login/device"]);
if (publicRoutes.has(url.pathname)) {
return { workspaceScoped: false };
return {};
}
const workspace = await loadJson<WorkspaceResponse>(fetch, "/api/workspace");
@@ -33,5 +21,5 @@ export const load: LayoutLoad = async ({ fetch, params, url }) => {
const scopedPath = workspaceRoute(workspace.data.workspace_id);
throw redirect(307, `${scopedPath}${url.search}`);
}
return { workspaceScoped: false };
return {};
};
@@ -0,0 +1,35 @@
<script lang="ts">
import { page } from '$app/state';
import SidebarOverride from '$lib/workspace/sidebar/SidebarOverride.svelte';
import WorkspaceSidebar from '$lib/workspace/sidebar/WorkspaceSidebar.svelte';
import { getSidebarController } from '$lib/workspace/sidebar/context';
import type { LayoutProps } from './$types';
let { data, children }: LayoutProps = $props();
let sidebarCollapsed = $state(false);
const sidebarController = getSidebarController();
function toggleSidebar() {
sidebarCollapsed = !sidebarCollapsed;
}
$effect(() => {
sidebarController.setCollapsed(sidebarCollapsed);
});
</script>
{#snippet workspaceSidebar()}
<WorkspaceSidebar
workspace={data.workspace ?? null}
workspaceError={data.workspaceError ?? null}
repositories={data.repositories ?? null}
repositoriesError={data.repositoriesError ?? null}
currentPath={page.url.pathname}
collapsed={sidebarCollapsed}
onToggleCollapsed={toggleSidebar}
/>
{/snippet}
<SidebarOverride sidebar={workspaceSidebar} />
{@render children()}
@@ -0,0 +1,19 @@
import { loadJson, workspaceApiPath } from "$lib/workspace/api/http";
import type { RepositoryListResponse, WorkspaceResponse } from "$lib/workspace/sidebar/types";
import type { LayoutLoad } from "./$types";
export const load: LayoutLoad = async ({ fetch, params }) => {
const workspaceId = params.workspaceId;
const apiPath = (path: string) => workspaceApiPath(workspaceId, path);
const [workspace, repositories] = await Promise.all([
loadJson<WorkspaceResponse>(fetch, apiPath("/workspace")),
loadJson<RepositoryListResponse>(fetch, apiPath("/repositories")),
]);
return {
workspace: workspace.data,
workspaceError: workspace.error,
repositories: repositories.data,
repositoriesError: repositories.error,
};
};