Skip to content

Commit e156a9a

Browse files
committed
wip: desktop sidebar refactor
1 parent b32aae8 commit e156a9a

File tree

6 files changed

+190
-62
lines changed

6 files changed

+190
-62
lines changed

desktop/public/icon.png

41.8 KB
Loading

desktop/src/components/PageWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export function PageWrapper({ children }: { children: React.ReactNode }) {
44
return (
55
<ScrollArea
66
h="calc(100vh - var(--app-shell-padding-total))"
7-
w="calc(100vw - var(--app-navbar-width) - var(--app-shell-padding-total))"
7+
w="100%"
88
type="auto"
99
scrollbarSize={6}
1010
scrollHideDelay={100}

desktop/src/layout/AppShell/AppShell.module.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@
1717

1818
.main {
1919
background-color: var(--mantine-color-other-backgroundColors-main);
20+
display: flex;
21+
flex-direction: column;
22+
min-height: 0; /* allow nested ScrollArea to compute height */
2023
}

desktop/src/layout/AppShell/AppShell.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
Notification as MantineNotification,
55
Text,
66
} from "@mantine/core";
7-
import { ReactNode, useEffect } from "react";
7+
import { ReactNode, useEffect, useState } from "react";
88
import { useAppContext } from "../../hooks/useAppContext.tsx";
99
import { useNotifier } from "../../hooks/useNotifier.tsx";
1010
import { colorFromType, NotificationType } from "../../types/notification";
@@ -18,6 +18,7 @@ interface AppShellProps {
1818
export function AppShell({ children }: AppShellProps) {
1919
const { isLoading, hasError } = useAppContext();
2020
const { notification, setNotification } = useNotifier();
21+
const [collapsed, setCollapsed] = useState(false);
2122

2223
useEffect(() => {
2324
if (hasError) {
@@ -33,7 +34,7 @@ export function AppShell({ children }: AppShellProps) {
3334
return (
3435
<MantineAppShell
3536
header={{ height: "var(--app-header-height)" }}
36-
navbar={{ width: "var(--app-navbar-width)", breakpoint: "sm" }}
37+
navbar={{ width: collapsed ? 56 : 250, breakpoint: "sm" }}
3738
padding="md"
3839
classNames={{
3940
root: styles.appShell,
@@ -43,7 +44,7 @@ export function AppShell({ children }: AppShellProps) {
4344
}}
4445
>
4546
<MantineAppShell.Navbar>
46-
<Sidebar />
47+
<Sidebar collapsed={collapsed} onToggle={() => setCollapsed((c) => !c)} />
4748
</MantineAppShell.Navbar>
4849

4950
<MantineAppShell.Main>
@@ -62,7 +63,7 @@ export function AppShell({ children }: AppShellProps) {
6263
<Text c="red">{hasError.message}</Text>
6364
</div>
6465
) : (
65-
<div style={{ position: "relative", height: "100%" }}>
66+
<div style={{ position: "relative", height: "100%", flex: 1, minHeight: 0 }}>
6667
{children}
6768
{isLoading && (
6869
<div

desktop/src/layout/Sidebar/Sidebar.module.css

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,65 @@
11
.sidebar {
22
display: flex;
33
flex-direction: column;
4-
padding: 0 1rem 1rem 1rem;
4+
padding: var(--mantine-spacing-sm);
55
height: 100%;
6-
overflow-y: auto;
6+
overflow: hidden;
7+
}
8+
9+
/* Collapsed state */
10+
.sidebarCollapsed {
11+
width: 56px;
12+
}
13+
14+
/* Hide labels when collapsed */
15+
.sidebarCollapsed :global(.mantine-NavLink-label) {
16+
display: none;
17+
}
18+
19+
/* Center icons in collapsed state */
20+
.sidebarCollapsed :global(.mantine-NavLink-root) {
21+
justify-content: center;
22+
padding-left: var(--mantine-spacing-sm);
23+
padding-right: var(--mantine-spacing-sm);
24+
}
25+
26+
27+
.sidebarCollapsed :global(.mantine-NavLink-section) {
28+
margin-inline-end: 0;
29+
}
30+
/* Hide caret/chevron if any in collapsed state */
31+
.sidebarCollapsed :global(.mantine-NavLink-rightSection) {
32+
display: none;
33+
}
34+
35+
/* Reduce logo area when collapsed */
36+
.sidebarCollapsed .sidebar__logo {
37+
height: 56px;
38+
padding: 0.5rem;
39+
}
40+
41+
.sidebar__context {
42+
background-color: var(--mantine-color-gray-3);
43+
color: var(--mantine-color-body-0);
44+
padding: var(--mantine-spacing-xs);
45+
margin: var(--mantine-spacing-xs);
46+
border-radius: var(--mantine-radius-sm);
747
}
848

949
.sidebar__logo {
10-
margin: -1rem -1rem 1rem -1rem;
1150
padding: 1rem;
12-
background-color: var(--mantine-color-other-backgroundColors-header);
13-
border-bottom: 1px solid var(--mantine-color-other-colors-border);
1451
display: flex;
1552
align-items: center;
1653
justify-content: center;
17-
height: 64px;
54+
height: 100px;
1855
}
1956

2057
.sidebar__logo img {
2158
display: block;
2259
width: 100%;
2360
height: 100%;
2461
object-fit: contain;
62+
filter: drop-shadow(0 0 20px color-mix(in srgb, var(--mantine-color-pink-0) 10%, transparent));
2563
}
2664

2765
.sidebar__content {
@@ -37,11 +75,6 @@
3775
gap: 0.5rem;
3876
}
3977

40-
.sidebar__tree {
41-
flex: 1;
42-
overflow-y: auto;
43-
}
44-
4578
.sidebar__section {
4679
padding: 1rem;
4780
border-bottom: 1px solid var(--mantine-color-other-colors-border);
Lines changed: 137 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,53 @@
1-
import { Group, Image, NavLink, Stack } from "@mantine/core";
1+
import {
2+
ActionIcon,
3+
Box,
4+
Button, ButtonGroup,
5+
Flex,
6+
Group,
7+
Image,
8+
Menu,
9+
NavLink,
10+
Stack,
11+
Text,
12+
Tooltip,
13+
} from "@mantine/core";
214
import {
315
IconDatabase,
416
IconFolders,
517
IconLogs,
618
IconSettings,
719
IconTerminal2,
20+
IconInfoCircle,
21+
IconReload,
22+
IconLayoutSidebarLeftExpand,
23+
IconLayoutSidebarLeftCollapse,
24+
IconStarFilled,
25+
IconStar,
26+
IconTools,
27+
IconArrowRight,
28+
IconChevronCompactRight,
29+
IconChevronRight,
30+
IconShield,
31+
IconShieldLockFilled,
32+
IconTemplate,
33+
IconShieldLock,
834
} from "@tabler/icons-react";
935
import { useCallback } from "react";
1036
import { Link, useLocation } from "wouter";
37+
import { useAppContext } from "../../hooks/useAppContext.tsx";
1138
import styles from "./Sidebar.module.css";
12-
import iconImage from "/logo-dark.png";
39+
import iconImage from "/icon.png";
40+
41+
interface SidebarProps {
42+
collapsed: boolean;
43+
onToggle: () => void;
44+
}
1345

14-
export function Sidebar() {
46+
export function Sidebar({ collapsed, onToggle }: SidebarProps) {
1547
const [location, setLocation] = useLocation();
48+
const { config, selectedWorkspace } = useAppContext();
49+
const currentWorkspaceName = selectedWorkspace || config?.currentWorkspace || "—";
50+
const currentNamespace = config?.currentNamespace || "—";
1651

1752
const navigateToWorkspaces = useCallback(() => {
1853
setLocation(`/workspaces`);
@@ -39,12 +74,30 @@ export function Sidebar() {
3974
}, [setLocation]);
4075

4176
return (
42-
<div className={styles.sidebar}>
43-
<Link to="/" className={styles.sidebar__logo}>
44-
<Image src={iconImage} alt="flow" fit="contain" />
45-
</Link>
46-
<Stack gap="xs">
47-
<Group gap="xs" mt="md">
77+
<Stack justify="space-between" className={`${styles.sidebar} ${collapsed ? styles.sidebarCollapsed : ""}`}>
78+
<Box>
79+
<Flex justify="space-between" align={ collapsed ? "center" : "flex-end" } direction={ collapsed ? "column-reverse" : "column" }>
80+
<ActionIcon
81+
variant="transparent"
82+
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
83+
onClick={onToggle}
84+
color="bodyLight"
85+
>
86+
{collapsed ? <IconLayoutSidebarLeftExpand size={16} /> : <IconLayoutSidebarLeftCollapse size={16} />}
87+
</ActionIcon>
88+
<Link to="/" className={styles.sidebar__logo}>
89+
<Image src={iconImage} alt="flow" fit="contain" />
90+
</Link>
91+
</Flex>
92+
93+
<Stack gap="xs" mt="xl" justify="stretch" align={ collapsed ? "center" : "flex-start" }>
94+
<NavLink
95+
label="Favorites"
96+
leftSection={<IconStar size={16} />}
97+
active={location.startsWith("/favorites")}
98+
variant="filled"
99+
onClick={navigateToWorkspaces}
100+
/>
48101
<NavLink
49102
label="Workspaces"
50103
leftSection={<IconFolders size={16} />}
@@ -60,45 +113,83 @@ export function Sidebar() {
60113
variant="filled"
61114
onClick={navigateToExecutables}
62115
/>
116+
</Stack>
117+
</Box>
118+
<Box>
119+
{collapsed ? (
120+
<Group justify="center" gap={8}>
121+
<Tooltip label={`Workspace: ${currentWorkspaceName} | Namespace: ${currentNamespace}`} position="right" openDelay={300}>
122+
<ActionIcon variant="transparent" size="sm" aria-label="Context">
123+
<IconInfoCircle size={14} />
124+
</ActionIcon>
125+
</Tooltip>
126+
<Tooltip label="Sync" position="right" openDelay={300}>
127+
<ActionIcon variant="light" size="sm" aria-label="Sync workspaces and executables">
128+
<IconReload size={14} />
129+
</ActionIcon>
130+
</Tooltip>
131+
</Group>
132+
) : (
63133

64-
<NavLink
65-
label="Logs"
66-
leftSection={<IconLogs size={16} />}
67-
active={location.startsWith("/logs")}
68-
variant="filled"
69-
onClick={navigateToLogs}
70-
/>
134+
<Stack gap="xs" align="stretch" justify="center">
71135

72-
<NavLink
73-
label="Data"
74-
leftSection={<IconDatabase size={16} />}
75-
variant="filled"
76-
childrenOffset={28}
77-
>
78-
<NavLink
79-
label="Cache"
80-
variant="filled"
81-
active={location.startsWith("/cache")}
82-
onClick={navigateToCache}
83-
/>
84-
<NavLink
85-
label="Vault"
86-
variant="filled"
87-
active={location.startsWith("/vault")}
88-
onClick={navigateToVault}
89-
/>
90-
</NavLink>
91-
92-
<NavLink
93-
label="Settings"
94-
leftSection={<IconSettings size={16} />}
95-
active={location.startsWith("/settings")}
96-
variant="filled"
97-
onClick={navigateToSettings}
98-
/>
99-
</Group>
136+
<Box className={styles.sidebar__context}>
137+
<Group gap="xs" justify="space-between" mb="xs">
138+
<Text size="xs" fw={700} c="dimmed">Context</Text>
139+
<IconInfoCircle size={16} />
140+
</Group>
141+
<Group>
142+
<Text size="xs" c="dimmed">Workspace</Text>
143+
<Text size="xs" truncate>{currentWorkspaceName}</Text>
144+
</Group>
145+
<Group>
146+
<Text size="xs" c="dimmed" mt={6}>Namespace</Text>
147+
<Text size="xs" truncate>{currentNamespace}</Text>
148+
</Group>
149+
</Box>
150+
<ButtonGroup style={{ alignSelf: 'center' }}>
151+
<Button
152+
leftSection={<IconReload size={12} />}
153+
size="compact-xs" variant="transparent" justify="start">Sync</Button>
154+
<Menu shadow="md" position="top-start" offset={15} width={200} withArrow>
155+
<Menu.Target>
156+
<Button
157+
leftSection={<IconTools size={12} />}
158+
size="compact-xs"
159+
variant="transparent"
160+
onClick={navigateToSettings}
161+
justify="start"
162+
>Tools</Button>
163+
</Menu.Target>
100164

101-
</Stack>
102-
</div>
165+
<Menu.Dropdown>
166+
<Menu.Item leftSection={<IconShieldLock size={14} />}>
167+
Vault
168+
</Menu.Item>
169+
<Menu.Item leftSection={<IconDatabase size={14} />}>
170+
Cache Store
171+
</Menu.Item>
172+
<Menu.Item leftSection={<IconTemplate size={14} />}>
173+
Templates
174+
</Menu.Item>
175+
<Menu.Item
176+
leftSection={<IconLogs size={14} />}
177+
>
178+
Logs
179+
</Menu.Item>
180+
</Menu.Dropdown>
181+
</Menu>
182+
<Button
183+
leftSection={<IconSettings size={12} />}
184+
size="compact-xs"
185+
variant="transparent"
186+
onClick={navigateToSettings}
187+
justify="start"
188+
>Settings</Button>
189+
</ButtonGroup>
190+
</Stack>
191+
)}
192+
</Box>
193+
</Stack>
103194
);
104195
}

0 commit comments

Comments
 (0)