mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
225 lines
7.1 KiB
TypeScript
225 lines
7.1 KiB
TypeScript
import {useRef, useState, FC, ReactNode} from "react";
|
|
import {
|
|
link,
|
|
Navbar as NextUINavbar,
|
|
NavbarContent,
|
|
NavbarMenu,
|
|
NavbarMenuToggle,
|
|
NavbarBrand,
|
|
NavbarItem,
|
|
Input,
|
|
Link,
|
|
Button,
|
|
Kbd,
|
|
Dropdown,
|
|
DropdownMenu,
|
|
DropdownItem,
|
|
DropdownTrigger,
|
|
} from "@nextui-org/react";
|
|
import dynamic from "next/dynamic";
|
|
import {ChevronDownIcon, LinkIcon} from "@nextui-org/shared-icons";
|
|
import {clsx} from "@nextui-org/shared-utils";
|
|
import NextLink from "next/link";
|
|
import {useRouter} from "next/router";
|
|
import {includes} from "lodash";
|
|
import {SearchIcon} from "@nextui-org/shared-icons";
|
|
import {motion, AnimatePresence} from "framer-motion";
|
|
|
|
import {Route} from "@/libs/docs/page";
|
|
import {LargeLogo, ThemeSwitch} from "@/components";
|
|
import {TwitterIcon, GithubIcon, DiscordIcon, HeartFilledIcon} from "@/components/icons";
|
|
import {useIsMounted} from "@/hooks/use-is-mounted";
|
|
import {isActive} from "@/utils/links";
|
|
import RouterEvents from "@/libs/router-events";
|
|
|
|
export interface NavbarProps {
|
|
routes: Route[];
|
|
tag?: string;
|
|
slug?: string;
|
|
children?: ReactNode;
|
|
}
|
|
|
|
const DocsSidebar = dynamic(
|
|
() => import("@/components/docs/sidebar").then((mod) => mod.DocsSidebar),
|
|
{ssr: false},
|
|
);
|
|
|
|
export const Navbar: FC<NavbarProps> = ({children, routes, slug, tag}) => {
|
|
const [isMenuOpen, setIsMenuOpen] = useState<boolean | undefined>(false);
|
|
|
|
const ref = useRef(null);
|
|
const isMounted = useIsMounted();
|
|
|
|
const router = useRouter();
|
|
|
|
const searchInput = (
|
|
<Input
|
|
aria-label="Search"
|
|
classNames={{
|
|
inputWrapper: "bg-default-400/20 dark:bg-default-500/20",
|
|
input: "text-sm",
|
|
}}
|
|
endContent={
|
|
<Kbd className="hidden lg:inline-block" keys={["command"]}>
|
|
K
|
|
</Kbd>
|
|
}
|
|
labelPlacement="outside"
|
|
placeholder="Search..."
|
|
startContent={
|
|
<SearchIcon className="text-base text-default-400 pointer-events-none flex-shrink-0" />
|
|
}
|
|
type="search"
|
|
/>
|
|
);
|
|
|
|
RouterEvents.on("routeChangeStart", () => {
|
|
if (isMenuOpen) setIsMenuOpen(false);
|
|
});
|
|
|
|
return (
|
|
<NextUINavbar
|
|
ref={ref}
|
|
className="z-[100001]"
|
|
isMenuOpen={isMenuOpen}
|
|
maxWidth="xl"
|
|
position="sticky"
|
|
onMenuOpenChange={setIsMenuOpen}
|
|
>
|
|
<NavbarContent className="basis-1/5 sm:basis-full" justify="start">
|
|
<NavbarBrand className="gap-3 max-w-fit">
|
|
<NextLink className="flex justify-start items-center gap-3" href="/">
|
|
<LargeLogo className="h-5 md:h-6" />
|
|
</NextLink>
|
|
{ref.current ? (
|
|
<Dropdown placement="bottom-start" portalContainer={ref.current}>
|
|
<AnimatePresence>
|
|
{isMounted && (
|
|
<motion.div animate={{opacity: 1}} exit={{opacity: 0}} initial={{opacity: 0}}>
|
|
<DropdownTrigger>
|
|
<Button
|
|
className="hidden w-[65px] sm:flex gap-0.5"
|
|
endIcon={<ChevronDownIcon className="text-xs" />}
|
|
radius="full"
|
|
size="xs"
|
|
variant="flat"
|
|
>
|
|
v2.0.0
|
|
</Button>
|
|
</DropdownTrigger>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
<DropdownMenu
|
|
aria-label="NextUI versions"
|
|
defaultSelectedKeys={["v2"]}
|
|
selectionMode="single"
|
|
>
|
|
<DropdownItem key="v2">v2.0.0</DropdownItem>
|
|
<DropdownItem key="v1" endContent={<LinkIcon />}>
|
|
v1.0.0
|
|
</DropdownItem>
|
|
</DropdownMenu>
|
|
</Dropdown>
|
|
) : (
|
|
<div className="w-[65px]" />
|
|
)}
|
|
</NavbarBrand>
|
|
<div className="hidden lg:flex gap-4 ml-10 justify-start">
|
|
<NavbarItem>
|
|
<NextLink
|
|
className={clsx(
|
|
link({color: "foreground"}),
|
|
"data-[active=true]:text-primary data-[active=true]:font-medium",
|
|
)}
|
|
color="foreground"
|
|
data-active={
|
|
!!(
|
|
isActive(router.pathname, "/docs/[[...slug]]") &&
|
|
!includes(router.asPath, "components")
|
|
)
|
|
}
|
|
href="/docs/guide/introduction"
|
|
>
|
|
Docs
|
|
</NextLink>
|
|
</NavbarItem>
|
|
<NavbarItem>
|
|
<NextLink
|
|
className={clsx(
|
|
link({color: "foreground"}),
|
|
"data-[active=true]:text-primary data-[active=true]:font-medium",
|
|
)}
|
|
color="foreground"
|
|
data-active={includes(router.asPath, "components")}
|
|
href="/docs/components/avatar"
|
|
>
|
|
Components
|
|
</NextLink>
|
|
</NavbarItem>
|
|
<NavbarItem>
|
|
<NextLink
|
|
className={clsx(
|
|
link({color: "foreground"}),
|
|
"data-[active=true]:text-primary data-[active=true]:font-medium",
|
|
)}
|
|
color="foreground"
|
|
data-active={router.asPath === "/figma"}
|
|
href="/figma"
|
|
>
|
|
Figma
|
|
</NextLink>
|
|
</NavbarItem>
|
|
</div>
|
|
</NavbarContent>
|
|
|
|
<NavbarContent className="flex w-full gap-3 sm:hidden" justify="end">
|
|
<Link isExternal href="https://github.com/nextui-org/nextui">
|
|
<GithubIcon className="text-default-600 dark:text-default-500" />
|
|
</Link>
|
|
<ThemeSwitch />
|
|
</NavbarContent>
|
|
|
|
<NavbarContent className="basis-1/5 sm:basis-full" justify="end">
|
|
<NavbarItem className="hidden sm:flex gap-2">
|
|
<Link isExternal href="https://twitter.com/getnextui">
|
|
<TwitterIcon className="text-default-600 dark:text-default-500" />
|
|
</Link>
|
|
<Link isExternal href="https://discord.gg/9b6yyZKmH4">
|
|
<DiscordIcon className="text-default-600 dark:text-default-500" />
|
|
</Link>
|
|
<Link isExternal href="https://github.com/nextui-org/nextui">
|
|
<GithubIcon className="text-default-600 dark:text-default-500" />
|
|
</Link>
|
|
<ThemeSwitch />
|
|
</NavbarItem>
|
|
<NavbarItem className="hidden sm:flex">{searchInput}</NavbarItem>
|
|
<NavbarItem className="hidden sm:flex">
|
|
<Button
|
|
isExternal
|
|
as={Link}
|
|
className="group text-sm font-normal text-default-600 bg-default-400/20 dark:bg-default-500/20"
|
|
href="https://patreon.com/jrgarciadev"
|
|
startIcon={
|
|
<HeartFilledIcon className="text-danger group-data-[hover=true]:animate-heartbeat" />
|
|
}
|
|
variant="flat"
|
|
>
|
|
Sponsor
|
|
</Button>
|
|
</NavbarItem>
|
|
<NavbarMenuToggle
|
|
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
|
className="sm:hidden"
|
|
/>
|
|
</NavbarContent>
|
|
|
|
<NavbarMenu disableAnimation>
|
|
{searchInput}
|
|
<DocsSidebar routes={routes} slug={slug} tag={tag} />
|
|
{children}
|
|
</NavbarMenu>
|
|
</NextUINavbar>
|
|
);
|
|
};
|