161 lines
5.8 KiB
TypeScript
161 lines
5.8 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { X, GitFork, Heart } from 'lucide-react';
|
|
import { cn } from '@/lib/utils/cn';
|
|
import Logo from '@/components/Logo';
|
|
import { useSidebar } from './SidebarProvider';
|
|
import { tools } from '@/lib/tools';
|
|
|
|
export function AppSidebar() {
|
|
const pathname = usePathname();
|
|
const { isOpen, isCollapsed, close } = useSidebar();
|
|
|
|
return (
|
|
<>
|
|
{/* Mobile backdrop */}
|
|
{isOpen && (
|
|
<div
|
|
className="fixed inset-0 bg-transparent backdrop-blur-sm z-40 lg:hidden"
|
|
onClick={close}
|
|
/>
|
|
)}
|
|
|
|
<aside className={cn(
|
|
'fixed inset-y-0 left-0 z-50 flex flex-col border-r border-border/20 bg-background/10 backdrop-blur-2xl transition-all duration-300 ease-in-out lg:relative lg:h-full',
|
|
isOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0',
|
|
isCollapsed ? 'lg:w-14' : 'w-60'
|
|
)}>
|
|
|
|
{/* Header */}
|
|
<div className={cn(
|
|
'flex h-14 items-center shrink-0 border-b border-border/20',
|
|
isCollapsed ? 'justify-center px-2' : 'justify-between px-4'
|
|
)}>
|
|
<Link
|
|
href="/"
|
|
className={cn(
|
|
'flex items-center group overflow-hidden',
|
|
isCollapsed ? 'justify-center' : 'gap-2.5'
|
|
)}
|
|
>
|
|
<div className="shrink-0">
|
|
<Logo size={isCollapsed ? 18 : 24} />
|
|
</div>
|
|
{!isCollapsed && (
|
|
<div className="min-w-0">
|
|
<span className="font-semibold text-base leading-tight block text-foreground">Kit</span>
|
|
<span className="text-[9px] leading-tight text-muted-foreground/50 block font-mono tracking-wider">
|
|
Browser-first toolkit
|
|
</span>
|
|
</div>
|
|
)}
|
|
</Link>
|
|
|
|
{!isCollapsed && (
|
|
<button
|
|
onClick={close}
|
|
className="lg:hidden w-7 h-7 flex items-center justify-center rounded-lg text-muted-foreground/40 hover:text-foreground hover:bg-white/5 transition-all"
|
|
>
|
|
<X className="w-3.5 h-3.5" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className={cn(
|
|
'flex-1 overflow-y-auto py-3 space-y-0.5 scrollbar-thin scrollbar-thumb-primary/10 scrollbar-track-transparent',
|
|
isCollapsed ? 'px-2' : 'px-3'
|
|
)}>
|
|
{tools.map((tool) => {
|
|
const isActive = pathname === tool.href || (tool.href !== '/' && pathname.startsWith(tool.href));
|
|
const Icon = tool.icon;
|
|
|
|
return (
|
|
<Link
|
|
key={tool.href}
|
|
href={tool.href}
|
|
onClick={() => { if (window.innerWidth < 1024) close(); }}
|
|
title={isCollapsed ? tool.navTitle : undefined}
|
|
className={cn(
|
|
'relative flex items-center rounded-lg text-sm transition-all duration-200 group/item',
|
|
isActive
|
|
? 'bg-primary/10 text-primary'
|
|
: 'text-foreground/55 hover:bg-white/4 hover:text-foreground',
|
|
isCollapsed ? 'justify-center p-2' : 'gap-3 px-3 py-2'
|
|
)}
|
|
>
|
|
{/* Active left bar */}
|
|
{isActive && (
|
|
<span className="absolute left-0 inset-y-2 w-0.5 rounded-r-full bg-primary" />
|
|
)}
|
|
|
|
<span className={cn(
|
|
'shrink-0 transition-colors duration-200',
|
|
isActive ? 'text-primary' : 'text-foreground/40 group-hover/item:text-foreground/70'
|
|
)}>
|
|
<Icon className="w-4 h-4" />
|
|
</span>
|
|
|
|
{!isCollapsed && (
|
|
<div className="min-w-0">
|
|
<span className="whitespace-nowrap block text-[13px] font-medium leading-tight">
|
|
{tool.navTitle}
|
|
</span>
|
|
<span className="text-[9px] text-muted-foreground/40 leading-tight block font-mono mt-0.5">
|
|
{tool.description}
|
|
</span>
|
|
</div>
|
|
)}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* Footer */}
|
|
<div className={cn(
|
|
'shrink-0 border-t border-border/20 py-3',
|
|
isCollapsed ? 'flex justify-center px-2' : 'px-4'
|
|
)}>
|
|
{isCollapsed ? (
|
|
<a
|
|
href="https://dev.pivoine.art/valknar/kit-ui"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
title="View source"
|
|
className="text-muted-foreground/40 hover:text-primary transition-colors"
|
|
>
|
|
<GitFork className="w-3.5 h-3.5" />
|
|
</a>
|
|
) : (
|
|
<div className="flex items-center justify-between">
|
|
<p className="flex items-center gap-1 text-[9px] text-muted-foreground/40 font-mono">
|
|
© {new Date().getFullYear()} Kit
|
|
<Heart className="w-2 h-2 text-primary/70 shrink-0 animate-pulse" fill="currentColor" />
|
|
<a
|
|
href="https://pivoine.art"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="hover:text-foreground/70 transition-colors"
|
|
>
|
|
Valknar
|
|
</a>
|
|
</p>
|
|
<a
|
|
href="https://dev.pivoine.art/valknar/kit-ui"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
title="View source"
|
|
className="text-muted-foreground/30 hover:text-primary transition-colors"
|
|
>
|
|
<GitFork className="w-3 h-3" />
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</aside>
|
|
</>
|
|
);
|
|
}
|