menubar.tsx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. "use client"
  2. import * as React from "react"
  3. import * as MenubarPrimitive from "@radix-ui/react-menubar"
  4. import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
  5. import { cn } from "@/lib/utils"
  6. function Menubar({
  7. className,
  8. ...props
  9. }: React.ComponentProps<typeof MenubarPrimitive.Root>) {
  10. return (
  11. <MenubarPrimitive.Root
  12. data-slot="menubar"
  13. className={cn(
  14. "bg-background flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs",
  15. className
  16. )}
  17. {...props}
  18. />
  19. )
  20. }
  21. function MenubarMenu({
  22. ...props
  23. }: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
  24. return <MenubarPrimitive.Menu data-slot="menubar-menu" {...props} />
  25. }
  26. function MenubarGroup({
  27. ...props
  28. }: React.ComponentProps<typeof MenubarPrimitive.Group>) {
  29. return <MenubarPrimitive.Group data-slot="menubar-group" {...props} />
  30. }
  31. function MenubarPortal({
  32. ...props
  33. }: React.ComponentProps<typeof MenubarPrimitive.Portal>) {
  34. return <MenubarPrimitive.Portal data-slot="menubar-portal" {...props} />
  35. }
  36. function MenubarRadioGroup({
  37. ...props
  38. }: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
  39. return (
  40. <MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />
  41. )
  42. }
  43. function MenubarTrigger({
  44. className,
  45. ...props
  46. }: React.ComponentProps<typeof MenubarPrimitive.Trigger>) {
  47. return (
  48. <MenubarPrimitive.Trigger
  49. data-slot="menubar-trigger"
  50. className={cn(
  51. "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none",
  52. className
  53. )}
  54. {...props}
  55. />
  56. )
  57. }
  58. function MenubarContent({
  59. className,
  60. align = "start",
  61. alignOffset = -4,
  62. sideOffset = 8,
  63. ...props
  64. }: React.ComponentProps<typeof MenubarPrimitive.Content>) {
  65. return (
  66. <MenubarPortal>
  67. <MenubarPrimitive.Content
  68. data-slot="menubar-content"
  69. align={align}
  70. alignOffset={alignOffset}
  71. sideOffset={sideOffset}
  72. className={cn(
  73. "bg-popover text-popover-foreground 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 z-50 min-w-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-md",
  74. className
  75. )}
  76. {...props}
  77. />
  78. </MenubarPortal>
  79. )
  80. }
  81. function MenubarItem({
  82. className,
  83. inset,
  84. variant = "default",
  85. ...props
  86. }: React.ComponentProps<typeof MenubarPrimitive.Item> & {
  87. inset?: boolean
  88. variant?: "default" | "destructive"
  89. }) {
  90. return (
  91. <MenubarPrimitive.Item
  92. data-slot="menubar-item"
  93. data-inset={inset}
  94. data-variant={variant}
  95. className={cn(
  96. "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
  97. className
  98. )}
  99. {...props}
  100. />
  101. )
  102. }
  103. function MenubarCheckboxItem({
  104. className,
  105. children,
  106. checked,
  107. ...props
  108. }: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>) {
  109. return (
  110. <MenubarPrimitive.CheckboxItem
  111. data-slot="menubar-checkbox-item"
  112. className={cn(
  113. "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
  114. className
  115. )}
  116. checked={checked}
  117. {...props}
  118. >
  119. <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
  120. <MenubarPrimitive.ItemIndicator>
  121. <CheckIcon className="size-4" />
  122. </MenubarPrimitive.ItemIndicator>
  123. </span>
  124. {children}
  125. </MenubarPrimitive.CheckboxItem>
  126. )
  127. }
  128. function MenubarRadioItem({
  129. className,
  130. children,
  131. ...props
  132. }: React.ComponentProps<typeof MenubarPrimitive.RadioItem>) {
  133. return (
  134. <MenubarPrimitive.RadioItem
  135. data-slot="menubar-radio-item"
  136. className={cn(
  137. "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
  138. className
  139. )}
  140. {...props}
  141. >
  142. <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
  143. <MenubarPrimitive.ItemIndicator>
  144. <CircleIcon className="size-2 fill-current" />
  145. </MenubarPrimitive.ItemIndicator>
  146. </span>
  147. {children}
  148. </MenubarPrimitive.RadioItem>
  149. )
  150. }
  151. function MenubarLabel({
  152. className,
  153. inset,
  154. ...props
  155. }: React.ComponentProps<typeof MenubarPrimitive.Label> & {
  156. inset?: boolean
  157. }) {
  158. return (
  159. <MenubarPrimitive.Label
  160. data-slot="menubar-label"
  161. data-inset={inset}
  162. className={cn(
  163. "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
  164. className
  165. )}
  166. {...props}
  167. />
  168. )
  169. }
  170. function MenubarSeparator({
  171. className,
  172. ...props
  173. }: React.ComponentProps<typeof MenubarPrimitive.Separator>) {
  174. return (
  175. <MenubarPrimitive.Separator
  176. data-slot="menubar-separator"
  177. className={cn("bg-border -mx-1 my-1 h-px", className)}
  178. {...props}
  179. />
  180. )
  181. }
  182. function MenubarShortcut({
  183. className,
  184. ...props
  185. }: React.ComponentProps<"span">) {
  186. return (
  187. <span
  188. data-slot="menubar-shortcut"
  189. className={cn(
  190. "text-muted-foreground ml-auto text-xs tracking-widest",
  191. className
  192. )}
  193. {...props}
  194. />
  195. )
  196. }
  197. function MenubarSub({
  198. ...props
  199. }: React.ComponentProps<typeof MenubarPrimitive.Sub>) {
  200. return <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} />
  201. }
  202. function MenubarSubTrigger({
  203. className,
  204. inset,
  205. children,
  206. ...props
  207. }: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
  208. inset?: boolean
  209. }) {
  210. return (
  211. <MenubarPrimitive.SubTrigger
  212. data-slot="menubar-sub-trigger"
  213. data-inset={inset}
  214. className={cn(
  215. "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
  216. className
  217. )}
  218. {...props}
  219. >
  220. {children}
  221. <ChevronRightIcon className="ml-auto h-4 w-4" />
  222. </MenubarPrimitive.SubTrigger>
  223. )
  224. }
  225. function MenubarSubContent({
  226. className,
  227. ...props
  228. }: React.ComponentProps<typeof MenubarPrimitive.SubContent>) {
  229. return (
  230. <MenubarPrimitive.SubContent
  231. data-slot="menubar-sub-content"
  232. className={cn(
  233. "bg-popover 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 z-50 min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
  234. className
  235. )}
  236. {...props}
  237. />
  238. )
  239. }
  240. export {
  241. Menubar,
  242. MenubarPortal,
  243. MenubarMenu,
  244. MenubarTrigger,
  245. MenubarContent,
  246. MenubarGroup,
  247. MenubarSeparator,
  248. MenubarLabel,
  249. MenubarItem,
  250. MenubarShortcut,
  251. MenubarCheckboxItem,
  252. MenubarRadioGroup,
  253. MenubarRadioItem,
  254. MenubarSub,
  255. MenubarSubTrigger,
  256. MenubarSubContent,
  257. }