feat: add navigation-menu (#31)

* feat: navigation-menu

* fix: export at the end

* fix: rounded-md

* fix: demo design

* rename: NavigationMenuRoot -> NavigationMenu

* feat: update roadmap

* feat: update style for navigation menu

* feat: implement navigation menu component

* feat: implement navigation menu for www

---------

Co-authored-by: shadcn <m@shadcn.com>
This commit is contained in:
Vincent Dusautoir
2023-01-29 18:28:13 +01:00
committed by GitHub
parent 8ccac51527
commit a0b1f89455
21 changed files with 610 additions and 100 deletions

View File

@@ -13,7 +13,7 @@ Beautifully designed components built with Radix UI and Tailwind CSS.
- [ ] Toggle
- [ ] Toggle Group
- [ ] Toolbar
- [ ] Navigation Menu
- [ ] Navigation Menu (Vertical and Submenus)
- [ ] Figma?
## Get Started

View File

@@ -3,8 +3,8 @@ import { AlertDialogDemo } from "@/components/examples/alert-dialog/demo"
import { AspectRatioDemo } from "@/components/examples/aspect-ratio/demo"
import { AvatarDemo } from "@/components/examples/avatar/demo"
import { ButtonDemo } from "@/components/examples/button/demo"
import { ButtonGhost } from "@/components/examples/button/ghost"
import { ButtonDestructive } from "@/components/examples/button/destructive"
import { ButtonGhost } from "@/components/examples/button/ghost"
import { ButtonLink } from "@/components/examples/button/link"
import { ButtonLoading } from "@/components/examples/button/loading"
import { ButtonOutline } from "@/components/examples/button/outline"
@@ -27,6 +27,7 @@ import { InputWithLabel } from "@/components/examples/input/with-label"
import { InputWithText } from "@/components/examples/input/with-text"
import { LabelDemo } from "@/components/examples/label/demo"
import { MenubarDemo } from "@/components/examples/menubar/demo"
import { NavigationMenuDemo } from "@/components/examples/navigation-menu/demo"
import { PopoverDemo } from "@/components/examples/popover/demo"
import { ProgressDemo } from "@/components/examples/progress/demo"
import { RadioGroupDemo } from "@/components/examples/radio-group/demo"
@@ -87,6 +88,7 @@ export const examples = {
InputWithText,
LabelDemo,
MenubarDemo,
NavigationMenuDemo,
PopoverDemo,
ProgressDemo,
RadioGroupDemo,

View File

@@ -0,0 +1,145 @@
"use client"
import * as React from "react"
import Link from "next/link"
import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu"
const components: { title: string; href: string; description: string }[] = [
{
title: "Alert Dialog",
href: "/docs/primitives/alert-dialog",
description:
"A modal dialog that interrupts the user with important content and expects a response.",
},
{
title: "Hover Card",
href: "/docs/primitives/hover-card",
description:
"For sighted users to preview content available behind a link.",
},
{
title: "Progress",
href: "/docs/primitives/progress",
description:
"Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
},
{
title: "Scroll-area",
href: "/docs/primitives/scroll-area",
description: "Visually or semantically separates content.",
},
{
title: "Tabs",
href: "/docs/primitives/tabs",
description:
"A set of layered sections of content—known as tab panels—that are displayed one at a time.",
},
{
title: "Tooltip",
href: "/docs/primitives/tooltip",
description:
"A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
},
]
export function NavigationMenuDemo() {
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Getting started</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
<li className="row-span-3">
<NavigationMenuLink asChild>
<a
className="flex h-full w-full select-none flex-col justify-end rounded-md bg-gradient-to-b from-rose-500 to-indigo-700 p-6 no-underline outline-none focus:shadow-md"
href="/"
>
<Icons.logo className="h-6 w-6 text-white" />
<div className="mt-4 mb-2 text-lg font-medium text-white">
shadcn/ui
</div>
<p className="text-sm leading-tight text-white/90">
Beautifully designed components built with Radix UI and
Tailwind CSS.
</p>
</a>
</NavigationMenuLink>
</li>
<ListItem href="/docs" title="Introduction">
Re-usable components built using Radix UI and Tailwind CSS.
</ListItem>
<ListItem href="/docs/installation" title="Installation">
How to install dependencies and structure your app.
</ListItem>
<ListItem href="/docs/primitives/typography" title="Typography">
Styles for headings, paragraphs, lists...etc
</ListItem>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[600px] gap-3 p-4 md:grid-cols-2">
{components.map((component) => (
<ListItem
key={component.title}
title={component.title}
href={component.href}
>
{component.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href="/docs" legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
Documentation
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)
}
const ListItem = React.forwardRef<
React.ElementRef<"a">,
React.ComponentPropsWithoutRef<"a">
>(({ className, title, children, ...props }, ref) => {
return (
<li>
<NavigationMenuLink asChild>
<a
ref={ref}
className={cn(
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-slate-100 focus:bg-slate-100 dark:hover:bg-slate-700 dark:focus:bg-slate-700",
className
)}
{...props}
>
<div className="text-sm font-medium leading-none">{title}</div>
<p className="text-sm leading-snug text-slate-500 line-clamp-2 dark:text-slate-400">
{children}
</p>
</a>
</NavigationMenuLink>
</li>
)
})
ListItem.displayName = "ListItem"

View File

@@ -2,111 +2,131 @@
import * as React from "react"
import Link from "next/link"
import { useSelectedLayoutSegment } from "next/navigation"
import { MainNavItem } from "types/nav"
import { allDocs } from "contentlayer/generated"
import { docsConfig } from "@/config/docs"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { ScrollArea } from "@/components/ui/scroll-area"
interface MainNavProps {
items?: MainNavItem[]
children?: React.ReactNode
}
export function MainNav({ items, children }: MainNavProps) {
const segment = useSelectedLayoutSegment()
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu"
import { buttonVariants } from "./ui/button"
import { Separator } from "./ui/separator"
export function MainNav() {
return (
<div className="flex gap-6 md:gap-10">
<Link href="/" className="hidden items-center space-x-2 md:flex">
<div className="hidden md:flex">
<Link href="/" className="mr-6 flex items-center space-x-2">
<Icons.logo className="h-6 w-6" />
<span className="hidden font-bold sm:inline-block">
{siteConfig.name}
</span>
</Link>
{items?.length ? (
<nav className="hidden gap-6 md:flex">
{items?.map(
(item, index) =>
item.href && (
<Link
key={index}
href={item.href}
className={cn(
"flex items-center text-lg font-semibold text-slate-600 hover:text-slate-900 dark:text-slate-100 sm:text-sm",
item.href.startsWith(`/${segment}`) && "text-slate-900",
item.disabled && "cursor-not-allowed opacity-80"
)}
>
{item.title}
</Link>
)
)}
</nav>
) : null}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="-ml-4 text-base hover:bg-transparent focus:ring-0 md:hidden"
>
<Icons.logo className="mr-2 h-4 w-4" />{" "}
<span className="font-bold">Menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
sideOffset={24}
className="w-[300px] overflow-scroll"
>
<DropdownMenuLabel>
<Link href="/" className="flex items-center">
<Icons.logo className="mr-2 h-4 w-4" /> {siteConfig.name}
</Link>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<ScrollArea className="h-[400px]">
{items?.map(
(item, index) =>
item.href && (
<DropdownMenuItem key={index} asChild>
<Link href={item.href}>{item.title}</Link>
</DropdownMenuItem>
)
)}
{docsConfig.sidebarNav.map((item, index) => (
<DropdownMenuGroup key={index}>
<DropdownMenuSeparator />
<DropdownMenuLabel>{item.title}</DropdownMenuLabel>
<DropdownMenuSeparator />
{item?.items?.length &&
item.items.map((item) => (
<DropdownMenuItem key={item.title} asChild>
{item.href ? (
<Link href={item.href}>{item.title}</Link>
) : (
item.title
)}
</DropdownMenuItem>
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger className="h-9">
Getting started
</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
<li className="row-span-3">
<Link href="/" passHref legacyBehavior>
<NavigationMenuLink
className="flex h-full w-full select-none
flex-col justify-end space-y-2 rounded-md bg-gradient-to-b from-rose-500 to-indigo-700 p-6 no-underline outline-none focus:shadow-md"
>
<div className="text-lg font-medium text-white">
{siteConfig.name}
</div>
<p className="text-sm leading-snug text-white/90">
{siteConfig.description}
</p>
</NavigationMenuLink>
</Link>
</li>
<ListItem href="/docs" title="Introduction">
Re-usable components built using Radix UI and Tailwind CSS.
</ListItem>
<ListItem href="/docs/installation" title="Installation">
How to install dependencies and structure your app.
</ListItem>
<ListItem href="/docs/primitives/typography" title="Typography">
Styles for headings, paragraphs, lists...etc
</ListItem>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger className="h-9">
Components
</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[600px] grid-cols-2 gap-3 p-4">
{allDocs
.filter((doc) => doc.featured)
.map((doc) => (
<ListItem key={doc._id} title={doc.title} href={doc.slug}>
{doc.description}
</ListItem>
))}
</DropdownMenuGroup>
))}
</ScrollArea>
</DropdownMenuContent>
</DropdownMenu>
</ul>
<div className="p-4 pt-0">
<Separator className="mb-4" />
<Link href="/docs/primitives/accordion" passHref legacyBehavior>
<NavigationMenuLink
className={cn(
buttonVariants({ variant: "outline" }),
"w-full dark:hover:bg-slate-700"
)}
>
Browse components
</NavigationMenuLink>
</Link>
</div>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem className="hidden lg:flex">
<Link href={siteConfig.links.github} legacyBehavior passHref>
<NavigationMenuLink
className={cn(navigationMenuTriggerStyle(), "h-9")}
>
GitHub
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>
)
}
const ListItem = React.forwardRef<
React.ElementRef<typeof Link>,
React.ComponentPropsWithoutRef<typeof Link>
>(({ className, title, children, href, ...props }, ref) => {
return (
<li>
<Link href={href} passHref legacyBehavior {...props}>
<NavigationMenuLink
className={cn(
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-slate-100 focus:bg-slate-100 dark:hover:bg-slate-700 dark:focus:bg-slate-700",
className
)}
>
<div className="text-sm font-medium leading-none">{title}</div>
<p className="text-sm leading-snug text-slate-500 line-clamp-2 dark:text-slate-400">
{children}
</p>
</NavigationMenuLink>
</Link>
</li>
)
})
ListItem.displayName = "ListItem"

View File

@@ -0,0 +1,80 @@
"use client"
import * as React from "react"
import Link from "next/link"
import { docsConfig } from "@/config/docs"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { ScrollArea } from "@/components/ui/scroll-area"
export function MobileNav() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="-ml-4 text-base hover:bg-transparent focus:ring-0 focus:ring-offset-0 md:hidden"
>
<Icons.logo className="mr-2 h-4 w-4" />{" "}
<span className="font-bold">Menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
sideOffset={24}
alignOffset={4}
className="w-[300px] overflow-scroll"
>
<DropdownMenuItem asChild>
<Link href="/" className="flex items-center">
<Icons.logo className="mr-2 h-4 w-4" /> {siteConfig.name}
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<ScrollArea className="h-[400px]">
{docsConfig.sidebarNav?.map(
(item, index) =>
item.href && (
<DropdownMenuItem key={index} asChild>
<Link href={item.href}>{item.title}</Link>
</DropdownMenuItem>
)
)}
{docsConfig.sidebarNav.map((item, index) => (
<DropdownMenuGroup key={index}>
<DropdownMenuSeparator
className={cn({
hidden: index === 0,
})}
/>
<DropdownMenuLabel>{item.title}</DropdownMenuLabel>
<DropdownMenuSeparator className="-mx-2" />
{item?.items?.length &&
item.items.map((item) => (
<DropdownMenuItem key={item.title} asChild>
{item.href ? (
<Link href={item.href}>{item.title}</Link>
) : (
item.title
)}
</DropdownMenuItem>
))}
</DropdownMenuGroup>
))}
</ScrollArea>
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -21,7 +21,7 @@ export function DocsSearch({ className, ...props }: DocsSearchProps) {
<Input
type="search"
placeholder="Search documentation..."
className="h-9 sm:w-64 sm:pr-12"
className="h-9 sm:pr-12 md:w-40 lg:w-64"
disabled
/>
<kbd className="pointer-events-none absolute top-2 right-1.5 hidden h-5 select-none items-center gap-1 rounded border border-slate-100 bg-slate-100 px-1.5 font-mono text-[10px] font-medium text-slate-600 opacity-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400 sm:flex">

View File

@@ -4,16 +4,17 @@ import { docsConfig } from "@/config/docs"
import { siteConfig } from "@/config/site"
import { Icons } from "@/components/icons"
import { MainNav } from "@/components/main-nav"
import { MobileNav } from "@/components/mobile-nav"
import { ModeToggle } from "@/components/mode-toggle"
import { DocsSearch } from "@/components/search"
import { DocsSidebarNav } from "@/components/sidebar-nav"
import { buttonVariants } from "@/components/ui/button"
export function SiteHeader() {
return (
<header className="sticky top-0 z-40 w-full border-b border-b-slate-200 bg-white dark:border-b-slate-700 dark:bg-slate-900">
<div className="container flex h-16 items-center space-x-4 sm:justify-between sm:space-x-0">
<MainNav items={docsConfig.mainNav} />
<div className="container flex h-16 items-center">
<MainNav />
<MobileNav />
<div className="flex flex-1 items-center justify-end space-x-4">
<div className="hidden flex-1 sm:grow-0 md:flex">
<DocsSearch />

View File

@@ -0,0 +1,128 @@
import * as React from "react"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import { cva } from "class-variance-authority"
import { ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
const NavigationMenu = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Root
ref={ref}
className={cn(
"relative z-10 flex flex-1 items-center justify-center",
className
)}
{...props}
>
{children}
<NavigationMenuViewport />
</NavigationMenuPrimitive.Root>
))
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
const NavigationMenuList = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.List>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.List
ref={ref}
className={cn(
"group flex flex-1 list-none items-center justify-center",
className
)}
{...props}
/>
))
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
const NavigationMenuItem = NavigationMenuPrimitive.Item
const navigationMenuTriggerStyle = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:bg-slate-100 disabled:opacity-50 dark:focus:bg-slate-800 disabled:pointer-events-none bg-transparent hover:bg-slate-100 dark:hover:bg-slate-800 dark:text-slate-100 dark:hover:text-slate-100 data-[state=open]:bg-slate-50 dark:data-[state=open]:bg-slate-800 h-10 py-2 px-4 group"
)
const NavigationMenuTrigger = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Trigger
ref={ref}
className={cn(navigationMenuTriggerStyle(), "group", className)}
{...props}
>
{children}{" "}
<ChevronDown
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
aria-hidden="true"
/>
</NavigationMenuPrimitive.Trigger>
))
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
const NavigationMenuContent = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Content
ref={ref}
className={cn(
"absolute top-0 left-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=to-start]:slide-out-to-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=from-end]:slide-in-from-right-52 md:w-auto",
className
)}
{...props}
/>
))
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
const NavigationMenuLink = NavigationMenuPrimitive.Link
const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-slate-200 bg-white shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:zoom-in-90 data-[state=closed]:zoom-out-95 dark:border-slate-700 dark:bg-slate-800 md:w-[var(--radix-navigation-menu-viewport-width)]",
className
)}
ref={ref}
{...props}
/>
</div>
))
NavigationMenuViewport.displayName =
NavigationMenuPrimitive.Viewport.displayName
const NavigationMenuIndicator = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Indicator
ref={ref}
className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=visible]:fade-in data-[state=hidden]:fade-out",
className
)}
{...props}
>
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-slate-200 shadow-md dark:bg-slate-800" />
</NavigationMenuPrimitive.Indicator>
))
NavigationMenuIndicator.displayName =
NavigationMenuPrimitive.Indicator.displayName
export {
navigationMenuTriggerStyle,
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuContent,
NavigationMenuTrigger,
NavigationMenuLink,
NavigationMenuIndicator,
NavigationMenuViewport,
}

View File

@@ -120,6 +120,11 @@ export const docsConfig: DocsConfig = {
href: "/docs/primitives/menubar",
items: [],
},
{
title: "Navigation Menu",
href: "/docs/primitives/navigation-menu",
items: [],
},
{
title: "Popover",
href: "/docs/primitives/popover",

View File

@@ -1,6 +1,7 @@
---
title: Alert Dialog
description: A modal dialog that interrupts the user with important content and expects a response.
featured: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/alert-dialog
api: https://www.radix-ui.com/docs/primitives/components/alert-dialog#api-reference

View File

@@ -1,6 +1,7 @@
---
title: Button
description: Displays a button or a component that looks like a button.
featured: true
---
<ComponentExample src="/components/examples/button/demo.tsx">
@@ -71,6 +72,7 @@ import { Button } from "@/components/ui/button"
---
---
### Link
<ComponentExample src="/components/examples/button/link.tsx">

View File

@@ -1,6 +1,7 @@
---
title: Collapsible
description: An interactive component which expands/collapses a panel.
featured: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/collapsible
api: https://www.radix-ui.com/docs/primitives/components/collapsible#api-reference

View File

@@ -1,6 +1,7 @@
---
title: Dialog
description: A modal dialog that interrupts the user with important content and expects a response.
featured: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/dialog
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference

View File

@@ -1,6 +1,7 @@
---
title: Dropdown Menu
description: Displays a menu to the user — such as a set of actions or functions — triggered by a button.
featured: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/dropdown-menu
api: https://www.radix-ui.com/docs/primitives/components/dropdown-menu#api-reference

View File

@@ -0,0 +1,76 @@
---
title: Navigation Menu
description: A collection of links for navigating websites.
radix:
link: https://www.radix-ui.com/docs/primitives/components/navigation-menu
api: https://www.radix-ui.com/docs/primitives/components/navigation-menu#api-reference
---
<ComponentExample src="/components/examples/navigation-menu/demo.tsx">
<NavigationMenuDemo />
</ComponentExample>
## Installation
1. Install the `@radix-ui/react-navigation-menu` component from radix-ui:
```bash
npm install @radix-ui/react-navigation-menu
```
2. Copy and paste the following code into your project.
<ComponentSource src="/components/ui/navigation-menu.tsx" />
<Callout>
This is the `<NavigationMenu />` primitive. You can place it in a file at
`components/ui/navigation-menu.tsx`.
</Callout>
## Usage
```tsx
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuIndicator,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
NavigationMenuViewport,
} from "@/components/ui/navigation-menu"
```
```tsx
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item One</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink>Link</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
```
### Link Component
When using the Next.js `<Link />` component, you can use `navigationMenuTriggerStyle()` to apply the correct styles to the trigger.
```tsx
import { navigationMenuTriggerStyle } from "@/components/ui/navigation-menu"
```
```tsx {3-5}
<NavigationMenuItem>
<Link href="/docs" legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
Documentation
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
```
See also the [Radix UI documentation](https://www.radix-ui.com/docs/primitives/components/navigation-menu#with-client-side-routing) for handling client side routing.

View File

@@ -1,6 +1,7 @@
---
title: Select
description: Displays a list of options for the user to pick from—triggered by a button.
featured: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/select
api: https://www.radix-ui.com/docs/primitives/components/select#api-reference

View File

@@ -60,6 +60,11 @@ export const Doc = defineDocumentType(() => ({
type: "nested",
of: RadixProperties,
},
featured: {
type: "boolean",
default: false,
required: false,
},
},
computedFields,
}))

View File

@@ -24,6 +24,7 @@
"@radix-ui/react-hover-card": "^1.0.3",
"@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-menubar": "^1.0.0",
"@radix-ui/react-navigation-menu": "^1.1.1",
"@radix-ui/react-popover": "^1.0.2",
"@radix-ui/react-progress": "^1.0.1",
"@radix-ui/react-radio-group": "^1.1.0",

View File

@@ -24,6 +24,7 @@
"dependencies": {
"@babel/core": "^7.20.7",
"@ianvs/prettier-plugin-sort-imports": "^3.7.1",
"@tailwindcss/line-clamp": "^0.4.2",
"eslint": "^8.31.0",
"eslint-config-next": "13.0.0",
"eslint-config-prettier": "^8.3.0",

41
pnpm-lock.yaml generated
View File

@@ -8,6 +8,7 @@ importers:
'@commitlint/cli': ^17.3.0
'@commitlint/config-conventional': ^17.3.0
'@ianvs/prettier-plugin-sort-imports': ^3.7.1
'@tailwindcss/line-clamp': ^0.4.2
'@types/node': ^18.11.9
'@types/react': 18.0.15
'@types/react-dom': 18.0.6
@@ -29,6 +30,7 @@ importers:
dependencies:
'@babel/core': 7.20.7
'@ianvs/prettier-plugin-sort-imports': 3.7.1_prettier@2.8.1
'@tailwindcss/line-clamp': 0.4.2_tailwindcss@3.2.4
eslint: 8.31.0
eslint-config-next: 13.0.0_wogtpudmlxya2leoxia5qf2rl4
eslint-config-prettier: 8.5.0_eslint@8.31.0
@@ -67,6 +69,7 @@ importers:
'@radix-ui/react-hover-card': ^1.0.3
'@radix-ui/react-label': ^2.0.0
'@radix-ui/react-menubar': ^1.0.0
'@radix-ui/react-navigation-menu': ^1.1.1
'@radix-ui/react-popover': ^1.0.2
'@radix-ui/react-progress': ^1.0.1
'@radix-ui/react-radio-group': ^1.1.0
@@ -129,6 +132,7 @@ importers:
'@radix-ui/react-hover-card': 1.0.3_ib3m5ricvtkl2cll7qpr2f6lvq
'@radix-ui/react-label': 2.0.0_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-menubar': 1.0.0_ib3m5ricvtkl2cll7qpr2f6lvq
'@radix-ui/react-navigation-menu': 1.1.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-popover': 1.0.2_ib3m5ricvtkl2cll7qpr2f6lvq
'@radix-ui/react-progress': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-radio-group': 1.1.0_biqbaboplfbrettd7655fr4n2y
@@ -2014,6 +2018,31 @@ packages:
- '@types/react'
dev: false
/@radix-ui/react-navigation-menu/1.1.1_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-Khgf+LwqYfUpbFAHcFPDMj6ZrWxnwCgC96liLYwE48x9YJbXGlutOWzZaSzrgl82xS+PwoPLQunfDe/i4ZITRA==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
'@babel/runtime': 7.20.6
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
'@radix-ui/react-direction': 1.0.0_react@18.2.0
'@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-id': 1.0.0_react@18.2.0
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
'@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
'@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0
'@radix-ui/react-use-previous': 1.0.0_react@18.2.0
'@radix-ui/react-visually-hidden': 1.0.1_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
/@radix-ui/react-popover/1.0.2_ib3m5ricvtkl2cll7qpr2f6lvq:
resolution: {integrity: sha512-4tqZEl9w95R5mlZ/sFdgBnfhCBOEPepLIurBA5kt/qaAhldJ1tNQd0ngr0ET0AHbPotT4mwxMPr7a+MA/wbK0g==}
peerDependencies:
@@ -2498,6 +2527,14 @@ packages:
tslib: 2.4.1
dev: false
/@tailwindcss/line-clamp/0.4.2_tailwindcss@3.2.4:
resolution: {integrity: sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==}
peerDependencies:
tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
dependencies:
tailwindcss: 3.2.4_ra2vnoek4vhbzktaezawwqbin4
dev: false
/@tsconfig/node10/1.0.9:
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
@@ -6166,6 +6203,7 @@ packages:
lilconfig: 2.0.6
postcss: 8.4.20
yaml: 1.10.2
dev: true
/postcss-load-config/3.1.4_ra2vnoek4vhbzktaezawwqbin4:
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
@@ -7068,7 +7106,7 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
dependencies:
tailwindcss: 3.2.4_postcss@8.4.20
tailwindcss: 3.2.4_ra2vnoek4vhbzktaezawwqbin4
dev: false
/tailwindcss/3.2.4_postcss@8.4.20:
@@ -7103,6 +7141,7 @@ packages:
resolve: 1.22.1
transitivePeerDependencies:
- ts-node
dev: true
/tailwindcss/3.2.4_ra2vnoek4vhbzktaezawwqbin4:
resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==}

View File

@@ -32,5 +32,5 @@ module.exports = {
},
},
},
plugins: [require("tailwindcss-animate")],
plugins: [require("tailwindcss-animate"), require("@tailwindcss/line-clamp")],
}