mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
UI: Disable hover to open navbar menu by default, can be enabled via nav.enableHoverToOpen
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Disable hover to open navbar menu by default, can be enabled via `nav.enableHoverToOpen`
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import * as React from 'react';
|
||||
import * as Primitive from '@radix-ui/react-navigation-menu';
|
||||
import { cn } from '@/utils/cn';
|
||||
@@ -6,7 +7,20 @@ const NavigationMenu = Primitive.Root;
|
||||
|
||||
const NavigationMenuList = Primitive.List;
|
||||
|
||||
const NavigationMenuItem = Primitive.Item;
|
||||
const NavigationMenuItem = React.forwardRef<
|
||||
React.ComponentRef<typeof Primitive.NavigationMenuItem>,
|
||||
React.ComponentPropsWithoutRef<typeof Primitive.NavigationMenuItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<Primitive.NavigationMenuItem
|
||||
ref={ref}
|
||||
className={cn('list-none', className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Primitive.NavigationMenuItem>
|
||||
));
|
||||
|
||||
NavigationMenuItem.displayName = Primitive.NavigationMenuItem.displayName;
|
||||
|
||||
const NavigationMenuTrigger = React.forwardRef<
|
||||
React.ComponentRef<typeof Primitive.Trigger>,
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { replaceOrDefault } from '@/layouts/shared';
|
||||
import { type NavOptions, replaceOrDefault } from '@/layouts/shared';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { getLinks, type BaseLayoutProps } from './shared';
|
||||
import { NavProvider, Title } from '@/components/layout/nav';
|
||||
import {
|
||||
NavigationMenuContent,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuList,
|
||||
NavigationMenuTrigger,
|
||||
} from '@/components/ui/navigation-menu';
|
||||
import { NavigationMenuList } from '@/components/ui/navigation-menu';
|
||||
import {
|
||||
Navbar,
|
||||
NavbarLink,
|
||||
NavbarMenu,
|
||||
NavbarMenuContent,
|
||||
NavbarMenuItem,
|
||||
NavbarMenuLink,
|
||||
NavbarMenuTrigger,
|
||||
} from '@/layouts/home/navbar';
|
||||
import { type LinkItemType } from '@/layouts/links';
|
||||
@@ -28,12 +23,27 @@ import {
|
||||
LanguageToggleText,
|
||||
} from '@/components/layout/language-toggle';
|
||||
import { ChevronDown, Languages } from 'lucide-react';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { SearchOnly } from '@/contexts/search';
|
||||
import Link from 'fumadocs-core/link';
|
||||
import { MenuLinkItem } from '@/layouts/home/menu';
|
||||
import {
|
||||
Menu,
|
||||
MenuContent,
|
||||
MenuLinkItem,
|
||||
MenuTrigger,
|
||||
} from '@/layouts/home/menu';
|
||||
|
||||
export type HomeLayoutProps = BaseLayoutProps & HTMLAttributes<HTMLElement>;
|
||||
export interface HomeLayoutProps
|
||||
extends BaseLayoutProps,
|
||||
HTMLAttributes<HTMLElement> {
|
||||
nav?: Partial<
|
||||
NavOptions & {
|
||||
/**
|
||||
* Open mobile menu when hovering the trigger
|
||||
*/
|
||||
enableHoverToOpen?: boolean;
|
||||
}
|
||||
>;
|
||||
}
|
||||
|
||||
export function HomeLayout(props: HomeLayoutProps) {
|
||||
const {
|
||||
@@ -68,7 +78,7 @@ function Header({
|
||||
i18n = false,
|
||||
finalLinks,
|
||||
disableThemeSwitch,
|
||||
}: BaseLayoutProps & {
|
||||
}: HomeLayoutProps & {
|
||||
finalLinks: LinkItemType[];
|
||||
}) {
|
||||
const navItems = finalLinks.filter((item) =>
|
||||
@@ -106,22 +116,17 @@ function Header({
|
||||
<NavbarLinkItem
|
||||
key={i}
|
||||
item={item}
|
||||
className="-me-1.5 list-none max-lg:hidden"
|
||||
className="-me-1.5 max-lg:hidden"
|
||||
/>
|
||||
))}
|
||||
<NavigationMenuItem className="list-none lg:hidden">
|
||||
<NavigationMenuTrigger
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
size: 'icon',
|
||||
color: 'ghost',
|
||||
}),
|
||||
'group -me-2',
|
||||
)}
|
||||
<Menu className="lg:hidden">
|
||||
<MenuTrigger
|
||||
className="group -me-2"
|
||||
enableHover={nav.enableHoverToOpen}
|
||||
>
|
||||
<ChevronDown className="size-3 transition-transform duration-300 group-data-[state=open]:rotate-180" />
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent className="flex flex-col p-4 sm:flex-row sm:items-center sm:justify-end">
|
||||
</MenuTrigger>
|
||||
<MenuContent className="sm:flex-row sm:items-center sm:justify-end">
|
||||
{menuItems
|
||||
.filter((item) => !isSecondary(item))
|
||||
.map((item, i) => (
|
||||
@@ -141,8 +146,8 @@ function Header({
|
||||
) : null}
|
||||
{!disableThemeSwitch ? <ThemeToggle /> : null}
|
||||
</div>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
</div>
|
||||
</Navbar>
|
||||
);
|
||||
@@ -164,7 +169,7 @@ function NavbarLinkItem({
|
||||
const { banner, footer, ...rest } = child.menu ?? {};
|
||||
|
||||
return (
|
||||
<NavbarMenuItem key={j} href={child.url} {...rest}>
|
||||
<NavbarMenuLink key={j} href={child.url} {...rest}>
|
||||
{banner ??
|
||||
(child.icon ? (
|
||||
<div className="w-fit rounded-md border bg-fd-muted p-1 [&_svg]:size-4">
|
||||
@@ -178,7 +183,7 @@ function NavbarLinkItem({
|
||||
</p>
|
||||
) : null}
|
||||
{footer}
|
||||
</NavbarMenuItem>
|
||||
</NavbarMenuLink>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
'use client';
|
||||
import { BaseLinkItem, type LinkItemType } from '@/layouts/links';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { NavigationMenuLink } from '@/components/ui/navigation-menu';
|
||||
import {
|
||||
NavigationMenuContent,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuTrigger,
|
||||
} from '@/components/ui/navigation-menu';
|
||||
import Link from 'fumadocs-core/link';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import type { ComponentPropsWithoutRef } from 'react';
|
||||
|
||||
const menuItemVariants = cva('', {
|
||||
variants: {
|
||||
@@ -35,22 +41,23 @@ export function MenuLinkItem({
|
||||
if (item.type === 'custom')
|
||||
return <div className={cn('grid', props.className)}>{item.children}</div>;
|
||||
|
||||
if (item.type === 'menu')
|
||||
if (item.type === 'menu') {
|
||||
const header = (
|
||||
<>
|
||||
{item.icon}
|
||||
{item.text}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={cn('mb-4 flex flex-col', props.className)}>
|
||||
<p className="mb-1 text-sm text-fd-muted-foreground">
|
||||
{item.url ? (
|
||||
<NavigationMenuLink asChild>
|
||||
<Link href={item.url}>
|
||||
{item.icon}
|
||||
{item.text}
|
||||
</Link>
|
||||
<Link href={item.url}>{header}</Link>
|
||||
</NavigationMenuLink>
|
||||
) : (
|
||||
<>
|
||||
{item.icon}
|
||||
{item.text}
|
||||
</>
|
||||
header
|
||||
)}
|
||||
</p>
|
||||
{item.items.map((child, i) => (
|
||||
@@ -58,6 +65,7 @@ export function MenuLinkItem({
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationMenuLink asChild>
|
||||
@@ -75,3 +83,44 @@ export function MenuLinkItem({
|
||||
</NavigationMenuLink>
|
||||
);
|
||||
}
|
||||
|
||||
export const Menu = NavigationMenuItem;
|
||||
|
||||
export function MenuTrigger({
|
||||
enableHover = false,
|
||||
...props
|
||||
}: ComponentPropsWithoutRef<typeof NavigationMenuTrigger> & {
|
||||
/**
|
||||
* Enable hover to trigger
|
||||
*/
|
||||
enableHover?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<NavigationMenuTrigger
|
||||
{...props}
|
||||
onPointerMove={enableHover ? undefined : (e) => e.preventDefault()}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
size: 'icon',
|
||||
color: 'ghost',
|
||||
}),
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</NavigationMenuTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
export function MenuContent(
|
||||
props: ComponentPropsWithoutRef<typeof NavigationMenuContent>,
|
||||
) {
|
||||
return (
|
||||
<NavigationMenuContent
|
||||
{...props}
|
||||
className={cn('flex flex-col p-4', props.className)}
|
||||
>
|
||||
{props.children}
|
||||
</NavigationMenuContent>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,23 @@ export function NavbarMenuTrigger(props: NavigationMenuTriggerProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const linkVariants = cva(undefined, {
|
||||
export function NavbarMenuLink(props: LinkProps) {
|
||||
return (
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-col gap-2 rounded-lg border bg-fd-card p-3 transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
);
|
||||
}
|
||||
|
||||
const linkVariants = cva('', {
|
||||
variants: {
|
||||
variant: {
|
||||
main: navItemVariants(),
|
||||
@@ -101,7 +117,7 @@ export function NavbarLink({
|
||||
...props
|
||||
}: ComponentProps<typeof BaseLinkItem> & VariantProps<typeof linkVariants>) {
|
||||
return (
|
||||
<NavigationMenuItem className="list-none">
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink asChild>
|
||||
<BaseLinkItem
|
||||
{...props}
|
||||
@@ -114,17 +130,3 @@ export function NavbarLink({
|
||||
</NavigationMenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
export function NavbarMenuItem(props: LinkProps) {
|
||||
return (
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-col gap-2 rounded-lg border bg-fd-card p-3 transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground',
|
||||
props.className,
|
||||
)}
|
||||
/>
|
||||
</NavigationMenuLink>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user