← full-stack-fastapi-template  /  frontend/src/components/ui/dropdown-menu.tsx

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