refactor: Migrate documentation content, rebuild UI components, and update core architecture.
This commit is contained in:
@@ -1,87 +1,158 @@
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import Search from "@/components/search";
|
||||
import Anchor from "@/components/anchor";
|
||||
import { SheetLeftbar } from "@/components/leftbar";
|
||||
import { SheetClose } from "@/components/ui/sheet";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import docuConfig from "@/docu.json"; // Import JSON
|
||||
"use client"
|
||||
|
||||
export function Navbar() {
|
||||
import { ArrowUpRight, ChevronDown, ChevronUp } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
import Search from "@/components/SearchBox"
|
||||
import Anchor from "@/components/anchor"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import docuConfig from "@/docu.json"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useState, useCallback } from "react"
|
||||
import { motion, AnimatePresence } from "framer-motion"
|
||||
import { ModeToggle } from "@/components/ThemeToggle"
|
||||
|
||||
interface NavbarProps {
|
||||
id?: string
|
||||
}
|
||||
|
||||
export function Navbar({ id }: NavbarProps) {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
||||
|
||||
const toggleMenu = useCallback(() => {
|
||||
setIsMenuOpen((prev) => !prev)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<nav className="sticky top-0 z-50 w-full h-16 border-b bg-background">
|
||||
<div className="sm:container mx-auto w-[95vw] h-full flex items-center justify-between md:gap-2">
|
||||
<div className="flex items-center gap-5">
|
||||
<SheetLeftbar />
|
||||
<div className="sticky top-0 z-50 w-full">
|
||||
<nav id={id} className="bg-background h-16 w-full border-b">
|
||||
<div className="mx-auto flex h-full w-[95vw] items-center justify-between sm:container md:gap-2">
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="hidden lg:flex">
|
||||
<div className="flex">
|
||||
<Logo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="items-center hidden gap-4 text-sm font-medium lg:flex text-muted-foreground">
|
||||
<div className="flex items-center gap-0 max-md:flex-row-reverse md:gap-2">
|
||||
<div className="text-muted-foreground hidden items-center gap-4 text-sm font-medium md:flex">
|
||||
<NavMenu />
|
||||
</div>
|
||||
<Separator className="hidden lg:flex my-4 h-9" orientation="vertical" />
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={toggleMenu}
|
||||
aria-label={isMenuOpen ? "Close navigation menu" : "Open navigation menu"}
|
||||
aria-expanded={isMenuOpen}
|
||||
className="flex items-center gap-1 px-2 text-sm font-medium md:hidden"
|
||||
>
|
||||
{isMenuOpen ? (
|
||||
<ChevronUp className="text-muted-foreground h-6 w-6" />
|
||||
) : (
|
||||
<ChevronDown className="text-muted-foreground h-6 w-6" />
|
||||
)}
|
||||
</Button>
|
||||
<Separator className="my-4 hidden h-9 md:flex" orientation="vertical" />
|
||||
<Search />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
</nav>
|
||||
|
||||
<AnimatePresence>
|
||||
{isMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: "auto" }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="bg-background/95 w-full border-b shadow-sm backdrop-blur-sm md:hidden"
|
||||
>
|
||||
<div className="mx-auto w-[95vw] sm:container">
|
||||
<ul className="flex flex-col py-2">
|
||||
<NavMenuCollapsible onItemClick={() => setIsMenuOpen(false)} />
|
||||
</ul>
|
||||
<div className="flex items-center justify-between border-t px-1 py-3">
|
||||
<ModeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Logo() {
|
||||
const { navbar } = docuConfig; // Extract navbar from JSON
|
||||
const { navbar } = docuConfig
|
||||
|
||||
return (
|
||||
<Link href="/" className="flex items-center gap-1.5">
|
||||
<div className="relative w-8 h-8">
|
||||
<Image
|
||||
src={navbar.logo.src}
|
||||
alt={navbar.logo.alt}
|
||||
fill
|
||||
sizes="32px"
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
<h2 className="font-bold font-code text-md">{navbar.logoText}</h2>
|
||||
</Link>
|
||||
);
|
||||
return (
|
||||
<Link href="/" className="flex items-center gap-1.5">
|
||||
<div className="relative h-8 w-8">
|
||||
<Image
|
||||
src={navbar.logo.src}
|
||||
alt={navbar.logo.alt}
|
||||
fill
|
||||
sizes="32px"
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
<h2 className="font-code dark:text-accent text-primary text-lg font-bold">
|
||||
{navbar.logoText}
|
||||
</h2>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export function NavMenu({ isSheet = false }) {
|
||||
const { navbar } = docuConfig; // Extract navbar from JSON
|
||||
// Desktop NavMenu — horizontal list
|
||||
export function NavMenu() {
|
||||
const { navbar } = docuConfig
|
||||
|
||||
return (
|
||||
<>
|
||||
{navbar?.menu?.map((item) => {
|
||||
const isExternal = item.href.startsWith("http");
|
||||
|
||||
const Comp = (
|
||||
const isExternal = item.href.startsWith("http")
|
||||
return (
|
||||
<Anchor
|
||||
key={`${item.title}-${item.href}`}
|
||||
activeClassName="text-primary dark:text-accent md:font-semibold font-medium"
|
||||
absolute
|
||||
className="flex items-center gap-1 text-foreground/80 hover:text-foreground transition-colors"
|
||||
className="text-foreground/80 hover:text-foreground flex items-center gap-1 transition-colors"
|
||||
href={item.href}
|
||||
target={isExternal ? "_blank" : undefined}
|
||||
rel={isExternal ? "noopener noreferrer" : undefined}
|
||||
>
|
||||
{item.title}
|
||||
{isExternal && <ArrowUpRight className="w-4 h-4 text-foreground/80" />}
|
||||
{isExternal && <ArrowUpRight className="text-foreground/80 h-4 w-4" />}
|
||||
</Anchor>
|
||||
);
|
||||
return isSheet ? (
|
||||
<SheetClose key={item.title + item.href} asChild>
|
||||
{Comp}
|
||||
</SheetClose>
|
||||
) : (
|
||||
Comp
|
||||
);
|
||||
)
|
||||
})}
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
// Mobile Collapsible NavMenu — vertical list items
|
||||
function NavMenuCollapsible({ onItemClick }: { onItemClick: () => void }) {
|
||||
const { navbar } = docuConfig
|
||||
|
||||
return (
|
||||
<>
|
||||
{navbar?.menu?.map((item) => {
|
||||
const isExternal = item.href.startsWith("http")
|
||||
return (
|
||||
<li key={item.title + item.href}>
|
||||
<Anchor
|
||||
activeClassName="text-primary dark:text-accent font-semibold"
|
||||
absolute
|
||||
className="text-foreground/80 hover:text-foreground hover:bg-muted flex w-full items-center justify-between gap-2 rounded-md px-3 py-2.5 text-sm font-medium transition-colors"
|
||||
href={item.href}
|
||||
target={isExternal ? "_blank" : undefined}
|
||||
rel={isExternal ? "noopener noreferrer" : undefined}
|
||||
onClick={onItemClick}
|
||||
>
|
||||
{item.title}
|
||||
{isExternal && <ArrowUpRight className="text-foreground/60 h-4 w-4 shrink-0" />}
|
||||
</Anchor>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user