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