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