Nested Menu
gaia/nested-menuFreeComponent
A hover-activated nested menu component for multi-level navigation with smooth animations.
Install it
npx shadcn@latest add https://ui.heygaia.io/r/nested-menu.jsonSource
1"use client";23import * as React from "react";4import { useState, useCallback, useRef, useEffect } from "react";5import * as PopoverPrimitive from "@radix-ui/react-popover";6import { cn } from "@/lib/utils";78// ============================================================================9// Types10// ============================================================================1112export interface NestedMenuItem {13 /** Unique key for the item */14 key: string;15 /** Display label */16 label: string;17 /** Icon component */18 icon?: React.ComponentType<{ className?: string }>;19 /** Click handler */20 onSelect?: () => void;21 /** Whether this item has a submenu */22 hasSubmenu?: boolean;23 /** Submenu items (if hasSubmenu is true) */24 submenuItems?: NestedMenuItem[];25 /** Color variant */26 variant?: "default" | "danger";27 /** Additional className for custom styling */28 className?: string;29 /** Icon color (CSS color value) */30 iconColor?: string;31 /** Separator after this item */32 separator?: boolean;33}3435export interface NestedMenuSectionProps {36 /** Section title */37 title?: string;38 /** Items in this section */39 items: NestedMenuItem[];40 /** Show divider after section */41 showDivider?: boolean;42}4344// ============================================================================45// Hook: useNestedMenu46// ============================================================================4748export function useNestedMenu() {49 const [isOpen, setIsOpen] = useState(false);50 const [itemRef, setItemRef] = useState<HTMLElement | null>(null);51 const timeoutRef = useRef<NodeJS.Timeout | null>(null);5253 const handleMouseEnter = useCallback((e: React.MouseEvent<HTMLElement>) => {54 if (timeoutRef.current) {55 clearTimeout(timeoutRef.current);56 timeoutRef.current = null;57 }58 setItemRef(e.currentTarget as HTMLElement);59 setIsOpen(true);60 }, []);6162 const handleMouseLeave = useCallback(() => {63 // Longer delay to allow moving to submenu64 timeoutRef.current = setTimeout(() => {65 setIsOpen(false);66 }, 300);67 }, []);6869 const cancelClose = useCallback(() => {70 if (timeoutRef.current) {71 clearTimeout(timeoutRef.current);72 timeoutRef.current = null;73 }74 }, []);7576 useEffect(() => {77 return () => {78 if (timeoutRef.current) {79 clearTimeout(timeoutRef.current);80 }81 };82 }, []);8384 return {85 isOpen,86 setIsOpen,87 itemRef,88 handleMouseEnter,89 handleMouseLeave,90 cancelClose,91 };92}9394// ============================================================================95// … truncated
What it pulls in
npm packages
Files it writes
More from gaia
All 26 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Area Chartarea-chart | gaia | Components | Free | 1 dep | |
| Author Tooltipauthor-tooltip | gaia | Components | Free | no deps | |
| Bar Chartbar-chart | gaia | Components | Free | 1 dep | |
| Composercomposer | gaia | Components | Free | no deps | |
| Email Compose Cardemail-compose-card | gaia | Components | Free | no deps | |
| Gauge Chartgauge-chart | gaia | Components | Free | 1 dep | |
| GitHub Stars Buttongithub-stars-button | gaia | Components | Free | 1 dep | |
| Holo Cardholo-card | gaia | Components | Free | 1 dep · 2 files |
