{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "base-dropdown-menu",
  "title": "Base Dropdown Menu",
  "description": "An accessible dropdown menu built on Base UI with smooth Motion spring scale and submenu slide animations.",
  "dependencies": ["@base-ui/react", "motion", "lucide-react"],
  "registryDependencies": ["utils", "@soralabs/hooks-use-controlled-state"],
  "files": [
    {
      "path": "registry/ui/base/dropdown-menu/index.tsx",
      "content": "\"use client\";\n\nimport { useDirection } from \"@base-ui/react/direction-provider\";\nimport { Menu as MenuPrimitive } from \"@base-ui/react/menu\";\nimport { useControlledState } from \"@/hooks/use-controlled-state\";\nimport { cn } from \"@/lib/utils\";\nimport { CheckIcon, ChevronRightIcon } from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { ComponentProps } from \"react\";\n\ninterface DropdownMenuContextValue {\n  open: boolean;\n  setOpen: (open: boolean) => void;\n}\n\nconst DropdownMenuContext = createContext<DropdownMenuContextValue | null>(\n  null\n);\n\nfunction useDropdownMenuContext() {\n  return useContext(DropdownMenuContext);\n}\n\ninterface DropdownMenuSubContextValue {\n  open: boolean;\n  setOpen: (open: boolean) => void;\n}\n\nconst DropdownMenuSubContext =\n  createContext<DropdownMenuSubContextValue | null>(null);\n\nfunction useDropdownMenuSubContext() {\n  return useContext(DropdownMenuSubContext);\n}\n\ntype DropdownMenuProps = ComponentProps<typeof MenuPrimitive.Root>;\n\n/**\n * Root container for the Dropdown Menu component powered by Base UI and Motion.\n */\nfunction DropdownMenu({\n  open: controlledOpen,\n  defaultOpen = false,\n  onOpenChange,\n  children,\n  ...props\n}: DropdownMenuProps) {\n  const [open, setOpen] = useControlledState({\n    defaultValue: defaultOpen,\n    onChange: (nextOpen: boolean) => {\n      onOpenChange?.(nextOpen, undefined as never);\n    },\n    value: controlledOpen,\n  });\n\n  const contextValue = useMemo(\n    () => ({ open: Boolean(open), setOpen }),\n    [open, setOpen]\n  );\n\n  return (\n    <DropdownMenuContext.Provider value={contextValue}>\n      <MenuPrimitive.Root\n        data-slot=\"dropdown-menu\"\n        onOpenChange={(nextOpen, eventDetails) => {\n          setOpen(nextOpen);\n          onOpenChange?.(nextOpen, eventDetails);\n        }}\n        open={open}\n        {...props}\n      >\n        {children}\n      </MenuPrimitive.Root>\n    </DropdownMenuContext.Provider>\n  );\n}\n\ntype DropdownMenuPortalProps = ComponentProps<typeof MenuPrimitive.Portal>;\n\n/**\n * Portals the dropdown menu content into the document body.\n */\nfunction DropdownMenuPortal({ ...props }: DropdownMenuPortalProps) {\n  return <MenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />;\n}\n\ntype DropdownMenuTriggerProps = ComponentProps<typeof MenuPrimitive.Trigger>;\n\n/**\n * The button or element that opens the dropdown menu on click.\n */\nfunction DropdownMenuTrigger({ ...props }: DropdownMenuTriggerProps) {\n  return <MenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" {...props} />;\n}\n\ninterface DropdownMenuContentProps\n  extends\n    ComponentProps<typeof MenuPrimitive.Popup>,\n    Pick<\n      ComponentProps<typeof MenuPrimitive.Positioner>,\n      | \"align\"\n      | \"alignOffset\"\n      | \"anchor\"\n      | \"arrowPadding\"\n      | \"collisionAvoidance\"\n      | \"collisionBoundary\"\n      | \"collisionPadding\"\n      | \"positionMethod\"\n      | \"side\"\n      | \"sideOffset\"\n      | \"sticky\"\n    > {\n  /**\n   * Whether to disable Motion spring physics and animations.\n   * @default false\n   */\n  disableAnimation?: boolean;\n  /**\n   * Additional CSS classes for the positioner container.\n   */\n  positionerClassName?: string;\n}\n\n/**\n * Dropdown menu popup container with smooth Motion spring scale and opacity\n * entrance/exit animations. Scales from the transform origin set by Base UI.\n */\nfunction DropdownMenuContent({\n  className,\n  positionerClassName,\n  align = \"start\",\n  alignOffset = 0,\n  side = \"bottom\",\n  sideOffset = 4,\n  anchor,\n  arrowPadding,\n  collisionAvoidance,\n  collisionBoundary,\n  collisionPadding = 8,\n  positionMethod,\n  sticky,\n  disableAnimation = false,\n  children,\n  render,\n  style,\n  ...props\n}: DropdownMenuContentProps) {\n  const context = useDropdownMenuContext();\n  const prefersReducedMotion = useReducedMotion();\n  const isOpen = context ? context.open : true;\n  const shouldAnimate = !(disableAnimation || prefersReducedMotion);\n\n  return (\n    <AnimatePresence>\n      {isOpen && (\n        <MenuPrimitive.Portal keepMounted>\n          <MenuPrimitive.Positioner\n            align={align}\n            alignOffset={alignOffset}\n            anchor={anchor}\n            arrowPadding={arrowPadding}\n            className={cn(\"isolate z-50 outline-none\", positionerClassName)}\n            collisionAvoidance={collisionAvoidance}\n            collisionBoundary={collisionBoundary}\n            collisionPadding={collisionPadding}\n            data-slot=\"dropdown-menu-positioner\"\n            positionMethod={positionMethod}\n            side={side}\n            sideOffset={sideOffset}\n            sticky={sticky}\n          >\n            <MenuPrimitive.Popup\n              className={cn(\n                \"border-border bg-popover/95 text-popover-foreground ring-foreground/5 z-50 max-h-(--available-height) min-w-44 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-xl border p-1 shadow-xl ring-1 backdrop-blur-md outline-none\",\n                className\n              )}\n              data-slot=\"dropdown-menu-content\"\n              render={\n                render ?? (\n                  <motion.div\n                    animate={{ opacity: 1, scale: 1 }}\n                    exit={\n                      shouldAnimate\n                        ? {\n                            opacity: 0,\n                            scale: 0.95,\n                            transition: {\n                              duration: 0.1,\n                            },\n                          }\n                        : { opacity: 0 }\n                    }\n                    initial={\n                      shouldAnimate\n                        ? { opacity: 0, scale: 0.95 }\n                        : { opacity: 0 }\n                    }\n                    style={{\n                      transformOrigin: \"var(--transform-origin)\",\n                      ...style,\n                    }}\n                    transition={\n                      shouldAnimate\n                        ? {\n                            type: \"spring\",\n                            stiffness: 400,\n                            damping: 24,\n                            opacity: { duration: 0.18 },\n                          }\n                        : { duration: 0 }\n                    }\n                  />\n                )\n              }\n              {...props}\n            >\n              {children}\n            </MenuPrimitive.Popup>\n          </MenuPrimitive.Positioner>\n        </MenuPrimitive.Portal>\n      )}\n    </AnimatePresence>\n  );\n}\n\ntype DropdownMenuGroupProps = ComponentProps<typeof MenuPrimitive.Group>;\n\n/**\n * Groups related dropdown menu items together.\n */\nfunction DropdownMenuGroup({ ...props }: DropdownMenuGroupProps) {\n  return <MenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />;\n}\n\ninterface DropdownMenuLabelProps extends ComponentProps<\n  typeof MenuPrimitive.GroupLabel\n> {\n  /**\n   * Whether to indent the label for alignment with checkable items.\n   * @default false\n   */\n  inset?: boolean;\n}\n\n/**\n * Section label for a group of dropdown menu items.\n * Must be used inside `<DropdownMenuGroup>` or `<DropdownMenuRadioGroup>`.\n */\nfunction DropdownMenuLabel({\n  className,\n  inset,\n  ...props\n}: DropdownMenuLabelProps) {\n  return (\n    <MenuPrimitive.GroupLabel\n      className={cn(\n        \"text-muted-foreground px-2 py-1.5 text-xs font-semibold data-inset:pl-8\",\n        className\n      )}\n      data-inset={inset}\n      data-slot=\"dropdown-menu-label\"\n      {...props}\n    />\n  );\n}\n\ninterface DropdownMenuItemProps extends ComponentProps<\n  typeof MenuPrimitive.Item\n> {\n  /**\n   * Whether to indent the item for alignment with checkable items.\n   * @default false\n   */\n  inset?: boolean;\n  /**\n   * Visual style variant of the item.\n   * @default \"default\"\n   */\n  variant?: \"default\" | \"destructive\";\n}\n\n/**\n * An actionable item within the dropdown menu.\n */\nfunction DropdownMenuItem({\n  className,\n  inset,\n  variant = \"default\",\n  ...props\n}: DropdownMenuItemProps) {\n  return (\n    <MenuPrimitive.Item\n      className={cn(\n        \"group/dropdown-menu-item focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className\n      )}\n      data-inset={inset}\n      data-slot=\"dropdown-menu-item\"\n      data-variant={variant}\n      {...props}\n    />\n  );\n}\n\ntype DropdownMenuSubProps = ComponentProps<typeof MenuPrimitive.SubmenuRoot>;\n\n/**\n * Submenu root container managing nested submenu state.\n */\nfunction DropdownMenuSub({\n  open: controlledOpen,\n  defaultOpen = false,\n  onOpenChange,\n  children,\n  ...props\n}: DropdownMenuSubProps) {\n  const [open, setOpen] = useControlledState({\n    defaultValue: defaultOpen,\n    onChange: (nextOpen: boolean) => {\n      onOpenChange?.(nextOpen, undefined as never);\n    },\n    value: controlledOpen,\n  });\n\n  const contextValue = useMemo(\n    () => ({ open: Boolean(open), setOpen }),\n    [open, setOpen]\n  );\n\n  return (\n    <DropdownMenuSubContext.Provider value={contextValue}>\n      <MenuPrimitive.SubmenuRoot\n        data-slot=\"dropdown-menu-sub\"\n        onOpenChange={(nextOpen, eventDetails) => {\n          setOpen(nextOpen);\n          onOpenChange?.(nextOpen, eventDetails);\n        }}\n        open={open}\n        {...props}\n      >\n        {children}\n      </MenuPrimitive.SubmenuRoot>\n    </DropdownMenuSubContext.Provider>\n  );\n}\n\ninterface DropdownMenuSubTriggerProps extends ComponentProps<\n  typeof MenuPrimitive.SubmenuTrigger\n> {\n  /**\n   * Whether to indent the item for alignment with checkable items.\n   * @default false\n   */\n  inset?: boolean;\n}\n\n/**\n * Interactive menu item that reveals a submenu on hover or click.\n */\nfunction DropdownMenuSubTrigger({\n  className,\n  inset,\n  children,\n  ...props\n}: DropdownMenuSubTriggerProps) {\n  return (\n    <MenuPrimitive.SubmenuTrigger\n      className={cn(\n        \"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-popup-open:bg-accent data-open:text-accent-foreground data-popup-open:text-accent-foreground flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className\n      )}\n      data-inset={inset}\n      data-slot=\"dropdown-menu-sub-trigger\"\n      {...props}\n    >\n      {children}\n      <ChevronRightIcon className=\"text-muted-foreground ms-auto size-4 rtl:rotate-180\" />\n    </MenuPrimitive.SubmenuTrigger>\n  );\n}\n\ninterface DropdownMenuSubContentProps\n  extends\n    ComponentProps<typeof MenuPrimitive.Popup>,\n    Pick<\n      ComponentProps<typeof MenuPrimitive.Positioner>,\n      | \"align\"\n      | \"alignOffset\"\n      | \"anchor\"\n      | \"arrowPadding\"\n      | \"collisionAvoidance\"\n      | \"collisionBoundary\"\n      | \"collisionPadding\"\n      | \"positionMethod\"\n      | \"side\"\n      | \"sideOffset\"\n      | \"sticky\"\n    > {\n  /**\n   * Whether to disable Motion transitions and animations.\n   * @default false\n   */\n  disableAnimation?: boolean;\n  /**\n   * Additional CSS classes for the positioner container.\n   */\n  positionerClassName?: string;\n}\n\n/**\n * Submenu popup content with smooth Motion spring slide entrance and exit animations.\n */\nfunction DropdownMenuSubContent({\n  className,\n  positionerClassName,\n  align = \"start\",\n  alignOffset = -4,\n  side = \"inline-end\",\n  sideOffset = 0,\n  anchor,\n  arrowPadding,\n  collisionAvoidance,\n  collisionBoundary,\n  collisionPadding = 8,\n  positionMethod,\n  sticky,\n  disableAnimation = false,\n  children,\n  render,\n  style,\n  ...props\n}: DropdownMenuSubContentProps) {\n  const context = useDropdownMenuSubContext();\n  const direction = useDirection();\n  const prefersReducedMotion = useReducedMotion();\n  const isOpen = context ? context.open : true;\n  const shouldAnimate = !(disableAnimation || prefersReducedMotion);\n  const slideOffset = direction === \"rtl\" ? 10 : -10;\n\n  return (\n    <AnimatePresence>\n      {isOpen && (\n        <MenuPrimitive.Portal keepMounted>\n          <MenuPrimitive.Positioner\n            align={align}\n            alignOffset={alignOffset}\n            anchor={anchor}\n            arrowPadding={arrowPadding}\n            className={cn(\"isolate z-50 outline-none\", positionerClassName)}\n            collisionAvoidance={collisionAvoidance}\n            collisionBoundary={collisionBoundary}\n            collisionPadding={collisionPadding}\n            data-slot=\"dropdown-menu-sub-positioner\"\n            positionMethod={positionMethod}\n            side={side}\n            sideOffset={sideOffset}\n            sticky={sticky}\n          >\n            <MenuPrimitive.Popup\n              className={cn(\n                \"border-border bg-popover/95 text-popover-foreground ring-foreground/5 z-50 max-h-(--available-height) min-w-40 overflow-x-hidden overflow-y-auto rounded-xl border p-1 shadow-2xl ring-1 backdrop-blur-md outline-none\",\n                className\n              )}\n              data-slot=\"dropdown-menu-sub-content\"\n              render={\n                render ?? (\n                  <motion.div\n                    animate={{ opacity: 1, x: 0 }}\n                    exit={\n                      shouldAnimate\n                        ? {\n                            opacity: 0,\n                            x: slideOffset,\n                            transition: {\n                              duration: 0.1,\n                            },\n                          }\n                        : { opacity: 0 }\n                    }\n                    initial={\n                      shouldAnimate\n                        ? { opacity: 0, x: slideOffset }\n                        : { opacity: 0 }\n                    }\n                    style={{ ...style }}\n                    transition={\n                      shouldAnimate\n                        ? {\n                            type: \"spring\",\n                            stiffness: 400,\n                            damping: 20,\n                            opacity: { duration: 0.2 },\n                          }\n                        : { duration: 0 }\n                    }\n                  />\n                )\n              }\n              {...props}\n            >\n              {children}\n            </MenuPrimitive.Popup>\n          </MenuPrimitive.Positioner>\n        </MenuPrimitive.Portal>\n      )}\n    </AnimatePresence>\n  );\n}\n\ninterface DropdownMenuCheckboxItemProps extends ComponentProps<\n  typeof MenuPrimitive.CheckboxItem\n> {\n  /**\n   * Whether to indent the item for alignment.\n   * @default false\n   */\n  inset?: boolean;\n}\n\n/**\n * An actionable dropdown menu item with a checkbox toggle state.\n */\nfunction DropdownMenuCheckboxItem({\n  className,\n  children,\n  checked,\n  inset,\n  ...props\n}: DropdownMenuCheckboxItemProps) {\n  return (\n    <MenuPrimitive.CheckboxItem\n      checked={checked}\n      className={cn(\n        \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className\n      )}\n      data-inset={inset}\n      data-slot=\"dropdown-menu-checkbox-item\"\n      {...props}\n    >\n      <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n        <MenuPrimitive.CheckboxItemIndicator>\n          <CheckIcon className=\"size-4\" />\n        </MenuPrimitive.CheckboxItemIndicator>\n      </span>\n      {children}\n    </MenuPrimitive.CheckboxItem>\n  );\n}\n\ntype DropdownMenuRadioGroupProps = ComponentProps<\n  typeof MenuPrimitive.RadioGroup\n>;\n\n/**\n * Radio group container for mutually exclusive dropdown menu selections.\n */\nfunction DropdownMenuRadioGroup({ ...props }: DropdownMenuRadioGroupProps) {\n  return (\n    <MenuPrimitive.RadioGroup\n      data-slot=\"dropdown-menu-radio-group\"\n      {...props}\n    />\n  );\n}\n\ninterface DropdownMenuRadioItemProps extends ComponentProps<\n  typeof MenuPrimitive.RadioItem\n> {\n  /**\n   * Whether to indent the item for alignment.\n   * @default false\n   */\n  inset?: boolean;\n}\n\n/**\n * A selectable radio option within a DropdownMenuRadioGroup.\n */\nfunction DropdownMenuRadioItem({\n  className,\n  children,\n  inset,\n  ...props\n}: DropdownMenuRadioItemProps) {\n  return (\n    <MenuPrimitive.RadioItem\n      className={cn(\n        \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className\n      )}\n      data-inset={inset}\n      data-slot=\"dropdown-menu-radio-item\"\n      {...props}\n    >\n      <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n        <MenuPrimitive.RadioItemIndicator>\n          <CheckIcon className=\"size-4\" />\n        </MenuPrimitive.RadioItemIndicator>\n      </span>\n      {children}\n    </MenuPrimitive.RadioItem>\n  );\n}\n\ntype DropdownMenuSeparatorProps = ComponentProps<\n  typeof MenuPrimitive.Separator\n>;\n\n/**\n * Divider separating groups of dropdown menu items.\n */\nfunction DropdownMenuSeparator({\n  className,\n  ...props\n}: DropdownMenuSeparatorProps) {\n  return (\n    <MenuPrimitive.Separator\n      className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n      data-slot=\"dropdown-menu-separator\"\n      {...props}\n    />\n  );\n}\n\ntype DropdownMenuShortcutProps = ComponentProps<\"span\">;\n\n/**\n * Renders keyboard shortcut combination hints aligned to the right.\n */\nfunction DropdownMenuShortcut({\n  className,\n  ...props\n}: DropdownMenuShortcutProps) {\n  return (\n    <span\n      className={cn(\n        \"text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest\",\n        className\n      )}\n      data-slot=\"dropdown-menu-shortcut\"\n      {...props}\n    />\n  );\n}\n\nexport type {\n  DropdownMenuCheckboxItemProps,\n  DropdownMenuContentProps,\n  DropdownMenuGroupProps,\n  DropdownMenuItemProps,\n  DropdownMenuLabelProps,\n  DropdownMenuPortalProps,\n  DropdownMenuProps,\n  DropdownMenuRadioGroupProps,\n  DropdownMenuRadioItemProps,\n  DropdownMenuSeparatorProps,\n  DropdownMenuShortcutProps,\n  DropdownMenuSubContentProps,\n  DropdownMenuSubProps,\n  DropdownMenuSubTriggerProps,\n  DropdownMenuTriggerProps,\n};\nexport {\n  DropdownMenu,\n  DropdownMenu as BaseDropdownMenu,\n  DropdownMenu as default,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuPortal,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuTrigger,\n  useDropdownMenuContext,\n  useDropdownMenuSubContext,\n};\n",
      "type": "registry:ui",
      "target": "components/sora-ui/base/dropdown-menu.tsx"
    }
  ],
  "meta": {
    "agentMetadata": {
      "intent": "Present a compact list of actions or choices from an anchored trigger.",
      "role": "menu",
      "a11yConstraints": [
        "Preserve roving focus, keyboard navigation, and Escape dismissal."
      ],
      "motionEngine": "Motion scale and opacity presence with collision-aware submenu cascades.",
      "compositionRules": ["Use a semantic button as the trigger."],
      "compositionRecipes": ["Compose with base/button for account actions."]
    }
  },
  "type": "registry:ui"
}
