V-/Srcv0.1.0beta

// dropdown-menu

Dropdown Menu

Glass menu with Radix keyboard navigation.

// install
npx shadcn add @vsrc/dropdown-menu

Props

Only the vsrc surface is documented here. Everything else passes through to Radix Dropdown Menu.

// DropdownMenuContent

proptypedefaultnotes
glass"subtle" | "regular" | "heavy" | LiquidGlassOptions | falseOptics override for the menu pane. A preset name replaces the tuned values, an object merges over them, `false` disables refraction entirely.

Optics

Forwards `glass` to GlassSurface — engine defaults.

Accessibility

  • Radix menu semantics: arrow keys, typeahead, escape, focus return to the trigger.
  • Items are flat on the pane — one glass surface per menu.

Source

The exact file the CLI copies into your project — you own it after install.

// registry/vsrc/ui/dropdown-menu.tsx
"use client";

import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import type { GlassPreset, LiquidGlassOptions } from "vsrc/react";

import { GlassSurface } from "@/registry/vsrc/ui/glass-surface";
import { cn } from "@/lib/utils";

const DropdownMenu = DropdownMenuPrimitive.Root;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenuGroup = DropdownMenuPrimitive.Group;

function DropdownMenuContent({
  className,
  sideOffset = 8,
  glass,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content> & { glass?: LiquidGlassOptions | GlassPreset | false }) {
  return (
    <DropdownMenuPrimitive.Portal>
      <GlassSurface asChild glass={glass}>
        <DropdownMenuPrimitive.Content
          data-slot="dropdown-menu-content"
          sideOffset={sideOffset}
          className={cn("animate-glass-in relative z-50 min-w-44 p-1.5 text-foreground outline-none", className)}
          {...props}
        />
      </GlassSurface>
    </DropdownMenuPrimitive.Portal>
  );
}

function DropdownMenuItem({
  className,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item>) {
  return (
    <DropdownMenuPrimitive.Item
      data-slot="dropdown-menu-item"
      className={cn(
        "flex cursor-default items-center gap-2 rounded-[calc(var(--glass-radius)-8px)] px-3 py-2 text-sm",
        "outline-none select-none focus:bg-foreground/15",
        "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
        "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
        className,
      )}
      {...props}
    />
  );
}

function DropdownMenuLabel({
  className,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label>) {
  return (
    <DropdownMenuPrimitive.Label
      data-slot="dropdown-menu-label"
      className={cn("px-3 py-1.5 font-mono text-xs tracking-widest text-muted-foreground uppercase", className)}
      {...props}
    />
  );
}

function DropdownMenuSeparator({
  className,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
  return (
    <DropdownMenuPrimitive.Separator
      data-slot="dropdown-menu-separator"
      className={cn("mx-2 my-1 h-px bg-foreground/15", className)}
      {...props}
    />
  );
}

export {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuGroup,
};