@moontra/moonui-pro 2.18.5 → 2.19.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.
- package/dist/index.mjs +651 -291
- package/package.json +1 -1
- package/src/components/animated-button/index.tsx +240 -53
- package/src/components/github-stars/github-api.ts +7 -7
- package/src/components/github-stars/hooks.ts +15 -15
- package/src/components/github-stars/index.tsx +6 -6
- package/src/components/index.ts +5 -1
- package/src/components/ui/hover-card-3d.tsx +397 -28
- package/src/components/ui/index.ts +5 -0
- package/src/components/ui/animated-button.tsx +0 -185
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moontra/moonui-pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -9,8 +9,8 @@ import { Card, CardContent } from "../ui/card"
|
|
|
9
9
|
import { Button } from "../ui/button"
|
|
10
10
|
import { useSubscription } from "../../hooks/use-subscription"
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
"relative inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-
|
|
12
|
+
const moonUIAnimatedButtonProVariants = cva(
|
|
13
|
+
"relative inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 overflow-hidden min-w-fit",
|
|
14
14
|
{
|
|
15
15
|
variants: {
|
|
16
16
|
variant: {
|
|
@@ -20,68 +20,143 @@ const animatedButtonVariants = cva(
|
|
|
20
20
|
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
21
21
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
22
22
|
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
gradient: "bg-gradient-to-r from-purple-600 to-pink-600 text-white hover:from-purple-700 hover:to-pink-700",
|
|
24
|
+
glow: "bg-primary text-primary-foreground shadow-lg hover:shadow-xl hover:shadow-primary/25",
|
|
23
25
|
},
|
|
24
26
|
size: {
|
|
25
27
|
default: "h-9 px-4 py-2",
|
|
26
28
|
sm: "h-8 rounded-md px-3 text-xs",
|
|
27
29
|
lg: "h-10 rounded-md px-8",
|
|
28
|
-
|
|
30
|
+
xl: "h-11 rounded-md px-6 text-base font-medium",
|
|
31
|
+
icon: "h-9 w-9",
|
|
32
|
+
}
|
|
29
33
|
},
|
|
30
34
|
defaultVariants: {
|
|
31
35
|
variant: "default",
|
|
32
|
-
size: "default"
|
|
36
|
+
size: "default"
|
|
33
37
|
},
|
|
34
38
|
}
|
|
35
39
|
)
|
|
36
40
|
|
|
37
|
-
interface
|
|
41
|
+
interface MoonUIAnimatedButtonProProps
|
|
38
42
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
-
VariantProps<typeof
|
|
43
|
+
VariantProps<typeof moonUIAnimatedButtonProVariants> {
|
|
40
44
|
state?: "idle" | "loading" | "success" | "error"
|
|
41
45
|
onStateChange?: (state: "idle" | "loading" | "success" | "error") => void
|
|
46
|
+
loadingText?: string
|
|
47
|
+
successText?: string
|
|
48
|
+
errorText?: string
|
|
49
|
+
ripple?: boolean
|
|
50
|
+
iconRotate?: boolean
|
|
51
|
+
shimmerSpeed?: "slow" | "normal" | "fast"
|
|
52
|
+
glowIntensity?: "low" | "medium" | "high"
|
|
53
|
+
showProgressBar?: boolean
|
|
54
|
+
autoResetDelay?: number
|
|
55
|
+
animation?: "none" | "bounce" | "pulse" | "shimmer" | "slide" | "scale" | "rotate" | "shake"
|
|
42
56
|
}
|
|
43
57
|
|
|
44
|
-
const
|
|
45
|
-
({
|
|
58
|
+
const MoonUIAnimatedButtonProInternal = React.forwardRef<HTMLButtonElement, MoonUIAnimatedButtonProProps>(
|
|
59
|
+
({
|
|
60
|
+
className,
|
|
61
|
+
variant,
|
|
62
|
+
size,
|
|
63
|
+
animation = "none",
|
|
64
|
+
state = "idle",
|
|
65
|
+
onStateChange,
|
|
66
|
+
children,
|
|
67
|
+
onClick,
|
|
68
|
+
loadingText = "Loading...",
|
|
69
|
+
successText = "Success!",
|
|
70
|
+
errorText = "Error!",
|
|
71
|
+
ripple = false,
|
|
72
|
+
iconRotate = false,
|
|
73
|
+
shimmerSpeed = "normal",
|
|
74
|
+
glowIntensity = "medium",
|
|
75
|
+
showProgressBar = false,
|
|
76
|
+
autoResetDelay = 2000,
|
|
77
|
+
onDrag,
|
|
78
|
+
onDragEnd,
|
|
79
|
+
onDragStart,
|
|
80
|
+
onAnimationStart,
|
|
81
|
+
onAnimationEnd,
|
|
82
|
+
...props
|
|
83
|
+
}, ref) => {
|
|
46
84
|
const [internalState, setInternalState] = useState<"idle" | "loading" | "success" | "error">("idle")
|
|
85
|
+
const [ripples, setRipples] = useState<Array<{x: number, y: number, id: number}>>([])
|
|
86
|
+
const [progress, setProgress] = useState(0)
|
|
87
|
+
const [isHovered, setIsHovered] = useState(false)
|
|
47
88
|
const currentState = state !== "idle" ? state : internalState
|
|
48
89
|
|
|
49
90
|
const handleClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
50
91
|
if (currentState === "loading") return
|
|
51
92
|
|
|
93
|
+
// Ripple effect
|
|
94
|
+
if (ripple) {
|
|
95
|
+
const rect = e.currentTarget.getBoundingClientRect()
|
|
96
|
+
const x = e.clientX - rect.left
|
|
97
|
+
const y = e.clientY - rect.top
|
|
98
|
+
const id = Date.now()
|
|
99
|
+
|
|
100
|
+
setRipples([...ripples, { x, y, id }])
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
setRipples(prev => prev.filter(r => r.id !== id))
|
|
103
|
+
}, 600)
|
|
104
|
+
}
|
|
105
|
+
|
|
52
106
|
setInternalState("loading")
|
|
53
107
|
onStateChange?.("loading")
|
|
108
|
+
setProgress(0)
|
|
109
|
+
|
|
110
|
+
// Simulate progress
|
|
111
|
+
if (showProgressBar) {
|
|
112
|
+
const progressInterval = setInterval(() => {
|
|
113
|
+
setProgress(prev => {
|
|
114
|
+
if (prev >= 90) {
|
|
115
|
+
clearInterval(progressInterval)
|
|
116
|
+
return 90
|
|
117
|
+
}
|
|
118
|
+
return prev + 10
|
|
119
|
+
})
|
|
120
|
+
}, 200)
|
|
121
|
+
}
|
|
54
122
|
|
|
55
123
|
if (onClick) {
|
|
56
124
|
try {
|
|
57
125
|
await onClick(e)
|
|
126
|
+
setProgress(100)
|
|
58
127
|
setInternalState("success")
|
|
59
128
|
onStateChange?.("success")
|
|
60
129
|
|
|
61
130
|
setTimeout(() => {
|
|
62
131
|
setInternalState("idle")
|
|
63
132
|
onStateChange?.("idle")
|
|
64
|
-
|
|
133
|
+
setProgress(0)
|
|
134
|
+
}, autoResetDelay)
|
|
65
135
|
} catch (error) {
|
|
136
|
+
setProgress(0)
|
|
66
137
|
setInternalState("error")
|
|
67
138
|
onStateChange?.("error")
|
|
68
139
|
|
|
69
140
|
setTimeout(() => {
|
|
70
141
|
setInternalState("idle")
|
|
71
142
|
onStateChange?.("idle")
|
|
72
|
-
},
|
|
143
|
+
}, autoResetDelay)
|
|
73
144
|
}
|
|
74
145
|
}
|
|
75
146
|
}
|
|
76
147
|
|
|
77
148
|
const getIcon = () => {
|
|
149
|
+
const iconClass = cn("h-4 w-4", {
|
|
150
|
+
"animate-spin": currentState === "loading" || (iconRotate && currentState !== "idle"),
|
|
151
|
+
})
|
|
152
|
+
|
|
78
153
|
switch (currentState) {
|
|
79
154
|
case "loading":
|
|
80
|
-
return <Loader2 className=
|
|
155
|
+
return <Loader2 className={iconClass} />
|
|
81
156
|
case "success":
|
|
82
|
-
return <Check className="
|
|
157
|
+
return <Check className={cn(iconClass, "animate-scale-in")} />
|
|
83
158
|
case "error":
|
|
84
|
-
return <X className="
|
|
159
|
+
return <X className={cn(iconClass, "animate-shake")} />
|
|
85
160
|
default:
|
|
86
161
|
return null
|
|
87
162
|
}
|
|
@@ -98,42 +173,140 @@ const AnimatedButtonInternal = React.forwardRef<HTMLButtonElement, AnimatedButto
|
|
|
98
173
|
}
|
|
99
174
|
}
|
|
100
175
|
|
|
176
|
+
const getShimmerSpeed = () => {
|
|
177
|
+
switch (shimmerSpeed) {
|
|
178
|
+
case "slow":
|
|
179
|
+
return "duration-1500"
|
|
180
|
+
case "fast":
|
|
181
|
+
return "duration-500"
|
|
182
|
+
default:
|
|
183
|
+
return "duration-1000"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const getGlowIntensity = () => {
|
|
188
|
+
switch (glowIntensity) {
|
|
189
|
+
case "low":
|
|
190
|
+
return "shadow-lg"
|
|
191
|
+
case "high":
|
|
192
|
+
return "shadow-2xl"
|
|
193
|
+
default:
|
|
194
|
+
return "shadow-xl"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
// whileHover direkt olarak kullanılacak
|
|
200
|
+
let whileHoverAnimation: any = undefined
|
|
201
|
+
let whileTapAnimation: any = undefined
|
|
202
|
+
|
|
203
|
+
// Animation specific hover states
|
|
204
|
+
if (animation === "bounce") {
|
|
205
|
+
whileHoverAnimation = {
|
|
206
|
+
y: [0, -10, 0],
|
|
207
|
+
transition: {
|
|
208
|
+
duration: 0.6,
|
|
209
|
+
repeat: Infinity,
|
|
210
|
+
repeatType: "loop"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} else if (animation === "pulse") {
|
|
214
|
+
whileHoverAnimation = {
|
|
215
|
+
scale: [1, 1.1, 1],
|
|
216
|
+
transition: {
|
|
217
|
+
duration: 1,
|
|
218
|
+
repeat: Infinity,
|
|
219
|
+
repeatType: "loop"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} else if (animation === "shake") {
|
|
223
|
+
// Shake için CSS animation kullanacağız
|
|
224
|
+
whileHoverAnimation = undefined
|
|
225
|
+
} else if (animation === "rotate") {
|
|
226
|
+
whileHoverAnimation = {
|
|
227
|
+
rotate: 10,
|
|
228
|
+
transition: { type: "spring", stiffness: 300 }
|
|
229
|
+
}
|
|
230
|
+
} else if (animation === "scale") {
|
|
231
|
+
whileHoverAnimation = { scale: 1.05 }
|
|
232
|
+
whileTapAnimation = { scale: 0.95 }
|
|
233
|
+
} else if (animation === "slide") {
|
|
234
|
+
whileHoverAnimation = { y: -4 }
|
|
235
|
+
}
|
|
236
|
+
|
|
101
237
|
return (
|
|
102
|
-
<button
|
|
103
|
-
|
|
238
|
+
<motion.button
|
|
239
|
+
{...props}
|
|
240
|
+
className={cn(
|
|
241
|
+
moonUIAnimatedButtonProVariants({ variant, size }),
|
|
242
|
+
variant === "glow" && getGlowIntensity(),
|
|
243
|
+
animation === "shake" && isHovered && "animate-shake",
|
|
244
|
+
className
|
|
245
|
+
)}
|
|
104
246
|
ref={ref}
|
|
105
247
|
onClick={handleClick}
|
|
106
248
|
disabled={currentState === "loading" || props.disabled}
|
|
107
|
-
{
|
|
249
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
250
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
251
|
+
whileHover={whileHoverAnimation}
|
|
252
|
+
whileTap={whileTapAnimation}
|
|
108
253
|
>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
animate={{
|
|
112
|
-
width: currentState !== "idle" ? "auto" : "100%"
|
|
113
|
-
}}
|
|
114
|
-
transition={{ duration: 0.2 }}
|
|
115
|
-
>
|
|
254
|
+
{/* Progress Bar */}
|
|
255
|
+
{showProgressBar && currentState === "loading" && (
|
|
116
256
|
<motion.div
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
257
|
+
className="absolute bottom-0 left-0 h-1 bg-primary-foreground/20 rounded-full"
|
|
258
|
+
initial={{ width: 0 }}
|
|
259
|
+
animate={{ width: `${progress}%` }}
|
|
260
|
+
transition={{ duration: 0.3 }}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
|
|
264
|
+
{/* Shimmer Effect */}
|
|
265
|
+
{animation === "shimmer" && (
|
|
266
|
+
<motion.div
|
|
267
|
+
className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent"
|
|
268
|
+
initial={{ x: "-100%" }}
|
|
269
|
+
animate={{ x: "100%" }}
|
|
270
|
+
transition={{
|
|
271
|
+
duration: shimmerSpeed === "slow" ? 1.5 : shimmerSpeed === "fast" ? 0.5 : 1,
|
|
272
|
+
repeat: Infinity,
|
|
273
|
+
repeatDelay: 1
|
|
131
274
|
}}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
275
|
+
/>
|
|
276
|
+
)}
|
|
277
|
+
|
|
278
|
+
{/* Content */}
|
|
279
|
+
<div className="relative flex items-center justify-center gap-2 z-10">
|
|
280
|
+
{/* Icon variant için özel düzenleme */}
|
|
281
|
+
{size === "icon" ? (
|
|
282
|
+
// Icon size için sadece icon göster
|
|
283
|
+
currentState === "idle" ? (
|
|
284
|
+
children
|
|
285
|
+
) : (
|
|
286
|
+
getIcon()
|
|
287
|
+
)
|
|
288
|
+
) : (
|
|
289
|
+
// Diğer boyutlar için normal akış
|
|
290
|
+
<>
|
|
291
|
+
{currentState === "idle" ? (
|
|
292
|
+
<>
|
|
293
|
+
{React.isValidElement(children) && React.cloneElement(children as React.ReactElement)}
|
|
294
|
+
{typeof children === 'string' && children}
|
|
295
|
+
{React.isValidElement(children) || typeof children === 'string' ? null : children}
|
|
296
|
+
</>
|
|
297
|
+
) : (
|
|
298
|
+
<>
|
|
299
|
+
{getIcon()}
|
|
300
|
+
<span className="ml-2">
|
|
301
|
+
{currentState === "loading" && loadingText}
|
|
302
|
+
{currentState === "success" && successText}
|
|
303
|
+
{currentState === "error" && errorText}
|
|
304
|
+
</span>
|
|
305
|
+
</>
|
|
306
|
+
)}
|
|
307
|
+
</>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
137
310
|
|
|
138
311
|
{/* Background animation */}
|
|
139
312
|
<motion.div
|
|
@@ -144,27 +317,41 @@ const AnimatedButtonInternal = React.forwardRef<HTMLButtonElement, AnimatedButto
|
|
|
144
317
|
initial={{ scale: 0, opacity: 0 }}
|
|
145
318
|
animate={{
|
|
146
319
|
scale: currentState === "success" || currentState === "error" ? 1 : 0,
|
|
147
|
-
opacity: currentState === "success" || currentState === "error" ?
|
|
320
|
+
opacity: currentState === "success" || currentState === "error" ? 0.2 : 0
|
|
148
321
|
}}
|
|
149
322
|
transition={{ duration: 0.3 }}
|
|
150
|
-
style={{ zIndex:
|
|
323
|
+
style={{ zIndex: 0 }}
|
|
151
324
|
/>
|
|
152
|
-
|
|
325
|
+
|
|
326
|
+
{/* Ripple Effect */}
|
|
327
|
+
{ripples.map(ripple => (
|
|
328
|
+
<span
|
|
329
|
+
key={ripple.id}
|
|
330
|
+
className="absolute bg-primary/20 rounded-full animate-ripple pointer-events-none"
|
|
331
|
+
style={{
|
|
332
|
+
left: ripple.x - 10,
|
|
333
|
+
top: ripple.y - 10,
|
|
334
|
+
width: 20,
|
|
335
|
+
height: 20,
|
|
336
|
+
}}
|
|
337
|
+
/>
|
|
338
|
+
))}
|
|
339
|
+
</motion.button>
|
|
153
340
|
)
|
|
154
341
|
}
|
|
155
342
|
)
|
|
156
343
|
|
|
157
|
-
|
|
344
|
+
MoonUIAnimatedButtonProInternal.displayName = "MoonUIAnimatedButtonProInternal"
|
|
158
345
|
|
|
159
|
-
export const
|
|
160
|
-
(
|
|
346
|
+
export const MoonUIAnimatedButtonPro = React.forwardRef<HTMLButtonElement, MoonUIAnimatedButtonProProps>(
|
|
347
|
+
(props, ref) => {
|
|
161
348
|
// Pro package - always show component
|
|
162
349
|
const { hasProAccess, isLoading } = useSubscription()
|
|
163
350
|
|
|
164
351
|
// Show upgrade prompt if no pro access
|
|
165
352
|
if (!isLoading && !hasProAccess) {
|
|
166
353
|
return (
|
|
167
|
-
<Card className={cn("w-fit", className)}>
|
|
354
|
+
<Card className={cn("w-fit", props.className)}>
|
|
168
355
|
<CardContent className="py-6 text-center">
|
|
169
356
|
<div className="space-y-4">
|
|
170
357
|
<div className="rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto">
|
|
@@ -188,11 +375,11 @@ export const AnimatedButton = React.forwardRef<HTMLButtonElement, AnimatedButton
|
|
|
188
375
|
)
|
|
189
376
|
}
|
|
190
377
|
|
|
191
|
-
return <
|
|
378
|
+
return <MoonUIAnimatedButtonProInternal {...props} ref={ref} />
|
|
192
379
|
}
|
|
193
380
|
)
|
|
194
381
|
|
|
195
|
-
|
|
382
|
+
MoonUIAnimatedButtonPro.displayName = "MoonUIAnimatedButtonPro"
|
|
196
383
|
|
|
197
|
-
export {
|
|
198
|
-
export type {
|
|
384
|
+
export { moonUIAnimatedButtonProVariants }
|
|
385
|
+
export type { MoonUIAnimatedButtonProProps }
|
|
@@ -12,8 +12,8 @@ const isDocsMode = () => {
|
|
|
12
12
|
|
|
13
13
|
// Get cache duration based on mode
|
|
14
14
|
const getCacheDuration = (defaultDuration: number) => {
|
|
15
|
-
//
|
|
16
|
-
return isDocsMode() ? 1800000 : defaultDuration // 30
|
|
15
|
+
// Increase cache duration to 30 minutes in docs mode
|
|
16
|
+
return isDocsMode() ? 1800000 : defaultDuration // 30 minutes : default
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// Language colors
|
|
@@ -134,7 +134,7 @@ export async function fetchUserRepositories(
|
|
|
134
134
|
cache.set(cacheKey, {
|
|
135
135
|
data: repos,
|
|
136
136
|
timestamp: Date.now(),
|
|
137
|
-
expiresAt: Date.now() + getCacheDuration(300000), //
|
|
137
|
+
expiresAt: Date.now() + getCacheDuration(300000), // 30 minutes in docs mode
|
|
138
138
|
})
|
|
139
139
|
|
|
140
140
|
return repos
|
|
@@ -159,7 +159,7 @@ export async function fetchRepository(
|
|
|
159
159
|
cache.set(cacheKey, {
|
|
160
160
|
data: repository,
|
|
161
161
|
timestamp: Date.now(),
|
|
162
|
-
expiresAt: Date.now() + getCacheDuration(300000), //
|
|
162
|
+
expiresAt: Date.now() + getCacheDuration(300000), // 30 minutes in docs mode
|
|
163
163
|
})
|
|
164
164
|
|
|
165
165
|
return repository
|
|
@@ -193,7 +193,7 @@ export async function fetchContributorsCount(
|
|
|
193
193
|
cache.set(cacheKey, {
|
|
194
194
|
data: count,
|
|
195
195
|
timestamp: Date.now(),
|
|
196
|
-
expiresAt: Date.now() + getCacheDuration(3600000), //
|
|
196
|
+
expiresAt: Date.now() + getCacheDuration(3600000), // 30 minutes in docs mode
|
|
197
197
|
})
|
|
198
198
|
return count
|
|
199
199
|
}
|
|
@@ -206,7 +206,7 @@ export async function fetchContributorsCount(
|
|
|
206
206
|
cache.set(cacheKey, {
|
|
207
207
|
data: count,
|
|
208
208
|
timestamp: Date.now(),
|
|
209
|
-
expiresAt: Date.now() + getCacheDuration(3600000), //
|
|
209
|
+
expiresAt: Date.now() + getCacheDuration(3600000), // 30 minutes in docs mode
|
|
210
210
|
})
|
|
211
211
|
|
|
212
212
|
return count
|
|
@@ -246,7 +246,7 @@ export async function fetchStarHistory(
|
|
|
246
246
|
cache.set(cacheKey, {
|
|
247
247
|
data: history,
|
|
248
248
|
timestamp: Date.now(),
|
|
249
|
-
expiresAt: Date.now() + getCacheDuration(3600000), //
|
|
249
|
+
expiresAt: Date.now() + getCacheDuration(3600000), // 30 minutes in docs mode
|
|
250
250
|
})
|
|
251
251
|
|
|
252
252
|
return history
|
|
@@ -29,7 +29,7 @@ interface UseGitHubDataOptions {
|
|
|
29
29
|
onDataUpdate?: (stats: GitHubStats) => void
|
|
30
30
|
onMilestoneReached?: (milestone: Milestone) => void
|
|
31
31
|
milestones?: number[]
|
|
32
|
-
//
|
|
32
|
+
// For docs mode optimizations
|
|
33
33
|
docsMode?: boolean
|
|
34
34
|
mockDataFallback?: boolean
|
|
35
35
|
forceMockData?: boolean // Force mock data instead of API
|
|
@@ -52,12 +52,12 @@ export function useGitHubData({
|
|
|
52
52
|
mockDataFallback = true,
|
|
53
53
|
forceMockData = false,
|
|
54
54
|
}: UseGitHubDataOptions) {
|
|
55
|
-
// Docs mode
|
|
55
|
+
// Docs mode detection
|
|
56
56
|
const isDocsMode = docsMode || (typeof window !== "undefined" &&
|
|
57
57
|
(window.location.pathname.includes("/docs/") ||
|
|
58
58
|
window.location.pathname.includes("/components/")))
|
|
59
59
|
|
|
60
|
-
//
|
|
60
|
+
// Disable autoRefresh in docs mode
|
|
61
61
|
const effectiveAutoRefresh = isDocsMode ? false : autoRefresh
|
|
62
62
|
const [repos, setRepos] = useState<GitHubRepository[]>([])
|
|
63
63
|
const [stats, setStats] = useState<GitHubStats | null>(null)
|
|
@@ -69,11 +69,11 @@ export function useGitHubData({
|
|
|
69
69
|
const refreshTimeoutRef = useRef<NodeJS.Timeout>()
|
|
70
70
|
const previousStarsRef = useRef<Map<string, number>>(new Map())
|
|
71
71
|
const errorCountRef = useRef<number>(0) // Hata sayısını takip et
|
|
72
|
-
const maxErrorCount = isDocsMode ? 1 : 2 //
|
|
72
|
+
const maxErrorCount = isDocsMode ? 1 : 2 // Fewer retries in docs mode
|
|
73
73
|
const hasInitialFetchedRef = useRef(false) // İlk fetch yapıldı mı?
|
|
74
74
|
const docsDataCacheRef = useRef<GitHubRepository[] | null>(null) // Docs mode cache
|
|
75
75
|
|
|
76
|
-
// checkMilestones
|
|
76
|
+
// Store checkMilestones function as ref - this way it won't be recreated on every render
|
|
77
77
|
const milestonesRef = useRef(milestones)
|
|
78
78
|
const onMilestoneReachedRef = useRef(onMilestoneReached)
|
|
79
79
|
|
|
@@ -135,7 +135,7 @@ export function useGitHubData({
|
|
|
135
135
|
if (errorCountRef.current >= maxErrorCount) {
|
|
136
136
|
console.warn("Maximum error count reached. Stopping requests.")
|
|
137
137
|
|
|
138
|
-
//
|
|
138
|
+
// Show mock data if in docs mode and mockDataFallback is active
|
|
139
139
|
if (isDocsMode && mockDataFallback) {
|
|
140
140
|
const mockData = getMockGitHubData(username, repository, repositories)
|
|
141
141
|
setRepos(mockData)
|
|
@@ -150,7 +150,7 @@ export function useGitHubData({
|
|
|
150
150
|
return
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
//
|
|
153
|
+
// Don't make requests if required info is missing
|
|
154
154
|
const hasValidInput =
|
|
155
155
|
(username && repository) || // Tek repository modu
|
|
156
156
|
(username && repositories && repositories.length > 0) || // Çoklu repository modu
|
|
@@ -269,10 +269,10 @@ export function useGitHubData({
|
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
setLastUpdated(new Date())
|
|
272
|
-
//
|
|
272
|
+
// Reset error counter on success
|
|
273
273
|
errorCountRef.current = 0
|
|
274
274
|
|
|
275
|
-
//
|
|
275
|
+
// Cache successful data in docs mode
|
|
276
276
|
if (isDocsMode) {
|
|
277
277
|
hasInitialFetchedRef.current = true
|
|
278
278
|
docsDataCacheRef.current = enhancedRepos
|
|
@@ -285,7 +285,7 @@ export function useGitHubData({
|
|
|
285
285
|
errorCountRef.current += 1
|
|
286
286
|
console.error(`GitHub API error (${errorCountRef.current}/${maxErrorCount}):`, errorMessage)
|
|
287
287
|
|
|
288
|
-
//
|
|
288
|
+
// Show mock data if in docs mode and mockDataFallback is active
|
|
289
289
|
if (isDocsMode && mockDataFallback && !hasInitialFetchedRef.current) {
|
|
290
290
|
console.warn("[Docs Mode] API failed, using mock data")
|
|
291
291
|
const mockData = getMockGitHubData(username, repository, repositories)
|
|
@@ -304,11 +304,11 @@ export function useGitHubData({
|
|
|
304
304
|
}, [
|
|
305
305
|
username,
|
|
306
306
|
repository,
|
|
307
|
-
repositories?.join(','), //
|
|
307
|
+
repositories?.join(','), // Convert array to string to keep reference stable
|
|
308
308
|
token,
|
|
309
309
|
sortBy,
|
|
310
310
|
maxItems,
|
|
311
|
-
checkMilestones, //
|
|
311
|
+
checkMilestones, // Now stable
|
|
312
312
|
onDataUpdate,
|
|
313
313
|
onError,
|
|
314
314
|
isDocsMode,
|
|
@@ -318,7 +318,7 @@ export function useGitHubData({
|
|
|
318
318
|
|
|
319
319
|
// Initial fetch
|
|
320
320
|
useEffect(() => {
|
|
321
|
-
//
|
|
321
|
+
// Only fetch if there's valid input
|
|
322
322
|
const hasValidInput =
|
|
323
323
|
(username && repository) ||
|
|
324
324
|
(username && repositories && repositories.length > 0) ||
|
|
@@ -334,7 +334,7 @@ export function useGitHubData({
|
|
|
334
334
|
|
|
335
335
|
// Auto-refresh
|
|
336
336
|
useEffect(() => {
|
|
337
|
-
//
|
|
337
|
+
// Completely disable auto-refresh in docs mode
|
|
338
338
|
if (!effectiveAutoRefresh) return
|
|
339
339
|
|
|
340
340
|
const scheduleRefresh = () => {
|
|
@@ -354,7 +354,7 @@ export function useGitHubData({
|
|
|
354
354
|
}, [effectiveAutoRefresh, refreshInterval, fetchData])
|
|
355
355
|
|
|
356
356
|
const refresh = useCallback(() => {
|
|
357
|
-
//
|
|
357
|
+
// Limit refresh in docs mode
|
|
358
358
|
if (isDocsMode && hasInitialFetchedRef.current) {
|
|
359
359
|
console.warn("[Docs Mode] Refresh disabled after initial fetch")
|
|
360
360
|
return Promise.resolve()
|
|
@@ -74,10 +74,10 @@ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
|
|
|
74
74
|
(window.location.pathname.includes("/docs/") ||
|
|
75
75
|
window.location.pathname.includes("/components/"))
|
|
76
76
|
|
|
77
|
-
//
|
|
77
|
+
// Override some features in docs mode
|
|
78
78
|
const effectiveAutoRefresh = isDocsMode ? false : autoRefresh
|
|
79
79
|
const effectiveNotifications = isDocsMode ? false : enableNotifications
|
|
80
|
-
const effectiveRefreshInterval = isDocsMode ? 3600000 : refreshInterval //
|
|
80
|
+
const effectiveRefreshInterval = isDocsMode ? 3600000 : refreshInterval // 1 hour in docs mode
|
|
81
81
|
const { notify } = useGitHubNotifications(effectiveNotifications)
|
|
82
82
|
|
|
83
83
|
// onMilestoneReached callback'ini memoize et
|
|
@@ -123,7 +123,7 @@ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
|
|
|
123
123
|
onMilestoneReached: handleMilestoneReached,
|
|
124
124
|
milestones,
|
|
125
125
|
docsMode: isDocsMode, // Docs mode flag'ini gönder
|
|
126
|
-
mockDataFallback: true, //
|
|
126
|
+
mockDataFallback: true, // Use mock data in docs mode
|
|
127
127
|
forceMockData: useMockData, // Force mock data if true
|
|
128
128
|
})
|
|
129
129
|
|
|
@@ -317,7 +317,7 @@ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
|
|
|
317
317
|
>
|
|
318
318
|
{renderVariant()}
|
|
319
319
|
|
|
320
|
-
{/* Rate limit warning -
|
|
320
|
+
{/* Rate limit warning - Don't show in docs mode */}
|
|
321
321
|
{!isDocsMode && rateLimitInfo && rateLimitInfo.remaining < 10 && (
|
|
322
322
|
<div className="mt-4 p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-md text-sm">
|
|
323
323
|
<p className="text-yellow-800 dark:text-yellow-200">
|
|
@@ -327,10 +327,10 @@ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
|
|
|
327
327
|
</div>
|
|
328
328
|
)}
|
|
329
329
|
|
|
330
|
-
{/* Docs mode indicator -
|
|
330
|
+
{/* Docs mode indicator - only show in development mode */}
|
|
331
331
|
{isDocsMode && process.env.NODE_ENV === "development" && (
|
|
332
332
|
<div className="mt-2 text-xs text-muted-foreground text-center">
|
|
333
|
-
📚 Docs Mode: API
|
|
333
|
+
📚 Docs Mode: API requests optimized
|
|
334
334
|
</div>
|
|
335
335
|
)}
|
|
336
336
|
</motion.div>
|
package/src/components/index.ts
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
export * from "./ui"
|
|
5
5
|
|
|
6
6
|
// Animated Button
|
|
7
|
-
export
|
|
7
|
+
export {
|
|
8
|
+
MoonUIAnimatedButtonPro,
|
|
9
|
+
moonUIAnimatedButtonProVariants,
|
|
10
|
+
type MoonUIAnimatedButtonProProps
|
|
11
|
+
} from "./animated-button"
|
|
8
12
|
|
|
9
13
|
// Error Boundary
|
|
10
14
|
export * from "./error-boundary"
|