/ components / ui / dropdown-menu.tsx
dropdown-menu.tsx
  1  "use client"
  2  
  3  import * as React from "react"
  4  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
  5  import { Check, ChevronRight, Circle } from "lucide-react"
  6  
  7  import { cn } from "@/lib/utils"
  8  
  9  const DropdownMenu = DropdownMenuPrimitive.Root
 10  
 11  const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
 12  
 13  const DropdownMenuGroup = DropdownMenuPrimitive.Group
 14  
 15  const DropdownMenuPortal = DropdownMenuPrimitive.Portal
 16  
 17  const DropdownMenuSub = DropdownMenuPrimitive.Sub
 18  
 19  const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
 20  
 21  const DropdownMenuSubTrigger = React.forwardRef<
 22    React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
 23    React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
 24      inset?: boolean
 25    }
 26  >(({ className, inset, children, ...props }, ref) => (
 27    <DropdownMenuPrimitive.SubTrigger
 28      ref={ref}
 29      className={cn(
 30        "flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
 31        inset && "pl-8",
 32        className
 33      )}
 34      {...props}
 35    >
 36      {children}
 37      <ChevronRight className="ml-auto" />
 38    </DropdownMenuPrimitive.SubTrigger>
 39  ))
 40  DropdownMenuSubTrigger.displayName =
 41    DropdownMenuPrimitive.SubTrigger.displayName
 42  
 43  const DropdownMenuSubContent = React.forwardRef<
 44    React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
 45    React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
 46  >(({ className, ...props }, ref) => (
 47    <DropdownMenuPrimitive.SubContent
 48      ref={ref}
 49      className={cn(
 50        "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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",
 51        className
 52      )}
 53      {...props}
 54    />
 55  ))
 56  DropdownMenuSubContent.displayName =
 57    DropdownMenuPrimitive.SubContent.displayName
 58  
 59  const DropdownMenuContent = React.forwardRef<
 60    React.ElementRef<typeof DropdownMenuPrimitive.Content>,
 61    React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
 62  >(({ className, sideOffset = 4, ...props }, ref) => (
 63    <DropdownMenuPrimitive.Portal>
 64      <DropdownMenuPrimitive.Content
 65        ref={ref}
 66        sideOffset={sideOffset}
 67        className={cn(
 68          "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
 69          "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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",
 70          className
 71        )}
 72        {...props}
 73      />
 74    </DropdownMenuPrimitive.Portal>
 75  ))
 76  DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
 77  
 78  const DropdownMenuItem = React.forwardRef<
 79    React.ElementRef<typeof DropdownMenuPrimitive.Item>,
 80    React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
 81      inset?: boolean
 82    }
 83  >(({ className, inset, ...props }, ref) => (
 84    <DropdownMenuPrimitive.Item
 85      ref={ref}
 86      className={cn(
 87        "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
 88        inset && "pl-8",
 89        className
 90      )}
 91      {...props}
 92    />
 93  ))
 94  DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
 95  
 96  const DropdownMenuCheckboxItem = React.forwardRef<
 97    React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
 98    React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
 99  >(({ className, children, checked, ...props }, ref) => (
100    <DropdownMenuPrimitive.CheckboxItem
101      ref={ref}
102      className={cn(
103        "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
104        className
105      )}
106      checked={checked}
107      {...props}
108    >
109      <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
110        <DropdownMenuPrimitive.ItemIndicator>
111          <Check className="h-4 w-4" />
112        </DropdownMenuPrimitive.ItemIndicator>
113      </span>
114      {children}
115    </DropdownMenuPrimitive.CheckboxItem>
116  ))
117  DropdownMenuCheckboxItem.displayName =
118    DropdownMenuPrimitive.CheckboxItem.displayName
119  
120  const DropdownMenuRadioItem = React.forwardRef<
121    React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
122    React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
123  >(({ className, children, ...props }, ref) => (
124    <DropdownMenuPrimitive.RadioItem
125      ref={ref}
126      className={cn(
127        "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
128        className
129      )}
130      {...props}
131    >
132      <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
133        <DropdownMenuPrimitive.ItemIndicator>
134          <Circle className="h-2 w-2 fill-current" />
135        </DropdownMenuPrimitive.ItemIndicator>
136      </span>
137      {children}
138    </DropdownMenuPrimitive.RadioItem>
139  ))
140  DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
141  
142  const DropdownMenuLabel = React.forwardRef<
143    React.ElementRef<typeof DropdownMenuPrimitive.Label>,
144    React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
145      inset?: boolean
146    }
147  >(({ className, inset, ...props }, ref) => (
148    <DropdownMenuPrimitive.Label
149      ref={ref}
150      className={cn(
151        "px-2 py-1.5 text-sm font-semibold",
152        inset && "pl-8",
153        className
154      )}
155      {...props}
156    />
157  ))
158  DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
159  
160  const DropdownMenuSeparator = React.forwardRef<
161    React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
162    React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
163  >(({ className, ...props }, ref) => (
164    <DropdownMenuPrimitive.Separator
165      ref={ref}
166      className={cn("-mx-1 my-1 h-px bg-muted", className)}
167      {...props}
168    />
169  ))
170  DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
171  
172  const DropdownMenuShortcut = ({
173    className,
174    ...props
175  }: React.HTMLAttributes<HTMLSpanElement>) => {
176    return (
177      <span
178        className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
179        {...props}
180      />
181    )
182  }
183  DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
184  
185  export {
186    DropdownMenu,
187    DropdownMenuTrigger,
188    DropdownMenuContent,
189    DropdownMenuItem,
190    DropdownMenuCheckboxItem,
191    DropdownMenuRadioItem,
192    DropdownMenuLabel,
193    DropdownMenuSeparator,
194    DropdownMenuShortcut,
195    DropdownMenuGroup,
196    DropdownMenuPortal,
197    DropdownMenuSub,
198    DropdownMenuSubContent,
199    DropdownMenuSubTrigger,
200    DropdownMenuRadioGroup,
201  }