Feat: Replace profile dock with server dock

This commit is contained in:
MangoPig
2026-06-16 13:05:31 +01:00
parent f41dbc43fa
commit 7d57792a82
5 changed files with 103 additions and 58 deletions

View File

@@ -4,7 +4,7 @@ import { createSignal, onMount, type JSX } from "solid-js";
import { getDocumentTheme, setTheme, type Theme } from "../../../theme/runtime";
import { WorkspaceHome } from "../../workspace-home/WorkspaceHome/WorkspaceHome";
import { LeftRail } from "../LeftRail/LeftRail";
import { ProfileDock } from "../ProfileDock/ProfileDock";
import { ServerDock } from "../ServerDock/ServerDock";
import { TopBar } from "../TopBar/TopBar";
import { WorkspaceSidebar } from "../WorkspaceSidebar/WorkspaceSidebar";
import styles from "./AppShell.module.scss";
@@ -36,7 +36,7 @@ export const AppShell = (): JSX.Element => {
<WorkspaceSidebar />
<div class={styles.sidebarDock}>
<ProfileDock />
<ServerDock />
</div>
</div>

View File

@@ -1,35 +0,0 @@
// Path: Frontend/src/components/shell/ProfileDock/ProfileDock.tsx
import type { JSX } from "solid-js";
import { Settings, User } from "../../../lib/icons";
import styles from "./ProfileDock.module.scss";
export const ProfileDock = (): JSX.Element => {
return (
<section class={styles.panel} aria-label="Profile dock">
<div class={styles.identity}>
<div class={styles.avatar} aria-hidden="true">
R
</div>
<div class={styles.copy}>
<span class={styles.name}>Ronald</span>
<span class={styles.status}>
<span class={styles.statusDot} aria-hidden="true" />
Online in Moku
</span>
</div>
</div>
<div class={styles.actions}>
<button type="button" class={styles.action}>
<User size={16} strokeWidth={2} />
<span class={styles.actionLabel}>Account</span>
</button>
<button type="button" class={styles.action}>
<Settings size={16} strokeWidth={2} />
<span class={styles.actionLabel}>Prefs</span>
</button>
</div>
</section>
);
};

View File

@@ -1,18 +1,17 @@
.panel {
--profile-dock-avatar-size: var(--control-size-md);
--profile-dock-action-min-height: var(--space-8);
--profile-dock-border: color-mix(in srgb, var(--color-border-strong) 75%, transparent);
--profile-dock-surface: color-mix(in srgb, var(--color-surface) 94%, transparent);
--profile-dock-status-ring: 0 0 0 3px color-mix(in srgb, var(--color-success) 18%, transparent);
--server-dock-glyph-size: var(--control-size-md);
--server-dock-action-min-height: var(--space-8);
--server-dock-border: color-mix(in srgb, var(--color-border-strong) 75%, transparent);
--server-dock-surface: color-mix(in srgb, var(--color-surface) 94%, transparent);
position: relative;
z-index: 1;
width: 100%;
display: grid;
gap: var(--space-2);
padding: var(--space-3) var(--space-3) var(--space-2);
border: 1px solid var(--profile-dock-border);
border: 1px solid var(--server-dock-border);
border-radius: calc(var(--radius-xl) + var(--space-1));
background: var(--profile-dock-surface);
background: var(--server-dock-surface);
box-shadow:
0 20px 48px color-mix(in srgb, black 16%, transparent),
var(--shadow-strong);
@@ -26,14 +25,14 @@
align-items: center;
}
.avatar {
width: var(--profile-dock-avatar-size);
height: var(--profile-dock-avatar-size);
.glyph {
width: var(--server-dock-glyph-size);
height: var(--server-dock-glyph-size);
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--color-accent-soft);
border-radius: var(--radius-lg);
background: color-mix(in srgb, var(--color-accent-soft) 84%, transparent);
color: var(--color-accent-strong);
@include text-label;
}
@@ -48,20 +47,25 @@
@include text-label;
}
.status {
.status,
.subtitle {
@include text-caption;
display: inline-flex;
align-items: center;
gap: var(--space-2);
color: var(--color-text-muted);
}
.status {
display: inline-flex;
align-items: center;
gap: var(--space-1);
}
.statusDot {
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
width: 0.45rem;
height: 0.45rem;
flex: 0 0 auto;
border-radius: 999px;
background: var(--color-success);
box-shadow: var(--profile-dock-status-ring);
box-shadow: 0 0 0 0.1rem color-mix(in srgb, var(--color-success) 18%, transparent);
}
.actions {
@@ -71,7 +75,7 @@
}
.action {
min-height: var(--profile-dock-action-min-height);
min-height: var(--server-dock-action-min-height);
display: inline-flex;
align-items: center;
justify-content: center;

View File

@@ -0,0 +1,46 @@
// Path: Frontend/src/components/shell/ServerDock/ServerDock.tsx
import { For, Show, type JSX } from "solid-js";
import { activeServer } from "../data/shell.data";
import styles from "./ServerDock.module.scss";
export const ServerDock = (): JSX.Element => {
return (
<section class={styles.panel} aria-label="Server dock">
<div class={styles.identity}>
<div class={styles.glyph} aria-hidden="true">
{activeServer.abbreviation}
</div>
<div class={styles.copy}>
<span class={styles.name}>{activeServer.name}</span>
<Show
when={activeServer.kind === "organization"}
fallback={<span class={styles.subtitle}>{activeServer.subtitle}</span>}
>
<span class={styles.status}>
<span class={styles.statusDot} aria-hidden="true" />
<span>{activeServer.connectedLabel}</span>
</span>
</Show>
</div>
</div>
<Show when={activeServer.dockActions.length > 0}>
<div class={styles.actions}>
<For each={activeServer.dockActions}>
{(item): JSX.Element => {
const Icon = item.icon;
return (
<button type="button" class={styles.action} aria-label={item.label} title={item.label}>
<Icon size={16} strokeWidth={2} />
<span class={styles.actionLabel}>{item.label}</span>
</button>
);
}}
</For>
</div>
</Show>
</section>
);
};

View File

@@ -18,6 +18,22 @@ export type RailItem = {
active?: boolean;
};
export type ServerDockAction = {
id: string;
label: string;
icon: ShellIcon;
};
export type ActiveServer = {
id: string;
name: string;
abbreviation: string;
kind: "personal" | "organization";
connectedLabel?: string;
subtitle?: string;
dockActions: readonly ServerDockAction[];
};
export type SidebarItem = {
id: string;
label: string;
@@ -32,12 +48,26 @@ export type TopBarAction = {
icon: ShellIcon;
};
const organizationServerActions: readonly ServerDockAction[] = [
{ id: "members", label: "Members", icon: User },
{ id: "server", label: "Server", icon: Settings },
] as const;
export const railItems: readonly RailItem[] = [
{ id: "personal", label: "Personal", abbreviation: "P" },
{ id: "moku", label: "Moku", abbreviation: "M", active: true },
{ id: "labs", label: "Labs", abbreviation: "L" },
] as const;
export const activeServer: ActiveServer = {
id: "organization-server",
name: "Organization Name",
abbreviation: "O",
kind: "organization",
connectedLabel: "12 connected",
dockActions: organizationServerActions,
};
export const workspaceSidebarItems: readonly SidebarItem[] = [
{ id: "home", label: "Home", icon: Home, active: true },
{ id: "boards", label: "Boards", icon: LayoutGrid, meta: "0" },