Feat: Add collapsible shell

This commit is contained in:
MangoPig
2026-06-17 05:37:29 +01:00
parent 630b3778db
commit 7fdc5f2d22
15 changed files with 478 additions and 52 deletions

View File

@@ -1,30 +1,74 @@
// Path: Frontend/src/components/shell/WorkspaceSidebar/WorkspaceSidebar.tsx
import { For, Show, createSignal, type JSX } from "solid-js";
import { ChevronLeft, ChevronRight } from "../../../lib/icons";
import { ProjectSelector } from "../ProjectSelector/ProjectSelector";
import { serverSidebarItems } from "../data/shell.data";
import { serverSidebarItems, workspaceSidebarHeaderActions } from "../data/shell.data";
import styles from "./WorkspaceSidebar.module.scss";
export const WorkspaceSidebar = (): JSX.Element => {
type WorkspaceSidebarProps = {
collapsed: boolean;
railCollapsed: boolean;
onToggleRailCollapse: () => void;
};
export const WorkspaceSidebar = (props: WorkspaceSidebarProps): JSX.Element => {
const [isProjectDrawerOpen, setIsProjectDrawerOpen] = createSignal(false);
const railToggleLabel = (): string => (props.railCollapsed ? "Expand server rail" : "Collapse server rail");
return (
<aside class={styles.sidebar} aria-label="Server navigation">
<aside
classList={{
[styles.sidebar]: true,
[styles.sidebarCollapsed]: props.collapsed,
}}
aria-label="Left workspace sidebar"
>
<div
classList={{
[styles.header]: true,
[styles.headerDrawerOpen]: isProjectDrawerOpen(),
}}
>
<ProjectSelector
isOpen={isProjectDrawerOpen()}
onToggle={(): void => {
setIsProjectDrawerOpen(true);
}}
onClose={(): void => {
setIsProjectDrawerOpen(false);
}}
/>
<div class={styles.headerActions}>
<button
type="button"
classList={{
[styles.headerActionButton]: true,
[styles.headerCollapseButton]: true,
}}
aria-label={railToggleLabel()}
title={railToggleLabel()}
onClick={props.onToggleRailCollapse}
>
{props.railCollapsed ? <ChevronRight size={16} strokeWidth={2} /> : <ChevronLeft size={16} strokeWidth={2} />}
</button>
<For each={workspaceSidebarHeaderActions}>
{(action): JSX.Element => {
const Icon = action.icon;
return (
<button type="button" class={styles.headerActionButton} aria-label={action.label} title={action.label}>
<Icon size={16} strokeWidth={2} />
</button>
);
}}
</For>
</div>
<div class={styles.headerControls}>
<ProjectSelector
compact={props.collapsed}
isOpen={isProjectDrawerOpen()}
onToggle={(): void => {
setIsProjectDrawerOpen(true);
}}
onClose={(): void => {
setIsProjectDrawerOpen(false);
}}
/>
</div>
</div>
<div
@@ -33,7 +77,9 @@ export const WorkspaceSidebar = (): JSX.Element => {
[styles.sectionHidden]: isProjectDrawerOpen(),
}}
>
<span class={styles.sectionLabel}>Navigation</span>
<Show when={!props.collapsed}>
<span class={styles.sectionLabel}>Navigation</span>
</Show>
<div class={styles.navScroller}>
<ul class={styles.navList} role="list">
<For each={serverSidebarItems}>
@@ -48,6 +94,8 @@ export const WorkspaceSidebar = (): JSX.Element => {
[styles.navItem]: true,
[styles.navItemActive]: !!item.active,
}}
aria-label={item.label}
title={item.label}
>
<Icon class={styles.icon} size={18} strokeWidth={2} />
<span class={styles.label}>{item.label}</span>