182 lines
5.8 KiB
TypeScript
182 lines
5.8 KiB
TypeScript
'use client';
|
|
|
|
/**
|
|
* Documentation Sidebar - Final Variant
|
|
*
|
|
* Based on Variant A with FIXED active states
|
|
*
|
|
* Key Improvements:
|
|
* - Consistent active state styling for both parent and child items
|
|
* - Clean left border indicator (no background boxes)
|
|
* - Parent active: purple left border + white text
|
|
* - Child active: purple left border + white text (NO background)
|
|
*
|
|
* Features:
|
|
* - Thin sidebar with subtle hover states
|
|
* - Collapsible section groups
|
|
* - Minimal icons (chevron for expandable items)
|
|
* - Clean, uncluttered appearance
|
|
*
|
|
* Navigation Structure:
|
|
* - Getting Started
|
|
* - API Reference (collapsible)
|
|
* - Guides (collapsible)
|
|
* - Examples
|
|
*/
|
|
|
|
import { useState } from 'react';
|
|
|
|
interface NavItem {
|
|
label: string;
|
|
href: string;
|
|
icon?: string;
|
|
children?: NavItem[];
|
|
}
|
|
|
|
interface DocsSidebarFinalProps {
|
|
currentPath: string;
|
|
}
|
|
|
|
const navigationItems: NavItem[] = [
|
|
{
|
|
label: 'Getting Started',
|
|
href: '/docs/final',
|
|
icon: '🚀',
|
|
},
|
|
{
|
|
label: 'API Reference',
|
|
href: '/docs/final/api',
|
|
icon: '📚',
|
|
children: [
|
|
{ label: 'Text to Image', href: '/docs/final/api/text-to-image' },
|
|
{ label: 'Upload', href: '/docs/final/api/upload' },
|
|
{ label: 'Images', href: '/docs/final/api/images' },
|
|
],
|
|
},
|
|
{
|
|
label: 'Guides',
|
|
href: '/docs/final/guides',
|
|
icon: '📖',
|
|
children: [
|
|
{ label: 'Authentication', href: '/docs/final/guides/authentication' },
|
|
{ label: 'Error Handling', href: '/docs/final/guides/error-handling' },
|
|
{ label: 'Rate Limits', href: '/docs/final/guides/rate-limits' },
|
|
],
|
|
},
|
|
{
|
|
label: 'Examples',
|
|
href: '/docs/final/examples',
|
|
icon: '💡',
|
|
},
|
|
];
|
|
|
|
export const DocsSidebarFinal = ({ currentPath }: DocsSidebarFinalProps) => {
|
|
const [expandedSections, setExpandedSections] = useState<string[]>(['API Reference', 'Guides']);
|
|
|
|
const toggleSection = (label: string) => {
|
|
setExpandedSections((prev) =>
|
|
prev.includes(label) ? prev.filter((item) => item !== label) : [...prev, label]
|
|
);
|
|
};
|
|
|
|
const isActive = (href: string) => currentPath === href;
|
|
const isExpanded = (label: string) => expandedSections.includes(label);
|
|
|
|
return (
|
|
<nav className="p-6" aria-label="Documentation navigation">
|
|
{/* Logo/Title */}
|
|
<div className="mb-8">
|
|
<h2 className="text-lg font-semibold text-white">Documentation</h2>
|
|
<p className="text-xs text-gray-500 mt-1">Final: Production</p>
|
|
</div>
|
|
|
|
{/* Navigation Items */}
|
|
<ul className="space-y-1">
|
|
{navigationItems.map((item) => {
|
|
const hasChildren = item.children && item.children.length > 0;
|
|
const expanded = isExpanded(item.label);
|
|
const active = isActive(item.href);
|
|
|
|
return (
|
|
<li key={item.label}>
|
|
{/* Parent Item */}
|
|
<div className="relative">
|
|
{active && (
|
|
<div className="absolute left-0 top-0 bottom-0 w-0.5 bg-purple-500 rounded-r"></div>
|
|
)}
|
|
<a
|
|
href={item.href}
|
|
onClick={
|
|
hasChildren
|
|
? (e) => {
|
|
e.preventDefault();
|
|
toggleSection(item.label);
|
|
}
|
|
: undefined
|
|
}
|
|
className={`
|
|
flex items-center justify-between px-3 py-2 rounded-lg text-sm transition-colors
|
|
${active ? 'bg-purple-500/10 text-white font-medium' : 'text-gray-400 hover:text-white hover:bg-white/5'}
|
|
`}
|
|
>
|
|
<span className="flex items-center gap-2">
|
|
{item.icon && <span className="text-base">{item.icon}</span>}
|
|
<span>{item.label}</span>
|
|
</span>
|
|
{hasChildren && (
|
|
<svg
|
|
className={`w-4 h-4 transition-transform ${expanded ? 'rotate-90' : ''}`}
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
</svg>
|
|
)}
|
|
</a>
|
|
</div>
|
|
|
|
{/* Children Items - FIXED ACTIVE STATE */}
|
|
{hasChildren && expanded && (
|
|
<ul className="ml-6 mt-1 space-y-1 border-l border-slate-800">
|
|
{item.children!.map((child) => {
|
|
const childActive = isActive(child.href);
|
|
return (
|
|
<li key={child.label}>
|
|
<a
|
|
href={child.href}
|
|
className={`
|
|
block px-3 py-1.5 text-sm rounded-lg transition-colors ml-[-2px]
|
|
${
|
|
childActive
|
|
? 'text-white font-medium border-l-2 border-purple-500 bg-transparent pl-4'
|
|
: 'text-gray-500 hover:text-gray-300 border-l-2 border-transparent pl-4'
|
|
}
|
|
`}
|
|
>
|
|
{child.label}
|
|
</a>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
)}
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
|
|
{/* Bottom Links */}
|
|
<div className="mt-12 pt-6 border-t border-slate-800">
|
|
<ul className="space-y-2 text-sm">
|
|
<li>
|
|
<a href="/docs" className="text-gray-500 hover:text-gray-300 transition-colors">
|
|
← Back to variants
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
);
|
|
};
|