{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "menu",
  "type": "registry:component",
  "title": "Menu",
  "description": "A list of actions that opens from a button, with shortcuts, checks and submenus.",
  "dependencies": [
    "@base-ui/react",
    "tailwind-variants"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/panelui/menu.tsx",
      "type": "registry:component",
      "target": "@components/panelui/menu.tsx",
      "content": "'use client';\n\n/**\n * Menu — a list of actions that opens from a button.\n *\n * ```tsx\n * <Menu>\n *   <Menu.Trigger render={<Button variant=\"outline\" />}>Options</Menu.Trigger>\n *   <Menu.Content>\n *     <Menu.Item onClick={rename}>\n *       <PencilIcon /> Rename <Menu.Shortcut>⌘R</Menu.Shortcut>\n *     </Menu.Item>\n *     <Menu.Sub>\n *       <Menu.SubTrigger><ShareIcon /> Share</Menu.SubTrigger>\n *       <Menu.SubContent>\n *         <Menu.Item>Copy link</Menu.Item>\n *       </Menu.SubContent>\n *     </Menu.Sub>\n *     <Menu.Separator />\n *     <Menu.CheckboxItem checked={pinned} onCheckedChange={setPinned}>Pinned</Menu.CheckboxItem>\n *     <Menu.Item variant=\"destructive\">Delete</Menu.Item>\n *   </Menu.Content>\n * </Menu>\n * ```\n *\n * ## One row shape\n *\n * Every row is the same: an icon, the label, and whatever belongs at the end\n * of it, a shortcut, a badge, a check or the chevron of a submenu. Icons are\n * written as the row's first child and sized by the row, so rows with and\n * without them still line up when every row in a group has one.\n *\n * ## Submenus\n *\n * A submenu opens to the side when its row is hovered or pressed, or with\n * the right arrow key, and closes with the left one. The pointer can cross\n * diagonally to it without closing it on the way.\n *\n * ## Items that stay open\n *\n * Pressing an item closes the menu. A check item or a radio item keeps it\n * open, so several can be switched in a row; pass `closeOnClick` to change\n * either.\n */\nimport { Menu as BaseMenu } from '@base-ui/react/menu';\nimport type { ComponentProps, ReactNode } from 'react';\nimport { tv } from 'tailwind-variants';\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport type { ElementType as RenderElementType } from 'react';\n\n/** Draws a part as another element, or composes it with a component: `render={<a href=\"/\" />}`. */\ntype RenderProp<Tag extends RenderElementType> = { render?: useRender.ComponentProps<Tag>['render'] };\n\nconst menuVariants = tv({\n  slots: {\n    positioner: 'z-50 outline-none',\n    popup: [\n      'min-w-48 origin-(--transform-origin) rounded-xl border bg-popover p-1 text-popover-foreground shadow-lg outline-none',\n      'max-h-(--available-height) overflow-y-auto [scrollbar-color:var(--border)_transparent] [scrollbar-width:thin]',\n      'transition-[opacity,scale] duration-150 ease-out',\n      'data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0',\n      'motion-reduce:transition-none',\n    ],\n    item: [\n      // The label never wraps; a description beside it is cut short instead.\n      'flex min-h-8 cursor-default items-center gap-2.5 whitespace-nowrap rounded-lg px-2 py-1.5 text-sm outline-none select-none',\n      'data-highlighted:bg-accent data-highlighted:text-accent-foreground',\n      'data-disabled:pointer-events-none data-disabled:opacity-50',\n      // A submenu's row stays lit while its submenu is open, so it is clear what it belongs to.\n      'data-popup-open:bg-accent',\n      '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n      '[&>svg:first-child]:text-muted-foreground data-highlighted:[&>svg:first-child]:text-current',\n    ],\n    end: 'ms-auto flex items-center ps-3',\n    check: 'ms-auto flex size-4 items-center justify-center ps-0 text-foreground',\n    description: 'min-w-0 truncate text-muted-foreground',\n    shortcut: 'ms-auto ps-3 text-xs tracking-wide text-muted-foreground',\n    badge:\n      'ms-auto inline-flex h-5 items-center gap-1 rounded-md bg-muted px-1.5 text-xs font-medium tabular-nums text-muted-foreground [&_svg:not([class*=size-])]:size-3',\n    chevron: 'ms-auto text-muted-foreground',\n    group: 'flex flex-col',\n    groupLabel: 'px-2 pb-1 pt-2 text-xs font-medium text-muted-foreground',\n    separator: 'mx-2 my-1 h-px bg-border',\n  },\n  variants: {\n    variant: {\n      default: {},\n      destructive: {\n        item: 'text-destructive data-highlighted:bg-destructive/10 data-highlighted:text-destructive [&>svg:first-child]:text-destructive',\n      },\n    },\n  },\n});\n\nexport interface MenuProps extends BaseMenu.Root.Props {}\n\n/** Holds whether the menu is open. Takes `open`/`onOpenChange`, `defaultOpen` and `modal`. */\nfunction MenuRoot(props: MenuProps) {\n  return <BaseMenu.Root {...props} />;\n}\nMenuRoot.displayName = 'Menu';\n\nexport interface MenuTriggerProps extends BaseMenu.Trigger.Props {}\n\n/** The button that opens the menu. `render` draws it as another element, such as a `Button`. */\nfunction MenuTrigger(props: MenuTriggerProps) {\n  return <BaseMenu.Trigger data-slot=\"menu-trigger\" {...props} />;\n}\nMenuTrigger.displayName = 'Menu.Trigger';\n\ninterface PlacementProps {\n  /** Which side of the trigger it opens on. Default `bottom`; a submenu opens to the side. */\n  side?: BaseMenu.Positioner.Props['side'];\n  /** How it lines up with the trigger along that side. Default `start`. */\n  align?: BaseMenu.Positioner.Props['align'];\n  /** The gap between the trigger and the menu, in pixels. Default `6`. */\n  sideOffset?: number;\n  /** Moves it along the side, in pixels. */\n  alignOffset?: number;\n}\n\nexport interface MenuContentProps extends BaseMenu.Popup.Props, PlacementProps {\n  /** Where in the document the menu is put. Default `document.body`. */\n  container?: BaseMenu.Portal.Props['container'];\n  /** What it is placed against instead of the trigger: an element or a ref to one. */\n  anchor?: BaseMenu.Positioner.Props['anchor'];\n  className?: string;\n}\n\n/** The list, opened in a layer over the page. It moves to the other side when there is no room. */\nfunction MenuContent({\n  side = 'bottom',\n  align = 'start',\n  sideOffset = 6,\n  alignOffset,\n  container,\n  anchor,\n  className,\n  ...props\n}: MenuContentProps) {\n  const slots = menuVariants();\n  return (\n    <BaseMenu.Portal container={container}>\n      <BaseMenu.Positioner\n        side={side}\n        align={align}\n        sideOffset={sideOffset}\n        alignOffset={alignOffset}\n        anchor={anchor}\n        className={slots.positioner()}\n      >\n        <BaseMenu.Popup data-slot=\"menu-content\" className={slots.popup({ className })} {...props} />\n      </BaseMenu.Positioner>\n    </BaseMenu.Portal>\n  );\n}\nMenuContent.displayName = 'Menu.Content';\n\nexport interface MenuItemProps extends BaseMenu.Item.Props {\n  /** `destructive` draws it in the destructive colour, for an action that cannot be undone. Default `default`. */\n  variant?: 'default' | 'destructive';\n  className?: string;\n}\n\n/** One action. Put an icon first, then the label, then a `Menu.Shortcut` or `Menu.Badge` if it has one. */\nfunction MenuItem({ variant, className, ...props }: MenuItemProps) {\n  return <BaseMenu.Item data-slot=\"menu-item\" className={menuVariants({ variant }).item({ className })} {...props} />;\n}\nMenuItem.displayName = 'Menu.Item';\n\nconst Check = () => (\n  <svg\n    aria-hidden=\"true\"\n    viewBox=\"0 0 24 24\"\n    fill=\"none\"\n    stroke=\"currentColor\"\n    strokeWidth=\"2.25\"\n    strokeLinecap=\"round\"\n    strokeLinejoin=\"round\"\n  >\n    <path d=\"M20 6 9 17l-5-5\" />\n  </svg>\n);\n\nexport interface MenuCheckboxItemProps extends BaseMenu.CheckboxItem.Props {\n  className?: string;\n}\n\n/** An item that is switched on and off, with a check at its end while on. The menu stays open. */\nfunction MenuCheckboxItem({ className, children, ...props }: MenuCheckboxItemProps) {\n  const slots = menuVariants();\n  return (\n    <BaseMenu.CheckboxItem data-slot=\"menu-checkbox-item\" className={slots.item({ className })} {...props}>\n      {children}\n      <span className={slots.check()}>\n        <BaseMenu.CheckboxItemIndicator className=\"flex\">\n          <Check />\n        </BaseMenu.CheckboxItemIndicator>\n      </span>\n    </BaseMenu.CheckboxItem>\n  );\n}\nMenuCheckboxItem.displayName = 'Menu.CheckboxItem';\n\nexport interface MenuRadioGroupProps extends BaseMenu.RadioGroup.Props {\n  className?: string;\n}\n\n/** Radio items of which one is chosen. Takes `value` and `onValueChange`. */\nfunction MenuRadioGroup({ className, ...props }: MenuRadioGroupProps) {\n  return (\n    <BaseMenu.RadioGroup data-slot=\"menu-radio-group\" className={menuVariants().group({ className })} {...props} />\n  );\n}\nMenuRadioGroup.displayName = 'Menu.RadioGroup';\n\nexport interface MenuRadioItemProps extends BaseMenu.RadioItem.Props {\n  className?: string;\n}\n\n/** One choice in a radio group, with a check at its end while chosen. */\nfunction MenuRadioItem({ className, children, ...props }: MenuRadioItemProps) {\n  const slots = menuVariants();\n  return (\n    <BaseMenu.RadioItem data-slot=\"menu-radio-item\" className={slots.item({ className })} {...props}>\n      {children}\n      <span className={slots.check()}>\n        <BaseMenu.RadioItemIndicator className=\"flex\">\n          <Check />\n        </BaseMenu.RadioItemIndicator>\n      </span>\n    </BaseMenu.RadioItem>\n  );\n}\nMenuRadioItem.displayName = 'Menu.RadioItem';\n\nexport interface MenuSubProps extends BaseMenu.SubmenuRoot.Props {}\n\n/** A submenu: a `Menu.SubTrigger` row and the `Menu.SubContent` it opens. */\nfunction MenuSub(props: MenuSubProps) {\n  return <BaseMenu.SubmenuRoot {...props} />;\n}\nMenuSub.displayName = 'Menu.Sub';\n\nexport interface MenuSubTriggerProps extends BaseMenu.SubmenuTrigger.Props {\n  className?: string;\n  /** Something before the chevron, such as a `Menu.Badge`. */\n  end?: ReactNode;\n}\n\n/** The row that opens a submenu, with a chevron at its end. It opens on hover as well as on press. */\nfunction MenuSubTrigger({ className, children, end, openOnHover = true, delay = 80, ...props }: MenuSubTriggerProps) {\n  const slots = menuVariants();\n  return (\n    <BaseMenu.SubmenuTrigger\n      data-slot=\"menu-sub-trigger\"\n      openOnHover={openOnHover}\n      delay={delay}\n      className={slots.item({ className })}\n      {...props}\n    >\n      {children}\n      <span className={slots.end()}>\n        {end}\n        <svg\n          aria-hidden=\"true\"\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth=\"2\"\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          className={slots.chevron({ className: end ? 'ms-2' : undefined })}\n        >\n          <path d=\"m9 18 6-6-6-6\" />\n        </svg>\n      </span>\n    </BaseMenu.SubmenuTrigger>\n  );\n}\nMenuSubTrigger.displayName = 'Menu.SubTrigger';\n\nexport interface MenuSubContentProps extends Omit<MenuContentProps, 'side'> {\n  /** Default `inline-end`: to the right in a left-to-right page. */\n  side?: BaseMenu.Positioner.Props['side'];\n}\n\n/** The submenu's list, opened beside its row with the first item level with it. */\nfunction MenuSubContent({\n  side = 'inline-end',\n  align = 'start',\n  sideOffset = 6,\n  alignOffset = -5,\n  ...props\n}: MenuSubContentProps) {\n  return <MenuContent side={side} align={align} sideOffset={sideOffset} alignOffset={alignOffset} {...props} />;\n}\nMenuSubContent.displayName = 'Menu.SubContent';\n\nexport interface MenuGroupProps extends BaseMenu.Group.Props {\n  className?: string;\n}\n\n/** Items that belong together, under a `Menu.GroupLabel`. */\nfunction MenuGroup({ className, ...props }: MenuGroupProps) {\n  return <BaseMenu.Group data-slot=\"menu-group\" className={menuVariants().group({ className })} {...props} />;\n}\nMenuGroup.displayName = 'Menu.Group';\n\nexport interface MenuGroupLabelProps extends BaseMenu.GroupLabel.Props {\n  className?: string;\n}\n\n/** The heading of a group. Screen readers announce the group by it. */\nfunction MenuGroupLabel({ className, ...props }: MenuGroupLabelProps) {\n  return (\n    <BaseMenu.GroupLabel data-slot=\"menu-group-label\" className={menuVariants().groupLabel({ className })} {...props} />\n  );\n}\nMenuGroupLabel.displayName = 'Menu.GroupLabel';\n\nexport interface MenuSeparatorProps extends ComponentProps<'div'> {}\n\n/** A rule between groups of items. */\nfunction MenuSeparator({ className, render, ...props }: MenuSeparatorProps & RenderProp<'div'>) {\n  return useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps(\n      { role: 'separator', 'data-slot': 'menu-separator', className: menuVariants().separator({ className }) },\n      props\n    ),\n  });\n}\nMenuSeparator.displayName = 'Menu.Separator';\n\nexport interface MenuDescriptionProps extends ComponentProps<'span'> {}\n\n/** What an item does, in quieter words after its label. */\nfunction MenuDescription({ className, render, ...props }: MenuDescriptionProps & RenderProp<'span'>) {\n  return useRender({\n    defaultTagName: 'span',\n    render,\n    props: mergeProps({ 'data-slot': 'menu-description', className: menuVariants().description({ className }) }, props),\n  });\n}\nMenuDescription.displayName = 'Menu.Description';\n\nexport interface MenuShortcutProps extends ComponentProps<'kbd'> {}\n\n/** The keys for an item, at its end. It only shows them: wire the keys up where the action lives. */\nfunction MenuShortcut({ className, render, ...props }: MenuShortcutProps & RenderProp<'kbd'>) {\n  return useRender({\n    defaultTagName: 'kbd',\n    render,\n    props: mergeProps({ 'data-slot': 'menu-shortcut', className: menuVariants().shortcut({ className }) }, props),\n  });\n}\nMenuShortcut.displayName = 'Menu.Shortcut';\n\nexport interface MenuBadgeProps extends ComponentProps<'span'> {}\n\n/** A small count or flag at an item's end: how many need attention, or that something is new. */\nfunction MenuBadge({ className, render, ...props }: MenuBadgeProps & RenderProp<'span'>) {\n  return useRender({\n    defaultTagName: 'span',\n    render,\n    props: mergeProps({ 'data-slot': 'menu-badge', className: menuVariants().badge({ className }) }, props),\n  });\n}\nMenuBadge.displayName = 'Menu.Badge';\n\nexport const Menu = Object.assign(MenuRoot, {\n  Trigger: MenuTrigger,\n  Content: MenuContent,\n  Item: MenuItem,\n  CheckboxItem: MenuCheckboxItem,\n  RadioGroup: MenuRadioGroup,\n  RadioItem: MenuRadioItem,\n  Sub: MenuSub,\n  SubTrigger: MenuSubTrigger,\n  SubContent: MenuSubContent,\n  Group: MenuGroup,\n  GroupLabel: MenuGroupLabel,\n  Separator: MenuSeparator,\n  Description: MenuDescription,\n  Shortcut: MenuShortcut,\n  Badge: MenuBadge,\n});\n\nexport {\n  MenuTrigger,\n  MenuContent,\n  MenuItem,\n  MenuCheckboxItem,\n  MenuRadioGroup,\n  MenuRadioItem,\n  MenuSub,\n  MenuSubTrigger,\n  MenuSubContent,\n  MenuGroup,\n  MenuGroupLabel,\n  MenuSeparator,\n  MenuDescription,\n  MenuShortcut,\n  MenuBadge,\n};\n"
    }
  ]
}
