@majapisoftwares/ui 5.0.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/AGENTS.md +11 -1
  2. package/README.md +19 -11
  3. package/THIRD_PARTY_NOTICES.md +25 -1
  4. package/components.json +1 -1
  5. package/docs/MIGRATION-6.0.md +33 -0
  6. package/docs/STANDARDS.md +11 -6
  7. package/docs/VERSIONING.md +28 -0
  8. package/package.json +19 -10
  9. package/scripts/check-standards.mjs +2 -0
  10. package/src/components/ui/accordion.tsx +82 -0
  11. package/src/components/ui/alert-dialog.tsx +187 -0
  12. package/src/components/ui/alert.tsx +2 -2
  13. package/src/components/ui/avatar.tsx +108 -0
  14. package/src/components/ui/badge.tsx +1 -1
  15. package/src/components/ui/breadcrumb.tsx +125 -0
  16. package/src/components/ui/button-group.tsx +89 -0
  17. package/src/components/ui/button.tsx +12 -16
  18. package/src/components/ui/card.tsx +101 -0
  19. package/src/components/ui/checkbox.tsx +27 -0
  20. package/src/components/ui/collapsible.tsx +21 -0
  21. package/src/components/ui/context-menu.tsx +271 -0
  22. package/src/components/ui/dialog.tsx +161 -0
  23. package/src/components/ui/dropdown-menu.tsx +271 -0
  24. package/src/components/ui/empty.tsx +4 -4
  25. package/src/components/ui/field.tsx +6 -6
  26. package/src/components/ui/hover-card.tsx +50 -0
  27. package/src/components/ui/input-group.tsx +155 -0
  28. package/src/components/ui/input.tsx +1 -1
  29. package/src/components/ui/kbd.tsx +28 -0
  30. package/src/components/ui/orb.css +1002 -0
  31. package/src/components/ui/orb.tsx +752 -0
  32. package/src/components/ui/popover.tsx +89 -0
  33. package/src/components/ui/progress.tsx +82 -0
  34. package/src/components/ui/radio-group.tsx +37 -0
  35. package/src/components/ui/scroll-area.tsx +54 -0
  36. package/src/components/ui/select.tsx +204 -0
  37. package/src/components/ui/sheet.tsx +8 -6
  38. package/src/components/ui/sidebar.tsx +1035 -0
  39. package/src/components/ui/skeleton.tsx +1 -1
  40. package/src/components/ui/slider.tsx +53 -0
  41. package/src/components/ui/sonner.tsx +44 -0
  42. package/src/components/ui/spinner.tsx +32 -0
  43. package/src/components/ui/switch.tsx +31 -0
  44. package/src/components/ui/table.tsx +10 -4
  45. package/src/components/ui/tabs.tsx +2 -2
  46. package/src/components/ui/textarea.tsx +19 -0
  47. package/src/components/ui/toggle-group.tsx +89 -0
  48. package/src/components/ui/toggle.tsx +44 -0
  49. package/src/components/ui/tooltip.tsx +65 -0
  50. package/src/hooks/use-mobile.ts +20 -0
  51. package/src/lib/i18n.tsx +109 -0
  52. package/src/patterns/app-shell.tsx +162 -53
  53. package/src/patterns/data-table.tsx +2441 -180
  54. package/src/patterns/details-sheet.tsx +10 -7
  55. package/src/patterns/filter-tabs.tsx +53 -0
  56. package/src/patterns/majapi-logo.tsx +32 -0
  57. package/src/patterns/page-header.tsx +44 -22
  58. package/src/patterns/preferences.tsx +172 -0
  59. package/src/patterns/status-badge.tsx +4 -3
  60. package/src/patterns/user-menu.tsx +218 -0
  61. package/src/patterns/workspace-switcher.tsx +173 -0
  62. package/src/styles.css +41 -341
  63. package/src/templates/admin-dashboard.tsx +14 -33
  64. package/src/templates/auth-layout.tsx +18 -24
  65. package/src/templates/login-form.tsx +97 -65
  66. package/src/templates/signup-form.tsx +139 -99
  67. package/src/components/ui/native-select.tsx +0 -66
  68. package/src/vite-env.d.ts +0 -1
@@ -0,0 +1,271 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu";
5
+ import { cn } from "cn";
6
+ import { ChevronRightIcon, CheckIcon } from "lucide-react";
7
+
8
+ function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
9
+ return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />;
10
+ }
11
+
12
+ function ContextMenuPortal({ ...props }: ContextMenuPrimitive.Portal.Props) {
13
+ return (
14
+ <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
15
+ );
16
+ }
17
+
18
+ function ContextMenuTrigger({
19
+ className,
20
+ ...props
21
+ }: ContextMenuPrimitive.Trigger.Props) {
22
+ return (
23
+ <ContextMenuPrimitive.Trigger
24
+ data-slot="context-menu-trigger"
25
+ className={cn("select-none", className)}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function ContextMenuContent({
32
+ className,
33
+ align = "start",
34
+ alignOffset = 4,
35
+ side = "right",
36
+ sideOffset = 0,
37
+ ...props
38
+ }: ContextMenuPrimitive.Popup.Props &
39
+ Pick<
40
+ ContextMenuPrimitive.Positioner.Props,
41
+ "align" | "alignOffset" | "side" | "sideOffset"
42
+ >) {
43
+ return (
44
+ <ContextMenuPrimitive.Portal>
45
+ <ContextMenuPrimitive.Positioner
46
+ className="isolate z-50 outline-none"
47
+ align={align}
48
+ alignOffset={alignOffset}
49
+ side={side}
50
+ sideOffset={sideOffset}
51
+ >
52
+ <ContextMenuPrimitive.Popup
53
+ data-slot="context-menu-content"
54
+ className={cn(
55
+ "z-50 max-h-(--available-height) min-w-48 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-3xl bg-popover p-1.5 text-popover-foreground shadow-lg ring-1 ring-foreground/5 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 dark:ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
56
+ className,
57
+ )}
58
+ {...props}
59
+ />
60
+ </ContextMenuPrimitive.Positioner>
61
+ </ContextMenuPrimitive.Portal>
62
+ );
63
+ }
64
+
65
+ function ContextMenuGroup({ ...props }: ContextMenuPrimitive.Group.Props) {
66
+ return (
67
+ <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
68
+ );
69
+ }
70
+
71
+ function ContextMenuLabel({
72
+ className,
73
+ inset,
74
+ ...props
75
+ }: ContextMenuPrimitive.GroupLabel.Props & {
76
+ inset?: boolean;
77
+ }) {
78
+ return (
79
+ <ContextMenuPrimitive.GroupLabel
80
+ data-slot="context-menu-label"
81
+ data-inset={inset}
82
+ className={cn(
83
+ "px-3 py-2.5 text-xs text-muted-foreground data-inset:pl-9.5",
84
+ className,
85
+ )}
86
+ {...props}
87
+ />
88
+ );
89
+ }
90
+
91
+ function ContextMenuItem({
92
+ className,
93
+ inset,
94
+ variant = "default",
95
+ ...props
96
+ }: ContextMenuPrimitive.Item.Props & {
97
+ inset?: boolean;
98
+ variant?: "default" | "destructive";
99
+ }) {
100
+ return (
101
+ <ContextMenuPrimitive.Item
102
+ data-slot="context-menu-item"
103
+ data-inset={inset}
104
+ data-variant={variant}
105
+ className={cn(
106
+ "group/context-menu-item relative flex cursor-default items-center gap-2.5 rounded-2xl px-3 py-2 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-9.5 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
107
+ className,
108
+ )}
109
+ {...props}
110
+ />
111
+ );
112
+ }
113
+
114
+ function ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {
115
+ return (
116
+ <ContextMenuPrimitive.SubmenuRoot data-slot="context-menu-sub" {...props} />
117
+ );
118
+ }
119
+
120
+ function ContextMenuSubTrigger({
121
+ className,
122
+ inset,
123
+ children,
124
+ ...props
125
+ }: ContextMenuPrimitive.SubmenuTrigger.Props & {
126
+ inset?: boolean;
127
+ }) {
128
+ return (
129
+ <ContextMenuPrimitive.SubmenuTrigger
130
+ data-slot="context-menu-sub-trigger"
131
+ data-inset={inset}
132
+ className={cn(
133
+ "flex cursor-default items-center rounded-2xl px-3 py-2 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-9.5 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
134
+ className,
135
+ )}
136
+ {...props}
137
+ >
138
+ {children}
139
+ <ChevronRightIcon className="ml-auto" />
140
+ </ContextMenuPrimitive.SubmenuTrigger>
141
+ );
142
+ }
143
+
144
+ function ContextMenuSubContent({
145
+ ...props
146
+ }: React.ComponentProps<typeof ContextMenuContent>) {
147
+ return (
148
+ <ContextMenuContent
149
+ data-slot="context-menu-sub-content"
150
+ className="shadow-lg"
151
+ side="right"
152
+ {...props}
153
+ />
154
+ );
155
+ }
156
+
157
+ function ContextMenuCheckboxItem({
158
+ className,
159
+ children,
160
+ checked,
161
+ inset,
162
+ ...props
163
+ }: ContextMenuPrimitive.CheckboxItem.Props & {
164
+ inset?: boolean;
165
+ }) {
166
+ return (
167
+ <ContextMenuPrimitive.CheckboxItem
168
+ data-slot="context-menu-checkbox-item"
169
+ data-inset={inset}
170
+ className={cn(
171
+ "relative flex cursor-default items-center gap-2.5 rounded-2xl py-2 pr-8 pl-3 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-9.5 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
172
+ className,
173
+ )}
174
+ checked={checked}
175
+ {...props}
176
+ >
177
+ <span className="pointer-events-none absolute right-2">
178
+ <ContextMenuPrimitive.CheckboxItemIndicator>
179
+ <CheckIcon />
180
+ </ContextMenuPrimitive.CheckboxItemIndicator>
181
+ </span>
182
+ {children}
183
+ </ContextMenuPrimitive.CheckboxItem>
184
+ );
185
+ }
186
+
187
+ function ContextMenuRadioGroup({
188
+ ...props
189
+ }: ContextMenuPrimitive.RadioGroup.Props) {
190
+ return (
191
+ <ContextMenuPrimitive.RadioGroup
192
+ data-slot="context-menu-radio-group"
193
+ {...props}
194
+ />
195
+ );
196
+ }
197
+
198
+ function ContextMenuRadioItem({
199
+ className,
200
+ children,
201
+ inset,
202
+ ...props
203
+ }: ContextMenuPrimitive.RadioItem.Props & {
204
+ inset?: boolean;
205
+ }) {
206
+ return (
207
+ <ContextMenuPrimitive.RadioItem
208
+ data-slot="context-menu-radio-item"
209
+ data-inset={inset}
210
+ className={cn(
211
+ "relative flex cursor-default items-center gap-2.5 rounded-2xl py-2 pr-8 pl-3 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-9.5 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
212
+ className,
213
+ )}
214
+ {...props}
215
+ >
216
+ <span className="pointer-events-none absolute right-2">
217
+ <ContextMenuPrimitive.RadioItemIndicator>
218
+ <CheckIcon />
219
+ </ContextMenuPrimitive.RadioItemIndicator>
220
+ </span>
221
+ {children}
222
+ </ContextMenuPrimitive.RadioItem>
223
+ );
224
+ }
225
+
226
+ function ContextMenuSeparator({
227
+ className,
228
+ ...props
229
+ }: ContextMenuPrimitive.Separator.Props) {
230
+ return (
231
+ <ContextMenuPrimitive.Separator
232
+ data-slot="context-menu-separator"
233
+ className={cn("-mx-1.5 my-1.5 h-px bg-border/50", className)}
234
+ {...props}
235
+ />
236
+ );
237
+ }
238
+
239
+ function ContextMenuShortcut({
240
+ className,
241
+ ...props
242
+ }: React.ComponentProps<"span">) {
243
+ return (
244
+ <span
245
+ data-slot="context-menu-shortcut"
246
+ className={cn(
247
+ "ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground",
248
+ className,
249
+ )}
250
+ {...props}
251
+ />
252
+ );
253
+ }
254
+
255
+ export {
256
+ ContextMenu,
257
+ ContextMenuTrigger,
258
+ ContextMenuContent,
259
+ ContextMenuItem,
260
+ ContextMenuCheckboxItem,
261
+ ContextMenuRadioItem,
262
+ ContextMenuLabel,
263
+ ContextMenuSeparator,
264
+ ContextMenuShortcut,
265
+ ContextMenuGroup,
266
+ ContextMenuPortal,
267
+ ContextMenuSub,
268
+ ContextMenuSubContent,
269
+ ContextMenuSubTrigger,
270
+ ContextMenuRadioGroup,
271
+ };
@@ -0,0 +1,161 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
5
+ import { cn } from "cn";
6
+ import { useUiMessages } from "../../lib/i18n";
7
+
8
+ import { Button } from "./button";
9
+ import { XIcon } from "lucide-react";
10
+
11
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
12
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
13
+ }
14
+
15
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
16
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
17
+ }
18
+
19
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
20
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
21
+ }
22
+
23
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
24
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
25
+ }
26
+
27
+ function DialogOverlay({
28
+ className,
29
+ ...props
30
+ }: DialogPrimitive.Backdrop.Props) {
31
+ return (
32
+ <DialogPrimitive.Backdrop
33
+ data-slot="dialog-overlay"
34
+ className={cn(
35
+ "fixed inset-0 isolate z-50 bg-black/30 duration-100 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
36
+ className,
37
+ )}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function DialogContent({
44
+ className,
45
+ children,
46
+ showCloseButton = true,
47
+ ...props
48
+ }: DialogPrimitive.Popup.Props & {
49
+ showCloseButton?: boolean;
50
+ }) {
51
+ const messages = useUiMessages();
52
+ return (
53
+ <DialogPortal>
54
+ <DialogOverlay />
55
+ <DialogPrimitive.Popup
56
+ data-slot="dialog-content"
57
+ className={cn(
58
+ "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 rounded-4xl bg-popover p-6 text-sm text-popover-foreground shadow-xl ring-1 ring-foreground/5 duration-100 outline-none sm:max-w-md dark:ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
59
+ className,
60
+ )}
61
+ {...props}
62
+ >
63
+ {children}
64
+ {showCloseButton && (
65
+ <DialogPrimitive.Close
66
+ data-slot="dialog-close"
67
+ render={
68
+ <Button
69
+ variant="ghost"
70
+ className="absolute top-4 right-4 bg-secondary"
71
+ size="icon-sm"
72
+ />
73
+ }
74
+ >
75
+ <XIcon />
76
+ <span className="sr-only">{messages.close}</span>
77
+ </DialogPrimitive.Close>
78
+ )}
79
+ </DialogPrimitive.Popup>
80
+ </DialogPortal>
81
+ );
82
+ }
83
+
84
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
85
+ return (
86
+ <div
87
+ data-slot="dialog-header"
88
+ className={cn("flex flex-col gap-1.5", className)}
89
+ {...props}
90
+ />
91
+ );
92
+ }
93
+
94
+ function DialogFooter({
95
+ className,
96
+ showCloseButton = false,
97
+ children,
98
+ ...props
99
+ }: React.ComponentProps<"div"> & {
100
+ showCloseButton?: boolean;
101
+ }) {
102
+ return (
103
+ <div
104
+ data-slot="dialog-footer"
105
+ className={cn(
106
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
107
+ className,
108
+ )}
109
+ {...props}
110
+ >
111
+ {children}
112
+ {showCloseButton && (
113
+ <DialogPrimitive.Close render={<Button variant="outline" />}>
114
+ Close
115
+ </DialogPrimitive.Close>
116
+ )}
117
+ </div>
118
+ );
119
+ }
120
+
121
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
122
+ return (
123
+ <DialogPrimitive.Title
124
+ data-slot="dialog-title"
125
+ className={cn(
126
+ "font-heading text-base leading-none font-medium",
127
+ className,
128
+ )}
129
+ {...props}
130
+ />
131
+ );
132
+ }
133
+
134
+ function DialogDescription({
135
+ className,
136
+ ...props
137
+ }: DialogPrimitive.Description.Props) {
138
+ return (
139
+ <DialogPrimitive.Description
140
+ data-slot="dialog-description"
141
+ className={cn(
142
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
143
+ className,
144
+ )}
145
+ {...props}
146
+ />
147
+ );
148
+ }
149
+
150
+ export {
151
+ Dialog,
152
+ DialogClose,
153
+ DialogContent,
154
+ DialogDescription,
155
+ DialogFooter,
156
+ DialogHeader,
157
+ DialogOverlay,
158
+ DialogPortal,
159
+ DialogTitle,
160
+ DialogTrigger,
161
+ };
@@ -0,0 +1,271 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
5
+ import { cn } from "cn";
6
+ import { ChevronRightIcon, CheckIcon } from "lucide-react";
7
+
8
+ function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
9
+ return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
10
+ }
11
+
12
+ function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
13
+ return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
14
+ }
15
+
16
+ function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
17
+ return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
18
+ }
19
+
20
+ function DropdownMenuContent({
21
+ align = "start",
22
+ alignOffset = 0,
23
+ side = "bottom",
24
+ sideOffset = 4,
25
+ className,
26
+ ...props
27
+ }: MenuPrimitive.Popup.Props &
28
+ Pick<
29
+ MenuPrimitive.Positioner.Props,
30
+ "align" | "alignOffset" | "side" | "sideOffset"
31
+ >) {
32
+ return (
33
+ <MenuPrimitive.Portal>
34
+ <MenuPrimitive.Positioner
35
+ className="isolate z-50 outline-none"
36
+ align={align}
37
+ alignOffset={alignOffset}
38
+ side={side}
39
+ sideOffset={sideOffset}
40
+ >
41
+ <MenuPrimitive.Popup
42
+ data-slot="dropdown-menu-content"
43
+ className={cn(
44
+ "z-50 max-h-(--available-height) w-(--anchor-width) min-w-48 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-3xl bg-popover p-1.5 text-popover-foreground shadow-lg ring-1 ring-foreground/5 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 dark:ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95",
45
+ className,
46
+ )}
47
+ {...props}
48
+ />
49
+ </MenuPrimitive.Positioner>
50
+ </MenuPrimitive.Portal>
51
+ );
52
+ }
53
+
54
+ function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
55
+ return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
56
+ }
57
+
58
+ function DropdownMenuLabel({
59
+ className,
60
+ inset,
61
+ ...props
62
+ }: MenuPrimitive.GroupLabel.Props & {
63
+ inset?: boolean;
64
+ }) {
65
+ return (
66
+ <MenuPrimitive.GroupLabel
67
+ data-slot="dropdown-menu-label"
68
+ data-inset={inset}
69
+ className={cn(
70
+ "px-3 py-2.5 text-xs text-muted-foreground data-inset:pl-9.5",
71
+ className,
72
+ )}
73
+ {...props}
74
+ />
75
+ );
76
+ }
77
+
78
+ function DropdownMenuItem({
79
+ className,
80
+ inset,
81
+ variant = "default",
82
+ ...props
83
+ }: MenuPrimitive.Item.Props & {
84
+ inset?: boolean;
85
+ variant?: "default" | "destructive";
86
+ }) {
87
+ return (
88
+ <MenuPrimitive.Item
89
+ data-slot="dropdown-menu-item"
90
+ data-inset={inset}
91
+ data-variant={variant}
92
+ className={cn(
93
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-2.5 rounded-2xl px-3 py-2 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-9.5 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
94
+ className,
95
+ )}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
102
+ return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
103
+ }
104
+
105
+ function DropdownMenuSubTrigger({
106
+ className,
107
+ inset,
108
+ children,
109
+ ...props
110
+ }: MenuPrimitive.SubmenuTrigger.Props & {
111
+ inset?: boolean;
112
+ }) {
113
+ return (
114
+ <MenuPrimitive.SubmenuTrigger
115
+ data-slot="dropdown-menu-sub-trigger"
116
+ data-inset={inset}
117
+ className={cn(
118
+ "flex cursor-default items-center gap-2 rounded-2xl px-3 py-2 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-9.5 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
119
+ className,
120
+ )}
121
+ {...props}
122
+ >
123
+ {children}
124
+ <ChevronRightIcon className="ml-auto" />
125
+ </MenuPrimitive.SubmenuTrigger>
126
+ );
127
+ }
128
+
129
+ function DropdownMenuSubContent({
130
+ align = "start",
131
+ alignOffset = -3,
132
+ side = "right",
133
+ sideOffset = 0,
134
+ className,
135
+ ...props
136
+ }: React.ComponentProps<typeof DropdownMenuContent>) {
137
+ return (
138
+ <DropdownMenuContent
139
+ data-slot="dropdown-menu-sub-content"
140
+ className={cn(
141
+ "w-auto min-w-36 rounded-3xl bg-popover p-1.5 text-popover-foreground shadow-lg ring-1 ring-foreground/5 duration-100 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 dark:ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
142
+ className,
143
+ )}
144
+ align={align}
145
+ alignOffset={alignOffset}
146
+ side={side}
147
+ sideOffset={sideOffset}
148
+ {...props}
149
+ />
150
+ );
151
+ }
152
+
153
+ function DropdownMenuCheckboxItem({
154
+ className,
155
+ children,
156
+ checked,
157
+ inset,
158
+ ...props
159
+ }: MenuPrimitive.CheckboxItem.Props & {
160
+ inset?: boolean;
161
+ }) {
162
+ return (
163
+ <MenuPrimitive.CheckboxItem
164
+ data-slot="dropdown-menu-checkbox-item"
165
+ data-inset={inset}
166
+ className={cn(
167
+ "relative flex cursor-default items-center gap-2.5 rounded-2xl py-2 pr-8 pl-3 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-9.5 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
168
+ className,
169
+ )}
170
+ checked={checked}
171
+ {...props}
172
+ >
173
+ <span
174
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
175
+ data-slot="dropdown-menu-checkbox-item-indicator"
176
+ >
177
+ <MenuPrimitive.CheckboxItemIndicator>
178
+ <CheckIcon />
179
+ </MenuPrimitive.CheckboxItemIndicator>
180
+ </span>
181
+ {children}
182
+ </MenuPrimitive.CheckboxItem>
183
+ );
184
+ }
185
+
186
+ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
187
+ return (
188
+ <MenuPrimitive.RadioGroup
189
+ data-slot="dropdown-menu-radio-group"
190
+ {...props}
191
+ />
192
+ );
193
+ }
194
+
195
+ function DropdownMenuRadioItem({
196
+ className,
197
+ children,
198
+ inset,
199
+ ...props
200
+ }: MenuPrimitive.RadioItem.Props & {
201
+ inset?: boolean;
202
+ }) {
203
+ return (
204
+ <MenuPrimitive.RadioItem
205
+ data-slot="dropdown-menu-radio-item"
206
+ data-inset={inset}
207
+ className={cn(
208
+ "relative flex cursor-default items-center gap-2.5 rounded-2xl py-2 pr-8 pl-3 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-9.5 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
209
+ className,
210
+ )}
211
+ {...props}
212
+ >
213
+ <span
214
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
215
+ data-slot="dropdown-menu-radio-item-indicator"
216
+ >
217
+ <MenuPrimitive.RadioItemIndicator>
218
+ <CheckIcon />
219
+ </MenuPrimitive.RadioItemIndicator>
220
+ </span>
221
+ {children}
222
+ </MenuPrimitive.RadioItem>
223
+ );
224
+ }
225
+
226
+ function DropdownMenuSeparator({
227
+ className,
228
+ ...props
229
+ }: MenuPrimitive.Separator.Props) {
230
+ return (
231
+ <MenuPrimitive.Separator
232
+ data-slot="dropdown-menu-separator"
233
+ className={cn("-mx-1.5 my-1.5 h-px bg-border/50", className)}
234
+ {...props}
235
+ />
236
+ );
237
+ }
238
+
239
+ function DropdownMenuShortcut({
240
+ className,
241
+ ...props
242
+ }: React.ComponentProps<"span">) {
243
+ return (
244
+ <span
245
+ data-slot="dropdown-menu-shortcut"
246
+ className={cn(
247
+ "ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
248
+ className,
249
+ )}
250
+ {...props}
251
+ />
252
+ );
253
+ }
254
+
255
+ export {
256
+ DropdownMenu,
257
+ DropdownMenuPortal,
258
+ DropdownMenuTrigger,
259
+ DropdownMenuContent,
260
+ DropdownMenuGroup,
261
+ DropdownMenuLabel,
262
+ DropdownMenuItem,
263
+ DropdownMenuCheckboxItem,
264
+ DropdownMenuRadioGroup,
265
+ DropdownMenuRadioItem,
266
+ DropdownMenuSeparator,
267
+ DropdownMenuShortcut,
268
+ DropdownMenuSub,
269
+ DropdownMenuSubTrigger,
270
+ DropdownMenuSubContent,
271
+ };