@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
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import React, { useRef, useState, useEffect } from "react"
|
|
4
|
-
import { motion, AnimatePresence } from "framer-motion"
|
|
5
|
-
import { Loader2, Check, X, Sparkles } from "lucide-react"
|
|
6
|
-
import { cn } from "../../lib/utils"
|
|
7
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
8
|
-
|
|
9
|
-
const enhancedButtonVariants = cva(
|
|
10
|
-
"relative inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 overflow-hidden transform active:scale-[0.98] rounded-lg",
|
|
11
|
-
{
|
|
12
|
-
variants: {
|
|
13
|
-
variant: {
|
|
14
|
-
default: "bg-primary text-primary-foreground dark:bg-primary dark:text-primary-foreground hover:bg-primary/90 dark:hover:bg-primary/90 shadow-sm hover:shadow-md",
|
|
15
|
-
destructive: "bg-destructive text-destructive-foreground dark:bg-destructive dark:text-destructive-foreground hover:bg-destructive/90 dark:hover:bg-destructive/90 shadow-sm hover:shadow-md",
|
|
16
|
-
outline: "border border-input dark:border-input bg-background dark:bg-background text-foreground dark:text-foreground hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent dark:hover:text-accent-foreground transition-all duration-200",
|
|
17
|
-
secondary: "bg-muted text-muted-foreground dark:bg-muted dark:text-muted-foreground hover:bg-muted/80 dark:hover:bg-muted/80 border border-input dark:border-input shadow-sm hover:shadow-md",
|
|
18
|
-
ghost: "text-foreground dark:text-foreground hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent dark:hover:text-accent-foreground transition-all duration-200",
|
|
19
|
-
link: "text-primary underline-offset-4 hover:underline hover:text-primary/80",
|
|
20
|
-
gradient: "bg-gradient-to-r from-purple-600 to-pink-600 text-white hover:from-purple-700 hover:to-pink-700 shadow-md hover:shadow-lg",
|
|
21
|
-
glow: "bg-primary text-primary-foreground shadow-lg shadow-primary/50 hover:shadow-xl hover:shadow-primary/60",
|
|
22
|
-
},
|
|
23
|
-
size: {
|
|
24
|
-
default: "h-10 px-4 py-2",
|
|
25
|
-
sm: "h-9 px-3",
|
|
26
|
-
lg: "h-11 px-8",
|
|
27
|
-
icon: "h-10 w-10",
|
|
28
|
-
},
|
|
29
|
-
animation: {
|
|
30
|
-
ripple: "ripple-effect",
|
|
31
|
-
morph: "morph-effect",
|
|
32
|
-
particles: "particles-effect",
|
|
33
|
-
magnetic: "magnetic-effect",
|
|
34
|
-
glitch: "glitch-effect",
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
defaultVariants: {
|
|
38
|
-
variant: "default",
|
|
39
|
-
size: "default",
|
|
40
|
-
animation: "ripple",
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
interface RippleEffect {
|
|
46
|
-
x: number
|
|
47
|
-
y: number
|
|
48
|
-
id: number
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface ButtonProProps
|
|
52
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
53
|
-
VariantProps<typeof enhancedButtonVariants> {
|
|
54
|
-
state?: "idle" | "loading" | "success" | "error"
|
|
55
|
-
onStateChange?: (state: "idle" | "loading" | "success" | "error") => void
|
|
56
|
-
enableRipple?: boolean
|
|
57
|
-
enableMorph?: boolean
|
|
58
|
-
enableParticles?: boolean
|
|
59
|
-
enableMagnetic?: boolean
|
|
60
|
-
enableGlitch?: boolean
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const ButtonPro = React.forwardRef<HTMLButtonElement, ButtonProProps>(
|
|
64
|
-
({
|
|
65
|
-
className,
|
|
66
|
-
variant,
|
|
67
|
-
size,
|
|
68
|
-
animation,
|
|
69
|
-
state = "idle",
|
|
70
|
-
onStateChange,
|
|
71
|
-
children,
|
|
72
|
-
onClick,
|
|
73
|
-
enableRipple = true,
|
|
74
|
-
enableMorph = true,
|
|
75
|
-
enableParticles = false,
|
|
76
|
-
enableMagnetic = false,
|
|
77
|
-
enableGlitch = false,
|
|
78
|
-
disabled,
|
|
79
|
-
type = "button",
|
|
80
|
-
...props
|
|
81
|
-
}, ref) => {
|
|
82
|
-
const [internalState, setInternalState] = useState<"idle" | "loading" | "success" | "error">("idle")
|
|
83
|
-
const [ripples, setRipples] = useState<RippleEffect[]>([])
|
|
84
|
-
const [particles, setParticles] = useState<{ x: number; y: number; id: number }[]>([])
|
|
85
|
-
const [magneticPosition, setMagneticPosition] = useState({ x: 0, y: 0 })
|
|
86
|
-
const buttonRef = useRef<HTMLButtonElement>(null)
|
|
87
|
-
const currentState = state !== "idle" ? state : internalState
|
|
88
|
-
|
|
89
|
-
// Ripple effect
|
|
90
|
-
const createRipple = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
91
|
-
if (!enableRipple) return
|
|
92
|
-
|
|
93
|
-
const button = event.currentTarget
|
|
94
|
-
const rect = button.getBoundingClientRect()
|
|
95
|
-
const ripple = {
|
|
96
|
-
x: event.clientX - rect.left,
|
|
97
|
-
y: event.clientY - rect.top,
|
|
98
|
-
id: Date.now()
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
setRipples([...ripples, ripple])
|
|
102
|
-
setTimeout(() => {
|
|
103
|
-
setRipples(prev => prev.filter(r => r.id !== ripple.id))
|
|
104
|
-
}, 1000)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Particle effect
|
|
108
|
-
const createParticles = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
109
|
-
if (!enableParticles) return
|
|
110
|
-
|
|
111
|
-
const button = event.currentTarget
|
|
112
|
-
const rect = button.getBoundingClientRect()
|
|
113
|
-
const newParticles = Array.from({ length: 8 }, (_, i) => ({
|
|
114
|
-
x: event.clientX - rect.left,
|
|
115
|
-
y: event.clientY - rect.top,
|
|
116
|
-
id: Date.now() + i
|
|
117
|
-
}))
|
|
118
|
-
|
|
119
|
-
setParticles([...particles, ...newParticles])
|
|
120
|
-
setTimeout(() => {
|
|
121
|
-
setParticles([])
|
|
122
|
-
}, 1000)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Magnetic effect
|
|
126
|
-
const handleMouseMove = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
127
|
-
if (!enableMagnetic) return
|
|
128
|
-
|
|
129
|
-
const button = event.currentTarget
|
|
130
|
-
const rect = button.getBoundingClientRect()
|
|
131
|
-
const centerX = rect.width / 2
|
|
132
|
-
const centerY = rect.height / 2
|
|
133
|
-
const x = event.clientX - rect.left - centerX
|
|
134
|
-
const y = event.clientY - rect.top - centerY
|
|
135
|
-
|
|
136
|
-
setMagneticPosition({
|
|
137
|
-
x: x * 0.2,
|
|
138
|
-
y: y * 0.2
|
|
139
|
-
})
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const handleMouseLeave = () => {
|
|
143
|
-
if (enableMagnetic) {
|
|
144
|
-
setMagneticPosition({ x: 0, y: 0 })
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const handleClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
149
|
-
createRipple(e)
|
|
150
|
-
createParticles(e)
|
|
151
|
-
|
|
152
|
-
if (currentState === "loading") return
|
|
153
|
-
|
|
154
|
-
if (enableMorph) {
|
|
155
|
-
setInternalState("loading")
|
|
156
|
-
onStateChange?.("loading")
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (onClick) {
|
|
160
|
-
try {
|
|
161
|
-
await onClick(e)
|
|
162
|
-
if (enableMorph) {
|
|
163
|
-
setInternalState("success")
|
|
164
|
-
onStateChange?.("success")
|
|
165
|
-
|
|
166
|
-
setTimeout(() => {
|
|
167
|
-
setInternalState("idle")
|
|
168
|
-
onStateChange?.("idle")
|
|
169
|
-
}, 2000)
|
|
170
|
-
}
|
|
171
|
-
} catch (error) {
|
|
172
|
-
if (enableMorph) {
|
|
173
|
-
setInternalState("error")
|
|
174
|
-
onStateChange?.("error")
|
|
175
|
-
|
|
176
|
-
setTimeout(() => {
|
|
177
|
-
setInternalState("idle")
|
|
178
|
-
onStateChange?.("idle")
|
|
179
|
-
}, 2000)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const getIcon = () => {
|
|
186
|
-
switch (currentState) {
|
|
187
|
-
case "loading":
|
|
188
|
-
return <Loader2 className="h-4 w-4 animate-spin" />
|
|
189
|
-
case "success":
|
|
190
|
-
return <Check className="h-4 w-4" />
|
|
191
|
-
case "error":
|
|
192
|
-
return <X className="h-4 w-4" />
|
|
193
|
-
default:
|
|
194
|
-
return null
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const getBackgroundColor = () => {
|
|
199
|
-
switch (currentState) {
|
|
200
|
-
case "success":
|
|
201
|
-
return "bg-success"
|
|
202
|
-
case "error":
|
|
203
|
-
return "bg-destructive"
|
|
204
|
-
default:
|
|
205
|
-
return ""
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return (
|
|
210
|
-
<motion.button
|
|
211
|
-
className={cn(enhancedButtonVariants({ variant, size }), className)}
|
|
212
|
-
ref={buttonRef || ref}
|
|
213
|
-
onClick={handleClick}
|
|
214
|
-
onMouseMove={handleMouseMove}
|
|
215
|
-
onMouseLeave={handleMouseLeave}
|
|
216
|
-
disabled={currentState === "loading" || disabled}
|
|
217
|
-
type={type}
|
|
218
|
-
animate={{
|
|
219
|
-
x: magneticPosition.x,
|
|
220
|
-
y: magneticPosition.y,
|
|
221
|
-
}}
|
|
222
|
-
transition={{
|
|
223
|
-
type: "spring",
|
|
224
|
-
stiffness: 150,
|
|
225
|
-
damping: 15,
|
|
226
|
-
mass: 0.1
|
|
227
|
-
}}
|
|
228
|
-
>
|
|
229
|
-
{/* Ripple effects */}
|
|
230
|
-
<AnimatePresence>
|
|
231
|
-
{ripples.map((ripple) => (
|
|
232
|
-
<motion.span
|
|
233
|
-
key={ripple.id}
|
|
234
|
-
className="absolute rounded-full bg-white/30 pointer-events-none"
|
|
235
|
-
style={{
|
|
236
|
-
left: ripple.x,
|
|
237
|
-
top: ripple.y,
|
|
238
|
-
}}
|
|
239
|
-
initial={{ width: 0, height: 0, x: 0, y: 0, opacity: 1 }}
|
|
240
|
-
animate={{
|
|
241
|
-
width: 200,
|
|
242
|
-
height: 200,
|
|
243
|
-
x: -100,
|
|
244
|
-
y: -100,
|
|
245
|
-
opacity: 0
|
|
246
|
-
}}
|
|
247
|
-
exit={{ opacity: 0 }}
|
|
248
|
-
transition={{ duration: 1, ease: "easeOut" }}
|
|
249
|
-
/>
|
|
250
|
-
))}
|
|
251
|
-
</AnimatePresence>
|
|
252
|
-
|
|
253
|
-
{/* Particle effects */}
|
|
254
|
-
<AnimatePresence>
|
|
255
|
-
{particles.map((particle, i) => (
|
|
256
|
-
<motion.span
|
|
257
|
-
key={particle.id}
|
|
258
|
-
className="absolute w-1 h-1 bg-primary rounded-full pointer-events-none"
|
|
259
|
-
style={{
|
|
260
|
-
left: particle.x,
|
|
261
|
-
top: particle.y,
|
|
262
|
-
}}
|
|
263
|
-
initial={{ scale: 0, opacity: 1 }}
|
|
264
|
-
animate={{
|
|
265
|
-
scale: [0, 1, 0],
|
|
266
|
-
x: (i % 2 === 0 ? 1 : -1) * (20 + Math.random() * 30),
|
|
267
|
-
y: -20 - Math.random() * 30,
|
|
268
|
-
opacity: [1, 1, 0]
|
|
269
|
-
}}
|
|
270
|
-
exit={{ opacity: 0 }}
|
|
271
|
-
transition={{ duration: 0.6, ease: "easeOut" }}
|
|
272
|
-
/>
|
|
273
|
-
))}
|
|
274
|
-
</AnimatePresence>
|
|
275
|
-
|
|
276
|
-
{/* Morph content */}
|
|
277
|
-
<motion.div
|
|
278
|
-
className="flex items-center gap-2"
|
|
279
|
-
animate={{
|
|
280
|
-
width: currentState !== "idle" ? "auto" : "100%"
|
|
281
|
-
}}
|
|
282
|
-
transition={{ duration: 0.2 }}
|
|
283
|
-
>
|
|
284
|
-
<AnimatePresence mode="wait">
|
|
285
|
-
{currentState !== "idle" && (
|
|
286
|
-
<motion.div
|
|
287
|
-
initial={{ scale: 0, opacity: 0 }}
|
|
288
|
-
animate={{ scale: 1, opacity: 1 }}
|
|
289
|
-
exit={{ scale: 0, opacity: 0 }}
|
|
290
|
-
transition={{ duration: 0.2 }}
|
|
291
|
-
className="flex items-center"
|
|
292
|
-
>
|
|
293
|
-
{getIcon()}
|
|
294
|
-
</motion.div>
|
|
295
|
-
)}
|
|
296
|
-
</AnimatePresence>
|
|
297
|
-
|
|
298
|
-
<motion.span
|
|
299
|
-
animate={{
|
|
300
|
-
opacity: currentState === "idle" ? 1 : 0,
|
|
301
|
-
x: currentState !== "idle" ? -20 : 0
|
|
302
|
-
}}
|
|
303
|
-
transition={{ duration: 0.2 }}
|
|
304
|
-
>
|
|
305
|
-
{children}
|
|
306
|
-
</motion.span>
|
|
307
|
-
</motion.div>
|
|
308
|
-
|
|
309
|
-
{/* Background animation for success/error states */}
|
|
310
|
-
<AnimatePresence>
|
|
311
|
-
{(currentState === "success" || currentState === "error") && (
|
|
312
|
-
<motion.div
|
|
313
|
-
className={cn(
|
|
314
|
-
"absolute inset-0 rounded-md -z-10",
|
|
315
|
-
getBackgroundColor()
|
|
316
|
-
)}
|
|
317
|
-
initial={{ scale: 0, opacity: 0 }}
|
|
318
|
-
animate={{ scale: 1, opacity: 1 }}
|
|
319
|
-
exit={{ scale: 0, opacity: 0 }}
|
|
320
|
-
transition={{ duration: 0.3 }}
|
|
321
|
-
/>
|
|
322
|
-
)}
|
|
323
|
-
</AnimatePresence>
|
|
324
|
-
|
|
325
|
-
{/* Glitch effect overlay */}
|
|
326
|
-
{enableGlitch && (
|
|
327
|
-
<motion.div
|
|
328
|
-
className="absolute inset-0 pointer-events-none"
|
|
329
|
-
animate={{
|
|
330
|
-
opacity: [0, 0.1, 0, 0.2, 0],
|
|
331
|
-
clipPath: [
|
|
332
|
-
"inset(0 0 100% 0)",
|
|
333
|
-
"inset(20% 0 60% 0)",
|
|
334
|
-
"inset(40% 0 40% 0)",
|
|
335
|
-
"inset(80% 0 10% 0)",
|
|
336
|
-
"inset(0 0 100% 0)",
|
|
337
|
-
]
|
|
338
|
-
}}
|
|
339
|
-
transition={{
|
|
340
|
-
duration: 0.5,
|
|
341
|
-
repeat: Infinity,
|
|
342
|
-
repeatDelay: 5,
|
|
343
|
-
}}
|
|
344
|
-
style={{
|
|
345
|
-
background: "linear-gradient(45deg, #ff0080, #00ff88, #0080ff)",
|
|
346
|
-
mixBlendMode: "screen"
|
|
347
|
-
}}
|
|
348
|
-
/>
|
|
349
|
-
)}
|
|
350
|
-
|
|
351
|
-
{/* Sparkle decoration for special variants */}
|
|
352
|
-
{variant === "gradient" && (
|
|
353
|
-
<Sparkles className="absolute top-1 right-1 h-3 w-3 text-white/50 animate-pulse" />
|
|
354
|
-
)}
|
|
355
|
-
</motion.button>
|
|
356
|
-
)
|
|
357
|
-
}
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
ButtonPro.displayName = "ButtonPro"
|
|
361
|
-
|
|
362
|
-
export { ButtonPro as Button, enhancedButtonVariants }
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import React, { useRef, useState } from "react"
|
|
4
|
-
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion"
|
|
5
|
-
import { cn } from "../../lib/utils"
|
|
6
|
-
|
|
7
|
-
export interface CardProProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
8
|
-
enableGlassmorphism?: boolean
|
|
9
|
-
enableParallax?: boolean
|
|
10
|
-
enableTilt?: boolean
|
|
11
|
-
enableGlow?: boolean
|
|
12
|
-
enableReveal?: boolean
|
|
13
|
-
tiltMaxAngle?: number
|
|
14
|
-
glowColor?: string
|
|
15
|
-
parallaxOffset?: number
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const CardPro = React.forwardRef<HTMLDivElement, CardProProps>(
|
|
19
|
-
({
|
|
20
|
-
className,
|
|
21
|
-
children,
|
|
22
|
-
enableGlassmorphism = true,
|
|
23
|
-
enableParallax = false,
|
|
24
|
-
enableTilt = true,
|
|
25
|
-
enableGlow = false,
|
|
26
|
-
enableReveal = false,
|
|
27
|
-
tiltMaxAngle = 15,
|
|
28
|
-
glowColor = "rgba(59, 130, 246, 0.5)",
|
|
29
|
-
parallaxOffset = 20,
|
|
30
|
-
...props
|
|
31
|
-
}, ref) => {
|
|
32
|
-
const cardRef = useRef<HTMLDivElement>(null)
|
|
33
|
-
const [isHovered, setIsHovered] = useState(false)
|
|
34
|
-
|
|
35
|
-
// Motion values for tilt effect
|
|
36
|
-
const mouseX = useMotionValue(0)
|
|
37
|
-
const mouseY = useMotionValue(0)
|
|
38
|
-
|
|
39
|
-
// Spring physics for smooth animations
|
|
40
|
-
const springConfig = { damping: 20, stiffness: 300 }
|
|
41
|
-
const mouseXSpring = useSpring(mouseX, springConfig)
|
|
42
|
-
const mouseYSpring = useSpring(mouseY, springConfig)
|
|
43
|
-
|
|
44
|
-
// Transform values for 3D tilt
|
|
45
|
-
const rotateX = useTransform(mouseYSpring, [-1, 1], [tiltMaxAngle, -tiltMaxAngle])
|
|
46
|
-
const rotateY = useTransform(mouseXSpring, [-1, 1], [-tiltMaxAngle, tiltMaxAngle])
|
|
47
|
-
|
|
48
|
-
// Glow effect position
|
|
49
|
-
const glowX = useTransform(mouseXSpring, [-1, 1], [0, 100])
|
|
50
|
-
const glowY = useTransform(mouseYSpring, [-1, 1], [0, 100])
|
|
51
|
-
|
|
52
|
-
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
53
|
-
if (!cardRef.current || (!enableTilt && !enableGlow)) return
|
|
54
|
-
|
|
55
|
-
const rect = cardRef.current.getBoundingClientRect()
|
|
56
|
-
const centerX = rect.left + rect.width / 2
|
|
57
|
-
const centerY = rect.top + rect.height / 2
|
|
58
|
-
|
|
59
|
-
const x = (e.clientX - centerX) / (rect.width / 2)
|
|
60
|
-
const y = (e.clientY - centerY) / (rect.height / 2)
|
|
61
|
-
|
|
62
|
-
mouseX.set(x)
|
|
63
|
-
mouseY.set(y)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const handleMouseEnter = () => {
|
|
67
|
-
setIsHovered(true)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const handleMouseLeave = () => {
|
|
71
|
-
setIsHovered(false)
|
|
72
|
-
mouseX.set(0)
|
|
73
|
-
mouseY.set(0)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<motion.div
|
|
78
|
-
ref={cardRef || ref}
|
|
79
|
-
className={cn(
|
|
80
|
-
"relative rounded-xl transition-all duration-300",
|
|
81
|
-
enableGlassmorphism && [
|
|
82
|
-
"backdrop-blur-md",
|
|
83
|
-
"bg-white/10 dark:bg-gray-900/10",
|
|
84
|
-
"border border-white/20 dark:border-gray-700/20",
|
|
85
|
-
"shadow-xl"
|
|
86
|
-
],
|
|
87
|
-
!enableGlassmorphism && [
|
|
88
|
-
"bg-card",
|
|
89
|
-
"border border-border",
|
|
90
|
-
"shadow-sm"
|
|
91
|
-
],
|
|
92
|
-
isHovered && "shadow-2xl",
|
|
93
|
-
className
|
|
94
|
-
)}
|
|
95
|
-
onMouseMove={handleMouseMove}
|
|
96
|
-
onMouseEnter={handleMouseEnter}
|
|
97
|
-
onMouseLeave={handleMouseLeave}
|
|
98
|
-
style={{
|
|
99
|
-
transformStyle: "preserve-3d",
|
|
100
|
-
perspective: "1000px"
|
|
101
|
-
}}
|
|
102
|
-
animate={{
|
|
103
|
-
rotateX: enableTilt ? rotateX.get() : 0,
|
|
104
|
-
rotateY: enableTilt ? rotateY.get() : 0,
|
|
105
|
-
}}
|
|
106
|
-
transition={{ type: "spring", ...springConfig }}
|
|
107
|
-
>
|
|
108
|
-
{/* Glow effect overlay */}
|
|
109
|
-
{enableGlow && (
|
|
110
|
-
<motion.div
|
|
111
|
-
className="absolute inset-0 rounded-xl pointer-events-none opacity-0"
|
|
112
|
-
style={{
|
|
113
|
-
background: `radial-gradient(600px circle at ${glowX}% ${glowY}%, ${glowColor}, transparent 40%)`,
|
|
114
|
-
}}
|
|
115
|
-
animate={{
|
|
116
|
-
opacity: isHovered ? 1 : 0
|
|
117
|
-
}}
|
|
118
|
-
transition={{ duration: 0.3 }}
|
|
119
|
-
/>
|
|
120
|
-
)}
|
|
121
|
-
|
|
122
|
-
{/* Glassmorphism noise texture */}
|
|
123
|
-
{enableGlassmorphism && (
|
|
124
|
-
<div
|
|
125
|
-
className="absolute inset-0 rounded-xl opacity-[0.03] pointer-events-none"
|
|
126
|
-
style={{
|
|
127
|
-
backgroundImage: `url("data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' /%3E%3C/filter%3E%3Crect width='100' height='100' filter='url(%23noise)' /%3E%3C/svg%3E")`,
|
|
128
|
-
}}
|
|
129
|
-
/>
|
|
130
|
-
)}
|
|
131
|
-
|
|
132
|
-
{/* Content with parallax effect */}
|
|
133
|
-
<motion.div
|
|
134
|
-
className="relative z-10"
|
|
135
|
-
style={{
|
|
136
|
-
transform: enableParallax ? "translateZ(50px)" : undefined,
|
|
137
|
-
}}
|
|
138
|
-
animate={{
|
|
139
|
-
x: enableParallax && isHovered ? mouseXSpring.get() * parallaxOffset : 0,
|
|
140
|
-
y: enableParallax && isHovered ? mouseYSpring.get() * parallaxOffset : 0,
|
|
141
|
-
}}
|
|
142
|
-
>
|
|
143
|
-
{children}
|
|
144
|
-
</motion.div>
|
|
145
|
-
|
|
146
|
-
{/* Reveal effect border */}
|
|
147
|
-
{enableReveal && (
|
|
148
|
-
<motion.div
|
|
149
|
-
className="absolute inset-0 rounded-xl pointer-events-none"
|
|
150
|
-
style={{
|
|
151
|
-
background: `conic-gradient(from ${glowX}deg, transparent, ${glowColor}, transparent 30%)`,
|
|
152
|
-
opacity: 0,
|
|
153
|
-
filter: "blur(5px)",
|
|
154
|
-
}}
|
|
155
|
-
animate={{
|
|
156
|
-
opacity: isHovered ? 0.5 : 0,
|
|
157
|
-
rotate: isHovered ? 360 : 0,
|
|
158
|
-
}}
|
|
159
|
-
transition={{
|
|
160
|
-
opacity: { duration: 0.3 },
|
|
161
|
-
rotate: { duration: 20, repeat: Infinity, ease: "linear" }
|
|
162
|
-
}}
|
|
163
|
-
/>
|
|
164
|
-
)}
|
|
165
|
-
|
|
166
|
-
{/* Soft shadow layers for depth */}
|
|
167
|
-
<div className="absolute inset-0 rounded-xl -z-10">
|
|
168
|
-
<motion.div
|
|
169
|
-
className="absolute inset-0 rounded-xl bg-black/5 dark:bg-white/5"
|
|
170
|
-
animate={{
|
|
171
|
-
scale: isHovered ? 1.02 : 1,
|
|
172
|
-
opacity: isHovered ? 0.5 : 0,
|
|
173
|
-
}}
|
|
174
|
-
transition={{ duration: 0.3 }}
|
|
175
|
-
style={{ filter: "blur(10px)" }}
|
|
176
|
-
/>
|
|
177
|
-
<motion.div
|
|
178
|
-
className="absolute inset-0 rounded-xl bg-black/5 dark:bg-white/5"
|
|
179
|
-
animate={{
|
|
180
|
-
scale: isHovered ? 1.05 : 1,
|
|
181
|
-
opacity: isHovered ? 0.3 : 0,
|
|
182
|
-
}}
|
|
183
|
-
transition={{ duration: 0.3 }}
|
|
184
|
-
style={{ filter: "blur(20px)" }}
|
|
185
|
-
/>
|
|
186
|
-
</div>
|
|
187
|
-
</motion.div>
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
CardPro.displayName = "CardPro"
|
|
193
|
-
|
|
194
|
-
// Enhanced Card sub-components with animations
|
|
195
|
-
export const CardProHeader = React.forwardRef<
|
|
196
|
-
HTMLDivElement,
|
|
197
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
198
|
-
>(({ className, ...props }, ref) => (
|
|
199
|
-
<motion.div
|
|
200
|
-
ref={ref}
|
|
201
|
-
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
202
|
-
initial={{ opacity: 0, y: -10 }}
|
|
203
|
-
animate={{ opacity: 1, y: 0 }}
|
|
204
|
-
transition={{ duration: 0.3, delay: 0.1 }}
|
|
205
|
-
/>
|
|
206
|
-
))
|
|
207
|
-
CardProHeader.displayName = "CardProHeader"
|
|
208
|
-
|
|
209
|
-
export const EnhancedCardTitle = React.forwardRef<
|
|
210
|
-
HTMLParagraphElement,
|
|
211
|
-
React.HTMLAttributes<HTMLHeadingElement>
|
|
212
|
-
>(({ className, ...props }, ref) => (
|
|
213
|
-
<motion.h3
|
|
214
|
-
ref={ref}
|
|
215
|
-
className={cn(
|
|
216
|
-
"text-2xl font-semibold leading-none tracking-tight",
|
|
217
|
-
className
|
|
218
|
-
)}
|
|
219
|
-
initial={{ opacity: 0, x: -10 }}
|
|
220
|
-
animate={{ opacity: 1, x: 0 }}
|
|
221
|
-
transition={{ duration: 0.3, delay: 0.2 }}
|
|
222
|
-
/>
|
|
223
|
-
))
|
|
224
|
-
EnhancedCardTitle.displayName = "EnhancedCardTitle"
|
|
225
|
-
|
|
226
|
-
export const EnhancedCardDescription = React.forwardRef<
|
|
227
|
-
HTMLParagraphElement,
|
|
228
|
-
React.HTMLAttributes<HTMLParagraphElement>
|
|
229
|
-
>(({ className, ...props }, ref) => (
|
|
230
|
-
<motion.p
|
|
231
|
-
ref={ref}
|
|
232
|
-
className={cn("text-sm text-muted-foreground", className)}
|
|
233
|
-
initial={{ opacity: 0 }}
|
|
234
|
-
animate={{ opacity: 1 }}
|
|
235
|
-
transition={{ duration: 0.3, delay: 0.3 }}
|
|
236
|
-
/>
|
|
237
|
-
))
|
|
238
|
-
EnhancedCardDescription.displayName = "EnhancedCardDescription"
|
|
239
|
-
|
|
240
|
-
export const CardProContent = React.forwardRef<
|
|
241
|
-
HTMLDivElement,
|
|
242
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
243
|
-
>(({ className, ...props }, ref) => (
|
|
244
|
-
<motion.div
|
|
245
|
-
ref={ref}
|
|
246
|
-
className={cn("p-6 pt-0", className)}
|
|
247
|
-
initial={{ opacity: 0 }}
|
|
248
|
-
animate={{ opacity: 1 }}
|
|
249
|
-
transition={{ duration: 0.3, delay: 0.4 }}
|
|
250
|
-
/>
|
|
251
|
-
))
|
|
252
|
-
CardProContent.displayName = "CardProContent"
|
|
253
|
-
|
|
254
|
-
export const CardProFooter = React.forwardRef<
|
|
255
|
-
HTMLDivElement,
|
|
256
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
257
|
-
>(({ className, ...props }, ref) => (
|
|
258
|
-
<motion.div
|
|
259
|
-
ref={ref}
|
|
260
|
-
className={cn("flex items-center p-6 pt-0", className)}
|
|
261
|
-
initial={{ opacity: 0, y: 10 }}
|
|
262
|
-
animate={{ opacity: 1, y: 0 }}
|
|
263
|
-
transition={{ duration: 0.3, delay: 0.5 }}
|
|
264
|
-
/>
|
|
265
|
-
))
|
|
266
|
-
CardProFooter.displayName = "CardProFooter"
|