dropdown-menu.tsx raw

   1  import * as React from "react";
   2  import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
   3  import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
   4  
   5  import { cn } from "src/lib/utils";
   6  
   7  function DropdownMenu({
   8    ...props
   9  }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
  10    return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
  11  }
  12  
  13  function DropdownMenuPortal({
  14    ...props
  15  }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
  16    return (
  17      <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
  18    );
  19  }
  20  
  21  function DropdownMenuTrigger({
  22    ...props
  23  }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
  24    return (
  25      <DropdownMenuPrimitive.Trigger
  26        data-slot="dropdown-menu-trigger"
  27        {...props}
  28      />
  29    );
  30  }
  31  
  32  function DropdownMenuContent({
  33    className,
  34    sideOffset = 4,
  35    ...props
  36  }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
  37    return (
  38      <DropdownMenuPrimitive.Portal>
  39        <DropdownMenuPrimitive.Content
  40          data-slot="dropdown-menu-content"
  41          sideOffset={sideOffset}
  42          className={cn(
  43            "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
  44            className
  45          )}
  46          {...props}
  47        />
  48      </DropdownMenuPrimitive.Portal>
  49    );
  50  }
  51  
  52  function DropdownMenuGroup({
  53    ...props
  54  }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
  55    return (
  56      <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
  57    );
  58  }
  59  
  60  function DropdownMenuItem({
  61    className,
  62    inset,
  63    variant = "default",
  64    ...props
  65  }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
  66    inset?: boolean;
  67    variant?: "default" | "destructive";
  68  }) {
  69    return (
  70      <DropdownMenuPrimitive.Item
  71        data-slot="dropdown-menu-item"
  72        data-inset={inset}
  73        data-variant={variant}
  74        className={cn(
  75          "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 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 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
  76          className
  77        )}
  78        {...props}
  79      />
  80    );
  81  }
  82  
  83  function DropdownMenuCheckboxItem({
  84    className,
  85    children,
  86    checked,
  87    ...props
  88  }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
  89    return (
  90      <DropdownMenuPrimitive.CheckboxItem
  91        data-slot="dropdown-menu-checkbox-item"
  92        className={cn(
  93          "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
  94          className
  95        )}
  96        checked={checked}
  97        {...props}
  98      >
  99        <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
 100          <DropdownMenuPrimitive.ItemIndicator>
 101            <CheckIcon className="size-4" />
 102          </DropdownMenuPrimitive.ItemIndicator>
 103        </span>
 104        {children}
 105      </DropdownMenuPrimitive.CheckboxItem>
 106    );
 107  }
 108  
 109  function DropdownMenuRadioGroup({
 110    ...props
 111  }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
 112    return (
 113      <DropdownMenuPrimitive.RadioGroup
 114        data-slot="dropdown-menu-radio-group"
 115        {...props}
 116      />
 117    );
 118  }
 119  
 120  function DropdownMenuRadioItem({
 121    className,
 122    children,
 123    ...props
 124  }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
 125    return (
 126      <DropdownMenuPrimitive.RadioItem
 127        data-slot="dropdown-menu-radio-item"
 128        className={cn(
 129          "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
 130          className
 131        )}
 132        {...props}
 133      >
 134        <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
 135          <DropdownMenuPrimitive.ItemIndicator>
 136            <CircleIcon className="size-2 fill-current" />
 137          </DropdownMenuPrimitive.ItemIndicator>
 138        </span>
 139        {children}
 140      </DropdownMenuPrimitive.RadioItem>
 141    );
 142  }
 143  
 144  function DropdownMenuLabel({
 145    className,
 146    inset,
 147    ...props
 148  }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
 149    inset?: boolean;
 150  }) {
 151    return (
 152      <DropdownMenuPrimitive.Label
 153        data-slot="dropdown-menu-label"
 154        data-inset={inset}
 155        className={cn(
 156          "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
 157          className
 158        )}
 159        {...props}
 160      />
 161    );
 162  }
 163  
 164  function DropdownMenuSeparator({
 165    className,
 166    ...props
 167  }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
 168    return (
 169      <DropdownMenuPrimitive.Separator
 170        data-slot="dropdown-menu-separator"
 171        className={cn("-mx-1 my-1 h-px bg-border", className)}
 172        {...props}
 173      />
 174    );
 175  }
 176  
 177  function DropdownMenuShortcut({
 178    className,
 179    ...props
 180  }: React.ComponentProps<"span">) {
 181    return (
 182      <span
 183        data-slot="dropdown-menu-shortcut"
 184        className={cn(
 185          "ml-auto text-xs tracking-widest text-muted-foreground",
 186          className
 187        )}
 188        {...props}
 189      />
 190    );
 191  }
 192  
 193  function DropdownMenuSub({
 194    ...props
 195  }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
 196    return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
 197  }
 198  
 199  function DropdownMenuSubTrigger({
 200    className,
 201    inset,
 202    children,
 203    ...props
 204  }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
 205    inset?: boolean;
 206  }) {
 207    return (
 208      <DropdownMenuPrimitive.SubTrigger
 209        data-slot="dropdown-menu-sub-trigger"
 210        data-inset={inset}
 211        className={cn(
 212          "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
 213          className
 214        )}
 215        {...props}
 216      >
 217        {children}
 218        <ChevronRightIcon className="ml-auto size-4" />
 219      </DropdownMenuPrimitive.SubTrigger>
 220    );
 221  }
 222  
 223  function DropdownMenuSubContent({
 224    className,
 225    ...props
 226  }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
 227    return (
 228      <DropdownMenuPrimitive.SubContent
 229        data-slot="dropdown-menu-sub-content"
 230        className={cn(
 231          "z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
 232          className
 233        )}
 234        {...props}
 235      />
 236    );
 237  }
 238  
 239  export {
 240    DropdownMenu,
 241    DropdownMenuPortal,
 242    DropdownMenuTrigger,
 243    DropdownMenuContent,
 244    DropdownMenuGroup,
 245    DropdownMenuLabel,
 246    DropdownMenuItem,
 247    DropdownMenuCheckboxItem,
 248    DropdownMenuRadioGroup,
 249    DropdownMenuRadioItem,
 250    DropdownMenuSeparator,
 251    DropdownMenuShortcut,
 252    DropdownMenuSub,
 253    DropdownMenuSubTrigger,
 254    DropdownMenuSubContent,
 255  };
 256