{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "dropdown-menu", "type": "registry:ui", "title": "Dropdown Menu", "files": [ { "path": "ui/dropdown-menu.tsrx", "type": "registry:ui", "target": "components/ui/dropdown-menu.tsrx", "content": "// Base UI base dropdown-menu — runs on @octanejs/base-ui's `Menu`. Class strings come from the\n// maintainer-supplied radix source; every difference below was verified by rendering the primitive\n// and reading the DOM, not inferred.\n//\n// PART TREE. Radix's `Content` is one element; Base UI splits positioning out, exactly as this\n// base's popover already does:\n//\n// radix Root > Portal > Content\n// Base UI Root > Portal > Positioner > Popup (Positioner owns align/sideOffset)\n//\n// THREE CSS VARIABLES ARE RENAMED. Radix publishes per-component names; Base UI's Positioner\n// publishes generic ones, and a utility pointing at a variable nothing sets silently does nothing:\n//\n// --radix-dropdown-menu-content-available-height -> --available-height\n// --radix-dropdown-menu-trigger-width -> --anchor-width\n// --radix-dropdown-menu-content-transform-origin -> --transform-origin\n//\n// THE SUBMENU TRIGGER'S OPEN ATTRIBUTE DIFFERS. Base UI marks an open trigger with\n// `data-popup-open` (triggerOpenStateMapping), not the `data-open` the popup itself carries — so\n// radix's `data-open:bg-accent` on the sub-trigger would never match and an open submenu's parent\n// row would not stay highlighted.\n//\n// WHAT DOES CARRY OVER UNCHANGED, and is worth recording because it looks like it should not:\n// radix styles item highlight with `focus:`, which works here because Base UI moves REAL DOM focus\n// onto the highlighted item (checked by driving ArrowDown and reading document.activeElement) as\n// well as publishing `data-highlighted`. `data-side`, `data-align`, `data-open`, `data-closed` and\n// `data-disabled` are all emitted with the same spellings radix uses.\n//\n// TWO PARTS ARE PLAIN HOST ELEMENTS ON PURPOSE:\n// - `Label` is a `
`, NOT `Menu.GroupLabel`. That part calls useMenuGroupRootContext and\n// THROWS \"MenuGroupContext is missing\" outside a `Menu.Group`, while shadcn's label is\n// routinely used standalone. Same trap as `Field.Label`, which already shipped as a crash once.\n// - `Separator` is a `
`, because the Menu namespace has no Separator part at all (its\n// sibling ContextMenu does). The radix classes are pure styling, so nothing is lost.\n// POSITIONING PROPS BELONG TO THE POSITIONER, NOT THE POPUP. Radix has one `Content` element that\n// takes them all; Base UI splits positioning into its own layer, so anything forwarded to `Popup`\n// instead is inert — `side=\"top\"` silently leaves the menu on the default side, and both `side` and\n// `alignOffset` land on the DOM as invalid attributes. They are therefore destructured and routed\n// explicitly rather than swept up by the rest spread.\nimport { type OctaneNode } from 'octane';\nimport { Menu as MenuPrimitive } from '@octanejs/base-ui/menu';\nimport { CheckIcon, ChevronRightIcon } from '@octanejs/lucide';\n\nimport { cn } from '@/lib/utils';\n\ntype Props = { className?: string; children?: OctaneNode } & Record;\n\nexport function DropdownMenu(props: Record) @{\n\t\n}\n\nexport function DropdownMenuPortal(props: Record) @{\n\t\n}\n\nexport function DropdownMenuTrigger(props: Record) @{\n\t\n}\n\nexport interface BaseUiPositioningProps {\n\t/** Forwarded to the Positioner; see the note at the top of this file. */\n\tside?: 'top' | 'right' | 'bottom' | 'left';\n\tsideOffset?: number;\n\talign?: 'start' | 'center' | 'end';\n\talignOffset?: number;\n\tanchor?: unknown;\n\tpositionMethod?: 'absolute' | 'fixed';\n\tcollisionBoundary?: unknown;\n\tcollisionPadding?: unknown;\n\tcollisionAvoidance?: unknown;\n\tarrowPadding?: number;\n\tsticky?: boolean;\n\tdisableAnchorTracking?: boolean;\n}\n\nexport interface DropdownMenuContentProps extends Props, BaseUiPositioningProps {}\n\nexport function DropdownMenuContent({\n\tclassName,\n\talign = 'start',\n\tsideOffset = 4,\n\tside,\n\talignOffset,\n\tanchor,\n\tpositionMethod,\n\tcollisionBoundary,\n\tcollisionPadding,\n\tcollisionAvoidance,\n\tarrowPadding,\n\tsticky,\n\tdisableAnchorTracking,\n\t...props\n}: DropdownMenuContentProps) @{\n\t\n\t\t\n\t\t\t\n\t\t\n\t\n}\n\nexport function DropdownMenuGroup(props: Record) @{\n\t\n}\n\nexport function DropdownMenuItem({ className, inset, variant = 'default', ...props }: Props & {\n\tinset?: boolean;\n\tvariant?: 'default' | 'destructive';\n}) @{\n\t\n}\n\nexport function DropdownMenuCheckboxItem(props: Props & {\n\tchecked?: boolean;\n\tinset?: boolean;\n}) @{\n\tconst { className, children: _children, checked, inset, ...rest } = props;\n\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\t{props.children}\n\t\n}\n\nexport function DropdownMenuRadioGroup(props: Record) @{\n\t\n}\n\nexport function DropdownMenuRadioItem(props: Props & { inset?: boolean }) @{\n\tconst { className, children: _children, inset, ...rest } = props;\n\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\t{props.children}\n\t\n}\n\nexport function DropdownMenuLabel({ className, inset, ...props }: Props & { inset?: boolean }) @{\n\t\n}\n\nexport function DropdownMenuSeparator({ className, ...props }: Props) @{\n\t\n}\n\nexport function DropdownMenuShortcut({ className, ...props }: Props) @{\n\t\n}\n\nexport function DropdownMenuSub(props: Record) @{\n\t\n}\n\nexport function DropdownMenuSubTrigger(props: Props & { inset?: boolean }) @{\n\tconst { className, children: _children, inset, ...rest } = props;\n\n\t\n\t\t{props.children}\n\t\t\n\t\n}\n\nexport function DropdownMenuSubContent({\n\tclassName,\n\talign,\n\tsideOffset,\n\tside,\n\talignOffset,\n\tanchor,\n\tpositionMethod,\n\tcollisionBoundary,\n\tcollisionPadding,\n\tcollisionAvoidance,\n\tarrowPadding,\n\tsticky,\n\tdisableAnchorTracking,\n\t...props\n}: Props & BaseUiPositioningProps) @{\n\t\n\t\t\n\t\t\t\n\t\t\n\t\n}\n" } ], "dependencies": [ "@octanejs/base-ui@0.1.33", "@octanejs/lucide@0.1.30" ], "registryDependencies": [ "@octane/utils" ] }