Dropdown Menu

by Dax · tsx · 46 lines

A polished dropdown menu with icons, keyboard shortcuts, separators, and a slide-down entrance. Built on Radix so it handles positioning, focus, and keyboard navigation out of the box.

dropdownmenunavigationradix
46 lines
"use client";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";

type MenuItem = {
  label: string;
  icon?: React.ReactNode;
  shortcut?: string;
  onClick?: () => void;
  separator?: boolean;
};

export function Dropdown({ trigger, items }: {
  trigger: React.ReactNode;
  items: MenuItem[];
}) {
  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger asChild>{trigger}</DropdownMenu.Trigger>
      <DropdownMenu.Portal>
        <DropdownMenu.Content
          sideOffset={6}
          className="min-w-[180px] rounded-xl border border-neutral-200 bg-white p-1 shadow-xl animate-in fade-in-0 zoom-in-95 slide-in-from-top-2"
        >
          {items.map((item, i) =>
            item.separator ? (
              <DropdownMenu.Separator key={i} className="my-1 h-px bg-neutral-100" />
            ) : (
              <DropdownMenu.Item
                key={i}
                onClick={item.onClick}
                className="flex cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-sm text-neutral-700 outline-none transition hover:bg-neutral-50 focus:bg-neutral-50"
              >
                {item.icon && <span className="text-neutral-400">{item.icon}</span>}
                <span className="flex-1">{item.label}</span>
                {item.shortcut && (
                  <span className="text-xs text-neutral-400">{item.shortcut}</span>
                )}
              </DropdownMenu.Item>
            )
          )}
        </DropdownMenu.Content>
      </DropdownMenu.Portal>
    </DropdownMenu.Root>
  );
}