menubar.tsx 8.2 KB

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