Files
claudecodeui/src/components/refactored/sidebar/view/SidebarHeader.tsx
2026-03-27 16:44:56 +03:00

138 lines
4.7 KiB
TypeScript

import { useState } from 'react';
import { FolderPlus, Plus, RefreshCw, PanelLeftClose } from 'lucide-react';
import type { SearchMode } from '../types';
import { SidebarSearch } from './SidebarSearch';
import { Button } from '@/shared/view/ui';
import { cn } from '@/lib/utils';
import { IS_PLATFORM } from '@/constants/config';
type SidebarHeaderProps = {
isCollapsed: boolean;
onToggleCollapse: () => void;
isRefreshing: boolean;
onRefresh: () => void;
onNewProject: () => void;
};
export default function SidebarHeader({
isCollapsed,
onToggleCollapse,
isRefreshing,
onRefresh,
onNewProject
}: SidebarHeaderProps) {
// UI States for search
const [searchMode, setSearchMode] = useState<SearchMode>('projects');
const [searchFilter, setSearchFilter] = useState('');
const LogoBlock = () => (
<div className="flex min-w-0 items-center gap-2.5">
<div className="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg bg-primary/90 shadow-sm">
<svg className="h-3.5 w-3.5 text-primary-foreground" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.2} strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
</svg>
</div>
<h1 className="truncate text-sm font-semibold tracking-tight text-foreground">Claude Code UI</h1>
</div>
);
const LogoWithLink = () => {
if (IS_PLATFORM) {
return (
<a
href="https://cloudcli.ai/dashboard"
className="flex min-w-0 items-center gap-2.5 transition-opacity hover:opacity-80 active:opacity-70"
title="View Environments Dashboard"
>
<LogoBlock />
</a>
);
}
return <LogoBlock />;
};
if (isCollapsed) return null;
return (
<div className="flex-shrink-0">
{/* Desktop header */}
<div className="hidden px-3 pb-2 pt-3 md:block">
<div className="flex items-center justify-between gap-2">
<LogoWithLink />
<div className="flex flex-shrink-0 items-center gap-0.5">
<Button
variant="ghost"
size="sm"
className="h-7 w-7 rounded-lg p-0 text-muted-foreground hover:bg-accent/80 hover:text-foreground"
onClick={onRefresh}
disabled={isRefreshing}
title="Refresh"
>
<RefreshCw className={cn("h-3.5 w-3.5 transition-opacity", isRefreshing && "animate-spin opacity-50")} />
</Button>
<Button
variant="ghost"
size="sm"
className="h-7 w-7 rounded-lg p-0 text-muted-foreground hover:bg-accent/80 hover:text-foreground"
onClick={onNewProject}
title="New Project"
>
<Plus className="h-3.5 w-3.5" />
</Button>
<Button
variant="ghost"
size="sm"
className="h-7 w-7 rounded-lg p-0 text-muted-foreground hover:bg-accent/80 hover:text-foreground"
onClick={onToggleCollapse}
title="Hide Sidebar"
>
<PanelLeftClose className="h-3.5 w-3.5" />
</Button>
</div>
</div>
<SidebarSearch
searchMode={searchMode}
onSearchModeChange={setSearchMode}
searchFilter={searchFilter}
onSearchFilterChange={setSearchFilter}
/>
</div>
{/* Desktop divider */}
<div className="nav-divider hidden md:block" />
{/* Mobile header */}
<div className="p-3 pb-2 md:hidden">
<div className="flex items-center justify-between">
<LogoWithLink />
<div className="flex flex-shrink-0 gap-1.5">
<button
className="flex h-8 w-8 items-center justify-center rounded-lg bg-muted/50 transition-all active:scale-95 disabled:opacity-70"
onClick={onRefresh}
disabled={isRefreshing}
>
<RefreshCw className={cn("h-4 w-4 text-muted-foreground transition-opacity", isRefreshing && "animate-spin opacity-50")} />
</button>
<button
className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary/90 text-primary-foreground transition-all active:scale-95"
onClick={onNewProject}
>
<FolderPlus className="h-4 w-4" />
</button>
</div>
</div>
<SidebarSearch
searchMode={searchMode}
onSearchModeChange={setSearchMode}
searchFilter={searchFilter}
onSearchFilterChange={setSearchFilter}
/>
</div>
{/* Mobile divider */}
<div className="nav-divider md:hidden" />
</div>
);
}