ui: refine workspace sidebar controls
This commit is contained in:
parent
384d162b74
commit
7f6a057e22
|
|
@ -43,7 +43,7 @@ rustPlatform.buildRustPackage rec {
|
|||
filter = sourceFilter;
|
||||
};
|
||||
|
||||
cargoHash = "sha256-9e99NfbErWlmyZqXd7d5UaJ88gx6ENbHOubqYtnjXVg=";
|
||||
cargoHash = "sha256-ICEIo7wR65s6CQCzCVa1KaEou7fpnaUk64zwokb92Fc=";
|
||||
|
||||
depsExtraArgs = {
|
||||
# Older fetchCargoVendor utilities used crates.io's API download endpoint,
|
||||
|
|
|
|||
|
|
@ -146,6 +146,10 @@
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
.workspace-layout.sidebar-collapsed {
|
||||
grid-template-columns: max-content minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -181,6 +185,10 @@
|
|||
overflow: visible;
|
||||
}
|
||||
|
||||
.workspace-layout.sidebar-collapsed {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.shell {
|
||||
overflow: visible;
|
||||
padding: var(--space-5) var(--space-4);
|
||||
|
|
@ -194,32 +202,44 @@
|
|||
min-width: 0;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: var(--space-6) var(--space-5);
|
||||
padding: var(--space-4) var(--space-3);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
display: grid;
|
||||
gap: var(--space-2);
|
||||
margin-bottom: var(--space-2);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sidebar-title-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-6);
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.workspace-label {
|
||||
display: grid;
|
||||
gap: var(--space-1);
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.workspace-sidebar h1 {
|
||||
margin: 0;
|
||||
.workspace-name {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-soft);
|
||||
padding: 0.45rem 0.6rem;
|
||||
color: var(--text-strong);
|
||||
font-size: 1.05rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.25;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workspace-status {
|
||||
margin: 0;
|
||||
margin: var(--space-1) 0 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.35;
|
||||
|
|
@ -231,17 +251,69 @@
|
|||
color: var(--danger);
|
||||
}
|
||||
|
||||
.settings-button {
|
||||
.sidebar-actions-row {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.sidebar-icon-button,
|
||||
.sidebar-collapse-button {
|
||||
flex: 0 0 auto;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-soft);
|
||||
background: var(--bg-subtle);
|
||||
color: var(--text-muted);
|
||||
cursor: not-allowed;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar-collapse-button {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sidebar-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.sidebar-icon-button:hover,
|
||||
.sidebar-icon-button:focus-visible,
|
||||
.sidebar-collapse-button:hover,
|
||||
.sidebar-collapse-button:focus-visible {
|
||||
background: var(--interactive-hover);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.workspace-sidebar.collapsed {
|
||||
overflow: hidden;
|
||||
padding-inline: var(--space-2);
|
||||
}
|
||||
|
||||
.workspace-sidebar.collapsed .sidebar-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.workspace-sidebar.collapsed .sidebar-title-row,
|
||||
.workspace-sidebar.collapsed .sidebar-actions-row {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.workspace-sidebar.collapsed .sidebar-actions-row {
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.workspace-sidebar.collapsed .workspace-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-sections {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
repositories?: RepositoryListResponse | null;
|
||||
repositoriesError?: string | null;
|
||||
currentPath?: string;
|
||||
collapsed?: boolean;
|
||||
onToggleCollapsed?: () => void;
|
||||
};
|
||||
|
||||
let {
|
||||
|
|
@ -17,40 +19,89 @@
|
|||
workspaceError = null,
|
||||
repositories = null,
|
||||
repositoriesError = null,
|
||||
currentPath = '/'
|
||||
currentPath = '/',
|
||||
collapsed = false,
|
||||
onToggleCollapsed
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<aside class="workspace-sidebar" aria-label="Workspace navigation">
|
||||
<aside
|
||||
class:collapsed
|
||||
class="workspace-sidebar"
|
||||
aria-label="Workspace navigation"
|
||||
>
|
||||
<header class="sidebar-header">
|
||||
<div class="workspace-label">
|
||||
{#if workspace}
|
||||
<p class="workspace-status">{workspace.workspace_id}</p>
|
||||
<h1>{workspace.display_name}</h1>
|
||||
{:else}
|
||||
<h1>Yoi workspace</h1>
|
||||
{#if workspaceError}
|
||||
<p class="workspace-status error">Workspace summary unavailable.</p>
|
||||
<div class="sidebar-title-row">
|
||||
<div class="workspace-label">
|
||||
{#if workspace}
|
||||
<div class="workspace-name">{workspace.display_name}</div>
|
||||
{:else}
|
||||
<p class="workspace-status">Loading workspace…</p>
|
||||
<div class="workspace-name">Yoi workspace</div>
|
||||
{#if workspaceError}
|
||||
<p class="workspace-status error">Workspace summary unavailable.</p>
|
||||
{:else}
|
||||
<p class="workspace-status">Loading workspace…</p>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="sidebar-collapse-button"
|
||||
type="button"
|
||||
aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
||||
aria-expanded={!collapsed}
|
||||
title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
||||
onclick={onToggleCollapsed}
|
||||
>
|
||||
{#if collapsed}
|
||||
<svg class="sidebar-icon" aria-hidden="true" viewBox="0 0 24 24">
|
||||
<path d="m6 17 5-5-5-5" />
|
||||
<path d="m13 17 5-5-5-5" />
|
||||
</svg>
|
||||
{:else}
|
||||
<svg class="sidebar-icon" aria-hidden="true" viewBox="0 0 24 24">
|
||||
<path d="m11 17-5-5 5-5" />
|
||||
<path d="m18 17-5-5 5-5" />
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<a
|
||||
class="settings-button"
|
||||
href="/settings"
|
||||
aria-label="Open Settings / Admin"
|
||||
title="Settings / Admin"
|
||||
>
|
||||
⚙
|
||||
</a>
|
||||
<div class="sidebar-actions-row">
|
||||
<a
|
||||
class="sidebar-icon-button"
|
||||
href="/"
|
||||
aria-label="Open workspace overview"
|
||||
title="Workspace overview"
|
||||
>
|
||||
<svg class="sidebar-icon" aria-hidden="true" viewBox="0 0 24 24">
|
||||
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
|
||||
<path
|
||||
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
class="sidebar-icon-button"
|
||||
href="/settings"
|
||||
aria-label="Open Settings / Admin"
|
||||
title="Settings / Admin"
|
||||
>
|
||||
<svg class="sidebar-icon" aria-hidden="true" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"
|
||||
/>
|
||||
<circle cx="12" cy="12" r="4" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="sidebar-sections" aria-label="Workspace sections">
|
||||
<RepositoriesNavSection {repositories} {repositoriesError} {currentPath} />
|
||||
<ObjectivesNavSection {currentPath} />
|
||||
<WorkersNavSection {currentPath} />
|
||||
|
||||
</nav>
|
||||
{#if !collapsed}
|
||||
<nav class="sidebar-sections" aria-label="Workspace sections">
|
||||
<RepositoriesNavSection {repositories} {repositoriesError} {currentPath} />
|
||||
<ObjectivesNavSection {currentPath} />
|
||||
<WorkersNavSection {currentPath} />
|
||||
</nav>
|
||||
{/if}
|
||||
</aside>
|
||||
|
|
|
|||
|
|
@ -5,15 +5,18 @@
|
|||
import type { LayoutProps } from './$types';
|
||||
|
||||
let { data, children }: LayoutProps = $props();
|
||||
let sidebarCollapsed = $state(false);
|
||||
</script>
|
||||
|
||||
<div class="workspace-layout">
|
||||
<div class:sidebar-collapsed={sidebarCollapsed} class="workspace-layout">
|
||||
<WorkspaceSidebar
|
||||
workspace={data.workspace}
|
||||
workspaceError={data.workspaceError}
|
||||
repositories={data.repositories}
|
||||
repositoriesError={data.repositoriesError}
|
||||
currentPath={page.url.pathname}
|
||||
collapsed={sidebarCollapsed}
|
||||
onToggleCollapsed={() => (sidebarCollapsed = !sidebarCollapsed)}
|
||||
/>
|
||||
<main class="shell">
|
||||
{@render children()}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user