@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui-pro",
3
- "version": "2.17.5",
3
+ "version": "2.18.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",
@@ -92,6 +92,7 @@
92
92
  "@radix-ui/react-toast": "^1.2.14",
93
93
  "@radix-ui/react-tooltip": "^1.2.7",
94
94
  "@tanstack/react-table": "^8.20.5",
95
+ "canvas-confetti": "^1.9.3",
95
96
  "class-variance-authority": "^0.7.0",
96
97
  "clsx": "^2.1.1",
97
98
  "date-fns": "^3.6.0",
@@ -114,6 +115,7 @@
114
115
  "@testing-library/jest-dom": "^6.6.3",
115
116
  "@testing-library/react": "^16.3.0",
116
117
  "@testing-library/user-event": "^14.6.1",
118
+ "@types/canvas-confetti": "^1.9.0",
117
119
  "@types/jest": "^30.0.0",
118
120
  "@types/node": "^18.16.0",
119
121
  "@types/react": "^19.0.0",
@@ -0,0 +1,413 @@
1
+ import { GitHubRepository, GitHubStats, GitHubActivity, LanguageStats, RateLimitInfo, StarHistory } from "./types"
2
+
3
+ // Cache management
4
+ const cache = new Map<string, { data: any; timestamp: number; expiresAt: number }>()
5
+
6
+ // Language colors
7
+ export const LANGUAGE_COLORS: Record<string, string> = {
8
+ JavaScript: "#f7df1e",
9
+ TypeScript: "#3178c6",
10
+ Python: "#3776ab",
11
+ Java: "#ed8b00",
12
+ "C++": "#00599c",
13
+ "C#": "#239120",
14
+ Go: "#00add8",
15
+ Rust: "#000000",
16
+ Swift: "#fa7343",
17
+ Kotlin: "#7f52ff",
18
+ PHP: "#777bb4",
19
+ Ruby: "#cc342d",
20
+ HTML: "#e34f26",
21
+ CSS: "#1572b6",
22
+ Vue: "#4fc08d",
23
+ React: "#61dafb",
24
+ Shell: "#89e051",
25
+ Dart: "#0175c2",
26
+ Elixir: "#6e4a7e",
27
+ Scala: "#c22d40",
28
+ R: "#198ce7",
29
+ Julia: "#9558b2",
30
+ Lua: "#000080",
31
+ Perl: "#39457e",
32
+ Haskell: "#5e5086",
33
+ Clojure: "#db5855",
34
+ Erlang: "#b83998",
35
+ Objective_C: "#438eff",
36
+ // Add more as needed
37
+ }
38
+
39
+ // API base URL
40
+ const API_BASE = "https://api.github.com"
41
+
42
+ // Helper to make authenticated requests
43
+ async function githubFetch(url: string, token?: string): Promise<Response> {
44
+ const headers: HeadersInit = {
45
+ Accept: "application/vnd.github.v3+json",
46
+ }
47
+
48
+ if (token) {
49
+ headers.Authorization = `token ${token}`
50
+ }
51
+
52
+ const response = await fetch(url, { headers })
53
+
54
+ if (response.status === 403 && response.headers.get("X-RateLimit-Remaining") === "0") {
55
+ const resetTime = parseInt(response.headers.get("X-RateLimit-Reset") || "0") * 1000
56
+ const resetDate = new Date(resetTime)
57
+ throw new Error(`GitHub API rate limit exceeded. Resets at ${resetDate.toLocaleTimeString()}`)
58
+ }
59
+
60
+ if (!response.ok) {
61
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
62
+ }
63
+
64
+ return response
65
+ }
66
+
67
+ // Get rate limit info
68
+ export async function getRateLimitInfo(token?: string): Promise<RateLimitInfo> {
69
+ const cacheKey = `rate-limit-${token || "public"}`
70
+ const cached = cache.get(cacheKey)
71
+
72
+ if (cached && cached.expiresAt > Date.now()) {
73
+ return cached.data
74
+ }
75
+
76
+ const response = await githubFetch(`${API_BASE}/rate_limit`, token)
77
+ const data = await response.json()
78
+
79
+ const rateLimitInfo: RateLimitInfo = {
80
+ limit: data.rate.limit,
81
+ remaining: data.rate.remaining,
82
+ reset: data.rate.reset * 1000,
83
+ used: data.rate.used,
84
+ }
85
+
86
+ cache.set(cacheKey, {
87
+ data: rateLimitInfo,
88
+ timestamp: Date.now(),
89
+ expiresAt: Date.now() + 60000, // Cache for 1 minute
90
+ })
91
+
92
+ return rateLimitInfo
93
+ }
94
+
95
+ // Fetch user repositories
96
+ export async function fetchUserRepositories(
97
+ username: string,
98
+ token?: string,
99
+ options?: {
100
+ sort?: string
101
+ per_page?: number
102
+ page?: number
103
+ }
104
+ ): Promise<GitHubRepository[]> {
105
+ const params = new URLSearchParams({
106
+ sort: options?.sort || "updated",
107
+ per_page: String(options?.per_page || 100),
108
+ page: String(options?.page || 1),
109
+ })
110
+
111
+ const cacheKey = `repos-${username}-${params.toString()}`
112
+ const cached = cache.get(cacheKey)
113
+
114
+ if (cached && cached.expiresAt > Date.now()) {
115
+ return cached.data
116
+ }
117
+
118
+ const response = await githubFetch(`${API_BASE}/users/${username}/repos?${params}`, token)
119
+ const repos = await response.json()
120
+
121
+ cache.set(cacheKey, {
122
+ data: repos,
123
+ timestamp: Date.now(),
124
+ expiresAt: Date.now() + 300000, // Cache for 5 minutes
125
+ })
126
+
127
+ return repos
128
+ }
129
+
130
+ // Fetch single repository
131
+ export async function fetchRepository(
132
+ owner: string,
133
+ repo: string,
134
+ token?: string
135
+ ): Promise<GitHubRepository> {
136
+ const cacheKey = `repo-${owner}-${repo}`
137
+ const cached = cache.get(cacheKey)
138
+
139
+ if (cached && cached.expiresAt > Date.now()) {
140
+ return cached.data
141
+ }
142
+
143
+ const response = await githubFetch(`${API_BASE}/repos/${owner}/${repo}`, token)
144
+ const repository = await response.json()
145
+
146
+ cache.set(cacheKey, {
147
+ data: repository,
148
+ timestamp: Date.now(),
149
+ expiresAt: Date.now() + 300000, // Cache for 5 minutes
150
+ })
151
+
152
+ return repository
153
+ }
154
+
155
+ // Fetch repository contributors count
156
+ export async function fetchContributorsCount(
157
+ owner: string,
158
+ repo: string,
159
+ token?: string
160
+ ): Promise<number> {
161
+ const cacheKey = `contributors-${owner}-${repo}`
162
+ const cached = cache.get(cacheKey)
163
+
164
+ if (cached && cached.expiresAt > Date.now()) {
165
+ return cached.data
166
+ }
167
+
168
+ try {
169
+ const response = await githubFetch(
170
+ `${API_BASE}/repos/${owner}/${repo}/contributors?per_page=1&anon=true`,
171
+ token
172
+ )
173
+
174
+ // Get total count from Link header
175
+ const linkHeader = response.headers.get("Link")
176
+ if (linkHeader) {
177
+ const match = linkHeader.match(/page=(\d+)>; rel="last"/)
178
+ if (match) {
179
+ const count = parseInt(match[1])
180
+ cache.set(cacheKey, {
181
+ data: count,
182
+ timestamp: Date.now(),
183
+ expiresAt: Date.now() + 3600000, // Cache for 1 hour
184
+ })
185
+ return count
186
+ }
187
+ }
188
+
189
+ // If no pagination, count the results
190
+ const contributors = await response.json()
191
+ const count = contributors.length
192
+
193
+ cache.set(cacheKey, {
194
+ data: count,
195
+ timestamp: Date.now(),
196
+ expiresAt: Date.now() + 3600000, // Cache for 1 hour
197
+ })
198
+
199
+ return count
200
+ } catch (error) {
201
+ console.error("Failed to fetch contributors:", error)
202
+ return 0
203
+ }
204
+ }
205
+
206
+ // Fetch repository star history (limited without token)
207
+ export async function fetchStarHistory(
208
+ owner: string,
209
+ repo: string,
210
+ token?: string
211
+ ): Promise<StarHistory[]> {
212
+ const cacheKey = `star-history-${owner}-${repo}`
213
+ const cached = cache.get(cacheKey)
214
+
215
+ if (cached && cached.expiresAt > Date.now()) {
216
+ return cached.data
217
+ }
218
+
219
+ try {
220
+ // This is a simplified version - for full star history you'd need
221
+ // to use the stargazers API with Accept: application/vnd.github.v3.star+json
222
+ // header and paginate through all results
223
+ const response = await githubFetch(`${API_BASE}/repos/${owner}/${repo}`, token)
224
+ const repoData = await response.json()
225
+
226
+ // For now, return current count as single data point
227
+ const history: StarHistory[] = [{
228
+ date: new Date().toISOString(),
229
+ count: repoData.stargazers_count,
230
+ repository: repoData.full_name,
231
+ }]
232
+
233
+ cache.set(cacheKey, {
234
+ data: history,
235
+ timestamp: Date.now(),
236
+ expiresAt: Date.now() + 3600000, // Cache for 1 hour
237
+ })
238
+
239
+ return history
240
+ } catch (error) {
241
+ console.error("Failed to fetch star history:", error)
242
+ return []
243
+ }
244
+ }
245
+
246
+ // Calculate repository statistics
247
+ export function calculateStats(repositories: GitHubRepository[]): GitHubStats {
248
+ const totalStars = repositories.reduce((sum, repo) => sum + repo.stargazers_count, 0)
249
+ const totalForks = repositories.reduce((sum, repo) => sum + repo.forks_count, 0)
250
+ const totalWatchers = repositories.reduce((sum, repo) => sum + repo.watchers_count, 0)
251
+ const totalIssues = repositories.reduce((sum, repo) => sum + repo.open_issues_count, 0)
252
+
253
+ const avgStarsPerRepo = repositories.length > 0 ? totalStars / repositories.length : 0
254
+
255
+ const mostStarredRepo = repositories.reduce((max, repo) =>
256
+ repo.stargazers_count > (max?.stargazers_count || 0) ? repo : max
257
+ , null as GitHubRepository | null)
258
+
259
+ // Calculate language statistics
260
+ const languageMap = new Map<string, number>()
261
+ repositories.forEach(repo => {
262
+ if (repo.language) {
263
+ languageMap.set(repo.language, (languageMap.get(repo.language) || 0) + 1)
264
+ }
265
+ })
266
+
267
+ const totalRepos = repositories.length
268
+ const languages: LanguageStats[] = Array.from(languageMap.entries())
269
+ .map(([language, count]) => ({
270
+ language,
271
+ count,
272
+ percentage: (count / totalRepos) * 100,
273
+ color: LANGUAGE_COLORS[language] || "#6b7280",
274
+ }))
275
+ .sort((a, b) => b.count - a.count)
276
+
277
+ // Generate recent activity (mock for now)
278
+ const recentActivity: GitHubActivity[] = repositories
279
+ .slice(0, 5)
280
+ .map(repo => ({
281
+ type: "star" as const,
282
+ repository: repo.full_name,
283
+ timestamp: repo.updated_at,
284
+ description: `Repository updated`,
285
+ }))
286
+
287
+ return {
288
+ totalStars,
289
+ totalForks,
290
+ totalWatchers,
291
+ totalIssues,
292
+ avgStarsPerRepo,
293
+ mostStarredRepo,
294
+ recentActivity,
295
+ languages,
296
+ }
297
+ }
298
+
299
+ // Format numbers for display
300
+ export function formatNumber(num: number): string {
301
+ if (num >= 1000000) {
302
+ return (num / 1000000).toFixed(1) + "M"
303
+ }
304
+ if (num >= 1000) {
305
+ return (num / 1000).toFixed(1) + "k"
306
+ }
307
+ return num.toString()
308
+ }
309
+
310
+ // Format date for display
311
+ export function formatDate(dateString: string): string {
312
+ const date = new Date(dateString)
313
+ const now = new Date()
314
+ const diffMs = now.getTime() - date.getTime()
315
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24))
316
+
317
+ if (diffDays === 0) {
318
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60))
319
+ if (diffHours === 0) {
320
+ const diffMinutes = Math.floor(diffMs / (1000 * 60))
321
+ return `${diffMinutes} minutes ago`
322
+ }
323
+ return `${diffHours} hours ago`
324
+ }
325
+
326
+ if (diffDays === 1) return "yesterday"
327
+ if (diffDays < 7) return `${diffDays} days ago`
328
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`
329
+ if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`
330
+
331
+ return date.toLocaleDateString("en-US", {
332
+ year: "numeric",
333
+ month: "short",
334
+ day: "numeric",
335
+ })
336
+ }
337
+
338
+ // Clear cache
339
+ export function clearCache(pattern?: string): void {
340
+ if (pattern) {
341
+ for (const key of cache.keys()) {
342
+ if (key.includes(pattern)) {
343
+ cache.delete(key)
344
+ }
345
+ }
346
+ } else {
347
+ cache.clear()
348
+ }
349
+ }
350
+
351
+ // Export data as JSON
352
+ export function exportData(data: any, filename: string): void {
353
+ const jsonString = JSON.stringify(data, null, 2)
354
+ const blob = new Blob([jsonString], { type: "application/json" })
355
+ const url = URL.createObjectURL(blob)
356
+
357
+ const link = document.createElement("a")
358
+ link.href = url
359
+ link.download = filename
360
+ document.body.appendChild(link)
361
+ link.click()
362
+ document.body.removeChild(link)
363
+
364
+ URL.revokeObjectURL(url)
365
+ }
366
+
367
+ // Export data as CSV
368
+ export function exportAsCSV(repositories: GitHubRepository[], filename: string): void {
369
+ const headers = [
370
+ "Name",
371
+ "Owner",
372
+ "Stars",
373
+ "Forks",
374
+ "Watchers",
375
+ "Issues",
376
+ "Language",
377
+ "Description",
378
+ "URL",
379
+ "Created",
380
+ "Updated",
381
+ ]
382
+
383
+ const rows = repositories.map(repo => [
384
+ repo.name,
385
+ repo.owner.login,
386
+ repo.stargazers_count,
387
+ repo.forks_count,
388
+ repo.watchers_count,
389
+ repo.open_issues_count,
390
+ repo.language || "",
391
+ repo.description || "",
392
+ repo.html_url,
393
+ new Date(repo.created_at).toLocaleDateString(),
394
+ new Date(repo.updated_at).toLocaleDateString(),
395
+ ])
396
+
397
+ const csvContent = [
398
+ headers.join(","),
399
+ ...rows.map(row => row.map(cell => `"${cell}"`).join(",")),
400
+ ].join("\n")
401
+
402
+ const blob = new Blob([csvContent], { type: "text/csv" })
403
+ const url = URL.createObjectURL(blob)
404
+
405
+ const link = document.createElement("a")
406
+ link.href = url
407
+ link.download = filename
408
+ document.body.appendChild(link)
409
+ link.click()
410
+ document.body.removeChild(link)
411
+
412
+ URL.revokeObjectURL(url)
413
+ }