feat: update navbar

This commit is contained in:
Oleg Proskurin 2025-10-21 23:31:08 +07:00
parent f90fd8f65a
commit da6887d41c
4 changed files with 231 additions and 83 deletions

View File

@ -4,20 +4,25 @@ import { ReactNode } from 'react';
import { usePathname } from 'next/navigation';
import { SubsectionNav } from '@/components/shared/SubsectionNav';
import { DocsSidebar } from '@/components/docs/layout/DocsSidebar';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
/**
* Root Documentation Layout
*
* Provides shared layout elements for all documentation pages:
* - SubsectionNav at the top
* - Left Sidebar (DocsSidebar)
* - Three-column layout (left sidebar + content + right TOC via DocPage)
* - Background gradients
* - Two-column flex wrapper (sidebar + main content)
*
* Uses ThreeColumnLayout for consistent column structure:
* - Left: DocsSidebar (w-64, hidden lg:block)
* - Center: Page content (flex-1)
* - Right: Handled by DocPage component
*
* Pages handle their own:
* - Breadcrumbs (manually specified)
* - Article content
* - TOC sidebar (on the right)
* - TOC sidebar (on the right via DocPage)
*
* Features:
* - Animated gradient background matching landing page
@ -54,17 +59,16 @@ export default function DocsRootLayout({ children }: DocsRootLayoutProps) {
ctaHref="/signup"
/>
{/* Two-column Documentation Layout */}
<div className="relative z-10 flex">
{/* Left Sidebar - Thin, minimal design */}
<aside className="hidden lg:block w-64 border-r border-white/10 bg-slate-950/50 backdrop-blur-sm sticky top-12 h-[calc(100vh-3rem)] overflow-y-auto">
<DocsSidebar currentPath={pathname} />
</aside>
{/* Main Content Area - Pages render here with their own article + TOC structure */}
<main className="flex-1 min-w-0">
{children}
</main>
{/* Three-column Documentation Layout */}
<div className="relative z-10">
<ThreeColumnLayout
left={
<div className="border-r border-white/10 bg-slate-950/50 backdrop-blur-sm sticky top-12 h-[calc(100vh-3rem)] overflow-y-auto">
<DocsSidebar currentPath={pathname} />
</div>
}
center={children}
/>
</div>
</div>
);

View File

@ -9,6 +9,10 @@
* - Next Steps section at bottom
* - Table of Contents sidebar on the right
*
* Uses ThreeColumnLayout for consistent column structure:
* - Center: Article content (max-w-3xl)
* - Right: Table of Contents (w-56, hidden xl:block)
*
* This component extracts common layout patterns from all doc pages,
* reducing duplication and ensuring consistency.
*
@ -29,6 +33,7 @@ import { ReactNode } from 'react';
import { Breadcrumb } from '@/components/docs/shared/Breadcrumb';
import { DocsTOC } from '@/components/docs/layout/DocsTOC';
import { SectionHeader, LinkCard, LinkCardGrid } from '@/components/docs/blocks';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
/**
* Next step link card configuration
@ -92,45 +97,46 @@ export const DocPage = ({
children,
}: DocPageProps) => {
return (
<div className="flex">
{/* Article Content */}
<article className="flex-1 max-w-3xl mx-auto px-6 lg:px-12 py-12">
{/* Breadcrumb Navigation */}
<Breadcrumb items={breadcrumbItems} />
<ThreeColumnLayout
center={
<article className="max-w-3xl mx-auto px-6 lg:px-12 py-12">
{/* Breadcrumb Navigation */}
<Breadcrumb items={breadcrumbItems} />
{/* Page Content (Hero + Sections) */}
{children}
{/* Page Content (Hero + Sections) */}
{children}
{/* Next Steps Section */}
<section id="next-steps" className="mb-12">
<SectionHeader level={2} id="next-steps" className="mb-6">
Next Steps
</SectionHeader>
{/* Next Steps Section */}
<section id="next-steps" className="mb-12">
<SectionHeader level={2} id="next-steps" className="mb-6">
Next Steps
</SectionHeader>
{nextSteps.description && (
<p className="text-gray-300 leading-relaxed mb-6">
{nextSteps.description}
</p>
)}
{nextSteps.description && (
<p className="text-gray-300 leading-relaxed mb-6">
{nextSteps.description}
</p>
)}
<LinkCardGrid columns={2}>
{nextSteps.links.map((link, index) => (
<LinkCard
key={index}
href={link.href}
title={link.title}
description={link.description}
accent={link.accent}
/>
))}
</LinkCardGrid>
</section>
</article>
{/* Right TOC Sidebar */}
<aside className="hidden xl:block w-56 border-l border-white/10 bg-slate-950/30 backdrop-blur-sm sticky top-12 h-[calc(100vh-3rem)] overflow-y-auto">
<DocsTOC items={tocItems} />
</aside>
</div>
<LinkCardGrid columns={2}>
{nextSteps.links.map((link, index) => (
<LinkCard
key={index}
href={link.href}
title={link.title}
description={link.description}
accent={link.accent}
/>
))}
</LinkCardGrid>
</section>
</article>
}
right={
<div className="border-l border-white/10 bg-slate-950/30 backdrop-blur-sm sticky top-12 h-[calc(100vh-3rem)] overflow-y-auto">
<DocsTOC items={tocItems} />
</div>
}
/>
);
};

View File

@ -0,0 +1,119 @@
'use client';
/**
* Three Column Layout - Core Wireframe Component
*
* Provides a consistent three-column layout structure used across the application.
* This is the single source of truth for column widths, breakpoints, and responsive behavior.
*
* Column Structure:
* - Left: w-64 (256px), hidden until lg breakpoint
* - Center: flex-1 (flexible, takes remaining space)
* - Right: w-56 (224px), hidden until xl breakpoint
*
* Usage Examples:
*
* 1. SubsectionNav (3 columns):
* <ThreeColumnLayout
* left={<Logo />}
* center={<NavItems />}
* right={<UserMenu />}
* />
*
* 2. Docs Layout (2 columns - left + center):
* <ThreeColumnLayout
* left={<DocsSidebar />}
* center={<DocPage />}
* />
*
* 3. Doc Page (2 columns - center + right):
* <ThreeColumnLayout
* center={<Article />}
* right={<DocsTOC />}
* />
*
* Design Principles:
* - Responsive breakpoints match Tailwind defaults (lg: 1024px, xl: 1280px)
* - Column widths ensure visual alignment across all usage contexts
* - Flexible center column adapts to available space
* - Optional columns enable 1, 2, or 3 column layouts
*/
import { ReactNode } from 'react';
/**
* Props for ThreeColumnLayout component
*/
export interface ThreeColumnLayoutProps {
/** Left column content (w-64, hidden until lg breakpoint) */
left?: ReactNode;
/** Center column content (flex-1, always visible) */
center: ReactNode;
/** Right column content (w-56, hidden until xl breakpoint) */
right?: ReactNode;
/** Additional classes for left column */
leftClassName?: string;
/** Additional classes for center column */
centerClassName?: string;
/** Additional classes for right column */
rightClassName?: string;
/** Additional classes for container */
containerClassName?: string;
/** Breakpoint for showing left column (default: 'lg') */
leftBreakpoint?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
/** Breakpoint for showing right column (default: 'xl') */
rightBreakpoint?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
}
/**
* Three Column Layout Component
*
* Core wireframe for consistent three-column layouts across the application.
* Handles responsive behavior and provides customization via className props.
*/
export const ThreeColumnLayout = ({
left,
center,
right,
leftClassName = '',
centerClassName = '',
rightClassName = '',
containerClassName = '',
leftBreakpoint = 'lg',
rightBreakpoint = 'xl',
}: ThreeColumnLayoutProps) => {
// Generate responsive visibility classes
const leftHidden = `hidden ${leftBreakpoint}:block`;
const rightHidden = `hidden ${rightBreakpoint}:block`;
return (
<div className={`flex ${containerClassName}`}>
{/* Left Column - w-64 (256px) */}
{left && (
<div className={`${leftHidden} w-64 shrink-0 ${leftClassName}`}>
{left}
</div>
)}
{/* Center Column - flex-1 (flexible) */}
<div className={`flex-1 min-w-0 ${centerClassName}`}>
{center}
</div>
{/* Right Column - w-56 (224px) */}
{right && (
<div className={`${rightHidden} w-56 shrink-0 ${rightClassName}`}>
{right}
</div>
)}
</div>
);
};

View File

@ -9,17 +9,24 @@
* - Active state indicator (purple color)
* - Optional API Key input on the right
* - Responsive (hamburger menu on mobile)
* - Can be reused across landing app sections
* - Three-column layout matching docs structure
* - Customizable left/right slots
*
* Uses ThreeColumnLayout for consistent alignment with docs pages.
* Columns align with docs layout at all screen widths.
*
* Usage:
* <SubsectionNav
* items={[...]}
* currentPath="/docs/final"
* showApiKeyInput={true}
* leftSlot={<Logo />}
* rightSlot={<UserMenu />}
* />
*/
import { useState, useEffect, useRef } from 'react';
import { useState, useEffect, useRef, ReactNode } from 'react';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
interface NavItem {
label: string;
@ -33,6 +40,10 @@ interface SubsectionNavProps {
ctaHref?: string;
onCtaClick?: () => void;
showApiKeyInput?: boolean;
/** Optional content for left column (w-64, hidden until lg) */
leftSlot?: ReactNode;
/** Optional content for right column (w-56, hidden until xl). If not provided and showApiKeyInput is true, API key input is used. */
rightSlot?: ReactNode;
}
const API_KEY_STORAGE = 'banatie_docs_api_key';
@ -41,6 +52,8 @@ export const SubsectionNav = ({
items,
currentPath,
showApiKeyInput = false,
leftSlot,
rightSlot,
}: SubsectionNavProps) => {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [apiKey, setApiKey] = useState('');
@ -110,32 +123,8 @@ export const SubsectionNav = ({
};
}, []);
return (
<nav className="sticky top-0 z-40 bg-slate-950/80 backdrop-blur-sm border-b border-white/10">
{/* Main Nav Container */}
<div className="max-w-7xl mx-auto px-6">
<div className="flex items-center justify-between h-12">
{/* Desktop Navigation */}
<div className="hidden md:flex items-center gap-8">
{items.map((item) => {
const active = isActive(item.href);
return (
<a
key={item.href}
href={item.href}
className={`
py-3 text-sm font-medium transition-colors
${active ? 'text-purple-400' : 'text-gray-400 hover:text-white'}
`}
>
{item.label}
</a>
);
})}
</div>
{/* Right Side: API Key Input (Desktop) */}
{showApiKeyInput && (
// Desktop API Key Input Component
const apiKeyComponent = showApiKeyInput && (
<div className="hidden md:block relative" ref={dropdownRef}>
<button
onClick={() => setIsApiKeyExpanded(!isApiKeyExpanded)}
@ -252,10 +241,37 @@ export const SubsectionNav = ({
</div>
)}
</div>
)}
);
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center ml-auto">
return (
<nav className="sticky top-0 z-40 bg-slate-950/80 backdrop-blur-sm border-b border-white/10">
{/* Three-Column Layout */}
<ThreeColumnLayout
left={leftSlot}
center={
<div className="max-w-7xl mx-auto px-6">
<div className="flex items-center justify-between h-12">
{/* Desktop Navigation Items */}
<div className="hidden md:flex items-center gap-8">
{items.map((item) => {
const active = isActive(item.href);
return (
<a
key={item.href}
href={item.href}
className={`
py-3 text-sm font-medium transition-colors
${active ? 'text-purple-400' : 'text-gray-400 hover:text-white'}
`}
>
{item.label}
</a>
);
})}
</div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center ml-auto">
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="p-2 text-gray-400 hover:text-white transition-colors"
@ -284,9 +300,12 @@ export const SubsectionNav = ({
)}
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
}
right={rightSlot || apiKeyComponent}
/>
{/* Decorative Wave Line */}
<div className="absolute bottom-0 left-0 right-0 h-px overflow-hidden">