menubar.tsx 7.8 KB

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