@moontra/moonui-pro 2.17.5 → 2.18.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.
@@ -0,0 +1,146 @@
1
+ export interface GitHubRepository {
2
+ id: number
3
+ name: string
4
+ full_name: string
5
+ description: string | null
6
+ html_url: string
7
+ homepage: string | null
8
+ stargazers_count: number
9
+ watchers_count: number
10
+ forks_count: number
11
+ language: string | null
12
+ topics: string[]
13
+ created_at: string
14
+ updated_at: string
15
+ pushed_at: string
16
+ size: number
17
+ open_issues_count: number
18
+ license: {
19
+ key: string
20
+ name: string
21
+ spdx_id: string
22
+ url: string
23
+ } | null
24
+ owner: {
25
+ login: string
26
+ avatar_url: string
27
+ html_url: string
28
+ type: string
29
+ }
30
+ // Extended properties
31
+ contributors_count?: number
32
+ commits_count?: number
33
+ releases_count?: number
34
+ }
35
+
36
+ export interface GitHubStats {
37
+ totalStars: number
38
+ totalForks: number
39
+ totalWatchers: number
40
+ totalIssues: number
41
+ avgStarsPerRepo: number
42
+ mostStarredRepo: GitHubRepository | null
43
+ recentActivity: GitHubActivity[]
44
+ languages: LanguageStats[]
45
+ }
46
+
47
+ export interface GitHubActivity {
48
+ type: "star" | "fork" | "issue" | "pr" | "release"
49
+ repository: string
50
+ timestamp: string
51
+ actor?: string
52
+ description?: string
53
+ }
54
+
55
+ export interface LanguageStats {
56
+ language: string
57
+ count: number
58
+ percentage: number
59
+ color: string
60
+ }
61
+
62
+ export interface StarHistory {
63
+ date: string
64
+ count: number
65
+ repository?: string
66
+ }
67
+
68
+ export interface Milestone {
69
+ count: number
70
+ reached: boolean
71
+ date?: string
72
+ celebration?: boolean
73
+ }
74
+
75
+ export type DisplayVariant = "compact" | "full" | "minimal" | "detailed" | "card"
76
+ export type AnimationOption = "bounce" | "pulse" | "fade" | "scale" | "slide" | "none"
77
+ export type SortOption = "stars" | "forks" | "updated" | "created" | "name" | "issues"
78
+ export type LayoutOption = "grid" | "list" | "masonry" | "carousel"
79
+
80
+ export interface GitHubStarsProps {
81
+ // Basic props
82
+ username?: string
83
+ repository?: string // For single repo mode
84
+ repositories?: string[] // For multiple repos
85
+ token?: string // GitHub token for higher rate limits
86
+
87
+ // Display options
88
+ variant?: DisplayVariant
89
+ layout?: LayoutOption
90
+ showDescription?: boolean
91
+ showTopics?: boolean
92
+ showStats?: boolean
93
+ showOwner?: boolean
94
+ showLanguage?: boolean
95
+ showActivity?: boolean
96
+ showTrending?: boolean
97
+ showMilestones?: boolean
98
+ showComparison?: boolean
99
+ showHistory?: boolean
100
+
101
+ // Behavior options
102
+ sortBy?: SortOption
103
+ maxItems?: number
104
+ autoRefresh?: boolean
105
+ refreshInterval?: number
106
+ enableNotifications?: boolean
107
+ enableExport?: boolean
108
+ enableAnalytics?: boolean
109
+ cacheEnabled?: boolean
110
+ cacheDuration?: number
111
+
112
+ // Animation options
113
+ animation?: AnimationOption
114
+ animationDuration?: number
115
+ staggerDelay?: number
116
+
117
+ // Milestone configuration
118
+ milestones?: number[]
119
+ celebrateAt?: number[]
120
+
121
+ // Callbacks
122
+ onRepositoryClick?: (repo: GitHubRepository) => void
123
+ onStarClick?: (repo: GitHubRepository) => void
124
+ onMilestoneReached?: (milestone: Milestone) => void
125
+ onDataUpdate?: (stats: GitHubStats) => void
126
+ onError?: (error: Error) => void
127
+
128
+ // Styling
129
+ className?: string
130
+ theme?: "light" | "dark" | "auto"
131
+ size?: "sm" | "md" | "lg" | "xl"
132
+ customColors?: Record<string, string>
133
+ }
134
+
135
+ export interface CacheEntry {
136
+ data: any
137
+ timestamp: number
138
+ expiresAt: number
139
+ }
140
+
141
+ export interface RateLimitInfo {
142
+ limit: number
143
+ remaining: number
144
+ reset: number
145
+ used: number
146
+ }
@@ -0,0 +1,380 @@
1
+ import React from "react"
2
+ import { motion } from "framer-motion"
3
+ import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"
4
+ import { Badge } from "../ui/badge"
5
+ import { Button } from "../ui/button"
6
+ import { Progress } from "../ui/progress"
7
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"
8
+ import {
9
+ Star,
10
+ GitFork,
11
+ Eye,
12
+ Users,
13
+ ExternalLink,
14
+ Github,
15
+ TrendingUp,
16
+ BarChart3,
17
+ Activity,
18
+ Code2,
19
+ Calendar,
20
+ Package,
21
+ Download,
22
+ } from "lucide-react"
23
+ import { GitHubRepository, GitHubStats, LanguageStats } from "./types"
24
+ import { formatNumber, formatDate, LANGUAGE_COLORS } from "./github-api"
25
+ import { cn } from "../../lib/utils"
26
+
27
+ interface BaseVariantProps {
28
+ repos: GitHubRepository[]
29
+ stats: GitHubStats | null
30
+ loading?: boolean
31
+ className?: string
32
+ onRepositoryClick?: (repo: GitHubRepository) => void
33
+ }
34
+
35
+ // Minimal variant - Single line with key stats
36
+ export const MinimalVariant: React.FC<BaseVariantProps> = ({ repos, stats, className }) => {
37
+ if (!stats) return null
38
+
39
+ return (
40
+ <div className={cn("flex items-center gap-4 text-sm", className)}>
41
+ <div className="flex items-center gap-2">
42
+ <Github className="h-4 w-4" />
43
+ <span className="font-medium">{repos.length} repositories</span>
44
+ </div>
45
+ <div className="flex items-center gap-2">
46
+ <Star className="h-4 w-4 text-yellow-500" />
47
+ <span>{formatNumber(stats.totalStars)} stars</span>
48
+ </div>
49
+ <div className="flex items-center gap-2">
50
+ <GitFork className="h-4 w-4 text-blue-500" />
51
+ <span>{formatNumber(stats.totalForks)} forks</span>
52
+ </div>
53
+ </div>
54
+ )
55
+ }
56
+
57
+ // Compact variant - Small card with essential info
58
+ export const CompactVariant: React.FC<BaseVariantProps> = ({
59
+ repos,
60
+ stats,
61
+ className,
62
+ onRepositoryClick,
63
+ }) => {
64
+ if (!stats) return null
65
+
66
+ const topRepos = repos.slice(0, 3)
67
+
68
+ return (
69
+ <Card className={cn("w-full max-w-sm", className)}>
70
+ <CardContent className="p-4">
71
+ <div className="space-y-4">
72
+ <div className="flex items-center justify-between">
73
+ <div className="flex items-center gap-2">
74
+ <Github className="h-5 w-5" />
75
+ <span className="font-semibold">{repos.length} Repos</span>
76
+ </div>
77
+ <div className="flex items-center gap-3 text-sm">
78
+ <div className="flex items-center gap-1">
79
+ <Star className="h-4 w-4 text-yellow-500" />
80
+ <span>{formatNumber(stats.totalStars)}</span>
81
+ </div>
82
+ <div className="flex items-center gap-1">
83
+ <GitFork className="h-4 w-4 text-blue-500" />
84
+ <span>{formatNumber(stats.totalForks)}</span>
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <div className="space-y-2">
90
+ {topRepos.map((repo) => (
91
+ <div
92
+ key={repo.id}
93
+ className="flex items-center justify-between p-2 rounded-md hover:bg-accent cursor-pointer transition-colors"
94
+ onClick={() => onRepositoryClick?.(repo)}
95
+ >
96
+ <div className="flex-1 min-w-0">
97
+ <p className="text-sm font-medium truncate">{repo.name}</p>
98
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
99
+ {repo.language && (
100
+ <div className="flex items-center gap-1">
101
+ <div
102
+ className="w-2 h-2 rounded-full"
103
+ style={{ backgroundColor: LANGUAGE_COLORS[repo.language] || "#6b7280" }}
104
+ />
105
+ <span>{repo.language}</span>
106
+ </div>
107
+ )}
108
+ <div className="flex items-center gap-1">
109
+ <Star className="h-3 w-3" />
110
+ <span>{formatNumber(repo.stargazers_count)}</span>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ <ExternalLink className="h-3 w-3 text-muted-foreground" />
115
+ </div>
116
+ ))}
117
+ </div>
118
+ </div>
119
+ </CardContent>
120
+ </Card>
121
+ )
122
+ }
123
+
124
+ // Card variant - Medium-sized cards in a grid
125
+ export const CardVariant: React.FC<BaseVariantProps> = ({
126
+ repos,
127
+ className,
128
+ onRepositoryClick,
129
+ }) => {
130
+ return (
131
+ <div className={cn("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", className)}>
132
+ {repos.map((repo, index) => (
133
+ <motion.div
134
+ key={repo.id}
135
+ initial={{ opacity: 0, y: 20 }}
136
+ animate={{ opacity: 1, y: 0 }}
137
+ transition={{ delay: index * 0.05 }}
138
+ >
139
+ <Card
140
+ className="h-full hover:shadow-lg transition-shadow cursor-pointer"
141
+ onClick={() => onRepositoryClick?.(repo)}
142
+ >
143
+ <CardContent className="p-6">
144
+ <div className="space-y-4">
145
+ <div>
146
+ <h3 className="font-semibold text-lg mb-1">{repo.name}</h3>
147
+ <p className="text-sm text-muted-foreground line-clamp-2">
148
+ {repo.description || "No description available"}
149
+ </p>
150
+ </div>
151
+
152
+ {repo.topics.length > 0 && (
153
+ <div className="flex flex-wrap gap-1">
154
+ {repo.topics.slice(0, 3).map((topic) => (
155
+ <Badge key={topic} variant="secondary" className="text-xs">
156
+ {topic}
157
+ </Badge>
158
+ ))}
159
+ </div>
160
+ )}
161
+
162
+ <div className="flex items-center justify-between text-sm">
163
+ <div className="flex items-center gap-3">
164
+ {repo.language && (
165
+ <div className="flex items-center gap-1">
166
+ <div
167
+ className="w-3 h-3 rounded-full"
168
+ style={{ backgroundColor: LANGUAGE_COLORS[repo.language] || "#6b7280" }}
169
+ />
170
+ <span>{repo.language}</span>
171
+ </div>
172
+ )}
173
+ </div>
174
+ <div className="flex items-center gap-3">
175
+ <div className="flex items-center gap-1">
176
+ <Star className="h-4 w-4 text-yellow-500" />
177
+ <span>{formatNumber(repo.stargazers_count)}</span>
178
+ </div>
179
+ <div className="flex items-center gap-1">
180
+ <GitFork className="h-4 w-4 text-blue-500" />
181
+ <span>{formatNumber(repo.forks_count)}</span>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+ </CardContent>
187
+ </Card>
188
+ </motion.div>
189
+ ))}
190
+ </div>
191
+ )
192
+ }
193
+
194
+ // Detailed variant - Full information with tabs
195
+ export const DetailedVariant: React.FC<BaseVariantProps & { onExport?: () => void }> = ({
196
+ repos,
197
+ stats,
198
+ className,
199
+ onRepositoryClick,
200
+ onExport,
201
+ }) => {
202
+ if (!stats) return null
203
+
204
+ return (
205
+ <Card className={cn("w-full", className)}>
206
+ <CardHeader>
207
+ <div className="flex items-center justify-between">
208
+ <CardTitle className="flex items-center gap-2">
209
+ <Github className="h-6 w-6" />
210
+ GitHub Repository Analytics
211
+ </CardTitle>
212
+ {onExport && (
213
+ <Button onClick={onExport} variant="outline" size="sm">
214
+ <Download className="h-4 w-4 mr-2" />
215
+ Export
216
+ </Button>
217
+ )}
218
+ </div>
219
+ </CardHeader>
220
+ <CardContent>
221
+ <Tabs defaultValue="overview" className="w-full">
222
+ <TabsList className="grid w-full grid-cols-4">
223
+ <TabsTrigger value="overview">Overview</TabsTrigger>
224
+ <TabsTrigger value="repositories">Repositories</TabsTrigger>
225
+ <TabsTrigger value="languages">Languages</TabsTrigger>
226
+ <TabsTrigger value="activity">Activity</TabsTrigger>
227
+ </TabsList>
228
+
229
+ <TabsContent value="overview" className="space-y-4">
230
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
231
+ <StatCard
232
+ icon={<Package className="h-4 w-4" />}
233
+ label="Total Repos"
234
+ value={repos.length}
235
+ />
236
+ <StatCard
237
+ icon={<Star className="h-4 w-4 text-yellow-500" />}
238
+ label="Total Stars"
239
+ value={formatNumber(stats.totalStars)}
240
+ />
241
+ <StatCard
242
+ icon={<GitFork className="h-4 w-4 text-blue-500" />}
243
+ label="Total Forks"
244
+ value={formatNumber(stats.totalForks)}
245
+ />
246
+ <StatCard
247
+ icon={<Eye className="h-4 w-4 text-green-500" />}
248
+ label="Total Watchers"
249
+ value={formatNumber(stats.totalWatchers)}
250
+ />
251
+ </div>
252
+
253
+ {stats.mostStarredRepo && (
254
+ <Card>
255
+ <CardContent className="p-4">
256
+ <div className="flex items-center justify-between">
257
+ <div>
258
+ <p className="text-sm text-muted-foreground mb-1">Most Starred Repository</p>
259
+ <h4 className="font-semibold">{stats.mostStarredRepo.name}</h4>
260
+ <p className="text-sm text-muted-foreground">
261
+ {formatNumber(stats.mostStarredRepo.stargazers_count)} stars
262
+ </p>
263
+ </div>
264
+ <TrendingUp className="h-8 w-8 text-muted-foreground" />
265
+ </div>
266
+ </CardContent>
267
+ </Card>
268
+ )}
269
+ </TabsContent>
270
+
271
+ <TabsContent value="repositories" className="space-y-4">
272
+ <div className="space-y-2">
273
+ {repos.map((repo) => (
274
+ <Card
275
+ key={repo.id}
276
+ className="hover:shadow-md transition-shadow cursor-pointer"
277
+ onClick={() => onRepositoryClick?.(repo)}
278
+ >
279
+ <CardContent className="p-4">
280
+ <div className="flex items-start justify-between">
281
+ <div className="flex-1">
282
+ <h4 className="font-semibold mb-1">{repo.name}</h4>
283
+ <p className="text-sm text-muted-foreground mb-2">
284
+ {repo.description || "No description"}
285
+ </p>
286
+ <div className="flex items-center gap-4 text-sm">
287
+ {repo.language && (
288
+ <div className="flex items-center gap-1">
289
+ <div
290
+ className="w-3 h-3 rounded-full"
291
+ style={{
292
+ backgroundColor: LANGUAGE_COLORS[repo.language] || "#6b7280",
293
+ }}
294
+ />
295
+ <span>{repo.language}</span>
296
+ </div>
297
+ )}
298
+ <div className="flex items-center gap-1">
299
+ <Star className="h-3 w-3" />
300
+ <span>{formatNumber(repo.stargazers_count)}</span>
301
+ </div>
302
+ <div className="flex items-center gap-1">
303
+ <GitFork className="h-3 w-3" />
304
+ <span>{formatNumber(repo.forks_count)}</span>
305
+ </div>
306
+ <div className="flex items-center gap-1">
307
+ <Calendar className="h-3 w-3" />
308
+ <span>{formatDate(repo.updated_at)}</span>
309
+ </div>
310
+ </div>
311
+ </div>
312
+ <ExternalLink className="h-4 w-4 text-muted-foreground" />
313
+ </div>
314
+ </CardContent>
315
+ </Card>
316
+ ))}
317
+ </div>
318
+ </TabsContent>
319
+
320
+ <TabsContent value="languages" className="space-y-4">
321
+ <div className="space-y-3">
322
+ {stats.languages.map((lang) => (
323
+ <div key={lang.language} className="space-y-2">
324
+ <div className="flex items-center justify-between text-sm">
325
+ <div className="flex items-center gap-2">
326
+ <div
327
+ className="w-3 h-3 rounded-full"
328
+ style={{ backgroundColor: lang.color }}
329
+ />
330
+ <span className="font-medium">{lang.language}</span>
331
+ </div>
332
+ <span className="text-muted-foreground">
333
+ {lang.count} repos ({lang.percentage.toFixed(1)}%)
334
+ </span>
335
+ </div>
336
+ <Progress value={lang.percentage} className="h-2" />
337
+ </div>
338
+ ))}
339
+ </div>
340
+ </TabsContent>
341
+
342
+ <TabsContent value="activity" className="space-y-4">
343
+ <div className="space-y-2">
344
+ {stats.recentActivity.map((activity, index) => (
345
+ <div key={index} className="flex items-center gap-3 p-3 rounded-lg bg-accent/50">
346
+ <Activity className="h-4 w-4 text-muted-foreground" />
347
+ <div className="flex-1">
348
+ <p className="text-sm">{activity.description}</p>
349
+ <p className="text-xs text-muted-foreground">
350
+ {activity.repository} • {formatDate(activity.timestamp)}
351
+ </p>
352
+ </div>
353
+ </div>
354
+ ))}
355
+ </div>
356
+ </TabsContent>
357
+ </Tabs>
358
+ </CardContent>
359
+ </Card>
360
+ )
361
+ }
362
+
363
+ // Helper component for stat cards
364
+ const StatCard: React.FC<{
365
+ icon: React.ReactNode
366
+ label: string
367
+ value: string | number
368
+ }> = ({ icon, label, value }) => (
369
+ <Card>
370
+ <CardContent className="p-4">
371
+ <div className="flex items-center justify-between">
372
+ <div>
373
+ <p className="text-sm text-muted-foreground">{label}</p>
374
+ <p className="text-2xl font-bold">{value}</p>
375
+ </div>
376
+ {icon}
377
+ </div>
378
+ </CardContent>
379
+ </Card>
380
+ )