From 4b041662e106ed08390d19034029f9db16e9203c Mon Sep 17 00:00:00 2001 From: TELVIN TEUM Date: Sat, 18 Jul 2026 10:25:40 +0300 Subject: [PATCH] Move desktop search and bell to TopBar Extracts desktop search and notifications from `Sidebar` into a new `TopBar` component and wires it into `AppShell`, keeping mobile behavior in `MobileTopBar` and simplifying sidebar props/usages. Also fixes a command palette hotkey closure issue by adding dependencies to the `useHotkeys` binding so `mod+k` properly toggles open and closed state. --- src/components/layout/AppShell.tsx | 17 ++++---- src/components/layout/CommandPalette.tsx | 15 +++++-- src/components/layout/Sidebar.tsx | 37 ++--------------- src/components/layout/TopBar.tsx | 51 ++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 47 deletions(-) create mode 100644 src/components/layout/TopBar.tsx diff --git a/src/components/layout/AppShell.tsx b/src/components/layout/AppShell.tsx index cabf22c..f244c3e 100644 --- a/src/components/layout/AppShell.tsx +++ b/src/components/layout/AppShell.tsx @@ -6,6 +6,7 @@ import { AnnouncementBanner } from './AnnouncementBanner'; import { CommandPalette } from './CommandPalette'; import { MobileTopBar } from './MobileTopBar'; import { Sidebar } from './Sidebar'; +import { TopBar } from './TopBar'; /** * The persistent application shell, mounted once as a layout route for the @@ -55,7 +56,7 @@ export function AppShell() {
{/* Desktop sidebar */}
- +
{/* Mobile drawer + backdrop */} @@ -84,17 +85,11 @@ export function AppShell() { drawerOpen ? 'translate-x-0' : '-translate-x-full', )} > - setDrawerOpen(false)} - onSearch={(seed) => { - setDrawerOpen(false); - openSearch(seed); - }} - /> + setDrawerOpen(false)} />
- {/* Content column: mobile top bar + floating canvas */} + {/* Content column: top bar (desktop) / mobile top bar + floating canvas */}
setDrawerOpen(true)} onSearch={() => openSearch()} /> + {/* Desktop header strip on the exposed outer shell: centered search, + bell far right. A shrink-0 sibling of the canvas, so the canvas + gives up exactly its height and stays the only scroll region. */} +
diff --git a/src/components/layout/CommandPalette.tsx b/src/components/layout/CommandPalette.tsx index 19976da..10e9db8 100644 --- a/src/components/layout/CommandPalette.tsx +++ b/src/components/layout/CommandPalette.tsx @@ -40,10 +40,17 @@ export function CommandPalette({ open, onOpenChange, initialQuery }: CommandPale const navigate = useNavigate(); const [q, setQ] = useState(''); - useHotkeys('mod+k', () => onOpenChange(!open), { - enableOnFormTags: true, - preventDefault: true, - }); + useHotkeys( + 'mod+k', + () => onOpenChange(!open), + { + enableOnFormTags: true, + preventDefault: true, + }, + // Without deps the callback keeps the first render's `open` (false), so + // the shortcut could open the palette but never toggle it closed. + [open, onOpenChange], + ); // Seed (or clear) the query whenever the palette opens; typing in the // sidebar input while open appends through the same channel. diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 9be7797..43e89a0 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -1,6 +1,4 @@ -import { Search } from 'lucide-react'; import { useMe } from '../../features/auth/hooks/useAuth'; -import { NotificationBell } from '../../features/notifications/components/NotificationBell'; import { Logo } from '../brand/Logo'; import { SidebarNav } from './SidebarNav'; import { UserFooter } from './UserFooter'; @@ -9,17 +7,16 @@ import { WorkspaceSwitcher } from './WorkspaceSwitcher'; interface SidebarProps { /** Called after a nav link is activated (closes the mobile drawer). */ onNavigate?: () => void; - /** Opens the command palette, optionally seeded with typed text. */ - onSearch: (seed?: string) => void; } /** * The persistent navigation frame: workspace identity + switcher fixed at the * top, an independently scrollable nav list in the middle, and the user * footer fixed at the bottom. Rendered both as the desktop rail and inside - * the mobile drawer. + * the mobile drawer. Search and the notification bell live in the shell's + * top bar (TopBar on desktop, MobileTopBar below lg), not here. */ -export function Sidebar({ onNavigate, onSearch }: SidebarProps) { +export function Sidebar({ onNavigate }: SidebarProps) { const { data: me } = useMe(); if (!me?.workspace) return null; // guarded by WorkspaceRoute; satisfies types @@ -29,34 +26,6 @@ export function Sidebar({ onNavigate, onSearch }: SidebarProps) {
- {/* Operational inbox: right edge of the shell header, before the - user's own controls — the "top of the app" position. */} -
- -
-
- {/* Real search input, centered at the top of the sidebar. Focusing - or typing hands off to the floating palette (seeded with the - typed text) — one search surface, so the input itself stays - empty and the palette owns the query. */} -
-