@moontra/moonui-pro 2.20.2 → 2.20.4
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 +8 -3
- package/plugin/index.d.ts +86 -0
- package/plugin/index.js +308 -0
- package/scripts/postinstall.js +191 -23
- package/src/components/advanced-chart/index.tsx +0 -1246
- package/src/components/advanced-forms/index.tsx +0 -585
- package/src/components/animated-button/index.tsx +0 -385
- package/src/components/calendar/event-dialog.tsx +0 -377
- package/src/components/calendar/index.tsx +0 -1220
- package/src/components/calendar-pro/index.tsx +0 -1697
- package/src/components/color-picker/index.tsx +0 -432
- package/src/components/credit-card-input/index.tsx +0 -406
- package/src/components/dashboard/dashboard-grid.tsx +0 -480
- package/src/components/dashboard/demo.tsx +0 -425
- package/src/components/dashboard/index.tsx +0 -1046
- package/src/components/dashboard/time-range-picker.tsx +0 -336
- package/src/components/dashboard/types.ts +0 -225
- package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
- package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
- package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
- package/src/components/dashboard/widgets/index.ts +0 -5
- package/src/components/dashboard/widgets/metric-card.tsx +0 -363
- package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
- package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
- package/src/components/data-table/data-table-column-toggle.tsx +0 -169
- package/src/components/data-table/data-table-export.ts +0 -156
- package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
- package/src/components/data-table/index.tsx +0 -845
- package/src/components/draggable-list/index.tsx +0 -100
- package/src/components/error-boundary/index.tsx +0 -232
- package/src/components/file-upload/index.tsx +0 -1660
- package/src/components/floating-action-button/index.tsx +0 -206
- package/src/components/form-wizard/form-wizard-context.tsx +0 -335
- package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
- package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
- package/src/components/form-wizard/form-wizard-step.tsx +0 -111
- package/src/components/form-wizard/index.tsx +0 -102
- package/src/components/form-wizard/types.ts +0 -77
- package/src/components/gesture-drawer/index.tsx +0 -551
- package/src/components/github-stars/github-api.ts +0 -426
- package/src/components/github-stars/hooks.ts +0 -517
- package/src/components/github-stars/index.tsx +0 -375
- package/src/components/github-stars/types.ts +0 -148
- package/src/components/github-stars/variants.tsx +0 -515
- package/src/components/health-check/index.tsx +0 -439
- package/src/components/hover-card-3d/index.tsx +0 -529
- package/src/components/index.ts +0 -130
- package/src/components/internal/index.ts +0 -78
- package/src/components/kanban/add-card-modal.tsx +0 -502
- package/src/components/kanban/card-detail-modal.tsx +0 -761
- package/src/components/kanban/index.ts +0 -13
- package/src/components/kanban/kanban.tsx +0 -1689
- package/src/components/kanban/types.ts +0 -168
- package/src/components/lazy-component/index.tsx +0 -823
- package/src/components/license-error/index.tsx +0 -31
- package/src/components/magnetic-button/index.tsx +0 -216
- package/src/components/memory-efficient-data/index.tsx +0 -1018
- package/src/components/moonui-quiz-form/index.tsx +0 -817
- package/src/components/navbar/index.tsx +0 -781
- package/src/components/optimized-image/index.tsx +0 -425
- package/src/components/performance-debugger/index.tsx +0 -613
- package/src/components/performance-monitor/index.tsx +0 -808
- package/src/components/phone-number-input/index.tsx +0 -343
- package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
- package/src/components/pinch-zoom/index.tsx +0 -566
- package/src/components/quiz-form/index.tsx +0 -479
- package/src/components/rich-text-editor/index.tsx +0 -2322
- package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
- package/src/components/rich-text-editor/slash-commands.css +0 -35
- package/src/components/rich-text-editor/table-styles.css +0 -65
- package/src/components/sidebar/index.tsx +0 -884
- package/src/components/spotlight-card/index.tsx +0 -191
- package/src/components/swipeable-card/index.tsx +0 -100
- package/src/components/timeline/index.tsx +0 -1183
- package/src/components/ui/accordion.tsx +0 -581
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/alert.tsx +0 -141
- package/src/components/ui/aspect-ratio.tsx +0 -245
- package/src/components/ui/avatar.tsx +0 -155
- package/src/components/ui/badge.tsx +0 -230
- package/src/components/ui/breadcrumb.tsx +0 -216
- package/src/components/ui/button.tsx +0 -228
- package/src/components/ui/calendar.tsx +0 -387
- package/src/components/ui/card.tsx +0 -216
- package/src/components/ui/checkbox.tsx +0 -259
- package/src/components/ui/collapsible.tsx +0 -631
- package/src/components/ui/color-picker.tsx +0 -97
- package/src/components/ui/command.tsx +0 -948
- package/src/components/ui/dialog.tsx +0 -752
- package/src/components/ui/dropdown-menu.tsx +0 -706
- package/src/components/ui/gesture-drawer.tsx +0 -11
- package/src/components/ui/hover-card.tsx +0 -29
- package/src/components/ui/index.ts +0 -222
- package/src/components/ui/input.tsx +0 -224
- package/src/components/ui/label.tsx +0 -29
- package/src/components/ui/lightbox.tsx +0 -606
- package/src/components/ui/magnetic-button.tsx +0 -129
- package/src/components/ui/media-gallery.tsx +0 -611
- package/src/components/ui/navigation-menu.tsx +0 -130
- package/src/components/ui/pagination.tsx +0 -125
- package/src/components/ui/popover.tsx +0 -185
- package/src/components/ui/progress.tsx +0 -30
- package/src/components/ui/radio-group.tsx +0 -257
- package/src/components/ui/scroll-area.tsx +0 -47
- package/src/components/ui/select.tsx +0 -378
- package/src/components/ui/separator.tsx +0 -145
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/skeleton.tsx +0 -20
- package/src/components/ui/slider.tsx +0 -354
- package/src/components/ui/spotlight-card.tsx +0 -119
- package/src/components/ui/switch.tsx +0 -86
- package/src/components/ui/table.tsx +0 -331
- package/src/components/ui/tabs-pro.tsx +0 -542
- package/src/components/ui/tabs.tsx +0 -54
- package/src/components/ui/textarea.tsx +0 -28
- package/src/components/ui/toast.tsx +0 -317
- package/src/components/ui/toggle.tsx +0 -119
- package/src/components/ui/tooltip.tsx +0 -151
- package/src/components/virtual-list/index.tsx +0 -668
- package/src/hooks/use-chart.ts +0 -205
- package/src/hooks/use-data-table.ts +0 -182
- package/src/hooks/use-docs-pro-access.ts +0 -13
- package/src/hooks/use-license-check.ts +0 -65
- package/src/hooks/use-subscription.ts +0 -19
- package/src/hooks/use-toast.ts +0 -15
- package/src/index.ts +0 -22
- package/src/lib/ai-providers.ts +0 -377
- package/src/lib/component-metadata.ts +0 -18
- package/src/lib/micro-interactions.ts +0 -255
- package/src/lib/paddle.ts +0 -17
- package/src/lib/utils.ts +0 -6
- package/src/patterns/login-form/index.tsx +0 -276
- package/src/patterns/login-form/types.ts +0 -67
- package/src/setupTests.ts +0 -41
- package/src/styles/advanced-chart.css +0 -239
- package/src/styles/calendar.css +0 -35
- package/src/styles/design-system.css +0 -363
- package/src/styles/index.css +0 -681
- package/src/styles/tailwind.css +0 -7
- package/src/styles/tokens.css +0 -455
- package/src/types/next-auth.d.ts +0 -21
- package/src/use-intersection-observer.tsx +0 -154
- package/src/use-local-storage.tsx +0 -71
- package/src/use-paddle.ts +0 -138
- package/src/use-performance-optimizer.ts +0 -389
- package/src/use-pro-access.ts +0 -141
- package/src/use-scroll-animation.ts +0 -219
- package/src/use-subscription.ts +0 -37
- package/src/use-toast.ts +0 -32
- package/src/utils/chart-helpers.ts +0 -357
- package/src/utils/cn.ts +0 -6
- package/src/utils/data-processing.ts +0 -151
- package/src/utils/license-validator.tsx +0 -183
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import React, { useState, useRef, useEffect } from "react"
|
|
4
|
-
import { motion, AnimatePresence } from "framer-motion"
|
|
5
|
-
import { Card, CardContent } from "../ui/card"
|
|
6
|
-
import { Button } from "../ui/button"
|
|
7
|
-
import { MoonUIBadgePro as Badge } from "../ui/badge"
|
|
8
|
-
import { MoonUISkeletonPro as Skeleton } from "../ui/skeleton"
|
|
9
|
-
import { cn } from "../../lib/utils"
|
|
10
|
-
import {
|
|
11
|
-
Image as ImageIcon,
|
|
12
|
-
Maximize2,
|
|
13
|
-
Download,
|
|
14
|
-
Eye,
|
|
15
|
-
EyeOff,
|
|
16
|
-
RotateCw,
|
|
17
|
-
ZoomIn,
|
|
18
|
-
ZoomOut,
|
|
19
|
-
X,
|
|
20
|
-
Lock,
|
|
21
|
-
Sparkles,
|
|
22
|
-
Loader2
|
|
23
|
-
} from "lucide-react"
|
|
24
|
-
import { useSubscription } from "../../hooks/use-subscription"
|
|
25
|
-
|
|
26
|
-
interface OptimizedImageProps {
|
|
27
|
-
src: string
|
|
28
|
-
alt: string
|
|
29
|
-
width?: number
|
|
30
|
-
height?: number
|
|
31
|
-
quality?: number
|
|
32
|
-
format?: "webp" | "avif" | "jpeg" | "png" | "auto"
|
|
33
|
-
lazy?: boolean
|
|
34
|
-
blur?: boolean
|
|
35
|
-
priority?: boolean
|
|
36
|
-
sizes?: string
|
|
37
|
-
srcSet?: string
|
|
38
|
-
fallbackSrc?: string
|
|
39
|
-
showPreview?: boolean
|
|
40
|
-
showZoom?: boolean
|
|
41
|
-
showDownload?: boolean
|
|
42
|
-
showInfo?: boolean
|
|
43
|
-
zoomLevel?: number
|
|
44
|
-
className?: string
|
|
45
|
-
onLoad?: () => void
|
|
46
|
-
onError?: (error: Error) => void
|
|
47
|
-
onZoom?: (level: number) => void
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const OptimizedImageInternal: React.FC<OptimizedImageProps> = ({
|
|
51
|
-
src,
|
|
52
|
-
alt,
|
|
53
|
-
width,
|
|
54
|
-
height,
|
|
55
|
-
quality = 75,
|
|
56
|
-
format = "auto",
|
|
57
|
-
lazy = true,
|
|
58
|
-
blur = true,
|
|
59
|
-
priority = false,
|
|
60
|
-
sizes,
|
|
61
|
-
srcSet,
|
|
62
|
-
fallbackSrc,
|
|
63
|
-
showPreview = true,
|
|
64
|
-
showZoom = true,
|
|
65
|
-
showDownload = true,
|
|
66
|
-
showInfo = true,
|
|
67
|
-
zoomLevel = 1,
|
|
68
|
-
className,
|
|
69
|
-
onLoad,
|
|
70
|
-
onError,
|
|
71
|
-
onZoom
|
|
72
|
-
}) => {
|
|
73
|
-
const [isLoaded, setIsLoaded] = useState(false)
|
|
74
|
-
const [isLoading, setIsLoading] = useState(true)
|
|
75
|
-
const [hasError, setHasError] = useState(false)
|
|
76
|
-
const [isPreviewOpen, setIsPreviewOpen] = useState(false)
|
|
77
|
-
const [currentZoom, setCurrentZoom] = useState(zoomLevel)
|
|
78
|
-
const [imageInfo, setImageInfo] = useState<{
|
|
79
|
-
naturalWidth: number
|
|
80
|
-
naturalHeight: number
|
|
81
|
-
fileSize?: number
|
|
82
|
-
} | null>(null)
|
|
83
|
-
const [showImageInfo, setShowImageInfo] = useState(false)
|
|
84
|
-
|
|
85
|
-
const imgRef = useRef<HTMLImageElement>(null)
|
|
86
|
-
const previewRef = useRef<HTMLImageElement>(null)
|
|
87
|
-
|
|
88
|
-
// Generate optimized image URL
|
|
89
|
-
const getOptimizedSrc = (originalSrc: string, options: {
|
|
90
|
-
width?: number
|
|
91
|
-
height?: number
|
|
92
|
-
quality?: number
|
|
93
|
-
format?: string
|
|
94
|
-
} = {}) => {
|
|
95
|
-
// This would typically integrate with image optimization services
|
|
96
|
-
// like Cloudinary, ImageKit, or Next.js Image Optimization
|
|
97
|
-
let optimizedUrl = originalSrc
|
|
98
|
-
|
|
99
|
-
// Example for a generic image optimization service
|
|
100
|
-
const params = new URLSearchParams()
|
|
101
|
-
if (options.width) params.set('w', options.width.toString())
|
|
102
|
-
if (options.height) params.set('h', options.height.toString())
|
|
103
|
-
if (options.quality) params.set('q', options.quality.toString())
|
|
104
|
-
if (options.format && options.format !== 'auto') params.set('f', options.format)
|
|
105
|
-
|
|
106
|
-
// For demo purposes, we'll just return the original URL
|
|
107
|
-
// In production, you'd construct an optimized URL like:
|
|
108
|
-
// return `https://your-image-service.com/transform?${params}&url=${encodeURIComponent(originalSrc)}`
|
|
109
|
-
|
|
110
|
-
return optimizedUrl
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const optimizedSrc = getOptimizedSrc(src, { width, height, quality, format })
|
|
114
|
-
|
|
115
|
-
const handleImageLoad = () => {
|
|
116
|
-
setIsLoaded(true)
|
|
117
|
-
setIsLoading(false)
|
|
118
|
-
|
|
119
|
-
if (imgRef.current) {
|
|
120
|
-
setImageInfo({
|
|
121
|
-
naturalWidth: imgRef.current.naturalWidth,
|
|
122
|
-
naturalHeight: imgRef.current.naturalHeight
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
onLoad?.()
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const handleImageError = () => {
|
|
130
|
-
setHasError(true)
|
|
131
|
-
setIsLoading(false)
|
|
132
|
-
onError?.(new Error("Failed to load image"))
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const handleDownload = async () => {
|
|
136
|
-
try {
|
|
137
|
-
const response = await fetch(optimizedSrc)
|
|
138
|
-
const blob = await response.blob()
|
|
139
|
-
const url = window.URL.createObjectURL(blob)
|
|
140
|
-
const link = document.createElement('a')
|
|
141
|
-
link.href = url
|
|
142
|
-
link.download = alt || 'image'
|
|
143
|
-
document.body.appendChild(link)
|
|
144
|
-
link.click()
|
|
145
|
-
document.body.removeChild(link)
|
|
146
|
-
window.URL.revokeObjectURL(url)
|
|
147
|
-
} catch (error) {
|
|
148
|
-
console.error('Download failed:', error)
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const handleZoomIn = () => {
|
|
153
|
-
const newZoom = Math.min(currentZoom * 1.2, 5)
|
|
154
|
-
setCurrentZoom(newZoom)
|
|
155
|
-
onZoom?.(newZoom)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const handleZoomOut = () => {
|
|
159
|
-
const newZoom = Math.max(currentZoom / 1.2, 0.2)
|
|
160
|
-
setCurrentZoom(newZoom)
|
|
161
|
-
onZoom?.(newZoom)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const resetZoom = () => {
|
|
165
|
-
setCurrentZoom(1)
|
|
166
|
-
onZoom?.(1)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const formatFileSize = (bytes: number) => {
|
|
170
|
-
if (bytes === 0) return '0 Bytes'
|
|
171
|
-
const k = 1024
|
|
172
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB']
|
|
173
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
174
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Generate srcSet for responsive images
|
|
178
|
-
const generateSrcSet = () => {
|
|
179
|
-
if (srcSet) return srcSet
|
|
180
|
-
|
|
181
|
-
if (!width) return undefined
|
|
182
|
-
|
|
183
|
-
const widths = [width * 0.5, width, width * 1.5, width * 2]
|
|
184
|
-
return widths
|
|
185
|
-
.map(w => `${getOptimizedSrc(src, { width: Math.round(w), quality })} ${Math.round(w)}w`)
|
|
186
|
-
.join(', ')
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return (
|
|
190
|
-
<>
|
|
191
|
-
<div className={cn("relative group overflow-hidden", className)}>
|
|
192
|
-
{/* Loading/Error States */}
|
|
193
|
-
{isLoading && (
|
|
194
|
-
<motion.div
|
|
195
|
-
initial={{ opacity: 0 }}
|
|
196
|
-
animate={{ opacity: 1 }}
|
|
197
|
-
className="absolute inset-0 flex items-center justify-center bg-muted"
|
|
198
|
-
>
|
|
199
|
-
{blur ? (
|
|
200
|
-
<Skeleton className="w-full h-full" />
|
|
201
|
-
) : (
|
|
202
|
-
<div className="text-center">
|
|
203
|
-
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground mx-auto mb-2" />
|
|
204
|
-
<p className="text-sm text-muted-foreground">Loading image...</p>
|
|
205
|
-
</div>
|
|
206
|
-
)}
|
|
207
|
-
</motion.div>
|
|
208
|
-
)}
|
|
209
|
-
|
|
210
|
-
{hasError && (
|
|
211
|
-
<div className="absolute inset-0 flex items-center justify-center bg-muted">
|
|
212
|
-
<div className="text-center">
|
|
213
|
-
<ImageIcon className="h-12 w-12 text-muted-foreground mx-auto mb-2" />
|
|
214
|
-
<p className="text-sm text-muted-foreground">Failed to load image</p>
|
|
215
|
-
{fallbackSrc && (
|
|
216
|
-
<img
|
|
217
|
-
src={fallbackSrc}
|
|
218
|
-
alt={alt}
|
|
219
|
-
className="mt-2 max-w-full max-h-32 object-contain"
|
|
220
|
-
/>
|
|
221
|
-
)}
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
)}
|
|
225
|
-
|
|
226
|
-
{/* Optimized Image */}
|
|
227
|
-
<motion.img
|
|
228
|
-
ref={imgRef}
|
|
229
|
-
src={optimizedSrc}
|
|
230
|
-
alt={alt}
|
|
231
|
-
width={width}
|
|
232
|
-
height={height}
|
|
233
|
-
sizes={sizes}
|
|
234
|
-
srcSet={generateSrcSet()}
|
|
235
|
-
loading={lazy ? "lazy" : priority ? "eager" : "lazy"}
|
|
236
|
-
onLoad={handleImageLoad}
|
|
237
|
-
onError={handleImageError}
|
|
238
|
-
className={cn(
|
|
239
|
-
"w-full h-full object-cover transition-opacity duration-300",
|
|
240
|
-
isLoaded ? "opacity-100" : "opacity-0"
|
|
241
|
-
)}
|
|
242
|
-
initial={{ scale: blur && !isLoaded ? 1.1 : 1 }}
|
|
243
|
-
animate={{ scale: 1 }}
|
|
244
|
-
transition={{ duration: 0.3 }}
|
|
245
|
-
/>
|
|
246
|
-
|
|
247
|
-
{/* Image Controls Overlay */}
|
|
248
|
-
<AnimatePresence>
|
|
249
|
-
{isLoaded && (
|
|
250
|
-
<motion.div
|
|
251
|
-
initial={{ opacity: 0 }}
|
|
252
|
-
animate={{ opacity: 1 }}
|
|
253
|
-
exit={{ opacity: 0 }}
|
|
254
|
-
className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors duration-200"
|
|
255
|
-
>
|
|
256
|
-
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
|
257
|
-
{showInfo && (
|
|
258
|
-
<Button
|
|
259
|
-
size="sm"
|
|
260
|
-
variant="secondary"
|
|
261
|
-
className="h-8 w-8 p-0"
|
|
262
|
-
onClick={() => setShowImageInfo(!showImageInfo)}
|
|
263
|
-
>
|
|
264
|
-
{showImageInfo ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
|
265
|
-
</Button>
|
|
266
|
-
)}
|
|
267
|
-
|
|
268
|
-
{showDownload && (
|
|
269
|
-
<Button
|
|
270
|
-
size="sm"
|
|
271
|
-
variant="secondary"
|
|
272
|
-
className="h-8 w-8 p-0"
|
|
273
|
-
onClick={handleDownload}
|
|
274
|
-
>
|
|
275
|
-
<Download className="h-4 w-4" />
|
|
276
|
-
</Button>
|
|
277
|
-
)}
|
|
278
|
-
|
|
279
|
-
{showPreview && (
|
|
280
|
-
<Button
|
|
281
|
-
size="sm"
|
|
282
|
-
variant="secondary"
|
|
283
|
-
className="h-8 w-8 p-0"
|
|
284
|
-
onClick={() => setIsPreviewOpen(true)}
|
|
285
|
-
>
|
|
286
|
-
<Maximize2 className="h-4 w-4" />
|
|
287
|
-
</Button>
|
|
288
|
-
)}
|
|
289
|
-
</div>
|
|
290
|
-
|
|
291
|
-
{/* Image Info */}
|
|
292
|
-
<AnimatePresence>
|
|
293
|
-
{showImageInfo && imageInfo && (
|
|
294
|
-
<motion.div
|
|
295
|
-
initial={{ opacity: 0, y: 10 }}
|
|
296
|
-
animate={{ opacity: 1, y: 0 }}
|
|
297
|
-
exit={{ opacity: 0, y: 10 }}
|
|
298
|
-
className="absolute bottom-2 left-2 bg-black/80 text-white text-xs p-2 rounded"
|
|
299
|
-
>
|
|
300
|
-
<div>{imageInfo.naturalWidth} × {imageInfo.naturalHeight}px</div>
|
|
301
|
-
<div>Quality: {quality}%</div>
|
|
302
|
-
<div>Format: {format}</div>
|
|
303
|
-
</motion.div>
|
|
304
|
-
)}
|
|
305
|
-
</AnimatePresence>
|
|
306
|
-
</motion.div>
|
|
307
|
-
)}
|
|
308
|
-
</AnimatePresence>
|
|
309
|
-
</div>
|
|
310
|
-
|
|
311
|
-
{/* Full Screen Preview Modal */}
|
|
312
|
-
<AnimatePresence>
|
|
313
|
-
{isPreviewOpen && (
|
|
314
|
-
<motion.div
|
|
315
|
-
initial={{ opacity: 0 }}
|
|
316
|
-
animate={{ opacity: 1 }}
|
|
317
|
-
exit={{ opacity: 0 }}
|
|
318
|
-
className="fixed inset-0 z-50 bg-black/90 flex items-center justify-center p-4"
|
|
319
|
-
onClick={() => setIsPreviewOpen(false)}
|
|
320
|
-
>
|
|
321
|
-
<div className="relative max-w-full max-h-full" onClick={(e) => e.stopPropagation()}>
|
|
322
|
-
{/* Preview Controls */}
|
|
323
|
-
<div className="absolute top-4 right-4 flex gap-2 z-10">
|
|
324
|
-
{showZoom && (
|
|
325
|
-
<>
|
|
326
|
-
<Button
|
|
327
|
-
size="sm"
|
|
328
|
-
variant="secondary"
|
|
329
|
-
onClick={handleZoomOut}
|
|
330
|
-
disabled={currentZoom <= 0.2}
|
|
331
|
-
>
|
|
332
|
-
<ZoomOut className="h-4 w-4" />
|
|
333
|
-
</Button>
|
|
334
|
-
|
|
335
|
-
<Badge variant="secondary" className="px-2">
|
|
336
|
-
{Math.round(currentZoom * 100)}%
|
|
337
|
-
</Badge>
|
|
338
|
-
|
|
339
|
-
<Button
|
|
340
|
-
size="sm"
|
|
341
|
-
variant="secondary"
|
|
342
|
-
onClick={handleZoomIn}
|
|
343
|
-
disabled={currentZoom >= 5}
|
|
344
|
-
>
|
|
345
|
-
<ZoomIn className="h-4 w-4" />
|
|
346
|
-
</Button>
|
|
347
|
-
|
|
348
|
-
<Button
|
|
349
|
-
size="sm"
|
|
350
|
-
variant="secondary"
|
|
351
|
-
onClick={resetZoom}
|
|
352
|
-
>
|
|
353
|
-
<RotateCw className="h-4 w-4" />
|
|
354
|
-
</Button>
|
|
355
|
-
</>
|
|
356
|
-
)}
|
|
357
|
-
|
|
358
|
-
<Button
|
|
359
|
-
size="sm"
|
|
360
|
-
variant="secondary"
|
|
361
|
-
onClick={() => setIsPreviewOpen(false)}
|
|
362
|
-
>
|
|
363
|
-
<X className="h-4 w-4" />
|
|
364
|
-
</Button>
|
|
365
|
-
</div>
|
|
366
|
-
|
|
367
|
-
{/* Preview Image */}
|
|
368
|
-
<motion.img
|
|
369
|
-
ref={previewRef}
|
|
370
|
-
src={optimizedSrc}
|
|
371
|
-
alt={alt}
|
|
372
|
-
className="max-w-full max-h-full object-contain"
|
|
373
|
-
style={{
|
|
374
|
-
transform: `scale(${currentZoom})`,
|
|
375
|
-
transformOrigin: 'center center'
|
|
376
|
-
}}
|
|
377
|
-
initial={{ scale: 0.9 }}
|
|
378
|
-
animate={{ scale: currentZoom }}
|
|
379
|
-
transition={{ duration: 0.2 }}
|
|
380
|
-
/>
|
|
381
|
-
</div>
|
|
382
|
-
</motion.div>
|
|
383
|
-
)}
|
|
384
|
-
</AnimatePresence>
|
|
385
|
-
</>
|
|
386
|
-
)
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export const OptimizedImage: React.FC<OptimizedImageProps> = ({ className, ...props }) => {
|
|
390
|
-
// Check if we're in docs mode or have pro access
|
|
391
|
-
const { hasProAccess, isLoading } = useSubscription()
|
|
392
|
-
|
|
393
|
-
// In docs mode, always show the component
|
|
394
|
-
|
|
395
|
-
// If not in docs mode and no pro access, show upgrade prompt
|
|
396
|
-
if (!isLoading && !hasProAccess) {
|
|
397
|
-
return (
|
|
398
|
-
<Card className={cn("w-fit", className)}>
|
|
399
|
-
<CardContent className="py-6 text-center">
|
|
400
|
-
<div className="space-y-4">
|
|
401
|
-
<div className="rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto">
|
|
402
|
-
<Lock className="h-6 w-6 text-purple-600 dark:text-purple-400" />
|
|
403
|
-
</div>
|
|
404
|
-
<div>
|
|
405
|
-
<h3 className="font-semibold text-sm mb-2">Pro Feature</h3>
|
|
406
|
-
<p className="text-muted-foreground text-xs mb-4">
|
|
407
|
-
Optimized Image is available exclusively to MoonUI Pro subscribers.
|
|
408
|
-
</p>
|
|
409
|
-
<a href="/pricing">
|
|
410
|
-
<Button size="sm">
|
|
411
|
-
<Sparkles className="mr-2 h-4 w-4" />
|
|
412
|
-
Upgrade to Pro
|
|
413
|
-
</Button>
|
|
414
|
-
</a>
|
|
415
|
-
</div>
|
|
416
|
-
</div>
|
|
417
|
-
</CardContent>
|
|
418
|
-
</Card>
|
|
419
|
-
)
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
return <OptimizedImageInternal className={className} {...props} />
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
export type { OptimizedImageProps }
|