@moontra/moonui-pro 2.0.22 → 2.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 (99) hide show
  1. package/dist/index.mjs +215 -214
  2. package/package.json +4 -2
  3. package/src/__tests__/use-intersection-observer.test.tsx +216 -0
  4. package/src/__tests__/use-local-storage.test.tsx +174 -0
  5. package/src/__tests__/use-pro-access.test.tsx +183 -0
  6. package/src/components/advanced-chart/advanced-chart.test.tsx +281 -0
  7. package/src/components/advanced-chart/index.tsx +412 -0
  8. package/src/components/advanced-forms/index.tsx +431 -0
  9. package/src/components/animated-button/index.tsx +202 -0
  10. package/src/components/calendar/event-dialog.tsx +372 -0
  11. package/src/components/calendar/index.tsx +557 -0
  12. package/src/components/color-picker/index.tsx +434 -0
  13. package/src/components/dashboard/index.tsx +334 -0
  14. package/src/components/data-table/data-table.test.tsx +187 -0
  15. package/src/components/data-table/index.tsx +368 -0
  16. package/src/components/draggable-list/index.tsx +100 -0
  17. package/src/components/enhanced/button.tsx +360 -0
  18. package/src/components/enhanced/card.tsx +272 -0
  19. package/src/components/enhanced/dialog.tsx +248 -0
  20. package/src/components/enhanced/index.ts +3 -0
  21. package/src/components/error-boundary/index.tsx +111 -0
  22. package/src/components/file-upload/file-upload.test.tsx +242 -0
  23. package/src/components/file-upload/index.tsx +362 -0
  24. package/src/components/floating-action-button/index.tsx +209 -0
  25. package/src/components/github-stars/index.tsx +414 -0
  26. package/src/components/health-check/index.tsx +441 -0
  27. package/src/components/hover-card-3d/index.tsx +170 -0
  28. package/src/components/index.ts +76 -0
  29. package/src/components/kanban/index.tsx +436 -0
  30. package/src/components/lazy-component/index.tsx +342 -0
  31. package/src/components/magnetic-button/index.tsx +170 -0
  32. package/src/components/memory-efficient-data/index.tsx +352 -0
  33. package/src/components/optimized-image/index.tsx +427 -0
  34. package/src/components/performance-debugger/index.tsx +591 -0
  35. package/src/components/performance-monitor/index.tsx +775 -0
  36. package/src/components/pinch-zoom/index.tsx +172 -0
  37. package/src/components/rich-text-editor/index-old-backup.tsx +443 -0
  38. package/src/components/rich-text-editor/index.tsx +1537 -0
  39. package/src/components/rich-text-editor/slash-commands-extension.ts +220 -0
  40. package/src/components/rich-text-editor/slash-commands.css +35 -0
  41. package/src/components/rich-text-editor/table-styles.css +65 -0
  42. package/src/components/spotlight-card/index.tsx +194 -0
  43. package/src/components/swipeable-card/index.tsx +100 -0
  44. package/src/components/timeline/index.tsx +333 -0
  45. package/src/components/ui/animated-button.tsx +185 -0
  46. package/src/components/ui/avatar.tsx +135 -0
  47. package/src/components/ui/badge.tsx +225 -0
  48. package/src/components/ui/button.tsx +221 -0
  49. package/src/components/ui/card.tsx +141 -0
  50. package/src/components/ui/checkbox.tsx +256 -0
  51. package/src/components/ui/color-picker.tsx +95 -0
  52. package/src/components/ui/dialog.tsx +332 -0
  53. package/src/components/ui/dropdown-menu.tsx +200 -0
  54. package/src/components/ui/hover-card-3d.tsx +103 -0
  55. package/src/components/ui/index.ts +33 -0
  56. package/src/components/ui/input.tsx +219 -0
  57. package/src/components/ui/label.tsx +26 -0
  58. package/src/components/ui/magnetic-button.tsx +129 -0
  59. package/src/components/ui/popover.tsx +183 -0
  60. package/src/components/ui/select.tsx +273 -0
  61. package/src/components/ui/separator.tsx +140 -0
  62. package/src/components/ui/slider.tsx +351 -0
  63. package/src/components/ui/spotlight-card.tsx +119 -0
  64. package/src/components/ui/switch.tsx +83 -0
  65. package/src/components/ui/tabs.tsx +195 -0
  66. package/src/components/ui/textarea.tsx +25 -0
  67. package/src/components/ui/toast.tsx +313 -0
  68. package/src/components/ui/tooltip.tsx +152 -0
  69. package/src/components/virtual-list/index.tsx +369 -0
  70. package/src/hooks/use-chart.ts +205 -0
  71. package/src/hooks/use-data-table.ts +182 -0
  72. package/src/hooks/use-docs-pro-access.ts +13 -0
  73. package/src/hooks/use-license-check.ts +65 -0
  74. package/src/hooks/use-subscription.ts +19 -0
  75. package/src/index.ts +14 -0
  76. package/src/lib/micro-interactions.ts +255 -0
  77. package/src/lib/utils.ts +6 -0
  78. package/src/patterns/login-form/index.tsx +276 -0
  79. package/src/patterns/login-form/types.ts +67 -0
  80. package/src/setupTests.ts +41 -0
  81. package/src/styles/design-system.css +365 -0
  82. package/src/styles/index.css +4 -0
  83. package/src/styles/tailwind.css +6 -0
  84. package/src/styles/tokens.css +453 -0
  85. package/src/types/moonui.d.ts +22 -0
  86. package/src/use-intersection-observer.tsx +154 -0
  87. package/src/use-local-storage.tsx +71 -0
  88. package/src/use-paddle.ts +138 -0
  89. package/src/use-performance-optimizer.ts +379 -0
  90. package/src/use-pro-access.ts +141 -0
  91. package/src/use-scroll-animation.ts +221 -0
  92. package/src/use-subscription.ts +37 -0
  93. package/src/use-toast.ts +32 -0
  94. package/src/utils/chart-helpers.ts +257 -0
  95. package/src/utils/cn.ts +69 -0
  96. package/src/utils/data-processing.ts +151 -0
  97. package/src/utils/license-guard.tsx +177 -0
  98. package/src/utils/license-validator.tsx +183 -0
  99. package/src/utils/package-guard.ts +60 -0
@@ -0,0 +1,414 @@
1
+ "use client"
2
+
3
+ import React, { useState, useEffect } from "react"
4
+ import { motion, AnimatePresence } from "framer-motion"
5
+ import { Card, CardContent } from "../ui/card"
6
+ import { Button } from "../ui/button"
7
+ import { Badge } from "../ui/badge"
8
+ import { Skeleton } from "@moontra/moonui"
9
+ import { cn } from "../../lib/utils"
10
+ import { Star, GitFork, Eye, Users, ExternalLink, Github, Lock, Sparkles, RefreshCw } from "lucide-react"
11
+ import { useSubscription } from "../../hooks/use-subscription"
12
+
13
+ export interface GitHubRepository {
14
+ id: number
15
+ name: string
16
+ full_name: string
17
+ description: string | null
18
+ html_url: string
19
+ homepage: string | null
20
+ stargazers_count: number
21
+ watchers_count: number
22
+ forks_count: number
23
+ language: string | null
24
+ topics: string[]
25
+ created_at: string
26
+ updated_at: string
27
+ owner: {
28
+ login: string
29
+ avatar_url: string
30
+ html_url: string
31
+ }
32
+ }
33
+
34
+ export interface GitHubStarsProps {
35
+ username: string
36
+ repositories?: string[]
37
+ showDescription?: boolean
38
+ showTopics?: boolean
39
+ showStats?: boolean
40
+ showOwner?: boolean
41
+ sortBy?: "stars" | "forks" | "updated" | "created"
42
+ layout?: "grid" | "list"
43
+ maxItems?: number
44
+ autoRefresh?: boolean
45
+ refreshInterval?: number
46
+ className?: string
47
+ onRepositoryClick?: (repo: GitHubRepository) => void
48
+ }
49
+
50
+ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
51
+ username,
52
+ repositories,
53
+ showDescription = true,
54
+ showTopics = true,
55
+ showStats = true,
56
+ showOwner = true,
57
+ sortBy = "stars",
58
+ layout = "grid",
59
+ maxItems = 6,
60
+ autoRefresh = false,
61
+ refreshInterval = 300000, // 5 minutes
62
+ className,
63
+ onRepositoryClick
64
+ }) => {
65
+ const [repos, setRepos] = useState<GitHubRepository[]>([])
66
+ const [loading, setLoading] = useState(true)
67
+ const [error, setError] = useState<string | null>(null)
68
+ const [lastUpdated, setLastUpdated] = useState<Date | null>(null)
69
+
70
+ const fetchRepositories = async () => {
71
+ try {
72
+ setLoading(true)
73
+ setError(null)
74
+
75
+ let url = `https://api.github.com/users/${username}/repos?sort=${sortBy}&per_page=100`
76
+
77
+ const response = await fetch(url)
78
+ if (!response.ok) {
79
+ throw new Error(`GitHub API error: ${response.status}`)
80
+ }
81
+
82
+ let data: GitHubRepository[] = await response.json()
83
+
84
+ // Filter specific repositories if provided
85
+ if (repositories && repositories.length > 0) {
86
+ data = data.filter(repo => repositories.includes(repo.name))
87
+ }
88
+
89
+ // Sort repositories
90
+ data.sort((a, b) => {
91
+ switch (sortBy) {
92
+ case "stars":
93
+ return b.stargazers_count - a.stargazers_count
94
+ case "forks":
95
+ return b.forks_count - a.forks_count
96
+ case "updated":
97
+ return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
98
+ case "created":
99
+ return new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
100
+ default:
101
+ return 0
102
+ }
103
+ })
104
+
105
+ // Limit results
106
+ if (maxItems) {
107
+ data = data.slice(0, maxItems)
108
+ }
109
+
110
+ setRepos(data)
111
+ setLastUpdated(new Date())
112
+ } catch (err) {
113
+ setError(err instanceof Error ? err.message : "Failed to fetch repositories")
114
+ } finally {
115
+ setLoading(false)
116
+ }
117
+ }
118
+
119
+ useEffect(() => {
120
+ fetchRepositories()
121
+ }, [username, repositories, sortBy, maxItems])
122
+
123
+ useEffect(() => {
124
+ if (!autoRefresh) return
125
+
126
+ const interval = setInterval(fetchRepositories, refreshInterval)
127
+ return () => clearInterval(interval)
128
+ }, [autoRefresh, refreshInterval])
129
+
130
+ const formatNumber = (num: number): string => {
131
+ if (num >= 1000) {
132
+ return (num / 1000).toFixed(1) + "k"
133
+ }
134
+ return num.toString()
135
+ }
136
+
137
+ const formatDate = (dateString: string): string => {
138
+ const date = new Date(dateString)
139
+ return date.toLocaleDateString("en-US", {
140
+ year: "numeric",
141
+ month: "short",
142
+ day: "numeric"
143
+ })
144
+ }
145
+
146
+ const getLanguageColor = (language: string | null): string => {
147
+ const colors: Record<string, string> = {
148
+ JavaScript: "#f7df1e",
149
+ TypeScript: "#3178c6",
150
+ Python: "#3776ab",
151
+ Java: "#ed8b00",
152
+ "C++": "#00599c",
153
+ "C#": "#239120",
154
+ Go: "#00add8",
155
+ Rust: "#000000",
156
+ Swift: "#fa7343",
157
+ Kotlin: "#7f52ff",
158
+ PHP: "#777bb4",
159
+ Ruby: "#cc342d",
160
+ HTML: "#e34f26",
161
+ CSS: "#1572b6",
162
+ Vue: "#4fc08d",
163
+ React: "#61dafb"
164
+ }
165
+ return colors[language || ""] || "#6b7280"
166
+ }
167
+
168
+ if (loading) {
169
+ return (
170
+ <Card className={cn("w-full", className)}>
171
+ <CardContent className="p-6">
172
+ <div className="space-y-4">
173
+ <div className="flex items-center justify-between">
174
+ <Skeleton className="h-6 w-48" />
175
+ <Skeleton className="h-8 w-20" />
176
+ </div>
177
+ <div className={cn(
178
+ layout === "grid"
179
+ ? "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
180
+ : "space-y-4"
181
+ )}>
182
+ {Array.from({ length: maxItems || 6 }).map((_, index) => (
183
+ <Card key={index}>
184
+ <CardContent className="p-4">
185
+ <div className="space-y-3">
186
+ <Skeleton className="h-5 w-3/4" />
187
+ <Skeleton className="h-4 w-full" />
188
+ <Skeleton className="h-4 w-2/3" />
189
+ <div className="flex gap-2">
190
+ <Skeleton className="h-6 w-16" />
191
+ <Skeleton className="h-6 w-16" />
192
+ <Skeleton className="h-6 w-16" />
193
+ </div>
194
+ </div>
195
+ </CardContent>
196
+ </Card>
197
+ ))}
198
+ </div>
199
+ </div>
200
+ </CardContent>
201
+ </Card>
202
+ )
203
+ }
204
+
205
+ if (error) {
206
+ return (
207
+ <Card className={cn("w-full", className)}>
208
+ <CardContent className="p-6 text-center">
209
+ <div className="space-y-4">
210
+ <div className="text-destructive">
211
+ <Github className="h-12 w-12 mx-auto mb-2" />
212
+ <h3 className="font-semibold">Failed to load repositories</h3>
213
+ <p className="text-sm text-muted-foreground">{error}</p>
214
+ </div>
215
+ <Button onClick={fetchRepositories} variant="outline">
216
+ <RefreshCw className="h-4 w-4 mr-2" />
217
+ Try Again
218
+ </Button>
219
+ </div>
220
+ </CardContent>
221
+ </Card>
222
+ )
223
+ }
224
+
225
+ return (
226
+ <Card className={cn("w-full", className)}>
227
+ <CardContent className="p-6">
228
+ <div className="space-y-6">
229
+ {/* Header */}
230
+ <div className="flex items-center justify-between">
231
+ <div className="flex items-center gap-3">
232
+ <Github className="h-6 w-6" />
233
+ <div>
234
+ <h3 className="font-semibold text-lg">{username}'s Repositories</h3>
235
+ {lastUpdated && (
236
+ <p className="text-sm text-muted-foreground">
237
+ Last updated: {lastUpdated.toLocaleTimeString()}
238
+ </p>
239
+ )}
240
+ </div>
241
+ </div>
242
+
243
+ <div className="flex items-center gap-2">
244
+ <Badge variant="outline">
245
+ {repos.length} repos
246
+ </Badge>
247
+ {autoRefresh && (
248
+ <Badge variant="secondary">
249
+ Auto-refresh
250
+ </Badge>
251
+ )}
252
+ <Button onClick={fetchRepositories} variant="outline" size="sm">
253
+ <RefreshCw className="h-4 w-4" />
254
+ </Button>
255
+ </div>
256
+ </div>
257
+
258
+ {/* Repository Grid/List */}
259
+ <div className={cn(
260
+ layout === "grid"
261
+ ? "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
262
+ : "space-y-4"
263
+ )}>
264
+ <AnimatePresence>
265
+ {repos.map((repo, index) => (
266
+ <motion.div
267
+ key={repo.id}
268
+ initial={{ opacity: 0, y: 20 }}
269
+ animate={{ opacity: 1, y: 0 }}
270
+ exit={{ opacity: 0, y: -20 }}
271
+ transition={{ delay: index * 0.1 }}
272
+ >
273
+ <Card
274
+ className="h-full hover:shadow-md transition-shadow cursor-pointer group"
275
+ onClick={() => onRepositoryClick?.(repo)}
276
+ >
277
+ <CardContent className="p-4">
278
+ <div className="space-y-3">
279
+ {/* Repository Name */}
280
+ <div className="flex items-start justify-between">
281
+ <div className="flex-1 min-w-0">
282
+ <h4 className="font-semibold text-sm group-hover:text-primary transition-colors">
283
+ {repo.name}
284
+ </h4>
285
+ {showOwner && (
286
+ <p className="text-xs text-muted-foreground truncate">
287
+ {repo.owner.login}
288
+ </p>
289
+ )}
290
+ </div>
291
+ <Button
292
+ variant="ghost"
293
+ size="sm"
294
+ className="opacity-0 group-hover:opacity-100 transition-opacity p-1 h-auto"
295
+ onClick={(e) => {
296
+ e.stopPropagation()
297
+ window.open(repo.html_url, '_blank')
298
+ }}
299
+ >
300
+ <ExternalLink className="h-3 w-3" />
301
+ </Button>
302
+ </div>
303
+
304
+ {/* Description */}
305
+ {showDescription && repo.description && (
306
+ <p className="text-sm text-muted-foreground line-clamp-2">
307
+ {repo.description}
308
+ </p>
309
+ )}
310
+
311
+ {/* Topics */}
312
+ {showTopics && repo.topics.length > 0 && (
313
+ <div className="flex flex-wrap gap-1">
314
+ {repo.topics.slice(0, 3).map((topic) => (
315
+ <Badge key={topic} variant="secondary" className="text-xs">
316
+ {topic}
317
+ </Badge>
318
+ ))}
319
+ {repo.topics.length > 3 && (
320
+ <Badge variant="outline" className="text-xs">
321
+ +{repo.topics.length - 3}
322
+ </Badge>
323
+ )}
324
+ </div>
325
+ )}
326
+
327
+ {/* Stats */}
328
+ {showStats && (
329
+ <div className="flex items-center gap-4 text-sm text-muted-foreground">
330
+ {repo.language && (
331
+ <div className="flex items-center gap-1">
332
+ <div
333
+ className="w-3 h-3 rounded-full"
334
+ style={{ backgroundColor: getLanguageColor(repo.language) }}
335
+ />
336
+ <span className="text-xs">{repo.language}</span>
337
+ </div>
338
+ )}
339
+
340
+ <div className="flex items-center gap-1">
341
+ <Star className="h-3 w-3" />
342
+ <span className="text-xs">{formatNumber(repo.stargazers_count)}</span>
343
+ </div>
344
+
345
+ <div className="flex items-center gap-1">
346
+ <GitFork className="h-3 w-3" />
347
+ <span className="text-xs">{formatNumber(repo.forks_count)}</span>
348
+ </div>
349
+ </div>
350
+ )}
351
+
352
+ {/* Updated Date */}
353
+ <div className="text-xs text-muted-foreground">
354
+ Updated {formatDate(repo.updated_at)}
355
+ </div>
356
+ </div>
357
+ </CardContent>
358
+ </Card>
359
+ </motion.div>
360
+ ))}
361
+ </AnimatePresence>
362
+ </div>
363
+
364
+ {repos.length === 0 && (
365
+ <div className="text-center py-8">
366
+ <Github className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
367
+ <p className="text-muted-foreground">No repositories found</p>
368
+ </div>
369
+ )}
370
+ </div>
371
+ </CardContent>
372
+ </Card>
373
+ )
374
+ }
375
+
376
+ export const GitHubStars: React.FC<GitHubStarsProps> = ({ className, ...props }) => {
377
+ // Check if we're in docs mode or have pro access
378
+ const docsProAccess = { hasAccess: true } // Pro access assumed in package
379
+ const { hasProAccess, isLoading } = useSubscription()
380
+
381
+ // In docs mode, always show the component
382
+ const canShowComponent = docsProAccess.isDocsMode || hasProAccess
383
+
384
+ // If not in docs mode and no pro access, show upgrade prompt
385
+ if (!docsProAccess.isDocsMode && !isLoading && !hasProAccess) {
386
+ return (
387
+ <Card className={cn("w-fit", className)}>
388
+ <CardContent className="py-6 text-center">
389
+ <div className="space-y-4">
390
+ <div className="rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto">
391
+ <Lock className="h-6 w-6 text-purple-600 dark:text-purple-400" />
392
+ </div>
393
+ <div>
394
+ <h3 className="font-semibold text-sm mb-2">Pro Feature</h3>
395
+ <p className="text-muted-foreground text-xs mb-4">
396
+ GitHub Stars is available exclusively to MoonUI Pro subscribers.
397
+ </p>
398
+ <a href="/pricing">
399
+ <Button size="sm">
400
+ <Sparkles className="mr-2 h-4 w-4" />
401
+ Upgrade to Pro
402
+ </Button>
403
+ </a>
404
+ </div>
405
+ </div>
406
+ </CardContent>
407
+ </Card>
408
+ )
409
+ }
410
+
411
+ return <GitHubStarsInternal className={className} {...props} />
412
+ }
413
+
414
+ export type { GitHubRepository, GitHubStarsProps }