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 { usePathname } from 'next/navigation';
import { SubsectionNav } from '@/components/shared/SubsectionNav'; import { SubsectionNav } from '@/components/shared/SubsectionNav';
import { DocsSidebar } from '@/components/docs/layout/DocsSidebar'; import { DocsSidebar } from '@/components/docs/layout/DocsSidebar';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
/** /**
* Root Documentation Layout * Root Documentation Layout
* *
* Provides shared layout elements for all documentation pages: * Provides shared layout elements for all documentation pages:
* - SubsectionNav at the top * - SubsectionNav at the top
* - Left Sidebar (DocsSidebar) * - Three-column layout (left sidebar + content + right TOC via DocPage)
* - Background gradients * - 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: * Pages handle their own:
* - Breadcrumbs (manually specified) * - Breadcrumbs (manually specified)
* - Article content * - Article content
* - TOC sidebar (on the right) * - TOC sidebar (on the right via DocPage)
* *
* Features: * Features:
* - Animated gradient background matching landing page * - Animated gradient background matching landing page
@ -54,17 +59,16 @@ export default function DocsRootLayout({ children }: DocsRootLayoutProps) {
ctaHref="/signup" ctaHref="/signup"
/> />
{/* Two-column Documentation Layout */} {/* Three-column Documentation Layout */}
<div className="relative z-10 flex"> <div className="relative z-10">
{/* Left Sidebar - Thin, minimal design */} <ThreeColumnLayout
<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"> 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} /> <DocsSidebar currentPath={pathname} />
</aside> </div>
}
{/* Main Content Area - Pages render here with their own article + TOC structure */} center={children}
<main className="flex-1 min-w-0"> />
{children}
</main>
</div> </div>
</div> </div>
); );

View File

@ -9,6 +9,10 @@
* - Next Steps section at bottom * - Next Steps section at bottom
* - Table of Contents sidebar on the right * - 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, * This component extracts common layout patterns from all doc pages,
* reducing duplication and ensuring consistency. * reducing duplication and ensuring consistency.
* *
@ -29,6 +33,7 @@ import { ReactNode } from 'react';
import { Breadcrumb } from '@/components/docs/shared/Breadcrumb'; import { Breadcrumb } from '@/components/docs/shared/Breadcrumb';
import { DocsTOC } from '@/components/docs/layout/DocsTOC'; import { DocsTOC } from '@/components/docs/layout/DocsTOC';
import { SectionHeader, LinkCard, LinkCardGrid } from '@/components/docs/blocks'; import { SectionHeader, LinkCard, LinkCardGrid } from '@/components/docs/blocks';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
/** /**
* Next step link card configuration * Next step link card configuration
@ -92,9 +97,9 @@ export const DocPage = ({
children, children,
}: DocPageProps) => { }: DocPageProps) => {
return ( return (
<div className="flex"> <ThreeColumnLayout
{/* Article Content */} center={
<article className="flex-1 max-w-3xl mx-auto px-6 lg:px-12 py-12"> <article className="max-w-3xl mx-auto px-6 lg:px-12 py-12">
{/* Breadcrumb Navigation */} {/* Breadcrumb Navigation */}
<Breadcrumb items={breadcrumbItems} /> <Breadcrumb items={breadcrumbItems} />
@ -126,11 +131,12 @@ export const DocPage = ({
</LinkCardGrid> </LinkCardGrid>
</section> </section>
</article> </article>
}
{/* Right TOC Sidebar */} right={
<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"> <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} /> <DocsTOC items={tocItems} />
</aside>
</div> </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) * - Active state indicator (purple color)
* - Optional API Key input on the right * - Optional API Key input on the right
* - Responsive (hamburger menu on mobile) * - 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: * Usage:
* <SubsectionNav * <SubsectionNav
* items={[...]} * items={[...]}
* currentPath="/docs/final" * currentPath="/docs/final"
* showApiKeyInput={true} * 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 { interface NavItem {
label: string; label: string;
@ -33,6 +40,10 @@ interface SubsectionNavProps {
ctaHref?: string; ctaHref?: string;
onCtaClick?: () => void; onCtaClick?: () => void;
showApiKeyInput?: boolean; 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'; const API_KEY_STORAGE = 'banatie_docs_api_key';
@ -41,6 +52,8 @@ export const SubsectionNav = ({
items, items,
currentPath, currentPath,
showApiKeyInput = false, showApiKeyInput = false,
leftSlot,
rightSlot,
}: SubsectionNavProps) => { }: SubsectionNavProps) => {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [apiKey, setApiKey] = useState(''); const [apiKey, setApiKey] = useState('');
@ -110,32 +123,8 @@ export const SubsectionNav = ({
}; };
}, []); }, []);
return ( // Desktop API Key Input Component
<nav className="sticky top-0 z-40 bg-slate-950/80 backdrop-blur-sm border-b border-white/10"> const apiKeyComponent = showApiKeyInput && (
{/* 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 && (
<div className="hidden md:block relative" ref={dropdownRef}> <div className="hidden md:block relative" ref={dropdownRef}>
<button <button
onClick={() => setIsApiKeyExpanded(!isApiKeyExpanded)} onClick={() => setIsApiKeyExpanded(!isApiKeyExpanded)}
@ -252,7 +241,34 @@ export const SubsectionNav = ({
</div> </div>
)} )}
</div> </div>
)} );
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 */} {/* Mobile Menu Button */}
<div className="md:hidden flex items-center ml-auto"> <div className="md:hidden flex items-center ml-auto">
@ -287,6 +303,9 @@ export const SubsectionNav = ({
</div> </div>
</div> </div>
</div> </div>
}
right={rightSlot || apiKeyComponent}
/>
{/* Decorative Wave Line */} {/* Decorative Wave Line */}
<div className="absolute bottom-0 left-0 right-0 h-px overflow-hidden"> <div className="absolute bottom-0 left-0 right-0 h-px overflow-hidden">