{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "menu",
  "title": "Menu",
  "dependencies": [
    "@base-ui/react",
    "@phosphor-icons/react",
    "react-remove-scroll"
  ],
  "registryDependencies": [
    "https://ui.hotfix.jobs/r/checkbox.json",
    "https://ui.hotfix.jobs/r/recipes.json",
    "https://ui.hotfix.jobs/r/tokens.json",
    "https://ui.hotfix.jobs/r/use-media-query.json",
    "https://ui.hotfix.jobs/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/components/ui/menu.tsx",
      "content": "\"use client\";\n\nimport { Menu as MenuPrimitive } from \"@base-ui/react/menu\";\nimport { CaretRight, Check } from \"@phosphor-icons/react/dist/ssr\";\nimport { RemoveScroll } from \"react-remove-scroll\";\nimport { createContext, useContext } from \"react\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  itemGroupLabel,\n  itemRow,\n  mobilePopupBackdrop,\n  mobilePopupSurface,\n  popupDivider,\n  popupSurface,\n} from \"@/lib/recipes\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport {\n  MOBILE_MEDIA_QUERY,\n  useMediaQuery,\n} from \"../hooks/use-media-query\";\n\ntype Density = \"compact\" | \"comfortable\";\n\nconst MenuDensityContext = createContext<Density>(\"comfortable\");\nconst MenuMobileContext = createContext(false);\n\n/* --------------------------------- Root -------------------------------- */\n\nexport interface MenuProps {\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  defaultOpen?: boolean;\n  modal?: boolean;\n  children: React.ReactNode;\n}\n\nexport function Menu({\n  open,\n  onOpenChange,\n  defaultOpen,\n  modal,\n  children,\n}: MenuProps): React.ReactElement {\n  const isMobile = useMediaQuery(MOBILE_MEDIA_QUERY);\n\n  return (\n    <MenuPrimitive.Root\n      open={open}\n      onOpenChange={onOpenChange}\n      defaultOpen={defaultOpen}\n      modal={isMobile ? true : (modal ?? false)}\n    >\n      <MenuMobileContext.Provider value={isMobile}>\n        {children}\n      </MenuMobileContext.Provider>\n    </MenuPrimitive.Root>\n  );\n}\n\n/* ------------------------------- Trigger ------------------------------- */\n\nexport type MenuTriggerProps = React.ComponentProps<\n  typeof MenuPrimitive.Trigger\n>;\n\nexport function MenuTrigger(props: MenuTriggerProps): React.ReactElement {\n  return <MenuPrimitive.Trigger data-slot=\"menu-trigger\" {...props} />;\n}\n\n/* -------------------------------- Popup -------------------------------- */\n\nexport interface MenuPopupProps\n  extends Omit<\n    React.ComponentProps<typeof MenuPrimitive.Popup>,\n    \"children\"\n  > {\n  density?: Density;\n  /** Desktop placement. Mobile uses a centered modal surface. */\n  side?: \"top\" | \"bottom\" | \"left\" | \"right\" | \"inline-start\" | \"inline-end\";\n  /** Desktop alignment. Mobile uses a centered modal surface. */\n  align?: \"start\" | \"center\" | \"end\";\n  /** Desktop trigger offset. Mobile uses viewport gutters. */\n  sideOffset?: number;\n  /** Internal: sub-menus opt out of the centered mobile treatment. */\n  sub?: boolean;\n  children?: React.ReactNode;\n}\n\nexport function MenuPopup({\n  density = \"comfortable\",\n  side = \"bottom\",\n  align = \"start\",\n  sideOffset = 4,\n  sub = false,\n  className,\n  children,\n  ...props\n}: MenuPopupProps): React.ReactElement {\n  const isMobile = useContext(MenuMobileContext);\n\n  if (isMobile && !sub) {\n    return (\n      <MenuPrimitive.Portal>\n        <RemoveScroll>\n        <MenuPrimitive.Backdrop\n          data-slot=\"menu-backdrop\"\n          className={mobilePopupBackdrop}\n        />\n        <MenuPrimitive.Positioner className=\"contents\">\n        <MenuPrimitive.Popup\n          data-slot=\"menu-popup\"\n          data-mobile=\"true\"\n          className={cn(\n            mobilePopupSurface,\n            \"p-1\",\n            className,\n          )}\n          {...props}\n        >\n          <div className=\"min-h-0 flex-1 overflow-y-auto\">\n            <MenuDensityContext.Provider value=\"comfortable\">\n              {children}\n            </MenuDensityContext.Provider>\n          </div>\n        </MenuPrimitive.Popup>\n        </MenuPrimitive.Positioner>\n        </RemoveScroll>\n      </MenuPrimitive.Portal>\n    );\n  }\n\n  return (\n    <MenuPrimitive.Portal>\n      <MenuPrimitive.Positioner\n        side={side}\n        align={align}\n        sideOffset={sideOffset}\n        className=\"z-[80] outline-none\"\n      >\n        <MenuPrimitive.Popup\n          data-slot=\"menu-popup\"\n          className={cn(\n            popupSurface,\n            \"min-w-32 p-1 outline-none\",\n            \"transition-[opacity,scale] duration-[var(--duration-state)] ease-[var(--ease-standard)]\",\n            \"data-starting-style:opacity-0 data-starting-style:scale-95\",\n            \"data-ending-style:opacity-0 data-ending-style:scale-95\",\n            className,\n          )}\n          {...props}\n        >\n          <MenuDensityContext.Provider value={density}>\n            {children}\n          </MenuDensityContext.Provider>\n        </MenuPrimitive.Popup>\n      </MenuPrimitive.Positioner>\n    </MenuPrimitive.Portal>\n  );\n}\n\n/* --------------------------------- Item -------------------------------- */\n\nexport interface MenuItemProps {\n  className?: string;\n  /** `error` renders in red for destructive actions. */\n  type?: \"default\" | \"error\";\n  /** @deprecated Use `type=\"error\"`. */\n  variant?: \"default\" | \"destructive\";\n  inset?: boolean;\n  /** Show a trailing check for single-select patterns. */\n  selected?: boolean;\n  description?: React.ReactNode;\n  disabled?: boolean;\n  label?: string;\n  prefix?: React.ReactNode;\n  suffix?: React.ReactNode;\n  /** When set, renders as an anchor tag. */\n  href?: string;\n  target?: string;\n  rel?: string;\n  closeOnClick?: boolean;\n  onClick?: (event: React.MouseEvent<HTMLElement>) => void;\n  children?: React.ReactNode;\n}\n\nfunction useItemRowClass({\n  className,\n  isError,\n  inset,\n}: {\n  className?: string;\n  isError: boolean;\n  inset?: boolean;\n}) {\n  const density = useContext(MenuDensityContext);\n  return cn(\n    itemRow.base,\n    \"no-underline [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n    density === \"compact\"\n      ? itemRow.compact\n      : cn(itemRow.comfortable, \"[&_svg:not([class*='size-'])]:size-[18px]\"),\n    isError &&\n      \"text-error data-highlighted:bg-error/10 data-highlighted:text-error [&_svg]:!text-error\",\n    inset && \"ps-8\",\n    className,\n  );\n}\n\nfunction MenuItemContent({\n  prefix,\n  suffix,\n  selected,\n  description,\n  children,\n}: {\n  prefix?: React.ReactNode;\n  suffix?: React.ReactNode;\n  selected?: boolean;\n  description?: React.ReactNode;\n  children?: React.ReactNode;\n}): React.ReactElement {\n  const trailingCheck = selected && (\n    <Check className=\"ms-auto size-3.5 shrink-0 text-ink-muted\" aria-hidden />\n  );\n  if (description != null) {\n    return (\n      <span className=\"flex w-full flex-col\">\n        <span className=\"flex w-full items-center gap-2\">\n          {prefix}\n          {children}\n          {suffix && <span className=\"ms-auto flex items-center\">{suffix}</span>}\n          {trailingCheck}\n        </span>\n        <span className=\"mt-0.5 truncate text-mini text-ink-muted\">\n          {description}\n        </span>\n      </span>\n    );\n  }\n  return (\n    <span className=\"flex w-full items-center gap-2\">\n      {prefix}\n      {children}\n      {suffix && <span className=\"ms-auto flex items-center\">{suffix}</span>}\n      {trailingCheck}\n    </span>\n  );\n}\n\nexport function MenuItem({\n  className,\n  type,\n  variant,\n  inset,\n  selected,\n  description,\n  disabled,\n  label,\n  prefix,\n  suffix,\n  href,\n  target,\n  rel,\n  closeOnClick,\n  onClick,\n  children,\n}: MenuItemProps): React.ReactElement {\n  const isError = type === \"error\" || variant === \"destructive\";\n  const rowClass = useItemRowClass({ className, isError, inset });\n  const content = (\n    <MenuItemContent\n      prefix={prefix}\n      suffix={suffix}\n      selected={selected}\n      description={description}\n    >\n      {children}\n    </MenuItemContent>\n  );\n\n  if (href) {\n    return (\n      <MenuPrimitive.Item\n        render={\n          <a\n            href={href}\n            target={target}\n            rel={rel}\n          />\n        }\n        data-slot=\"menu-item\"\n        data-selected={selected ? \"\" : undefined}\n        data-type={isError ? \"error\" : \"default\"}\n        disabled={disabled}\n        label={label}\n        closeOnClick={closeOnClick}\n        onClick={onClick}\n        className={rowClass}\n      >\n        {content}\n      </MenuPrimitive.Item>\n    );\n  }\n\n  return (\n    <MenuPrimitive.Item\n      data-slot=\"menu-item\"\n      data-selected={selected ? \"\" : undefined}\n      data-type={isError ? \"error\" : \"default\"}\n      disabled={disabled}\n      label={label}\n      closeOnClick={closeOnClick}\n      onClick={onClick}\n      className={rowClass}\n    >\n      {content}\n    </MenuPrimitive.Item>\n  );\n}\n\n/* ---------------------------- CheckboxItem ---------------------------- */\n\nexport interface MenuCheckboxItemProps {\n  className?: string;\n  checked?: boolean;\n  onCheckedChange?: (checked: boolean) => void;\n  disabled?: boolean;\n  label?: string;\n  prefix?: React.ReactNode;\n  suffix?: React.ReactNode;\n  children?: React.ReactNode;\n}\n\nexport function MenuCheckboxItem({\n  className,\n  checked = false,\n  onCheckedChange,\n  disabled,\n  label,\n  prefix,\n  suffix,\n  children,\n}: MenuCheckboxItemProps): React.ReactElement {\n  const density = useContext(MenuDensityContext);\n  return (\n    <MenuPrimitive.CheckboxItem\n      checked={checked}\n      onCheckedChange={onCheckedChange}\n      disabled={disabled}\n      label={label}\n      closeOnClick={false}\n      data-slot=\"menu-checkbox-item\"\n      data-selected={checked ? \"\" : undefined}\n      className={cn(\n        itemRow.base,\n        \"gap-2 transition-colors duration-[var(--duration-state)] ease-[var(--ease-standard)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        density === \"compact\" ? itemRow.compact : itemRow.comfortable,\n        className,\n      )}\n    >\n      <Checkbox\n        checked={checked}\n        tabIndex={-1}\n        aria-hidden\n        className=\"pointer-events-none\"\n      />\n      {prefix}\n      <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n      {suffix && <span className=\"ms-auto flex items-center\">{suffix}</span>}\n    </MenuPrimitive.CheckboxItem>\n  );\n}\n\n/* ------------------------- RadioGroup + RadioItem ---------------------- */\n\nexport interface MenuRadioGroupProps {\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  children: React.ReactNode;\n}\n\nexport function MenuRadioGroup({\n  value,\n  defaultValue,\n  onValueChange,\n  children,\n}: MenuRadioGroupProps): React.ReactElement {\n  return (\n    <MenuPrimitive.RadioGroup\n      value={value}\n      defaultValue={defaultValue}\n      onValueChange={onValueChange}\n    >\n      {children}\n    </MenuPrimitive.RadioGroup>\n  );\n}\n\nexport interface MenuRadioItemProps {\n  className?: string;\n  value: string;\n  disabled?: boolean;\n  label?: string;\n  children?: React.ReactNode;\n}\n\nexport function MenuRadioItem({\n  className,\n  value,\n  disabled,\n  label,\n  children,\n}: MenuRadioItemProps): React.ReactElement {\n  const density = useContext(MenuDensityContext);\n  return (\n    <MenuPrimitive.RadioItem\n      value={value}\n      disabled={disabled}\n      label={label}\n      closeOnClick\n      data-slot=\"menu-radio-item\"\n      className={cn(\n        itemRow.base,\n        \"w-full gap-2 transition-colors duration-[var(--duration-state)] ease-[var(--ease-standard)]\",\n        density === \"compact\" ? itemRow.compact : itemRow.comfortable,\n        className,\n      )}\n    >\n      <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n      <MenuPrimitive.RadioItemIndicator className=\"ms-auto flex items-center\">\n        <Check className=\"size-3.5 shrink-0 text-ink-muted\" aria-hidden />\n      </MenuPrimitive.RadioItemIndicator>\n    </MenuPrimitive.RadioItem>\n  );\n}\n\n/* ---------------------- Group / Section / Label ---------------------- */\n\nexport function MenuGroup({\n  className,\n  children,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>): React.ReactElement {\n  return (\n    <MenuPrimitive.Group\n      data-slot=\"menu-group\"\n      className={className}\n      {...props}\n    >\n      {children}\n    </MenuPrimitive.Group>\n  );\n}\n\nexport function MenuSection({\n  title,\n  label,\n  className,\n  children,\n  ...props\n}: Omit<React.HTMLAttributes<HTMLDivElement>, \"children\" | \"title\"> & {\n  title?: React.ReactNode;\n  /** @deprecated Use `title`. */\n  label?: React.ReactNode;\n  children: React.ReactNode;\n}): React.ReactElement {\n  const heading = title ?? label;\n  return (\n    <MenuPrimitive.Group\n      data-slot=\"menu-section\"\n      className={cn(\"py-1 first:pt-0 last:pb-0\", className)}\n      {...props}\n    >\n      {heading != null && <MenuGroupLabel>{heading}</MenuGroupLabel>}\n      {children}\n    </MenuPrimitive.Group>\n  );\n}\n\nexport function MenuGroupLabel({\n  className,\n  inset,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement> & {\n  inset?: boolean;\n}): React.ReactElement {\n  const density = useContext(MenuDensityContext);\n  return (\n    <MenuPrimitive.GroupLabel\n      data-slot=\"menu-label\"\n      className={cn(\n        itemGroupLabel.base,\n        density === \"compact\"\n          ? itemGroupLabel.compact\n          : itemGroupLabel.comfortable,\n        inset && \"ps-8\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function MenuDivider({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>): React.ReactElement {\n  return (\n    <MenuPrimitive.Separator\n      data-slot=\"menu-divider\"\n      className={cn(popupDivider, className)}\n      {...props}\n    />\n  );\n}\n\nexport function MenuShortcut({\n  className,\n  ...props\n}: React.ComponentProps<\"kbd\">): React.ReactElement {\n  return (\n    <kbd\n      className={cn(\n        \"ms-auto font-medium font-sans text-ink-muted text-xs tracking-widest\",\n        className,\n      )}\n      data-slot=\"menu-shortcut\"\n      {...props}\n    />\n  );\n}\n\n/* ------------------------------ Submenu ------------------------------ */\n\nexport function MenuSub({\n  children,\n}: {\n  children: React.ReactNode;\n}): React.ReactElement {\n  return <MenuPrimitive.SubmenuRoot>{children}</MenuPrimitive.SubmenuRoot>;\n}\n\nexport interface MenuSubTriggerProps {\n  className?: string;\n  inset?: boolean;\n  disabled?: boolean;\n  label?: string;\n  children?: React.ReactNode;\n}\n\nexport function MenuSubTrigger({\n  className,\n  inset,\n  disabled,\n  label,\n  children,\n}: MenuSubTriggerProps): React.ReactElement {\n  const density = useContext(MenuDensityContext);\n  return (\n    <MenuPrimitive.SubmenuTrigger\n      disabled={disabled}\n      label={label}\n      data-slot=\"menu-sub-trigger\"\n      className={cn(\n        itemRow.base,\n        \"gap-2 transition-colors duration-[var(--duration-state)] ease-[var(--ease-standard)] [&_svg]:pointer-events-none\",\n        density === \"compact\"\n          ? cn(itemRow.compact, \"[&_svg:not([class*='size-'])]:size-4\")\n          : cn(itemRow.comfortable, \"[&_svg:not([class*='size-'])]:size-[18px]\"),\n        inset && \"ps-8\",\n        className,\n      )}\n    >\n      {children}\n      <CaretRight\n        aria-hidden\n        className=\"ms-auto -me-0.5 size-4 opacity-80\"\n      />\n    </MenuPrimitive.SubmenuTrigger>\n  );\n}\n\nexport function MenuSubPopup(\n  props: Omit<MenuPopupProps, \"side\" | \"align\" | \"sub\">,\n): React.ReactElement {\n  return <MenuPopup side=\"right\" align=\"start\" sub {...props} />;\n}\n\nexport const MenuPortal = ({\n  children,\n}: {\n  children: React.ReactNode;\n}): React.ReactElement => <>{children}</>;\n\nexport { MenuPrimitive };\n",
      "type": "registry:ui",
      "target": "components/ui/menu.tsx"
    }
  ],
  "type": "registry:ui"
}