@moontra/moonui 2.1.5 → 2.1.7

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 (53) hide show
  1. package/dist/index.js +8251 -2872
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +8100 -2653
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/src/components/ui/accordion.tsx +8 -11
  7. package/src/components/ui/alert.tsx +10 -13
  8. package/src/components/ui/analyze-components.txt +4 -0
  9. package/src/components/ui/aspect-ratio.tsx +6 -9
  10. package/src/components/ui/avatar.tsx +18 -21
  11. package/src/components/ui/badge.tsx +5 -8
  12. package/src/components/ui/breadcrumb.tsx +29 -31
  13. package/src/components/ui/button.tsx +11 -15
  14. package/src/components/ui/calendar.tsx +6 -9
  15. package/src/components/ui/card.tsx +19 -22
  16. package/src/components/ui/checkbox.tsx +12 -15
  17. package/src/components/ui/collapsible.tsx +12 -14
  18. package/src/components/ui/color-picker.tsx +2 -2
  19. package/src/components/ui/command.tsx +33 -35
  20. package/src/components/ui/component-analysis-report.md +118 -0
  21. package/src/components/ui/data-table.tsx +2 -2
  22. package/src/components/ui/date-picker.tsx +9 -9
  23. package/src/components/ui/dialog.tsx +29 -31
  24. package/src/components/ui/draggable-list.tsx +81 -0
  25. package/src/components/ui/dropdown-menu.tsx +42 -44
  26. package/src/components/ui/file-upload.tsx +3 -3
  27. package/src/components/ui/gesture-drawer.tsx +146 -0
  28. package/src/components/ui/index.ts +43 -9
  29. package/src/components/ui/input.tsx +6 -9
  30. package/src/components/ui/label.tsx +5 -8
  31. package/src/components/ui/moon-logo.tsx +1 -1
  32. package/src/components/ui/pagination.tsx +17 -19
  33. package/src/components/ui/popover.tsx +16 -18
  34. package/src/components/ui/progress.tsx +6 -9
  35. package/src/components/ui/radio-group.tsx +11 -14
  36. package/src/components/ui/rich-text-editor/index.tsx +71 -0
  37. package/src/components/ui/select.tsx +30 -32
  38. package/src/components/ui/separator.tsx +6 -9
  39. package/src/components/ui/simple-editor.tsx +4 -4
  40. package/src/components/ui/skeleton.tsx +13 -16
  41. package/src/components/ui/slider.tsx +5 -8
  42. package/src/components/ui/swipeable-card.tsx +69 -0
  43. package/src/components/ui/switch.tsx +4 -7
  44. package/src/components/ui/table.tsx +29 -31
  45. package/src/components/ui/tabs.tsx +13 -16
  46. package/src/components/ui/textarea.tsx +4 -7
  47. package/src/components/ui/toast.tsx +18 -20
  48. package/src/components/ui/toggle.tsx +5 -8
  49. package/src/components/ui/tooltip.tsx +16 -18
  50. package/src/index.tsx +13 -7
  51. package/src/lib/utils.ts +2 -65
  52. package/src/components/ui/locked-component.tsx +0 -215
  53. package/src/use-pro-access.ts +0 -141
package/src/lib/utils.ts CHANGED
@@ -1,69 +1,6 @@
1
- import { type ClassValue, clsx } from "clsx";
2
- import { twMerge } from "tailwind-merge";
1
+ import { type ClassValue, clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
3
 
4
- // Utility function using clsx and tailwind-merge
5
4
  export function cn(...inputs: ClassValue[]) {
6
5
  return twMerge(clsx(inputs));
7
6
  }
8
-
9
- // Date formatting utility
10
- export function formatDate(date: Date): string {
11
- if (isNaN(date.getTime())) {
12
- return 'Invalid Date'
13
- }
14
- return date.toLocaleDateString('en-US', {
15
- year: 'numeric',
16
- month: 'short',
17
- day: 'numeric'
18
- })
19
- }
20
-
21
- // Currency formatting utility
22
- export function formatCurrency(amount: number): string {
23
- return new Intl.NumberFormat('en-US', {
24
- style: 'currency',
25
- currency: 'USD'
26
- }).format(amount)
27
- }
28
-
29
- // Text truncation utility
30
- export function truncateText(text: string, maxLength: number): string {
31
- if (text.length <= maxLength) {
32
- return text
33
- }
34
- return text.slice(0, maxLength) + '...'
35
- }
36
-
37
- // ID generation utility
38
- export function generateId(length: number = 8): string {
39
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
40
- let result = ''
41
- for (let i = 0; i < length; i++) {
42
- result += chars.charAt(Math.floor(Math.random() * chars.length))
43
- }
44
- return result
45
- }
46
-
47
- // Get client IP address from request
48
- export function getClientIP(request: Request): string {
49
- // Check various headers for IP address
50
- const forwarded = request.headers.get('x-forwarded-for')
51
- const realIP = request.headers.get('x-real-ip')
52
- const remoteAddr = request.headers.get('x-remote-addr')
53
-
54
- if (forwarded) {
55
- // x-forwarded-for can contain multiple IPs, take the first one
56
- return forwarded.split(',')[0].trim()
57
- }
58
-
59
- if (realIP) {
60
- return realIP
61
- }
62
-
63
- if (remoteAddr) {
64
- return remoteAddr
65
- }
66
-
67
- // Fallback to a default IP if none found
68
- return '127.0.0.1'
69
- }
@@ -1,215 +0,0 @@
1
- // Locked Component UI
2
- // Pro componentlerin locked state'ini gösteren wrapper component
3
-
4
- import React, { useState } from 'react'
5
- import { cn } from '../../lib/utils'
6
- import { Button } from './button'
7
- import { Badge } from './badge'
8
- import { Lock, Crown, Zap } from 'lucide-react'
9
- import Link from 'next/link'
10
- import { useComponentAccess } from '../../use-pro-access'
11
-
12
- interface MoonUILockedComponentProps {
13
- children: React.ReactNode
14
- componentName: string
15
- className?: string
16
- showPreview?: boolean
17
- previewDuration?: number // ms
18
- }
19
-
20
- export function LockedComponent({
21
- children,
22
- componentName,
23
- className,
24
- showPreview = true,
25
- previewDuration = 2000
26
- }: LockedComponentProps) {
27
- const [isPreviewActive, setIsPreviewActive] = useState(false)
28
- const [previewTimeout, setPreviewTimeout] = useState<NodeJS.Timeout | null>(null)
29
-
30
- const handleMouseEnter = () => {
31
- if (!showPreview) return
32
-
33
- setIsPreviewActive(true)
34
-
35
- // Clear existing timeout
36
- if (previewTimeout) {
37
- clearTimeout(previewTimeout)
38
- }
39
-
40
- // Set new timeout
41
- const timeout = setTimeout(() => {
42
- setIsPreviewActive(false)
43
- }, previewDuration)
44
-
45
- setPreviewTimeout(timeout)
46
- }
47
-
48
- const handleMouseLeave = () => {
49
- if (previewTimeout) {
50
- clearTimeout(previewTimeout)
51
- setPreviewTimeout(null)
52
- }
53
- setIsPreviewActive(false)
54
- }
55
-
56
- return (
57
- <div
58
- className={cn(
59
- "relative group cursor-pointer transition-all duration-300",
60
- className
61
- )}
62
- onMouseEnter={handleMouseEnter}
63
- onMouseLeave={handleMouseLeave}
64
- >
65
- {/* Component Content */}
66
- <div
67
- className={cn(
68
- "transition-all duration-300",
69
- !isPreviewActive && "blur-sm grayscale-[0.3] opacity-60"
70
- )}
71
- >
72
- {children}
73
- </div>
74
-
75
- {/* Overlay */}
76
- <div
77
- className={cn(
78
- "absolute inset-0 bg-gradient-to-br from-amber-500/10 via-transparent to-purple-500/10",
79
- "border-2 border-dashed border-amber-400/30 rounded-lg",
80
- "flex items-center justify-center",
81
- "transition-all duration-300",
82
- isPreviewActive ? "opacity-0" : "opacity-100"
83
- )}
84
- >
85
- <div className="text-center space-y-3 p-6">
86
- {/* Pro Badge */}
87
- <div className="flex justify-center">
88
- <Badge
89
- variant="secondary"
90
- className="bg-gradient-to-r from-amber-500 to-orange-500 text-white border-0 px-3 py-1"
91
- >
92
- <Crown className="w-3 h-3 mr-1" />
93
- PRO ONLY
94
- </Badge>
95
- </div>
96
-
97
- {/* Lock Icon */}
98
- <div className="flex justify-center">
99
- <div className="p-3 rounded-full bg-amber-100 dark:bg-amber-900/30">
100
- <Lock className="w-6 h-6 text-amber-600 dark:text-amber-400" />
101
- </div>
102
- </div>
103
-
104
- {/* Component Name */}
105
- <h3 className="font-semibold text-gray-900 dark:text-gray-100">
106
- {componentName}
107
- </h3>
108
-
109
- {/* Description */}
110
- <p className="text-sm text-gray-600 dark:text-gray-400 max-w-xs">
111
- This premium component is available with Pro subscription
112
- </p>
113
-
114
- {/* Upgrade Button */}
115
- <Link href="/pricing">
116
- <Button
117
- size="sm"
118
- className="bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 border-0"
119
- >
120
- <Zap className="w-4 h-4 mr-2" />
121
- Upgrade to Pro
122
- </Button>
123
- </Link>
124
- </div>
125
- </div>
126
-
127
- {/* Preview Hint */}
128
- {showPreview && !isPreviewActive && (
129
- <div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
130
- <div className="bg-black/80 text-white text-xs px-2 py-1 rounded">
131
- Hover to preview
132
- </div>
133
- </div>
134
- )}
135
- </div>
136
- )
137
- }
138
-
139
- // Pro Component Wrapper - Otomatik erişim kontrolü ile
140
- interface ProComponentWrapperProps {
141
- children: React.ReactNode
142
- componentId: string
143
- componentName?: string
144
- className?: string
145
- fallback?: React.ReactNode
146
- }
147
-
148
- export function ProComponentWrapper({
149
- children,
150
- componentId,
151
- componentName,
152
- className,
153
- fallback
154
- }: ProComponentWrapperProps) {
155
- const { canUse, isLocked, isLoading } = useComponentAccess(componentId)
156
-
157
- // Loading state
158
- if (isLoading) {
159
- return (
160
- <div className={cn("animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-32", className)}>
161
- <div className="flex items-center justify-center h-full">
162
- <div className="text-sm text-gray-500">Loading...</div>
163
- </div>
164
- </div>
165
- )
166
- }
167
-
168
- // Unlocked - show actual component
169
- if (canUse) {
170
- return <div className={className}>{children}</div>
171
- }
172
-
173
- // Locked - show locked state
174
- if (isLocked) {
175
- return (
176
- <LockedComponent
177
- componentName={componentName || componentId}
178
- className={className}
179
- >
180
- {fallback || children}
181
- </LockedComponent>
182
- )
183
- }
184
-
185
- // Default fallback
186
- return <div className={className}>{children}</div>
187
- }
188
-
189
- // Pro Badge Component
190
- export function ProBadge({ className }: { className?: string }) {
191
- return (
192
- <Badge
193
- variant="secondary"
194
- className={cn(
195
- "bg-gradient-to-r from-amber-500 to-orange-500 text-white border-0 text-xs",
196
- className
197
- )}
198
- >
199
- <Crown className="w-3 h-3 mr-1" />
200
- PRO
201
- </Badge>
202
- )
203
- }
204
-
205
- // Free Badge Component
206
- export function FreeBadge({ className }: { className?: string }) {
207
- return (
208
- <Badge
209
- variant="outline"
210
- className={cn("border-green-500 text-green-600 dark:text-green-400 text-xs", className)}
211
- >
212
- FREE
213
- </Badge>
214
- )
215
- }
@@ -1,141 +0,0 @@
1
- // Pro Access Hook
2
- // Kullanıcının pro componentlere erişim durumunu kontrol eder
3
-
4
- import React from 'react'
5
- import { useSession } from 'next-auth/react'
6
- import { useQuery } from '@tanstack/react-query'
7
- import { getComponentAccess } from '@/lib/component-metadata'
8
-
9
- export interface UserSubscription {
10
- userId: string
11
- plan: 'free' | 'pro_monthly' | 'pro_annual' | 'pro_lifetime'
12
- status: 'active' | 'expired' | 'cancelled' | 'trial'
13
- expiresAt: Date | null // lifetime için null
14
- createdAt: Date
15
- updatedAt: Date
16
- }
17
-
18
- export interface ProAccessStatus {
19
- hasProAccess: boolean
20
- subscription: UserSubscription | null | undefined
21
- isLoading: boolean
22
- error: Error | null
23
- daysUntilExpiry: number | null
24
- }
25
-
26
- // Subscription durumunu API'den çek
27
- async function fetchSubscriptionStatus(): Promise<UserSubscription | null> {
28
- const response = await fetch('/api/user/subscription-status')
29
-
30
- if (!response.ok) {
31
- throw new Error('Subscription status fetch failed')
32
- }
33
-
34
- const data = await response.json()
35
- return data.subscription
36
- }
37
-
38
- // Pro erişim durumunu hesapla
39
- function calculateProAccess(subscription: UserSubscription | null | undefined): {
40
- hasProAccess: boolean
41
- daysUntilExpiry: number | null
42
- } {
43
- if (!subscription) {
44
- return { hasProAccess: false, daysUntilExpiry: null }
45
- }
46
-
47
- // Lifetime üyelik
48
- if (subscription.plan === 'pro_lifetime') {
49
- return { hasProAccess: true, daysUntilExpiry: null }
50
- }
51
-
52
- // Aktif olmayan subscription
53
- if (subscription.status !== 'active') {
54
- return { hasProAccess: false, daysUntilExpiry: null }
55
- }
56
-
57
- // Süre kontrolü
58
- if (subscription.expiresAt) {
59
- const now = new Date()
60
- const expiryDate = new Date(subscription.expiresAt)
61
- const daysUntilExpiry = Math.ceil((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
62
-
63
- return {
64
- hasProAccess: daysUntilExpiry > 0,
65
- daysUntilExpiry: daysUntilExpiry > 0 ? daysUntilExpiry : 0
66
- }
67
- }
68
-
69
- return { hasProAccess: false, daysUntilExpiry: null }
70
- }
71
-
72
- export function useProAccess(): ProAccessStatus {
73
- const { data: session, status } = useSession()
74
-
75
- const {
76
- data: subscription,
77
- isLoading,
78
- error
79
- } = useQuery({
80
- queryKey: ['subscription-status', session?.user?.id],
81
- queryFn: fetchSubscriptionStatus,
82
- enabled: !!session?.user?.id && status === 'authenticated',
83
- staleTime: 5 * 60 * 1000, // 5 dakika cache
84
- refetchInterval: 10 * 60 * 1000, // 10 dakikada bir otomatik refresh
85
- })
86
-
87
- const { hasProAccess, daysUntilExpiry } = calculateProAccess(subscription)
88
-
89
- return {
90
- hasProAccess,
91
- subscription,
92
- isLoading: status === 'loading' || isLoading,
93
- error: error as Error | null,
94
- daysUntilExpiry
95
- }
96
- }
97
-
98
- // Component erişim kontrolü için helper hook
99
- export function useComponentAccess(componentId: string) {
100
- const { hasProAccess, isLoading } = useProAccess()
101
-
102
- const access = React.useMemo(() => {
103
- return getComponentAccess(componentId, hasProAccess ? 'pro' : 'free')
104
- }, [componentId, hasProAccess])
105
-
106
- return {
107
- ...access,
108
- isLoading,
109
- canUse: access.access === 'unlocked',
110
- isLocked: access.access === 'locked'
111
- }
112
- }
113
-
114
- // Subscription durumu için utility functions
115
- export function getSubscriptionDisplayName(plan: string): string {
116
- switch (plan) {
117
- case 'pro_monthly':
118
- return 'Pro Monthly'
119
- case 'pro_annual':
120
- return 'Pro Annual'
121
- case 'pro_lifetime':
122
- return 'Pro Lifetime'
123
- default:
124
- return 'Free'
125
- }
126
- }
127
-
128
- export function getSubscriptionColor(status: string): string {
129
- switch (status) {
130
- case 'active':
131
- return 'green'
132
- case 'trial':
133
- return 'blue'
134
- case 'expired':
135
- return 'red'
136
- case 'cancelled':
137
- return 'gray'
138
- default:
139
- return 'gray'
140
- }
141
- }