feat: update navbar
This commit is contained in:
parent
f90fd8f65a
commit
da6887d41c
|
|
@ -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={
|
||||||
<DocsSidebar currentPath={pathname} />
|
<div className="border-r border-white/10 bg-slate-950/50 backdrop-blur-sm sticky top-12 h-[calc(100vh-3rem)] overflow-y-auto">
|
||||||
</aside>
|
<DocsSidebar currentPath={pathname} />
|
||||||
|
</div>
|
||||||
{/* Main Content Area - Pages render here with their own article + TOC structure */}
|
}
|
||||||
<main className="flex-1 min-w-0">
|
center={children}
|
||||||
{children}
|
/>
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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,45 +97,46 @@ 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} />
|
||||||
|
|
||||||
{/* Page Content (Hero + Sections) */}
|
{/* Page Content (Hero + Sections) */}
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
{/* Next Steps Section */}
|
{/* Next Steps Section */}
|
||||||
<section id="next-steps" className="mb-12">
|
<section id="next-steps" className="mb-12">
|
||||||
<SectionHeader level={2} id="next-steps" className="mb-6">
|
<SectionHeader level={2} id="next-steps" className="mb-6">
|
||||||
Next Steps
|
Next Steps
|
||||||
</SectionHeader>
|
</SectionHeader>
|
||||||
|
|
||||||
{nextSteps.description && (
|
{nextSteps.description && (
|
||||||
<p className="text-gray-300 leading-relaxed mb-6">
|
<p className="text-gray-300 leading-relaxed mb-6">
|
||||||
{nextSteps.description}
|
{nextSteps.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<LinkCardGrid columns={2}>
|
<LinkCardGrid columns={2}>
|
||||||
{nextSteps.links.map((link, index) => (
|
{nextSteps.links.map((link, index) => (
|
||||||
<LinkCard
|
<LinkCard
|
||||||
key={index}
|
key={index}
|
||||||
href={link.href}
|
href={link.href}
|
||||||
title={link.title}
|
title={link.title}
|
||||||
description={link.description}
|
description={link.description}
|
||||||
accent={link.accent}
|
accent={link.accent}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</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>
|
}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -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,10 +241,37 @@ export const SubsectionNav = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
);
|
||||||
|
|
||||||
{/* Mobile Menu Button */}
|
return (
|
||||||
<div className="md:hidden flex items-center ml-auto">
|
<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
|
<button
|
||||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||||
className="p-2 text-gray-400 hover:text-white transition-colors"
|
className="p-2 text-gray-400 hover:text-white transition-colors"
|
||||||
|
|
@ -284,9 +300,12 @@ export const SubsectionNav = ({
|
||||||
)}
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
</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">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue