@moontra/moonui 2.3.8 → 2.3.10
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.d.mts +55 -12
- package/dist/index.d.ts +55 -12
- package/dist/index.global.js +38 -539
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +411 -679
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +406 -680
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/index.ts +8 -0
- package/src/components/ui/scroll-reveal.tsx +245 -0
- package/src/components/ui/table.tsx +40 -14
- package/src/components/ui/pro-component-wrapper.tsx +0 -261
package/package.json
CHANGED
|
@@ -218,6 +218,13 @@ export {
|
|
|
218
218
|
ScrollBar as MoonUIScrollBar,
|
|
219
219
|
} from "./scroll-area";
|
|
220
220
|
|
|
221
|
+
// ScrollReveal
|
|
222
|
+
export {
|
|
223
|
+
ScrollReveal as MoonUIScrollReveal,
|
|
224
|
+
ScrollRevealContainer as MoonUIScrollRevealContainer,
|
|
225
|
+
ScrollRevealItem as MoonUIScrollRevealItem,
|
|
226
|
+
} from "./scroll-reveal";
|
|
227
|
+
|
|
221
228
|
// Select
|
|
222
229
|
export {
|
|
223
230
|
Select as MoonUISelect,
|
|
@@ -342,6 +349,7 @@ export * from "./progress";
|
|
|
342
349
|
export * from "./radio-group";
|
|
343
350
|
export * from "./rich-text-editor";
|
|
344
351
|
export * from "./scroll-area";
|
|
352
|
+
export * from "./scroll-reveal";
|
|
345
353
|
export * from "./select";
|
|
346
354
|
export * from "./separator";
|
|
347
355
|
export * from "./simple-editor";
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { motion, useInView, useAnimation, Variants } from "framer-motion"
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
interface ScrollRevealProps {
|
|
8
|
+
children: React.ReactNode
|
|
9
|
+
direction?: "up" | "down" | "left" | "right"
|
|
10
|
+
delay?: number
|
|
11
|
+
duration?: number
|
|
12
|
+
distance?: number
|
|
13
|
+
threshold?: number
|
|
14
|
+
triggerOnce?: boolean
|
|
15
|
+
stagger?: number
|
|
16
|
+
className?: string
|
|
17
|
+
variants?: Variants
|
|
18
|
+
animate?: boolean
|
|
19
|
+
style?: React.CSSProperties
|
|
20
|
+
id?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const defaultVariants: Record<string, Variants> = {
|
|
24
|
+
up: {
|
|
25
|
+
hidden: { opacity: 0, y: 50 },
|
|
26
|
+
visible: { opacity: 1, y: 0 }
|
|
27
|
+
},
|
|
28
|
+
down: {
|
|
29
|
+
hidden: { opacity: 0, y: -50 },
|
|
30
|
+
visible: { opacity: 1, y: 0 }
|
|
31
|
+
},
|
|
32
|
+
left: {
|
|
33
|
+
hidden: { opacity: 0, x: 50 },
|
|
34
|
+
visible: { opacity: 1, x: 0 }
|
|
35
|
+
},
|
|
36
|
+
right: {
|
|
37
|
+
hidden: { opacity: 0, x: -50 },
|
|
38
|
+
visible: { opacity: 1, x: 0 }
|
|
39
|
+
},
|
|
40
|
+
fade: {
|
|
41
|
+
hidden: { opacity: 0 },
|
|
42
|
+
visible: { opacity: 1 }
|
|
43
|
+
},
|
|
44
|
+
scale: {
|
|
45
|
+
hidden: { opacity: 0, scale: 0.8 },
|
|
46
|
+
visible: { opacity: 1, scale: 1 }
|
|
47
|
+
},
|
|
48
|
+
blur: {
|
|
49
|
+
hidden: { opacity: 0, filter: "blur(10px)" },
|
|
50
|
+
visible: { opacity: 1, filter: "blur(0px)" }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ScrollReveal = React.forwardRef<HTMLDivElement, ScrollRevealProps>(
|
|
55
|
+
({
|
|
56
|
+
children,
|
|
57
|
+
direction = "up",
|
|
58
|
+
delay = 0,
|
|
59
|
+
duration = 0.6,
|
|
60
|
+
distance = 50,
|
|
61
|
+
threshold = 0.1,
|
|
62
|
+
triggerOnce = true,
|
|
63
|
+
stagger = 0,
|
|
64
|
+
className,
|
|
65
|
+
variants,
|
|
66
|
+
animate = true,
|
|
67
|
+
style,
|
|
68
|
+
id
|
|
69
|
+
}, ref) => {
|
|
70
|
+
const controls = useAnimation()
|
|
71
|
+
const elementRef = React.useRef<HTMLDivElement>(null)
|
|
72
|
+
const isInView = useInView(elementRef, {
|
|
73
|
+
amount: threshold,
|
|
74
|
+
once: triggerOnce
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Custom variants with distance
|
|
78
|
+
const customVariants = React.useMemo(() => {
|
|
79
|
+
if (variants) return variants
|
|
80
|
+
|
|
81
|
+
const baseVariants = defaultVariants[direction] || defaultVariants.up
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
hidden: {
|
|
85
|
+
...baseVariants.hidden,
|
|
86
|
+
...(direction === "up" && { y: distance }),
|
|
87
|
+
...(direction === "down" && { y: -distance }),
|
|
88
|
+
...(direction === "left" && { x: distance }),
|
|
89
|
+
...(direction === "right" && { x: -distance })
|
|
90
|
+
},
|
|
91
|
+
visible: baseVariants.visible
|
|
92
|
+
}
|
|
93
|
+
}, [direction, distance, variants])
|
|
94
|
+
|
|
95
|
+
React.useEffect(() => {
|
|
96
|
+
if (!animate) return
|
|
97
|
+
|
|
98
|
+
if (isInView) {
|
|
99
|
+
controls.start("visible")
|
|
100
|
+
} else if (!triggerOnce) {
|
|
101
|
+
controls.start("hidden")
|
|
102
|
+
}
|
|
103
|
+
}, [isInView, controls, triggerOnce, animate])
|
|
104
|
+
|
|
105
|
+
React.useImperativeHandle(ref, () => elementRef.current!)
|
|
106
|
+
|
|
107
|
+
if (!animate) {
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
ref={elementRef}
|
|
111
|
+
className={className}
|
|
112
|
+
style={style}
|
|
113
|
+
id={id}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<motion.div
|
|
122
|
+
ref={elementRef}
|
|
123
|
+
variants={customVariants}
|
|
124
|
+
initial="hidden"
|
|
125
|
+
animate={controls}
|
|
126
|
+
transition={{
|
|
127
|
+
duration,
|
|
128
|
+
delay: delay + stagger,
|
|
129
|
+
ease: "easeOut"
|
|
130
|
+
}}
|
|
131
|
+
className={cn(className)}
|
|
132
|
+
style={style}
|
|
133
|
+
id={id}
|
|
134
|
+
>
|
|
135
|
+
{children}
|
|
136
|
+
</motion.div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
ScrollReveal.displayName = "ScrollReveal"
|
|
142
|
+
|
|
143
|
+
// ScrollReveal container for staggered animations
|
|
144
|
+
interface ScrollRevealContainerProps {
|
|
145
|
+
children: React.ReactNode
|
|
146
|
+
stagger?: number
|
|
147
|
+
className?: string
|
|
148
|
+
style?: React.CSSProperties
|
|
149
|
+
id?: string
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const ScrollRevealContainer = React.forwardRef<HTMLDivElement, ScrollRevealContainerProps>(
|
|
153
|
+
({ children, stagger = 0.1, className, style, id }, ref) => {
|
|
154
|
+
const containerRef = React.useRef<HTMLDivElement>(null)
|
|
155
|
+
const isInView = useInView(containerRef, { amount: 0.1, once: true })
|
|
156
|
+
|
|
157
|
+
React.useImperativeHandle(ref, () => containerRef.current!)
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<motion.div
|
|
161
|
+
ref={containerRef}
|
|
162
|
+
initial="hidden"
|
|
163
|
+
animate={isInView ? "visible" : "hidden"}
|
|
164
|
+
variants={{
|
|
165
|
+
hidden: {},
|
|
166
|
+
visible: {
|
|
167
|
+
transition: {
|
|
168
|
+
staggerChildren: stagger
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}}
|
|
172
|
+
className={cn(className)}
|
|
173
|
+
style={style}
|
|
174
|
+
id={id}
|
|
175
|
+
>
|
|
176
|
+
{children}
|
|
177
|
+
</motion.div>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
ScrollRevealContainer.displayName = "ScrollRevealContainer"
|
|
183
|
+
|
|
184
|
+
// ScrollReveal item for use within containers
|
|
185
|
+
interface ScrollRevealItemProps {
|
|
186
|
+
children: React.ReactNode
|
|
187
|
+
direction?: "up" | "down" | "left" | "right"
|
|
188
|
+
duration?: number
|
|
189
|
+
distance?: number
|
|
190
|
+
className?: string
|
|
191
|
+
variants?: Variants
|
|
192
|
+
style?: React.CSSProperties
|
|
193
|
+
id?: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const ScrollRevealItem = React.forwardRef<HTMLDivElement, ScrollRevealItemProps>(
|
|
197
|
+
({
|
|
198
|
+
children,
|
|
199
|
+
direction = "up",
|
|
200
|
+
duration = 0.6,
|
|
201
|
+
distance = 50,
|
|
202
|
+
className,
|
|
203
|
+
variants,
|
|
204
|
+
style,
|
|
205
|
+
id
|
|
206
|
+
}, ref) => {
|
|
207
|
+
const customVariants = React.useMemo(() => {
|
|
208
|
+
if (variants) return variants
|
|
209
|
+
|
|
210
|
+
const baseVariants = defaultVariants[direction] || defaultVariants.up
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
hidden: {
|
|
214
|
+
...baseVariants.hidden,
|
|
215
|
+
...(direction === "up" && { y: distance }),
|
|
216
|
+
...(direction === "down" && { y: -distance }),
|
|
217
|
+
...(direction === "left" && { x: distance }),
|
|
218
|
+
...(direction === "right" && { x: -distance })
|
|
219
|
+
},
|
|
220
|
+
visible: baseVariants.visible
|
|
221
|
+
}
|
|
222
|
+
}, [direction, distance, variants])
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<motion.div
|
|
226
|
+
ref={ref}
|
|
227
|
+
variants={customVariants}
|
|
228
|
+
transition={{
|
|
229
|
+
duration,
|
|
230
|
+
ease: "easeOut"
|
|
231
|
+
}}
|
|
232
|
+
className={cn(className)}
|
|
233
|
+
style={style}
|
|
234
|
+
id={id}
|
|
235
|
+
>
|
|
236
|
+
{children}
|
|
237
|
+
</motion.div>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
ScrollRevealItem.displayName = "ScrollRevealItem"
|
|
243
|
+
|
|
244
|
+
export { ScrollReveal, ScrollRevealContainer, ScrollRevealItem }
|
|
245
|
+
export type { ScrollRevealProps, ScrollRevealContainerProps, ScrollRevealItemProps }
|
|
@@ -207,10 +207,12 @@ interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
|
|
207
207
|
sorted?: SortDirection;
|
|
208
208
|
/** Sıralama değiştiğinde çağrılacak fonksiyon */
|
|
209
209
|
onSort?: () => void;
|
|
210
|
+
/** Text alignment */
|
|
211
|
+
align?: 'left' | 'center' | 'right';
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
213
|
-
({ className, sortable, sorted, onSort, children, ...props }, ref) => {
|
|
215
|
+
({ className, sortable, sorted, onSort, align = 'left', children, ...props }, ref) => {
|
|
214
216
|
// Sıralama için simgeler
|
|
215
217
|
const renderSortIcon = () => {
|
|
216
218
|
if (!sortable) return null;
|
|
@@ -265,37 +267,61 @@ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
|
265
267
|
);
|
|
266
268
|
};
|
|
267
269
|
|
|
270
|
+
const alignmentClasses = {
|
|
271
|
+
left: 'text-left justify-start',
|
|
272
|
+
center: 'text-center justify-center',
|
|
273
|
+
right: 'text-right justify-end'
|
|
274
|
+
};
|
|
275
|
+
|
|
268
276
|
return (
|
|
269
277
|
<th
|
|
270
278
|
ref={ref}
|
|
271
279
|
className={cn(
|
|
272
|
-
"h-10 px-4
|
|
280
|
+
"h-10 px-4 align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
|
281
|
+
alignmentClasses[align].split(' ')[0], // text alignment
|
|
273
282
|
sortable && "cursor-pointer hover:text-foreground select-none",
|
|
274
283
|
className
|
|
275
284
|
)}
|
|
276
285
|
onClick={sortable ? onSort : undefined}
|
|
277
286
|
{...props}
|
|
278
287
|
>
|
|
279
|
-
|
|
280
|
-
{
|
|
281
|
-
|
|
282
|
-
|
|
288
|
+
{sortable || align !== 'left' ? (
|
|
289
|
+
<div className={cn("flex items-center", alignmentClasses[align].split(' ')[1])}>
|
|
290
|
+
{children}
|
|
291
|
+
{sortable && renderSortIcon()}
|
|
292
|
+
</div>
|
|
293
|
+
) : (
|
|
294
|
+
children
|
|
295
|
+
)}
|
|
283
296
|
</th>
|
|
284
297
|
);
|
|
285
298
|
}
|
|
286
299
|
);
|
|
287
300
|
TableHead.displayName = "TableHead";
|
|
288
301
|
|
|
302
|
+
interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
|
303
|
+
/** Text alignment */
|
|
304
|
+
align?: 'left' | 'center' | 'right';
|
|
305
|
+
}
|
|
306
|
+
|
|
289
307
|
const TableCell = React.forwardRef<
|
|
290
308
|
HTMLTableCellElement,
|
|
291
|
-
|
|
292
|
-
>(({ className, ...props }, ref) =>
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
309
|
+
TableCellProps
|
|
310
|
+
>(({ className, align = 'left', ...props }, ref) => {
|
|
311
|
+
const alignmentClass = {
|
|
312
|
+
left: 'text-left',
|
|
313
|
+
center: 'text-center',
|
|
314
|
+
right: 'text-right'
|
|
315
|
+
}[align];
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<td
|
|
319
|
+
ref={ref}
|
|
320
|
+
className={cn("moonui-theme", "p-4 align-middle [&:has([role=checkbox])]:pr-0", alignmentClass, className)}
|
|
321
|
+
{...props}
|
|
322
|
+
/>
|
|
323
|
+
);
|
|
324
|
+
});
|
|
299
325
|
TableCell.displayName = "TableCell";
|
|
300
326
|
|
|
301
327
|
const TableCaption = React.forwardRef<
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
// Pro Component Wrapper - Simplified Version
|
|
4
|
-
// Development: CLI auth required
|
|
5
|
-
// Production: License key validation with 24h cache
|
|
6
|
-
|
|
7
|
-
import React, { useState, useEffect } from 'react'
|
|
8
|
-
import { cn } from '../../lib/utils'
|
|
9
|
-
import { Badge } from './badge'
|
|
10
|
-
import { Lock, Terminal, AlertCircle } from 'lucide-react'
|
|
11
|
-
|
|
12
|
-
interface ProComponentWrapperProps {
|
|
13
|
-
children: React.ReactNode
|
|
14
|
-
componentId: string
|
|
15
|
-
componentName?: string
|
|
16
|
-
className?: string
|
|
17
|
-
fallback?: React.ReactNode
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface LicenseCache {
|
|
21
|
-
timestamp: number
|
|
22
|
-
hasProAccess: boolean
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const CACHE_KEY = 'moonui_license_cache'
|
|
26
|
-
const CACHE_DURATION = 24 * 60 * 60 * 1000 // 24 hours
|
|
27
|
-
|
|
28
|
-
export function ProComponentWrapper({
|
|
29
|
-
children,
|
|
30
|
-
componentId,
|
|
31
|
-
componentName,
|
|
32
|
-
className,
|
|
33
|
-
fallback
|
|
34
|
-
}: ProComponentWrapperProps) {
|
|
35
|
-
const [state, setState] = useState<{
|
|
36
|
-
hasAccess: boolean
|
|
37
|
-
loading: boolean
|
|
38
|
-
message?: string
|
|
39
|
-
}>({
|
|
40
|
-
hasAccess: false,
|
|
41
|
-
loading: true
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
// Check if development environment
|
|
46
|
-
const isDevelopment = () => {
|
|
47
|
-
if (typeof window === 'undefined') return false
|
|
48
|
-
|
|
49
|
-
const isLocalhost =
|
|
50
|
-
window.location.hostname === 'localhost' ||
|
|
51
|
-
window.location.hostname === '127.0.0.1' ||
|
|
52
|
-
window.location.hostname.includes('.local')
|
|
53
|
-
|
|
54
|
-
const isDevPort =
|
|
55
|
-
window.location.port === '3000' ||
|
|
56
|
-
window.location.port === '3001' ||
|
|
57
|
-
window.location.port === '8080' ||
|
|
58
|
-
window.location.port === '5173'
|
|
59
|
-
|
|
60
|
-
// Must be localhost with dev port
|
|
61
|
-
return process.env.NODE_ENV === 'development' && isLocalhost && isDevPort
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// DEVELOPMENT: Check CLI auth
|
|
65
|
-
if (isDevelopment()) {
|
|
66
|
-
const checkDevAuth = () => {
|
|
67
|
-
const cliToken = localStorage.getItem('moonui_cli_token')
|
|
68
|
-
const deviceId = localStorage.getItem('moonui_device_id')
|
|
69
|
-
|
|
70
|
-
if (!cliToken || !deviceId) {
|
|
71
|
-
setState({
|
|
72
|
-
hasAccess: false,
|
|
73
|
-
loading: false,
|
|
74
|
-
message: 'CLI authentication required'
|
|
75
|
-
})
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Token exists - allow access
|
|
80
|
-
setState({
|
|
81
|
-
hasAccess: true,
|
|
82
|
-
loading: false
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
checkDevAuth()
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// PRODUCTION: Check license cache
|
|
91
|
-
const checkProdLicense = () => {
|
|
92
|
-
try {
|
|
93
|
-
// Check cache first
|
|
94
|
-
const cached = localStorage.getItem(CACHE_KEY)
|
|
95
|
-
if (cached) {
|
|
96
|
-
const data = JSON.parse(cached) as LicenseCache
|
|
97
|
-
const now = Date.now()
|
|
98
|
-
|
|
99
|
-
// Cache still valid (24 hours)
|
|
100
|
-
if (data.timestamp && (now - data.timestamp) < CACHE_DURATION) {
|
|
101
|
-
setState({
|
|
102
|
-
hasAccess: data.hasProAccess,
|
|
103
|
-
loading: false
|
|
104
|
-
})
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// No valid cache - need license key
|
|
110
|
-
const licenseKey = process.env.NEXT_PUBLIC_MOONUI_LICENSE_KEY
|
|
111
|
-
|
|
112
|
-
if (!licenseKey) {
|
|
113
|
-
setState({
|
|
114
|
-
hasAccess: false,
|
|
115
|
-
loading: false,
|
|
116
|
-
message: 'No license key configured'
|
|
117
|
-
})
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Validate license async
|
|
122
|
-
validateLicense(licenseKey).then(result => {
|
|
123
|
-
// Save to cache
|
|
124
|
-
const cacheData: LicenseCache = {
|
|
125
|
-
timestamp: Date.now(),
|
|
126
|
-
hasProAccess: result.hasProAccess
|
|
127
|
-
}
|
|
128
|
-
localStorage.setItem(CACHE_KEY, JSON.stringify(cacheData))
|
|
129
|
-
|
|
130
|
-
setState({
|
|
131
|
-
hasAccess: result.hasProAccess,
|
|
132
|
-
loading: false
|
|
133
|
-
})
|
|
134
|
-
}).catch(error => {
|
|
135
|
-
console.error('License validation failed:', error)
|
|
136
|
-
|
|
137
|
-
// Try expired cache as fallback
|
|
138
|
-
const cached = localStorage.getItem(CACHE_KEY)
|
|
139
|
-
if (cached) {
|
|
140
|
-
const data = JSON.parse(cached) as LicenseCache
|
|
141
|
-
setState({
|
|
142
|
-
hasAccess: data.hasProAccess,
|
|
143
|
-
loading: false,
|
|
144
|
-
message: 'Using cached license'
|
|
145
|
-
})
|
|
146
|
-
} else {
|
|
147
|
-
setState({
|
|
148
|
-
hasAccess: false,
|
|
149
|
-
loading: false,
|
|
150
|
-
message: 'License validation failed'
|
|
151
|
-
})
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
} catch (error) {
|
|
156
|
-
console.error('License check error:', error)
|
|
157
|
-
setState({
|
|
158
|
-
hasAccess: false,
|
|
159
|
-
loading: false,
|
|
160
|
-
message: 'License check failed'
|
|
161
|
-
})
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
checkProdLicense()
|
|
166
|
-
}, [])
|
|
167
|
-
|
|
168
|
-
// Loading state
|
|
169
|
-
if (state.loading) {
|
|
170
|
-
return (
|
|
171
|
-
<div className={cn("animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-32", className)}>
|
|
172
|
-
<div className="flex items-center justify-center h-full">
|
|
173
|
-
<div className="text-sm text-gray-500">Loading...</div>
|
|
174
|
-
</div>
|
|
175
|
-
</div>
|
|
176
|
-
)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Access granted
|
|
180
|
-
if (state.hasAccess) {
|
|
181
|
-
return <>{children}</>
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Development: Show CLI auth required
|
|
185
|
-
if (state.message === 'CLI authentication required') {
|
|
186
|
-
return (
|
|
187
|
-
<div className={cn("relative", className)}>
|
|
188
|
-
<div className="blur-sm grayscale opacity-60 pointer-events-none">
|
|
189
|
-
{fallback || children}
|
|
190
|
-
</div>
|
|
191
|
-
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/10 via-transparent to-purple-500/10 border-2 border-dashed border-blue-400/30 rounded-lg flex items-center justify-center">
|
|
192
|
-
<div className="text-center space-y-3 p-6 bg-white/90 dark:bg-gray-900/90 rounded-lg">
|
|
193
|
-
<Badge variant="secondary" className="mb-2">
|
|
194
|
-
<Terminal className="w-3 h-3 mr-1" />
|
|
195
|
-
DEV AUTH REQUIRED
|
|
196
|
-
</Badge>
|
|
197
|
-
<h3 className="font-semibold">CLI Authentication Required</h3>
|
|
198
|
-
<p className="text-sm text-gray-600 dark:text-gray-400 max-w-xs">
|
|
199
|
-
Login with MoonUI CLI to use Pro components in development
|
|
200
|
-
</p>
|
|
201
|
-
<code className="block bg-black text-green-400 p-2 rounded text-xs font-mono">
|
|
202
|
-
npx moonui auth login
|
|
203
|
-
</code>
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
206
|
-
</div>
|
|
207
|
-
)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Production: Show license required
|
|
211
|
-
return (
|
|
212
|
-
<div className={cn("relative", className)}>
|
|
213
|
-
<div className="blur-sm grayscale opacity-60 pointer-events-none">
|
|
214
|
-
{fallback || children}
|
|
215
|
-
</div>
|
|
216
|
-
<div className="absolute inset-0 bg-gradient-to-br from-amber-500/10 via-transparent to-orange-500/10 border-2 border-dashed border-amber-400/30 rounded-lg flex items-center justify-center">
|
|
217
|
-
<div className="text-center space-y-3 p-6 bg-white/90 dark:bg-gray-900/90 rounded-lg">
|
|
218
|
-
<Badge variant="destructive" className="mb-2">
|
|
219
|
-
<AlertCircle className="w-3 h-3 mr-1" />
|
|
220
|
-
LICENSE REQUIRED
|
|
221
|
-
</Badge>
|
|
222
|
-
<h3 className="font-semibold">{componentName || componentId}</h3>
|
|
223
|
-
<p className="text-sm text-gray-600 dark:text-gray-400 max-w-xs">
|
|
224
|
-
{state.message || 'Pro license required to use this component'}
|
|
225
|
-
</p>
|
|
226
|
-
<a
|
|
227
|
-
href="https://moonui.dev/pricing"
|
|
228
|
-
target="_blank"
|
|
229
|
-
rel="noopener noreferrer"
|
|
230
|
-
className="inline-block bg-gradient-to-r from-amber-500 to-orange-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:from-amber-600 hover:to-orange-600 transition-colors"
|
|
231
|
-
>
|
|
232
|
-
Get Pro License
|
|
233
|
-
</a>
|
|
234
|
-
</div>
|
|
235
|
-
</div>
|
|
236
|
-
</div>
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Validate license with API
|
|
241
|
-
async function validateLicense(licenseKey: string): Promise<{ hasProAccess: boolean }> {
|
|
242
|
-
const response = await fetch('/api/v1/license/validate', {
|
|
243
|
-
method: 'POST',
|
|
244
|
-
headers: {
|
|
245
|
-
'Content-Type': 'application/json'
|
|
246
|
-
},
|
|
247
|
-
body: JSON.stringify({
|
|
248
|
-
licenseKey,
|
|
249
|
-
domain: window.location.hostname
|
|
250
|
-
})
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
if (!response.ok) {
|
|
254
|
-
throw new Error('License validation failed')
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const data = await response.json()
|
|
258
|
-
return {
|
|
259
|
-
hasProAccess: data.hasProAccess || false
|
|
260
|
-
}
|
|
261
|
-
}
|