110 lines
4.1 KiB
TypeScript
110 lines
4.1 KiB
TypeScript
'use client';
|
||
|
||
import Link from 'next/link';
|
||
import { usePathname } from 'next/navigation';
|
||
import { BookOpen, ExternalLink, Moon, Sun } from 'lucide-react';
|
||
import { Button } from '@/components/ui/button';
|
||
import { usePublicSettings } from '@/providers/public-settings-provider';
|
||
import { useTheme } from '@/providers/theme-provider';
|
||
import { getApiBaseUrl } from '@/lib/api';
|
||
import { cn } from '@/lib/utils';
|
||
|
||
export function DocsHeader() {
|
||
const { projectName } = usePublicSettings();
|
||
const { resolvedTheme, setTheme } = useTheme();
|
||
const apiUrl = getApiBaseUrl();
|
||
|
||
return (
|
||
<header className="sticky top-0 z-50 w-full border-b border-zinc-200 bg-white/80 backdrop-blur dark:border-zinc-800 dark:bg-zinc-950/80">
|
||
<div className="mx-auto flex h-14 max-w-screen-2xl items-center justify-between gap-4 px-4 lg:px-8">
|
||
<Link href="/docs" className="flex items-center gap-2 font-semibold tracking-tight">
|
||
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-zinc-900 text-white dark:bg-zinc-50 dark:text-zinc-900">
|
||
<BookOpen className="h-4 w-4" />
|
||
</div>
|
||
<span className="hidden sm:inline">{projectName}</span>
|
||
<span className="text-zinc-400 font-normal hidden sm:inline">Docs</span>
|
||
</Link>
|
||
|
||
<div className="flex items-center gap-2">
|
||
<Button variant="ghost" size="sm" asChild className="hidden md:inline-flex">
|
||
<a href={`${apiUrl}/api`} target="_blank" rel="noreferrer">
|
||
Swagger
|
||
<ExternalLink className="h-3.5 w-3.5" />
|
||
</a>
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
size="icon"
|
||
aria-label="Переключить тему"
|
||
onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
|
||
>
|
||
{resolvedTheme === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
);
|
||
}
|
||
|
||
export function DocsSidebar({ groups }: { groups: Array<[string, Array<{ slug: string; title: string }>]> }) {
|
||
const pathname = usePathname();
|
||
|
||
return (
|
||
<nav className="space-y-6">
|
||
<Link
|
||
href="/docs"
|
||
className={cn(
|
||
'block rounded-lg px-3 py-2 text-sm font-medium transition-colors',
|
||
pathname === '/docs'
|
||
? 'bg-zinc-100 text-zinc-900 dark:bg-zinc-800 dark:text-zinc-50'
|
||
: 'text-zinc-600 hover:bg-zinc-50 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-900 dark:hover:text-zinc-50'
|
||
)}
|
||
>
|
||
Главная
|
||
</Link>
|
||
{groups.map(([group, items]) => (
|
||
<div key={group}>
|
||
<p className="mb-2 px-2 text-xs font-semibold uppercase tracking-wider text-zinc-500">{group}</p>
|
||
<div className="space-y-1">
|
||
{items.map((item) => {
|
||
const href = `/docs/${item.slug}`;
|
||
const active = pathname === href;
|
||
return (
|
||
<Link
|
||
key={item.slug}
|
||
href={href}
|
||
className={cn(
|
||
'block rounded-lg px-3 py-2 text-sm transition-colors',
|
||
active
|
||
? 'bg-zinc-100 font-medium text-zinc-900 dark:bg-zinc-800 dark:text-zinc-50'
|
||
: 'text-zinc-600 hover:bg-zinc-50 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-900 dark:hover:text-zinc-50'
|
||
)}
|
||
>
|
||
{item.title}
|
||
</Link>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</nav>
|
||
);
|
||
}
|
||
|
||
export function DocsToc({ sections }: { sections: Array<{ id: string; title: string }> }) {
|
||
return (
|
||
<nav className="space-y-2 text-sm">
|
||
<p className="mb-3 font-semibold">На этой странице</p>
|
||
{sections.map((section) => (
|
||
<a
|
||
key={section.id}
|
||
href={`#${section.id}`}
|
||
className="block text-zinc-500 transition-colors hover:text-zinc-900 dark:hover:text-zinc-50"
|
||
>
|
||
{section.title}
|
||
</a>
|
||
))}
|
||
</nav>
|
||
);
|
||
}
|