@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui",
3
- "version": "2.3.8",
3
+ "version": "2.3.10",
4
4
  "description": "Premium React component library with modern design and customization",
5
5
  "author": "MoonUI",
6
6
  "license": "MIT",
@@ -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 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
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
- <div className="flex items-center">
280
- {children}
281
- {sortable && renderSortIcon()}
282
- </div>
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
- React.TdHTMLAttributes<HTMLTableCellElement>
292
- >(({ className, ...props }, ref) => (
293
- <td
294
- ref={ref}
295
- className={cn("moonui-theme", "p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
296
- {...props}
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
- }