Menu
trovecn/menuFreeComponent
Base UI menu with submenus, checkbox/radio items, and a spring-driven popup.
Install it
npx shadcn@latest add https://trovecn.dev/r/menu.jsonSource
1"use client";23import {4 Children,5 cloneElement,6 createContext,7 isValidElement,8 useContext,9 useEffect,10 useMemo,11 useRef,12 type ComponentProps,13 type ReactElement,14 type ReactNode,15} from "react";16import { Menu as MenuPrimitive } from "@base-ui/react/menu";17import { motion, AnimatePresence, useReducedMotion } from "motion/react";18import { CheckIcon, ChevronRightIcon } from "lucide-react";1920import { cn } from "@/lib/utils";21import { spring } from "@/lib/springs";22import { useProximityHover, proximityHoverWashClassName } from "@/hooks/use-proximity-hover";2324// ─── Proximity hover ─────────────────────────────────────────────────────────25// Every MenuContent/MenuSubContent popup owns one useProximityHover instance26// — the same measured-rect hover wash Accordion/Tabs use, scoped to that27// popup's own items so a submenu's pill never reaches into its parent's.28// Base UI's own `data-highlighted` (keyboard nav and pointer hover both set29// it) still drives each row's text color, same split Accordion uses between30// its pill-owns-background and item-owns-text-color.3132interface MenuProximityContextValue {33 registerItem: (index: number, element: HTMLElement | null) => void;34}3536const MenuProximityContext = createContext<MenuProximityContextValue | null>(null);3738/** Position for proximity hover — auto-assigned by MenuContent's child walk; only present on items it recognized as interactive rows. */39type MenuIndexProp = { _index?: number };4041function useMenuItemRegistration(ref: React.RefObject<HTMLElement | null>, index?: number) {42 const ctx = useContext(MenuProximityContext);43 useEffect(() => {44 if (index === undefined || !ctx) return;45 ctx.registerItem(index, ref.current);46 return () => ctx.registerItem(index, null);47 // eslint-disable-next-line react-hooks/exhaustive-deps48 }, [index, ctx]);49}5051/**52 * Auto-indexes MenuContent's children so callers never hand-thread an index53 * just for proximity hover — mirrors Accordion's indexedChildren, but as a54 * recursive walk (not a flat Children.map) since indexable rows can sit one55 * level down inside a MenuGroup/MenuRadioGroup. MenuSub's trigger is indexed56 * as a row in *this* popup, but Base UI requires it nested one level inside57 * MenuSub (alongside the portaled SubContent), so that level is unwrapped58// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from trovecn
All 26 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | trovecn | Components | Free | 3 deps | |
| Agent Activityagent-activity | trovecn | Components | Free | 2 deps | |
| Approval Requestapproval-request | trovecn | Components | Free | 2 deps | |
| Buttonbutton | trovecn | Components | Free | 2 deps | |
| Checkboxcheckbox | trovecn | Components | Free | 2 deps | |
| Checkbox Groupcheckbox-group | trovecn | Components | Free | 2 deps | |
| Comboboxcombobox | trovecn | Components | Free | 3 deps | |
| Comparison Slidercomparison-slider | trovecn | Components | Free | 1 dep |
