Dropdown Menu

Installation

Run the following command

npx vibes@latest add dropdown-menu

Usage

import { DropdownMenu } from '@/vibes/soul/primitives/dropdown-menu';import type { DropdownMenuProps } from '@/vibes/soul/primitives/dropdown-menu';import { Button } from '@/vibes/soul/primitives/button';import { EllipsisIcon } from 'lucide-react';function Usage() {  const items: DropdownMenuProps['items'] = [      {          type: 'item',          props: { children: 'Profile', onSelect: () => console.log('Profile clicked') },      },      { type: 'separator' },      {          type: 'checkbox',          props: {              children: 'Notifications',              checked: true,              onCheckedChange: (checked) => console.log('Notifications:', checked),          },      },      { type: 'separator' },      {          type: 'group',          items: [              { type: 'item', props: { children: 'Settings' } },              { type: 'item', props: { children: 'Help' } },              {                  type: 'sub',                  trigger: { props: { children: 'More options' } },                  content: {                      items: [                          { type: 'item', props: { children: 'Export' } },                          { type: 'item', props: { children: 'Import' } },                      ],                  },              },          ],      },      { type: 'separator' },      {          type: 'item',          props: {              children: 'Sign out',              variant: 'danger',              onSelect: () => console.log('Sign out clicked'),          },      },  ];  return (      <DropdownMenu          items={items}          label="User Actions"          trigger={              <Button shape="circle" size="small" variant="tertiary">                  <EllipsisIcon size={20} />              </Button>          }      />  );}

API Reference

PropTypeDefault
items*
MenuNode[]
label*
string
className
string
trigger
ReactNode
Default button with ellipsis icon
align
'start' | 'center' | 'end'
sideOffset
number
showScrollArea
boolean
true

The items prop accepts an array of MenuNode objects. Each node can be one of the following types:

Item Node

{    type: 'item';    props?: {        children: ReactNode;        onSelect?: () => void;        disabled?: boolean;        variant?: 'default' | 'danger';        icon?: ReactNode;        className?: string;    };}

Checkbox Node

{    type: 'checkbox';    props?: {        children: ReactNode;        checked?: boolean;        onCheckedChange?: (checked: boolean) => void;        disabled?: boolean;        className?: string;    };}

Separator Node

{    type: 'separator';    props?: {        className?: string;    };}

Group Node

{    type: 'group';    props?: {        className?: string;    };    items: MenuNode[];}

Sub Menu Node

{    type: 'sub';    props?: {        className?: string;    };    trigger: {        props?: {            children: ReactNode;            disabled?: boolean;            className?: string;        };    };    content?: {        props?: {            className?: string;        };        items: MenuNode[];    };}

CSS Variables

This component supports various CSS variables for theming. Here's a comprehensive list.

:root {  --dropdown-menu-background: var(--background);  --dropdown-menu-border: var(--contrast-100);  --dropdown-menu-focus: var(--primary);  --dropdown-menu-item-focus: var(--primary);  --dropdown-menu-item-text: var(--contrast-400);  --dropdown-menu-item-text-hover: var(--foreground);  --dropdown-menu-item-danger-text: var(--error);  --dropdown-menu-item-danger-text-hover: color-mix(in oklab, var(--error), black 75%);  --dropdown-menu-item-background: transparent;  --dropdown-menu-item-background-hover: var(--contrast-100);  --dropdown-menu-item-danger-background: var(--error);  --dropdown-menu-item-danger-background-hover: color-mix(in oklab, var(--error), white 75%);  --dropdown-menu-item-font-family: var(--font-family-body);  --dropdown-menu-seperator: var(--contrast-200);}