Merge branch 'Features/Server'
This commit is contained in:
@@ -4,7 +4,7 @@ import { createSignal, onMount, type JSX } from "solid-js";
|
|||||||
import { getDocumentTheme, setTheme, type Theme } from "../../../theme/runtime";
|
import { getDocumentTheme, setTheme, type Theme } from "../../../theme/runtime";
|
||||||
import { WorkspaceHome } from "../../workspace-home/WorkspaceHome/WorkspaceHome";
|
import { WorkspaceHome } from "../../workspace-home/WorkspaceHome/WorkspaceHome";
|
||||||
import { LeftRail } from "../LeftRail/LeftRail";
|
import { LeftRail } from "../LeftRail/LeftRail";
|
||||||
import { ProfileDock } from "../ProfileDock/ProfileDock";
|
import { ServerDock } from "../ServerDock/ServerDock";
|
||||||
import { TopBar } from "../TopBar/TopBar";
|
import { TopBar } from "../TopBar/TopBar";
|
||||||
import { WorkspaceSidebar } from "../WorkspaceSidebar/WorkspaceSidebar";
|
import { WorkspaceSidebar } from "../WorkspaceSidebar/WorkspaceSidebar";
|
||||||
import styles from "./AppShell.module.scss";
|
import styles from "./AppShell.module.scss";
|
||||||
@@ -36,7 +36,7 @@ export const AppShell = (): JSX.Element => {
|
|||||||
<WorkspaceSidebar />
|
<WorkspaceSidebar />
|
||||||
|
|
||||||
<div class={styles.sidebarDock}>
|
<div class={styles.sidebarDock}>
|
||||||
<ProfileDock />
|
<ServerDock />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
.panel {
|
.panel {
|
||||||
--profile-dock-avatar-size: var(--control-size-md);
|
--server-dock-glyph-size: var(--control-size-md);
|
||||||
--profile-dock-action-min-height: var(--space-8);
|
--server-dock-action-min-height: var(--space-8);
|
||||||
--profile-dock-border: color-mix(in srgb, var(--color-border-strong) 75%, transparent);
|
--server-dock-border: color-mix(in srgb, var(--color-border-strong) 75%, transparent);
|
||||||
--profile-dock-surface: color-mix(in srgb, var(--color-surface) 94%, transparent);
|
--server-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);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--space-2);
|
gap: var(--space-2);
|
||||||
padding: var(--space-3) var(--space-3) 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));
|
border-radius: calc(var(--radius-xl) + var(--space-1));
|
||||||
background: var(--profile-dock-surface);
|
background: var(--server-dock-surface);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 20px 48px color-mix(in srgb, black 16%, transparent),
|
0 20px 48px color-mix(in srgb, black 16%, transparent),
|
||||||
var(--shadow-strong);
|
var(--shadow-strong);
|
||||||
@@ -26,14 +25,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.glyph {
|
||||||
width: var(--profile-dock-avatar-size);
|
width: var(--server-dock-glyph-size);
|
||||||
height: var(--profile-dock-avatar-size);
|
height: var(--server-dock-glyph-size);
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 50%;
|
border-radius: var(--radius-lg);
|
||||||
background: var(--color-accent-soft);
|
background: color-mix(in srgb, var(--color-accent-soft) 84%, transparent);
|
||||||
color: var(--color-accent-strong);
|
color: var(--color-accent-strong);
|
||||||
@include text-label;
|
@include text-label;
|
||||||
}
|
}
|
||||||
@@ -48,20 +47,25 @@
|
|||||||
@include text-label;
|
@include text-label;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status,
|
||||||
|
.subtitle {
|
||||||
@include text-caption;
|
@include text-caption;
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
.statusDot {
|
.statusDot {
|
||||||
width: 0.5rem;
|
width: 0.45rem;
|
||||||
height: 0.5rem;
|
height: 0.45rem;
|
||||||
border-radius: 50%;
|
flex: 0 0 auto;
|
||||||
|
border-radius: 999px;
|
||||||
background: var(--color-success);
|
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 {
|
.actions {
|
||||||
@@ -71,7 +75,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.action {
|
.action {
|
||||||
min-height: var(--profile-dock-action-min-height);
|
min-height: var(--server-dock-action-min-height);
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
46
Frontend/src/components/shell/ServerDock/ServerDock.tsx
Normal file
46
Frontend/src/components/shell/ServerDock/ServerDock.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -18,6 +18,22 @@ export type RailItem = {
|
|||||||
active?: boolean;
|
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 = {
|
export type SidebarItem = {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -32,12 +48,26 @@ export type TopBarAction = {
|
|||||||
icon: ShellIcon;
|
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[] = [
|
export const railItems: readonly RailItem[] = [
|
||||||
{ id: "personal", label: "Personal", abbreviation: "P" },
|
{ id: "personal", label: "Personal", abbreviation: "P" },
|
||||||
{ id: "moku", label: "Moku", abbreviation: "M", active: true },
|
{ id: "moku", label: "Moku", abbreviation: "M", active: true },
|
||||||
{ id: "labs", label: "Labs", abbreviation: "L" },
|
{ id: "labs", label: "Labs", abbreviation: "L" },
|
||||||
] as const;
|
] 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[] = [
|
export const workspaceSidebarItems: readonly SidebarItem[] = [
|
||||||
{ id: "home", label: "Home", icon: Home, active: true },
|
{ id: "home", label: "Home", icon: Home, active: true },
|
||||||
{ id: "boards", label: "Boards", icon: LayoutGrid, meta: "0" },
|
{ id: "boards", label: "Boards", icon: LayoutGrid, meta: "0" },
|
||||||
|
|||||||
Reference in New Issue
Block a user