context-menu.tsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import * as React from 'react'
  2. import * as ContextMenuPrimitive from '@radix-ui/react-context-menu'
  3. import { Check, ChevronRight, Circle } from 'lucide-react'
  4. // @ts-ignore
  5. import { cn } from '@/lib/utils'
  6. const ContextMenu = ContextMenuPrimitive.Root
  7. const ContextMenuTrigger = ContextMenuPrimitive.Trigger
  8. const ContextMenuGroup = ContextMenuPrimitive.Group
  9. const ContextMenuPortal = ContextMenuPrimitive.Portal
  10. const ContextMenuSub = ContextMenuPrimitive.Sub
  11. const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
  12. const ContextMenuSubTrigger = React.forwardRef<
  13. React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
  14. React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
  15. inset?: boolean
  16. }
  17. >(({ className, inset, children, ...props }, ref) => (
  18. <ContextMenuPrimitive.SubTrigger
  19. ref={ref}
  20. className={cn(
  21. 'ui__context-menu-sub-trigger',
  22. 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm ' +
  23. 'outline-none focus:bg-accent focus:text-accent-foreground ' +
  24. 'data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
  25. inset && 'pl-8',
  26. className
  27. )}
  28. {...props}
  29. >
  30. {children}
  31. <ChevronRight className="ml-auto h-4 w-4"/>
  32. </ContextMenuPrimitive.SubTrigger>
  33. ))
  34. ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
  35. const ContextMenuSubContent = React.forwardRef<
  36. React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
  37. React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
  38. >(({ className, ...props }, ref) => (
  39. <ContextMenuPrimitive.SubContent
  40. ref={ref}
  41. className={cn(
  42. 'ui__context-menu-sub-content',
  43. 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in' +
  44. ' data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95' +
  45. ' data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2' +
  46. ' data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
  47. className
  48. )}
  49. {...props}
  50. />
  51. ))
  52. ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
  53. const ContextMenuContent = React.forwardRef<
  54. React.ElementRef<typeof ContextMenuPrimitive.Content>,
  55. React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
  56. >(({ className, ...props }, ref) => (
  57. <ContextMenuPrimitive.Portal>
  58. <ContextMenuPrimitive.Content
  59. ref={ref}
  60. className={cn(
  61. 'ui__context-menu-content',
  62. 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 ' +
  63. 'text-popover-foreground shadow-md animate-in fade-in-80 ' +
  64. 'data-[state=open]:animate-in data-[state=closed]:animate-out ' +
  65. 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 ' +
  66. 'data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 ' +
  67. 'data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
  68. className
  69. )}
  70. {...props}
  71. />
  72. </ContextMenuPrimitive.Portal>
  73. ))
  74. ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName
  75. const ContextMenuItem = React.forwardRef<
  76. React.ElementRef<typeof ContextMenuPrimitive.Item>,
  77. React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
  78. inset?: boolean
  79. }
  80. >(({ className, inset, ...props }, ref) => (
  81. <ContextMenuPrimitive.Item
  82. ref={ref}
  83. className={cn(
  84. 'relative flex cursor-default select-none items-center rounded-sm px-2 ' +
  85. 'text-popover-foreground/75 hover:text-popover-foreground/100 py-1.5 text-sm ' +
  86. 'outline-none focus:bg-accent focus:text-accent-foreground ' +
  87. 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
  88. inset && 'pl-8',
  89. className
  90. )}
  91. {...props}
  92. />
  93. ))
  94. ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
  95. const ContextMenuCheckboxItem = React.forwardRef<
  96. React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
  97. React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
  98. >(({ className, children, checked, ...props }, ref) => (
  99. <ContextMenuPrimitive.CheckboxItem
  100. ref={ref}
  101. className={cn(
  102. 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm ' +
  103. 'text-popover-foreground/75 hover:text-popover-foreground/100 ' +
  104. 'outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none ' +
  105. 'data-[disabled]:opacity-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. <ContextMenuPrimitive.ItemIndicator>
  113. <Check className="h-4 w-4"/>
  114. </ContextMenuPrimitive.ItemIndicator>
  115. </span>
  116. {children}
  117. </ContextMenuPrimitive.CheckboxItem>
  118. ))
  119. ContextMenuCheckboxItem.displayName =
  120. ContextMenuPrimitive.CheckboxItem.displayName
  121. const ContextMenuRadioItem = React.forwardRef<
  122. React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
  123. React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
  124. >(({ className, children, ...props }, ref) => (
  125. <ContextMenuPrimitive.RadioItem
  126. ref={ref}
  127. className={cn(
  128. 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 ' +
  129. 'text-popover-foreground/75 hover:text-popover-foreground/100 ' +
  130. 'pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground ' +
  131. 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
  132. className
  133. )}
  134. {...props}
  135. >
  136. <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
  137. <ContextMenuPrimitive.ItemIndicator>
  138. <Circle className="h-2 w-2 fill-current"/>
  139. </ContextMenuPrimitive.ItemIndicator>
  140. </span>
  141. {children}
  142. </ContextMenuPrimitive.RadioItem>
  143. ))
  144. ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName
  145. const ContextMenuLabel = React.forwardRef<
  146. React.ElementRef<typeof ContextMenuPrimitive.Label>,
  147. React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
  148. inset?: boolean
  149. }
  150. >(({ className, inset, ...props }, ref) => (
  151. <ContextMenuPrimitive.Label
  152. ref={ref}
  153. className={cn(
  154. 'px-2 py-1.5 text-sm font-semibold text-foreground',
  155. inset && 'pl-8',
  156. className
  157. )}
  158. {...props}
  159. />
  160. ))
  161. ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName
  162. const ContextMenuSeparator = React.forwardRef<
  163. React.ElementRef<typeof ContextMenuPrimitive.Separator>,
  164. React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
  165. >(({ className, ...props }, ref) => (
  166. <ContextMenuPrimitive.Separator
  167. ref={ref}
  168. className={cn('-mx-1 my-1 h-px bg-border', className)}
  169. {...props}
  170. />
  171. ))
  172. ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
  173. const ContextMenuShortcut = ({
  174. className,
  175. ...props
  176. }: React.HTMLAttributes<HTMLSpanElement>) => {
  177. return (
  178. <span
  179. className={cn(
  180. 'ml-auto text-xs tracking-widest text-muted-foreground',
  181. className
  182. )}
  183. {...props}
  184. />
  185. )
  186. }
  187. ContextMenuShortcut.displayName = 'ContextMenuShortcut'
  188. export {
  189. ContextMenu,
  190. ContextMenuTrigger,
  191. ContextMenuContent,
  192. ContextMenuItem,
  193. ContextMenuCheckboxItem,
  194. ContextMenuRadioItem,
  195. ContextMenuLabel,
  196. ContextMenuSeparator,
  197. ContextMenuShortcut,
  198. ContextMenuGroup,
  199. ContextMenuPortal,
  200. ContextMenuSub,
  201. ContextMenuSubContent,
  202. ContextMenuSubTrigger,
  203. ContextMenuRadioGroup,
  204. }