/ src / components / ui / dropdown-menu.tsx
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  };