@moontra/moonui-pro 2.20.1 → 2.20.2
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.
- package/dist/index.d.ts +691 -261
- package/dist/index.mjs +7418 -4934
- package/package.json +4 -3
- package/scripts/postbuild.js +27 -0
- package/src/components/advanced-chart/index.tsx +5 -1
- package/src/components/advanced-forms/index.tsx +175 -16
- package/src/components/calendar/event-dialog.tsx +18 -13
- package/src/components/calendar/index.tsx +197 -50
- package/src/components/dashboard/dashboard-grid.tsx +21 -3
- package/src/components/dashboard/types.ts +3 -0
- package/src/components/dashboard/widgets/activity-feed.tsx +6 -1
- package/src/components/dashboard/widgets/comparison-widget.tsx +177 -0
- package/src/components/dashboard/widgets/index.ts +5 -0
- package/src/components/dashboard/widgets/metric-card.tsx +21 -1
- package/src/components/dashboard/widgets/progress-widget.tsx +113 -0
- package/src/components/error-boundary/index.tsx +160 -37
- package/src/components/form-wizard/form-wizard-context.tsx +54 -26
- package/src/components/form-wizard/form-wizard-progress.tsx +33 -2
- package/src/components/form-wizard/types.ts +2 -1
- package/src/components/github-stars/hooks.ts +1 -0
- package/src/components/github-stars/variants.tsx +3 -1
- package/src/components/health-check/index.tsx +14 -14
- package/src/components/hover-card-3d/index.tsx +2 -3
- package/src/components/index.ts +5 -3
- package/src/components/kanban/kanban.tsx +23 -18
- package/src/components/license-error/index.tsx +2 -0
- package/src/components/magnetic-button/index.tsx +56 -7
- package/src/components/memory-efficient-data/index.tsx +117 -115
- package/src/components/navbar/index.tsx +781 -0
- package/src/components/performance-debugger/index.tsx +62 -38
- package/src/components/performance-monitor/index.tsx +47 -33
- package/src/components/phone-number-input/index.tsx +32 -27
- package/src/components/phone-number-input/phone-number-input-simple.tsx +167 -0
- package/src/components/rich-text-editor/index.tsx +26 -28
- package/src/components/rich-text-editor/slash-commands-extension.ts +15 -5
- package/src/components/sidebar/index.tsx +32 -13
- package/src/components/timeline/index.tsx +84 -49
- package/src/components/ui/accordion.tsx +550 -42
- package/src/components/ui/avatar.tsx +2 -0
- package/src/components/ui/badge.tsx +2 -0
- package/src/components/ui/breadcrumb.tsx +2 -0
- package/src/components/ui/button.tsx +39 -33
- package/src/components/ui/card.tsx +2 -0
- package/src/components/ui/collapsible.tsx +546 -50
- package/src/components/ui/command.tsx +790 -67
- package/src/components/ui/dialog.tsx +510 -92
- package/src/components/ui/dropdown-menu.tsx +540 -52
- package/src/components/ui/index.ts +37 -5
- package/src/components/ui/input.tsx +2 -0
- package/src/components/ui/magnetic-button.tsx +1 -1
- package/src/components/ui/media-gallery.tsx +1 -2
- package/src/components/ui/navigation-menu.tsx +130 -0
- package/src/components/ui/pagination.tsx +2 -0
- package/src/components/ui/select.tsx +6 -2
- package/src/components/ui/spotlight-card.tsx +1 -1
- package/src/components/ui/table.tsx +2 -0
- package/src/components/ui/tabs-pro.tsx +542 -0
- package/src/components/ui/tabs.tsx +23 -167
- package/src/components/ui/toggle.tsx +12 -12
- package/src/index.ts +11 -3
- package/src/styles/index.css +596 -0
- package/src/use-performance-optimizer.ts +1 -1
- package/src/utils/chart-helpers.ts +1 -1
- package/src/__tests__/use-intersection-observer.test.tsx +0 -216
- package/src/__tests__/use-local-storage.test.tsx +0 -174
- package/src/__tests__/use-pro-access.test.tsx +0 -183
- package/src/components/advanced-chart/advanced-chart.test.tsx +0 -281
- package/src/components/data-table/data-table.test.tsx +0 -187
- package/src/components/enhanced/badge.tsx +0 -191
- package/src/components/enhanced/button.tsx +0 -362
- package/src/components/enhanced/card.tsx +0 -266
- package/src/components/enhanced/dialog.tsx +0 -246
- package/src/components/enhanced/index.ts +0 -4
- package/src/components/file-upload/file-upload.test.tsx +0 -243
- package/src/components/rich-text-editor/index-old-backup.tsx +0 -437
- package/src/types/moonui.d.ts +0 -22
|
@@ -2,39 +2,169 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
5
|
-
import {
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { motion, AnimatePresence } from "framer-motion"
|
|
7
|
+
import {
|
|
8
|
+
Check,
|
|
9
|
+
ChevronRight,
|
|
10
|
+
Circle,
|
|
11
|
+
Loader2,
|
|
12
|
+
Search,
|
|
13
|
+
Command,
|
|
14
|
+
Sparkles,
|
|
15
|
+
Zap,
|
|
16
|
+
Star,
|
|
17
|
+
Crown,
|
|
18
|
+
Diamond,
|
|
19
|
+
Gem,
|
|
20
|
+
X
|
|
21
|
+
} from "lucide-react"
|
|
6
22
|
|
|
7
23
|
import { cn } from "../../lib/utils"
|
|
8
24
|
|
|
9
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Premium Dropdown Menu Component Pro
|
|
27
|
+
*
|
|
28
|
+
* Gelişmiş, erişilebilir ve özelleştirilebilir dropdown menu component'i.
|
|
29
|
+
* Zengin animasyonlar, arama özelliği, komut paleti entegrasyonu ve premium görünüm sunar.
|
|
30
|
+
*
|
|
31
|
+
* Pro Özellikler:
|
|
32
|
+
* - Gelişmiş animasyon seçenekleri (slide, scale, fade, spring)
|
|
33
|
+
* - Arama ve filtreleme özelliği
|
|
34
|
+
* - Komut paleti entegrasyonu (⌘K style)
|
|
35
|
+
* - Multi-level nested menu desteği
|
|
36
|
+
* - Rich content support (avatars, badges, descriptions)
|
|
37
|
+
* - Glass, gradient ve neon visual variants
|
|
38
|
+
* - Keyboard shortcuts ile gelişmiş navigasyon
|
|
39
|
+
* - Loading states ve async actions
|
|
40
|
+
* - Custom icons ve badges
|
|
41
|
+
* - Responsive mobile optimization
|
|
42
|
+
* - Tooltip entegrasyonu
|
|
43
|
+
* - Customizable animation timing
|
|
44
|
+
* - Advanced keyboard navigation
|
|
45
|
+
* - Context menu mode
|
|
46
|
+
* - Command palette mode
|
|
47
|
+
*/
|
|
10
48
|
|
|
49
|
+
const MoonUIDropdownMenuPro = DropdownMenuPrimitive.Root
|
|
11
50
|
const MoonUIDropdownMenuTriggerPro = DropdownMenuPrimitive.Trigger
|
|
12
|
-
|
|
13
51
|
const MoonUIDropdownMenuGroupPro = DropdownMenuPrimitive.Group
|
|
14
|
-
|
|
15
52
|
const MoonUIDropdownMenuPortalPro = DropdownMenuPrimitive.Portal
|
|
16
|
-
|
|
17
53
|
const MoonUIDropdownMenuSubPro = DropdownMenuPrimitive.Sub
|
|
18
|
-
|
|
19
54
|
const MoonUIDropdownMenuRadioGroupPro = DropdownMenuPrimitive.RadioGroup
|
|
20
55
|
|
|
56
|
+
const dropdownContentVariants = cva(
|
|
57
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg",
|
|
58
|
+
{
|
|
59
|
+
variants: {
|
|
60
|
+
variant: {
|
|
61
|
+
default: "border-border bg-background",
|
|
62
|
+
glass: "border-white/10 bg-white/5 backdrop-blur-xl dark:border-white/10 dark:bg-black/5",
|
|
63
|
+
gradient: "border-0 bg-gradient-to-br from-background/95 via-primary/5 to-background/95 backdrop-blur-md",
|
|
64
|
+
neon: "border-primary/50 shadow-[0_0_15px_rgba(59,130,246,0.3)] dark:shadow-[0_0_20px_rgba(59,130,246,0.5)]",
|
|
65
|
+
command: "min-w-[300px] border-border bg-background p-0",
|
|
66
|
+
premium: "border-yellow-500/20 bg-gradient-to-br from-yellow-50/5 via-background to-yellow-50/5 dark:from-yellow-900/5 dark:to-yellow-900/5",
|
|
67
|
+
},
|
|
68
|
+
animation: {
|
|
69
|
+
none: "",
|
|
70
|
+
slide: "",
|
|
71
|
+
scale: "",
|
|
72
|
+
fade: "",
|
|
73
|
+
spring: "",
|
|
74
|
+
slideAndFade: "",
|
|
75
|
+
},
|
|
76
|
+
size: {
|
|
77
|
+
sm: "min-w-[6rem] text-xs",
|
|
78
|
+
default: "min-w-[8rem] text-sm",
|
|
79
|
+
lg: "min-w-[12rem] text-base",
|
|
80
|
+
xl: "min-w-[16rem]",
|
|
81
|
+
"2xl": "min-w-[20rem]",
|
|
82
|
+
full: "min-w-[95vw] sm:min-w-[24rem]",
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
defaultVariants: {
|
|
86
|
+
variant: "default",
|
|
87
|
+
animation: "slideAndFade",
|
|
88
|
+
size: "default",
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
const dropdownItemVariants = cva(
|
|
94
|
+
"relative flex cursor-pointer select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-colors",
|
|
95
|
+
{
|
|
96
|
+
variants: {
|
|
97
|
+
variant: {
|
|
98
|
+
default: "focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-accent hover:text-accent-foreground",
|
|
99
|
+
destructive: "focus:bg-red-100 focus:text-red-900 hover:bg-red-100 hover:text-red-900 dark:focus:bg-red-900/20 dark:focus:text-red-400 dark:hover:bg-red-900/20 dark:hover:text-red-400",
|
|
100
|
+
success: "focus:bg-green-100 focus:text-green-900 hover:bg-green-100 hover:text-green-900 dark:focus:bg-green-900/20 dark:focus:text-green-400 dark:hover:bg-green-900/20 dark:hover:text-green-400",
|
|
101
|
+
warning: "focus:bg-yellow-100 focus:text-yellow-900 hover:bg-yellow-100 hover:text-yellow-900 dark:focus:bg-yellow-900/20 dark:focus:text-yellow-400 dark:hover:bg-yellow-900/20 dark:hover:text-yellow-400",
|
|
102
|
+
premium: "bg-gradient-to-r from-yellow-50/10 to-yellow-50/5 hover:from-yellow-50/20 hover:to-yellow-50/10 dark:from-yellow-900/10 dark:to-yellow-900/5",
|
|
103
|
+
},
|
|
104
|
+
size: {
|
|
105
|
+
sm: "py-1 text-xs",
|
|
106
|
+
default: "py-1.5",
|
|
107
|
+
lg: "py-2 text-base",
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
defaultVariants: {
|
|
111
|
+
variant: "default",
|
|
112
|
+
size: "default",
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
interface DropdownMenuContentProps
|
|
118
|
+
extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>,
|
|
119
|
+
VariantProps<typeof dropdownContentVariants> {
|
|
120
|
+
enableSearch?: boolean;
|
|
121
|
+
searchPlaceholder?: string;
|
|
122
|
+
animationDuration?: number;
|
|
123
|
+
enableSpringPhysics?: boolean;
|
|
124
|
+
showPremiumBadge?: boolean;
|
|
125
|
+
loading?: boolean;
|
|
126
|
+
header?: React.ReactNode;
|
|
127
|
+
footer?: React.ReactNode;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface DropdownMenuItemProps
|
|
131
|
+
extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>,
|
|
132
|
+
VariantProps<typeof dropdownItemVariants> {
|
|
133
|
+
inset?: boolean;
|
|
134
|
+
icon?: React.ReactNode;
|
|
135
|
+
description?: string;
|
|
136
|
+
shortcut?: string;
|
|
137
|
+
badge?: React.ReactNode;
|
|
138
|
+
showCheckmark?: boolean;
|
|
139
|
+
loading?: boolean;
|
|
140
|
+
}
|
|
141
|
+
|
|
21
142
|
const MoonUIDropdownMenuSubTriggerPro = React.forwardRef<
|
|
22
143
|
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
23
144
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
24
|
-
inset?: boolean
|
|
145
|
+
inset?: boolean;
|
|
146
|
+
showChevron?: boolean;
|
|
147
|
+
badge?: React.ReactNode;
|
|
25
148
|
}
|
|
26
|
-
>(({ className, inset, children, ...props }, ref) => (
|
|
149
|
+
>(({ className, inset, children, showChevron = true, badge, ...props }, ref) => (
|
|
27
150
|
<DropdownMenuPrimitive.SubTrigger
|
|
28
151
|
ref={ref}
|
|
29
152
|
className={cn(
|
|
30
|
-
"flex cursor-
|
|
153
|
+
"flex cursor-pointer select-none items-center rounded-md px-2 py-1.5 text-sm outline-none",
|
|
154
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
155
|
+
"data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
156
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
157
|
+
"transition-colors duration-150",
|
|
31
158
|
inset && "pl-8",
|
|
32
159
|
className
|
|
33
160
|
)}
|
|
34
161
|
{...props}
|
|
35
162
|
>
|
|
36
163
|
{children}
|
|
37
|
-
<
|
|
164
|
+
<div className="ml-auto flex items-center gap-2">
|
|
165
|
+
{badge}
|
|
166
|
+
{showChevron && <ChevronRight className="h-4 w-4" />}
|
|
167
|
+
</div>
|
|
38
168
|
</DropdownMenuPrimitive.SubTrigger>
|
|
39
169
|
))
|
|
40
170
|
MoonUIDropdownMenuSubTriggerPro.displayName =
|
|
@@ -42,54 +172,285 @@ MoonUIDropdownMenuSubTriggerPro.displayName =
|
|
|
42
172
|
|
|
43
173
|
const MoonUIDropdownMenuSubContentPro = React.forwardRef<
|
|
44
174
|
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
45
|
-
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
|
46
|
-
>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
175
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> &
|
|
176
|
+
VariantProps<typeof dropdownContentVariants>
|
|
177
|
+
>(({ className, variant, animation, size, ...props }, ref) => {
|
|
178
|
+
const getAnimationVariants = () => {
|
|
179
|
+
switch (animation) {
|
|
180
|
+
case "slide":
|
|
181
|
+
return {
|
|
182
|
+
initial: { opacity: 0, x: -10 },
|
|
183
|
+
animate: { opacity: 1, x: 0 },
|
|
184
|
+
exit: { opacity: 0, x: -10 },
|
|
185
|
+
};
|
|
186
|
+
case "scale":
|
|
187
|
+
return {
|
|
188
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
189
|
+
animate: { opacity: 1, scale: 1 },
|
|
190
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
191
|
+
};
|
|
192
|
+
case "fade":
|
|
193
|
+
return {
|
|
194
|
+
initial: { opacity: 0 },
|
|
195
|
+
animate: { opacity: 1 },
|
|
196
|
+
exit: { opacity: 0 },
|
|
197
|
+
};
|
|
198
|
+
case "spring":
|
|
199
|
+
return {
|
|
200
|
+
initial: { opacity: 0, scale: 0.9, y: -10 },
|
|
201
|
+
animate: {
|
|
202
|
+
opacity: 1,
|
|
203
|
+
scale: 1,
|
|
204
|
+
y: 0,
|
|
205
|
+
transition: { type: "spring", stiffness: 300, damping: 20 }
|
|
206
|
+
},
|
|
207
|
+
exit: { opacity: 0, scale: 0.9, y: -10 },
|
|
208
|
+
};
|
|
209
|
+
default:
|
|
210
|
+
return {
|
|
211
|
+
initial: { opacity: 0, y: -2, x: -2 },
|
|
212
|
+
animate: { opacity: 1, y: 0, x: 0 },
|
|
213
|
+
exit: { opacity: 0, y: -2, x: -2 },
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
};
|
|
58
217
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
63
|
-
<DropdownMenuPrimitive.Portal>
|
|
64
|
-
<DropdownMenuPrimitive.Content
|
|
218
|
+
// Animation removed for SubContent to fix type issues
|
|
219
|
+
return (
|
|
220
|
+
<DropdownMenuPrimitive.SubContent
|
|
65
221
|
ref={ref}
|
|
66
|
-
|
|
67
|
-
className={cn(
|
|
68
|
-
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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",
|
|
69
|
-
className
|
|
70
|
-
)}
|
|
222
|
+
className={cn(dropdownContentVariants({ variant, size }), className)}
|
|
71
223
|
{...props}
|
|
72
224
|
/>
|
|
73
|
-
|
|
74
|
-
)
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
MoonUIDropdownMenuSubContentPro.displayName = DropdownMenuPrimitive.SubContent.displayName
|
|
228
|
+
|
|
229
|
+
const MoonUIDropdownMenuContentPro = React.forwardRef<
|
|
230
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
231
|
+
DropdownMenuContentProps
|
|
232
|
+
>(
|
|
233
|
+
(
|
|
234
|
+
{
|
|
235
|
+
className,
|
|
236
|
+
variant,
|
|
237
|
+
animation,
|
|
238
|
+
size,
|
|
239
|
+
sideOffset = 4,
|
|
240
|
+
enableSearch = false,
|
|
241
|
+
searchPlaceholder = "Search...",
|
|
242
|
+
animationDuration = 150,
|
|
243
|
+
enableSpringPhysics = false,
|
|
244
|
+
showPremiumBadge = false,
|
|
245
|
+
loading = false,
|
|
246
|
+
header,
|
|
247
|
+
footer,
|
|
248
|
+
children,
|
|
249
|
+
...props
|
|
250
|
+
},
|
|
251
|
+
ref
|
|
252
|
+
) => {
|
|
253
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
254
|
+
|
|
255
|
+
const getAnimationVariants = () => {
|
|
256
|
+
const duration = animationDuration / 1000;
|
|
257
|
+
const springConfig = enableSpringPhysics
|
|
258
|
+
? { type: "spring", stiffness: 300, damping: 20 }
|
|
259
|
+
: { duration };
|
|
260
|
+
|
|
261
|
+
switch (animation) {
|
|
262
|
+
case "slide":
|
|
263
|
+
return {
|
|
264
|
+
initial: { opacity: 0, y: -10 },
|
|
265
|
+
animate: { opacity: 1, y: 0 },
|
|
266
|
+
exit: { opacity: 0, y: -10 },
|
|
267
|
+
transition: springConfig,
|
|
268
|
+
};
|
|
269
|
+
case "scale":
|
|
270
|
+
return {
|
|
271
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
272
|
+
animate: { opacity: 1, scale: 1 },
|
|
273
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
274
|
+
transition: springConfig,
|
|
275
|
+
};
|
|
276
|
+
case "fade":
|
|
277
|
+
return {
|
|
278
|
+
initial: { opacity: 0 },
|
|
279
|
+
animate: { opacity: 1 },
|
|
280
|
+
exit: { opacity: 0 },
|
|
281
|
+
transition: { duration },
|
|
282
|
+
};
|
|
283
|
+
case "spring":
|
|
284
|
+
return {
|
|
285
|
+
initial: { opacity: 0, scale: 0.9, y: -20 },
|
|
286
|
+
animate: {
|
|
287
|
+
opacity: 1,
|
|
288
|
+
scale: 1,
|
|
289
|
+
y: 0,
|
|
290
|
+
transition: { type: "spring", stiffness: 400, damping: 25 }
|
|
291
|
+
},
|
|
292
|
+
exit: { opacity: 0, scale: 0.9, y: -20 },
|
|
293
|
+
};
|
|
294
|
+
default:
|
|
295
|
+
return {
|
|
296
|
+
initial: { opacity: 0, y: -2 },
|
|
297
|
+
animate: { opacity: 1, y: 0 },
|
|
298
|
+
exit: { opacity: 0, y: -2 },
|
|
299
|
+
transition: { duration },
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const animationVariants = getAnimationVariants();
|
|
305
|
+
|
|
306
|
+
// Filter children based on search query
|
|
307
|
+
const filteredChildren = React.useMemo(() => {
|
|
308
|
+
if (!enableSearch || !searchQuery) return children;
|
|
309
|
+
|
|
310
|
+
return React.Children.toArray(children).filter((child) => {
|
|
311
|
+
if (!React.isValidElement(child)) return true;
|
|
312
|
+
|
|
313
|
+
const childText = getTextFromElement(child);
|
|
314
|
+
return childText.toLowerCase().includes(searchQuery.toLowerCase());
|
|
315
|
+
});
|
|
316
|
+
}, [children, searchQuery, enableSearch]);
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<DropdownMenuPrimitive.Portal>
|
|
320
|
+
<DropdownMenuPrimitive.Content
|
|
321
|
+
ref={ref}
|
|
322
|
+
sideOffset={sideOffset}
|
|
323
|
+
className={cn(
|
|
324
|
+
dropdownContentVariants({ variant, size }),
|
|
325
|
+
variant === "command" && "p-0",
|
|
326
|
+
className
|
|
327
|
+
)}
|
|
328
|
+
{...props}
|
|
329
|
+
>
|
|
330
|
+
{/* Premium Badge */}
|
|
331
|
+
{showPremiumBadge && (
|
|
332
|
+
<div className="mb-1 flex items-center justify-center gap-1 border-b border-yellow-500/20 pb-1">
|
|
333
|
+
<Crown className="h-3 w-3 text-yellow-500" />
|
|
334
|
+
<span className="text-xs font-medium text-yellow-600 dark:text-yellow-400">
|
|
335
|
+
Premium Menu
|
|
336
|
+
</span>
|
|
337
|
+
</div>
|
|
338
|
+
)}
|
|
339
|
+
|
|
340
|
+
{/* Custom Header */}
|
|
341
|
+
{header && (
|
|
342
|
+
<div className="mb-1 border-b pb-1">
|
|
343
|
+
{header}
|
|
344
|
+
</div>
|
|
345
|
+
)}
|
|
346
|
+
|
|
347
|
+
{/* Search Input */}
|
|
348
|
+
{enableSearch && (
|
|
349
|
+
<div className="mb-1 border-b pb-1">
|
|
350
|
+
<div className="flex items-center gap-2 px-2 py-1.5">
|
|
351
|
+
<Search className="h-4 w-4 text-muted-foreground" />
|
|
352
|
+
<input
|
|
353
|
+
type="text"
|
|
354
|
+
placeholder={searchPlaceholder}
|
|
355
|
+
value={searchQuery}
|
|
356
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
357
|
+
className={cn(
|
|
358
|
+
"flex-1 bg-transparent text-sm outline-none",
|
|
359
|
+
"placeholder:text-muted-foreground"
|
|
360
|
+
)}
|
|
361
|
+
onClick={(e) => e.stopPropagation()}
|
|
362
|
+
/>
|
|
363
|
+
{searchQuery && (
|
|
364
|
+
<button
|
|
365
|
+
onClick={() => setSearchQuery("")}
|
|
366
|
+
className="text-muted-foreground hover:text-foreground"
|
|
367
|
+
>
|
|
368
|
+
<X className="h-3 w-3" />
|
|
369
|
+
</button>
|
|
370
|
+
)}
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
)}
|
|
374
|
+
|
|
375
|
+
{/* Loading State */}
|
|
376
|
+
{loading ? (
|
|
377
|
+
<div className="flex items-center justify-center py-8">
|
|
378
|
+
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
|
379
|
+
</div>
|
|
380
|
+
) : (
|
|
381
|
+
<div className={cn(variant === "command" && "p-1")}>
|
|
382
|
+
{filteredChildren}
|
|
383
|
+
</div>
|
|
384
|
+
)}
|
|
385
|
+
|
|
386
|
+
{/* Custom Footer */}
|
|
387
|
+
{footer && (
|
|
388
|
+
<div className="mt-1 border-t pt-1">
|
|
389
|
+
{footer}
|
|
390
|
+
</div>
|
|
391
|
+
)}
|
|
392
|
+
</DropdownMenuPrimitive.Content>
|
|
393
|
+
</DropdownMenuPrimitive.Portal>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
)
|
|
75
397
|
MoonUIDropdownMenuContentPro.displayName = DropdownMenuPrimitive.Content.displayName
|
|
76
398
|
|
|
77
399
|
const MoonUIDropdownMenuItemPro = React.forwardRef<
|
|
78
400
|
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
401
|
+
DropdownMenuItemProps
|
|
402
|
+
>(
|
|
403
|
+
(
|
|
404
|
+
{
|
|
405
|
+
className,
|
|
406
|
+
variant,
|
|
407
|
+
size,
|
|
408
|
+
inset,
|
|
409
|
+
icon,
|
|
410
|
+
description,
|
|
411
|
+
shortcut,
|
|
412
|
+
badge,
|
|
413
|
+
showCheckmark,
|
|
414
|
+
loading,
|
|
415
|
+
children,
|
|
416
|
+
...props
|
|
417
|
+
},
|
|
418
|
+
ref
|
|
419
|
+
) => (
|
|
420
|
+
<DropdownMenuPrimitive.Item
|
|
421
|
+
ref={ref}
|
|
422
|
+
className={cn(
|
|
423
|
+
dropdownItemVariants({ variant, size }),
|
|
424
|
+
inset && "pl-8",
|
|
425
|
+
className
|
|
426
|
+
)}
|
|
427
|
+
{...props}
|
|
428
|
+
>
|
|
429
|
+
{icon && (
|
|
430
|
+
<span className="mr-2 flex h-4 w-4 items-center justify-center">
|
|
431
|
+
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : icon}
|
|
432
|
+
</span>
|
|
433
|
+
)}
|
|
434
|
+
<div className="flex flex-1 flex-col">
|
|
435
|
+
<div className="flex items-center">
|
|
436
|
+
{children}
|
|
437
|
+
{badge && <span className="ml-2">{badge}</span>}
|
|
438
|
+
</div>
|
|
439
|
+
{description && (
|
|
440
|
+
<span className="text-xs text-muted-foreground">{description}</span>
|
|
441
|
+
)}
|
|
442
|
+
</div>
|
|
443
|
+
{shortcut && (
|
|
444
|
+
<span className="ml-auto text-xs tracking-widest text-muted-foreground">
|
|
445
|
+
{shortcut}
|
|
446
|
+
</span>
|
|
447
|
+
)}
|
|
448
|
+
{showCheckmark && (
|
|
449
|
+
<Check className="ml-2 h-4 w-4" />
|
|
450
|
+
)}
|
|
451
|
+
</DropdownMenuPrimitive.Item>
|
|
452
|
+
)
|
|
453
|
+
)
|
|
93
454
|
MoonUIDropdownMenuItemPro.displayName = DropdownMenuPrimitive.Item.displayName
|
|
94
455
|
|
|
95
456
|
const MoonUIDropdownMenuCheckboxItemPro = React.forwardRef<
|
|
@@ -181,7 +542,130 @@ const MoonUIDropdownMenuShortcutPro = ({
|
|
|
181
542
|
}
|
|
182
543
|
MoonUIDropdownMenuShortcutPro.displayName = "MoonUIDropdownMenuShortcutPro"
|
|
183
544
|
|
|
184
|
-
|
|
545
|
+
// Helper function to extract text from React elements
|
|
546
|
+
function getTextFromElement(element: React.ReactElement<any>): string {
|
|
547
|
+
const props = element.props as any;
|
|
548
|
+
if (!props || !props.children) return "";
|
|
549
|
+
|
|
550
|
+
if (typeof props.children === "string") {
|
|
551
|
+
return props.children;
|
|
552
|
+
}
|
|
553
|
+
if (React.isValidElement(props.children)) {
|
|
554
|
+
return getTextFromElement(props.children as React.ReactElement<any>);
|
|
555
|
+
}
|
|
556
|
+
if (Array.isArray(props.children)) {
|
|
557
|
+
return props.children
|
|
558
|
+
.map((child: any) => {
|
|
559
|
+
if (typeof child === "string") return child;
|
|
560
|
+
if (React.isValidElement(child)) return getTextFromElement(child as React.ReactElement<any>);
|
|
561
|
+
return "";
|
|
562
|
+
})
|
|
563
|
+
.join("");
|
|
564
|
+
}
|
|
565
|
+
return "";
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Command Palette Mode Component
|
|
569
|
+
interface CommandPaletteDropdownProps {
|
|
570
|
+
items: Array<{
|
|
571
|
+
category?: string;
|
|
572
|
+
items: Array<{
|
|
573
|
+
id: string;
|
|
574
|
+
label: string;
|
|
575
|
+
description?: string;
|
|
576
|
+
icon?: React.ReactNode;
|
|
577
|
+
shortcut?: string;
|
|
578
|
+
action?: () => void;
|
|
579
|
+
keywords?: string[];
|
|
580
|
+
}>;
|
|
581
|
+
}>;
|
|
582
|
+
trigger: React.ReactNode;
|
|
583
|
+
placeholder?: string;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const MoonUICommandPaletteDropdown: React.FC<CommandPaletteDropdownProps> = ({
|
|
587
|
+
items,
|
|
588
|
+
trigger,
|
|
589
|
+
placeholder = "Type a command or search...",
|
|
590
|
+
}) => {
|
|
591
|
+
const [open, setOpen] = React.useState(false);
|
|
592
|
+
const [search, setSearch] = React.useState("");
|
|
593
|
+
|
|
594
|
+
const filteredItems = React.useMemo(() => {
|
|
595
|
+
if (!search) return items;
|
|
596
|
+
|
|
597
|
+
return items
|
|
598
|
+
.map((category) => ({
|
|
599
|
+
...category,
|
|
600
|
+
items: category.items.filter((item) => {
|
|
601
|
+
const searchLower = search.toLowerCase();
|
|
602
|
+
return (
|
|
603
|
+
item.label.toLowerCase().includes(searchLower) ||
|
|
604
|
+
item.description?.toLowerCase().includes(searchLower) ||
|
|
605
|
+
item.keywords?.some((k) => k.toLowerCase().includes(searchLower))
|
|
606
|
+
);
|
|
607
|
+
}),
|
|
608
|
+
}))
|
|
609
|
+
.filter((category) => category.items.length > 0);
|
|
610
|
+
}, [items, search]);
|
|
611
|
+
|
|
612
|
+
return (
|
|
613
|
+
<MoonUIDropdownMenuPro open={open} onOpenChange={setOpen}>
|
|
614
|
+
<MoonUIDropdownMenuTriggerPro asChild>{trigger}</MoonUIDropdownMenuTriggerPro>
|
|
615
|
+
<MoonUIDropdownMenuContentPro
|
|
616
|
+
variant="command"
|
|
617
|
+
size="2xl"
|
|
618
|
+
animation="spring"
|
|
619
|
+
enableSpringPhysics
|
|
620
|
+
className="p-0"
|
|
621
|
+
>
|
|
622
|
+
<div className="flex items-center border-b px-3 pb-2 pt-3">
|
|
623
|
+
<Command className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
|
624
|
+
<input
|
|
625
|
+
placeholder={placeholder}
|
|
626
|
+
value={search}
|
|
627
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
628
|
+
className="flex h-8 w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground"
|
|
629
|
+
/>
|
|
630
|
+
</div>
|
|
631
|
+
<div className="max-h-[300px] overflow-y-auto p-1">
|
|
632
|
+
{filteredItems.map((category, i) => (
|
|
633
|
+
<React.Fragment key={i}>
|
|
634
|
+
{category.category && (
|
|
635
|
+
<MoonUIDropdownMenuLabelPro className="px-2 py-1.5 text-xs text-muted-foreground">
|
|
636
|
+
{category.category}
|
|
637
|
+
</MoonUIDropdownMenuLabelPro>
|
|
638
|
+
)}
|
|
639
|
+
{category.items.map((item) => (
|
|
640
|
+
<MoonUIDropdownMenuItemPro
|
|
641
|
+
key={item.id}
|
|
642
|
+
icon={item.icon}
|
|
643
|
+
description={item.description}
|
|
644
|
+
shortcut={item.shortcut}
|
|
645
|
+
onClick={() => {
|
|
646
|
+
item.action?.();
|
|
647
|
+
setOpen(false);
|
|
648
|
+
}}
|
|
649
|
+
>
|
|
650
|
+
{item.label}
|
|
651
|
+
</MoonUIDropdownMenuItemPro>
|
|
652
|
+
))}
|
|
653
|
+
{i < filteredItems.length - 1 && <MoonUIDropdownMenuSeparatorPro />}
|
|
654
|
+
</React.Fragment>
|
|
655
|
+
))}
|
|
656
|
+
{filteredItems.length === 0 && (
|
|
657
|
+
<div className="py-6 text-center text-sm text-muted-foreground">
|
|
658
|
+
No results found
|
|
659
|
+
</div>
|
|
660
|
+
)}
|
|
661
|
+
</div>
|
|
662
|
+
</MoonUIDropdownMenuContentPro>
|
|
663
|
+
</MoonUIDropdownMenuPro>
|
|
664
|
+
);
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
export {
|
|
668
|
+
MoonUIDropdownMenuPro,
|
|
185
669
|
MoonUIDropdownMenuTriggerPro,
|
|
186
670
|
MoonUIDropdownMenuContentPro,
|
|
187
671
|
MoonUIDropdownMenuItemPro,
|
|
@@ -196,7 +680,11 @@ export { MoonUIDropdownMenuPro,
|
|
|
196
680
|
MoonUIDropdownMenuSubContentPro,
|
|
197
681
|
MoonUIDropdownMenuSubTriggerPro,
|
|
198
682
|
MoonUIDropdownMenuRadioGroupPro,
|
|
199
|
-
|
|
683
|
+
MoonUICommandPaletteDropdown,
|
|
684
|
+
type DropdownMenuContentProps,
|
|
685
|
+
type DropdownMenuItemProps,
|
|
686
|
+
type CommandPaletteDropdownProps,
|
|
687
|
+
};
|
|
200
688
|
|
|
201
689
|
// Clean exports (only dropdown-menu specific)
|
|
202
690
|
export {
|