@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,606 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
|
5
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
-
import { X, ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Download, Maximize2, Play, Pause, Volume2, VolumeX, Loader2 } from "lucide-react"
|
|
7
|
-
import { motion, AnimatePresence } from "framer-motion"
|
|
8
|
-
|
|
9
|
-
import { cn } from "../../lib/utils"
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Premium Lightbox Component
|
|
13
|
-
*
|
|
14
|
-
* Advanced lightbox/modal for displaying images and videos with zoom,
|
|
15
|
-
* navigation, fullscreen support, and smooth animations.
|
|
16
|
-
* Perfect for galleries, product showcases, and media presentations.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
const lightboxVariants = cva(
|
|
20
|
-
"fixed inset-0 z-50 flex items-center justify-center",
|
|
21
|
-
{
|
|
22
|
-
variants: {
|
|
23
|
-
backdrop: {
|
|
24
|
-
dark: "bg-black/95",
|
|
25
|
-
blur: "bg-black/90 backdrop-blur-md",
|
|
26
|
-
light: "bg-white/90",
|
|
27
|
-
gradient: "bg-gradient-to-b from-black/95 to-black/80",
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
defaultVariants: {
|
|
31
|
-
backdrop: "blur",
|
|
32
|
-
},
|
|
33
|
-
}
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
interface MediaItem {
|
|
37
|
-
type: "image" | "video"
|
|
38
|
-
src: string
|
|
39
|
-
alt?: string
|
|
40
|
-
thumbnail?: string
|
|
41
|
-
poster?: string
|
|
42
|
-
title?: string
|
|
43
|
-
description?: string
|
|
44
|
-
category?: string
|
|
45
|
-
date?: Date | number
|
|
46
|
-
[key: string]: any
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
interface LightboxContextValue {
|
|
50
|
-
items: MediaItem[]
|
|
51
|
-
currentIndex: number
|
|
52
|
-
setCurrentIndex: (index: number) => void
|
|
53
|
-
isOpen: boolean
|
|
54
|
-
setIsOpen: (open: boolean) => void
|
|
55
|
-
zoom: number
|
|
56
|
-
setZoom: (zoom: number) => void
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const LightboxContext = React.createContext<LightboxContextValue | undefined>(undefined)
|
|
60
|
-
|
|
61
|
-
function useLightbox() {
|
|
62
|
-
const context = React.useContext(LightboxContext)
|
|
63
|
-
if (!context) {
|
|
64
|
-
throw new Error("useLightbox must be used within a LightboxProvider")
|
|
65
|
-
}
|
|
66
|
-
return context
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
interface LightboxProviderProps {
|
|
70
|
-
children: React.ReactNode
|
|
71
|
-
items?: MediaItem[]
|
|
72
|
-
defaultIndex?: number
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function LightboxProvider({ children, items = [], defaultIndex = 0 }: LightboxProviderProps) {
|
|
76
|
-
const [currentIndex, setCurrentIndex] = React.useState(defaultIndex)
|
|
77
|
-
const [isOpen, setIsOpen] = React.useState(false)
|
|
78
|
-
const [zoom, setZoom] = React.useState(1)
|
|
79
|
-
|
|
80
|
-
const value = React.useMemo(
|
|
81
|
-
() => ({
|
|
82
|
-
items,
|
|
83
|
-
currentIndex,
|
|
84
|
-
setCurrentIndex,
|
|
85
|
-
isOpen,
|
|
86
|
-
setIsOpen,
|
|
87
|
-
zoom,
|
|
88
|
-
setZoom,
|
|
89
|
-
}),
|
|
90
|
-
[items, currentIndex, isOpen, zoom]
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
<LightboxContext.Provider value={value}>
|
|
95
|
-
{children}
|
|
96
|
-
</LightboxContext.Provider>
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
interface LightboxTriggerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
101
|
-
index?: number
|
|
102
|
-
asChild?: boolean
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const LightboxTrigger = React.forwardRef<HTMLDivElement, LightboxTriggerProps>(
|
|
106
|
-
({ index = 0, asChild, children, onClick, ...props }, ref) => {
|
|
107
|
-
const { setCurrentIndex, setIsOpen, setZoom } = useLightbox()
|
|
108
|
-
|
|
109
|
-
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
110
|
-
setCurrentIndex(index)
|
|
111
|
-
setIsOpen(true)
|
|
112
|
-
setZoom(1)
|
|
113
|
-
onClick?.(e)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (asChild) {
|
|
117
|
-
const child = children as React.ReactElement<any>
|
|
118
|
-
// Filter out drag events to avoid conflicts
|
|
119
|
-
const { onDrag, onDragStart, onDragEnd, onDragOver, onDrop, ...filteredProps } = props
|
|
120
|
-
return React.cloneElement(child, {
|
|
121
|
-
...child.props,
|
|
122
|
-
...filteredProps,
|
|
123
|
-
onClick: handleClick,
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Filter out drag events to avoid conflicts
|
|
128
|
-
const { onDrag, onDragStart, onDragEnd, onDragOver, onDrop, ...filteredProps } = props
|
|
129
|
-
return (
|
|
130
|
-
<div ref={ref} onClick={handleClick} className="cursor-pointer" {...filteredProps}>
|
|
131
|
-
{children}
|
|
132
|
-
</div>
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
)
|
|
136
|
-
LightboxTrigger.displayName = "LightboxTrigger"
|
|
137
|
-
|
|
138
|
-
interface LightboxContentProps
|
|
139
|
-
extends React.HTMLAttributes<HTMLDivElement>,
|
|
140
|
-
VariantProps<typeof lightboxVariants> {
|
|
141
|
-
showNavigation?: boolean
|
|
142
|
-
showZoomControls?: boolean
|
|
143
|
-
showDownload?: boolean
|
|
144
|
-
showFullscreen?: boolean
|
|
145
|
-
showThumbnails?: boolean
|
|
146
|
-
enableKeyboardNavigation?: boolean
|
|
147
|
-
enableSwipeGestures?: boolean
|
|
148
|
-
animationDuration?: number
|
|
149
|
-
autoPlayVideo?: boolean
|
|
150
|
-
onClose?: () => void
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const LightboxContent = React.forwardRef<HTMLDivElement, LightboxContentProps>(
|
|
154
|
-
({
|
|
155
|
-
className,
|
|
156
|
-
backdrop,
|
|
157
|
-
showNavigation = true,
|
|
158
|
-
showZoomControls = true,
|
|
159
|
-
showDownload = true,
|
|
160
|
-
showFullscreen = true,
|
|
161
|
-
showThumbnails = false,
|
|
162
|
-
enableKeyboardNavigation = true,
|
|
163
|
-
enableSwipeGestures = true,
|
|
164
|
-
animationDuration = 300,
|
|
165
|
-
autoPlayVideo = false,
|
|
166
|
-
onClose,
|
|
167
|
-
...restProps
|
|
168
|
-
}, ref) => {
|
|
169
|
-
// Filter out drag and animation events to avoid conflicts with Framer Motion
|
|
170
|
-
const { onDrag, onDragStart, onDragEnd, onDragOver, onDrop, onAnimationStart, onAnimationEnd, onAnimationIteration, ...props } = restProps
|
|
171
|
-
const { items, currentIndex, setCurrentIndex, isOpen, setIsOpen, zoom, setZoom } = useLightbox()
|
|
172
|
-
const [isLoading, setIsLoading] = React.useState(true)
|
|
173
|
-
const [isPlaying, setIsPlaying] = React.useState(false)
|
|
174
|
-
const [isMuted, setIsMuted] = React.useState(true)
|
|
175
|
-
const [isFullscreen, setIsFullscreen] = React.useState(false)
|
|
176
|
-
const [position, setPosition] = React.useState({ x: 0, y: 0 })
|
|
177
|
-
const [isDragging, setIsDragging] = React.useState(false)
|
|
178
|
-
const containerRef = React.useRef<HTMLDivElement>(null)
|
|
179
|
-
const videoRef = React.useRef<HTMLVideoElement>(null)
|
|
180
|
-
|
|
181
|
-
const currentItem = items[currentIndex]
|
|
182
|
-
|
|
183
|
-
// Auto-play video when item changes
|
|
184
|
-
React.useEffect(() => {
|
|
185
|
-
if (currentItem?.type === "video" && autoPlayVideo) {
|
|
186
|
-
setIsPlaying(true)
|
|
187
|
-
setIsMuted(true) // Mute for autoplay policy
|
|
188
|
-
} else {
|
|
189
|
-
setIsPlaying(false)
|
|
190
|
-
}
|
|
191
|
-
}, [currentIndex, currentItem, autoPlayVideo])
|
|
192
|
-
|
|
193
|
-
// Keyboard navigation
|
|
194
|
-
React.useEffect(() => {
|
|
195
|
-
if (!enableKeyboardNavigation || !isOpen) return
|
|
196
|
-
|
|
197
|
-
const handleKeyDown = (e: KeyboardEvent) => {
|
|
198
|
-
switch (e.key) {
|
|
199
|
-
case "ArrowLeft":
|
|
200
|
-
handlePrevious()
|
|
201
|
-
break
|
|
202
|
-
case "ArrowRight":
|
|
203
|
-
handleNext()
|
|
204
|
-
break
|
|
205
|
-
case "Escape":
|
|
206
|
-
handleClose()
|
|
207
|
-
break
|
|
208
|
-
case "+":
|
|
209
|
-
case "=":
|
|
210
|
-
handleZoomIn()
|
|
211
|
-
break
|
|
212
|
-
case "-":
|
|
213
|
-
handleZoomOut()
|
|
214
|
-
break
|
|
215
|
-
case " ":
|
|
216
|
-
if (currentItem?.type === "video") {
|
|
217
|
-
e.preventDefault()
|
|
218
|
-
setIsPlaying(!isPlaying)
|
|
219
|
-
}
|
|
220
|
-
break
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
window.addEventListener("keydown", handleKeyDown)
|
|
225
|
-
return () => window.removeEventListener("keydown", handleKeyDown)
|
|
226
|
-
}, [isOpen, currentIndex, isPlaying, enableKeyboardNavigation])
|
|
227
|
-
|
|
228
|
-
// Handle video play/pause
|
|
229
|
-
React.useEffect(() => {
|
|
230
|
-
if (videoRef.current && currentItem?.type === "video") {
|
|
231
|
-
if (isPlaying) {
|
|
232
|
-
videoRef.current.play().catch(err => {
|
|
233
|
-
console.error("Video play error:", err)
|
|
234
|
-
setIsPlaying(false)
|
|
235
|
-
})
|
|
236
|
-
} else {
|
|
237
|
-
videoRef.current.pause()
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}, [isPlaying, currentItem])
|
|
241
|
-
|
|
242
|
-
const handleClose = () => {
|
|
243
|
-
setIsOpen(false)
|
|
244
|
-
setZoom(1)
|
|
245
|
-
setPosition({ x: 0, y: 0 })
|
|
246
|
-
onClose?.()
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const handlePrevious = () => {
|
|
250
|
-
setCurrentIndex(currentIndex > 0 ? currentIndex - 1 : items.length - 1)
|
|
251
|
-
setZoom(1)
|
|
252
|
-
setPosition({ x: 0, y: 0 })
|
|
253
|
-
setIsLoading(true)
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const handleNext = () => {
|
|
257
|
-
setCurrentIndex(currentIndex < items.length - 1 ? currentIndex + 1 : 0)
|
|
258
|
-
setZoom(1)
|
|
259
|
-
setPosition({ x: 0, y: 0 })
|
|
260
|
-
setIsLoading(true)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const handleZoomIn = () => {
|
|
264
|
-
setZoom(Math.min(zoom + 0.5, 3))
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const handleZoomOut = () => {
|
|
268
|
-
setZoom(Math.max(zoom - 0.5, 0.5))
|
|
269
|
-
if (zoom - 0.5 <= 1) {
|
|
270
|
-
setPosition({ x: 0, y: 0 })
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const handleDownload = () => {
|
|
275
|
-
if (currentItem) {
|
|
276
|
-
const link = document.createElement("a")
|
|
277
|
-
link.href = currentItem.src
|
|
278
|
-
link.download = currentItem.alt || "download"
|
|
279
|
-
link.click()
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const handleFullscreen = () => {
|
|
284
|
-
if (!isFullscreen) {
|
|
285
|
-
containerRef.current?.requestFullscreen()
|
|
286
|
-
} else {
|
|
287
|
-
document.exitFullscreen()
|
|
288
|
-
}
|
|
289
|
-
setIsFullscreen(!isFullscreen)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const handleMouseDown = (e: React.MouseEvent) => {
|
|
293
|
-
if (zoom > 1) {
|
|
294
|
-
setIsDragging(true)
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const handleMouseMove = (e: React.MouseEvent) => {
|
|
299
|
-
if (isDragging && zoom > 1) {
|
|
300
|
-
setPosition({
|
|
301
|
-
x: position.x + e.movementX,
|
|
302
|
-
y: position.y + e.movementY,
|
|
303
|
-
})
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const handleMouseUp = () => {
|
|
308
|
-
setIsDragging(false)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (!currentItem) return null
|
|
312
|
-
|
|
313
|
-
return (
|
|
314
|
-
<DialogPrimitive.Root open={isOpen} onOpenChange={setIsOpen}>
|
|
315
|
-
<AnimatePresence>
|
|
316
|
-
{isOpen && (
|
|
317
|
-
<DialogPrimitive.Portal forceMount>
|
|
318
|
-
<DialogPrimitive.Overlay asChild>
|
|
319
|
-
<motion.div
|
|
320
|
-
initial={{ opacity: 0 }}
|
|
321
|
-
animate={{ opacity: 1 }}
|
|
322
|
-
exit={{ opacity: 0 }}
|
|
323
|
-
transition={{ duration: animationDuration / 1000 }}
|
|
324
|
-
className={cn(lightboxVariants({ backdrop }), className)}
|
|
325
|
-
ref={ref}
|
|
326
|
-
onClick={(e) => {
|
|
327
|
-
if (e.target === e.currentTarget) {
|
|
328
|
-
handleClose()
|
|
329
|
-
}
|
|
330
|
-
}}
|
|
331
|
-
{...props}
|
|
332
|
-
>
|
|
333
|
-
<div
|
|
334
|
-
ref={containerRef}
|
|
335
|
-
className="relative w-full h-full flex items-center justify-center pointer-events-none"
|
|
336
|
-
onMouseMove={handleMouseMove}
|
|
337
|
-
onMouseUp={handleMouseUp}
|
|
338
|
-
onMouseLeave={handleMouseUp}
|
|
339
|
-
>
|
|
340
|
-
{/* Click catcher for backdrop */}
|
|
341
|
-
<div className="absolute inset-0 pointer-events-auto" onClick={handleClose} />
|
|
342
|
-
{/* Close button */}
|
|
343
|
-
<button
|
|
344
|
-
onClick={handleClose}
|
|
345
|
-
className="absolute top-4 right-4 z-50 p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors pointer-events-auto"
|
|
346
|
-
>
|
|
347
|
-
<X className="h-5 w-5" />
|
|
348
|
-
</button>
|
|
349
|
-
|
|
350
|
-
{/* Navigation */}
|
|
351
|
-
{showNavigation && items.length > 1 && (
|
|
352
|
-
<>
|
|
353
|
-
<button
|
|
354
|
-
onClick={handlePrevious}
|
|
355
|
-
className="absolute left-4 z-50 p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors pointer-events-auto"
|
|
356
|
-
>
|
|
357
|
-
<ChevronLeft className="h-5 w-5" />
|
|
358
|
-
</button>
|
|
359
|
-
<button
|
|
360
|
-
onClick={handleNext}
|
|
361
|
-
className="absolute right-4 z-50 p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors pointer-events-auto"
|
|
362
|
-
>
|
|
363
|
-
<ChevronRight className="h-5 w-5" />
|
|
364
|
-
</button>
|
|
365
|
-
</>
|
|
366
|
-
)}
|
|
367
|
-
|
|
368
|
-
{/* Controls */}
|
|
369
|
-
<div
|
|
370
|
-
className="absolute bottom-4 left-1/2 -translate-x-1/2 z-50 flex items-center gap-2 pointer-events-auto">
|
|
371
|
-
{showZoomControls && currentItem.type === "image" && (
|
|
372
|
-
<>
|
|
373
|
-
<button
|
|
374
|
-
onClick={handleZoomOut}
|
|
375
|
-
className="p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors"
|
|
376
|
-
>
|
|
377
|
-
<ZoomOut className="h-4 w-4" />
|
|
378
|
-
</button>
|
|
379
|
-
<span className="px-3 py-1 rounded-full bg-black/50 text-white text-sm">
|
|
380
|
-
{Math.round(zoom * 100)}%
|
|
381
|
-
</span>
|
|
382
|
-
<button
|
|
383
|
-
onClick={handleZoomIn}
|
|
384
|
-
className="p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors"
|
|
385
|
-
>
|
|
386
|
-
<ZoomIn className="h-4 w-4" />
|
|
387
|
-
</button>
|
|
388
|
-
</>
|
|
389
|
-
)}
|
|
390
|
-
|
|
391
|
-
{currentItem.type === "video" && (
|
|
392
|
-
<>
|
|
393
|
-
<button
|
|
394
|
-
onClick={() => {
|
|
395
|
-
setIsPlaying(!isPlaying)
|
|
396
|
-
if (!isPlaying && videoRef.current) {
|
|
397
|
-
videoRef.current.play().catch(err => {
|
|
398
|
-
console.error("Play failed:", err)
|
|
399
|
-
})
|
|
400
|
-
}
|
|
401
|
-
}}
|
|
402
|
-
className="p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors"
|
|
403
|
-
>
|
|
404
|
-
{isPlaying ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
|
|
405
|
-
</button>
|
|
406
|
-
<button
|
|
407
|
-
onClick={() => {
|
|
408
|
-
setIsMuted(!isMuted)
|
|
409
|
-
if (videoRef.current) {
|
|
410
|
-
videoRef.current.muted = !isMuted
|
|
411
|
-
}
|
|
412
|
-
}}
|
|
413
|
-
className="p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors"
|
|
414
|
-
>
|
|
415
|
-
{isMuted ? <VolumeX className="h-4 w-4" /> : <Volume2 className="h-4 w-4" />}
|
|
416
|
-
</button>
|
|
417
|
-
</>
|
|
418
|
-
)}
|
|
419
|
-
|
|
420
|
-
{showDownload && (
|
|
421
|
-
<button
|
|
422
|
-
onClick={handleDownload}
|
|
423
|
-
className="p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors"
|
|
424
|
-
>
|
|
425
|
-
<Download className="h-4 w-4" />
|
|
426
|
-
</button>
|
|
427
|
-
)}
|
|
428
|
-
|
|
429
|
-
{showFullscreen && (
|
|
430
|
-
<button
|
|
431
|
-
onClick={handleFullscreen}
|
|
432
|
-
className="p-2 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors"
|
|
433
|
-
>
|
|
434
|
-
<Maximize2 className="h-4 w-4" />
|
|
435
|
-
</button>
|
|
436
|
-
)}
|
|
437
|
-
</div>
|
|
438
|
-
|
|
439
|
-
{/* Media content */}
|
|
440
|
-
<motion.div
|
|
441
|
-
initial={{ scale: 0.9, opacity: 0 }}
|
|
442
|
-
animate={{ scale: 1, opacity: 1 }}
|
|
443
|
-
exit={{ scale: 0.9, opacity: 0 }}
|
|
444
|
-
transition={{ duration: animationDuration / 1000 }}
|
|
445
|
-
className="relative flex items-center justify-center pointer-events-auto"
|
|
446
|
-
style={{
|
|
447
|
-
transform: `scale(${zoom}) translate(${position.x / zoom}px, ${position.y / zoom}px)`,
|
|
448
|
-
cursor: zoom > 1 ? (isDragging ? "grabbing" : "grab") : "auto",
|
|
449
|
-
}}
|
|
450
|
-
onMouseDown={handleMouseDown}
|
|
451
|
-
>
|
|
452
|
-
{isLoading && (
|
|
453
|
-
<div className="absolute inset-0 flex items-center justify-center">
|
|
454
|
-
<Loader2 className="h-8 w-8 animate-spin text-white" />
|
|
455
|
-
</div>
|
|
456
|
-
)}
|
|
457
|
-
|
|
458
|
-
{currentItem.type === "image" ? (
|
|
459
|
-
<img
|
|
460
|
-
src={currentItem.src}
|
|
461
|
-
alt={currentItem.alt}
|
|
462
|
-
className="max-w-[90vw] max-h-[90vh] w-auto h-auto object-contain select-none"
|
|
463
|
-
onLoad={() => setIsLoading(false)}
|
|
464
|
-
draggable={false}
|
|
465
|
-
/>
|
|
466
|
-
) : (
|
|
467
|
-
<video
|
|
468
|
-
ref={videoRef}
|
|
469
|
-
src={currentItem.src}
|
|
470
|
-
poster={currentItem.poster}
|
|
471
|
-
muted={isMuted}
|
|
472
|
-
controls={true}
|
|
473
|
-
autoPlay={autoPlayVideo && isPlaying}
|
|
474
|
-
playsInline
|
|
475
|
-
className="max-w-[90vw] max-h-[90vh] w-auto h-auto"
|
|
476
|
-
onLoadedData={() => {
|
|
477
|
-
setIsLoading(false)
|
|
478
|
-
if (autoPlayVideo && isPlaying && videoRef.current) {
|
|
479
|
-
videoRef.current.play().catch(err => {
|
|
480
|
-
console.error("Autoplay failed:", err)
|
|
481
|
-
})
|
|
482
|
-
}
|
|
483
|
-
}}
|
|
484
|
-
onError={(e) => {
|
|
485
|
-
console.error("Video error:", e)
|
|
486
|
-
setIsLoading(false)
|
|
487
|
-
}}
|
|
488
|
-
/>
|
|
489
|
-
)}
|
|
490
|
-
</motion.div>
|
|
491
|
-
|
|
492
|
-
{/* Info overlay */}
|
|
493
|
-
{(currentItem.title || currentItem.description) && (
|
|
494
|
-
<motion.div
|
|
495
|
-
initial={{ opacity: 0, y: 20 }}
|
|
496
|
-
animate={{ opacity: 1, y: 0 }}
|
|
497
|
-
className="absolute top-4 left-4 max-w-md pointer-events-auto"
|
|
498
|
-
>
|
|
499
|
-
{currentItem.title && (
|
|
500
|
-
<h3 className="text-white text-xl font-semibold mb-1">{currentItem.title}</h3>
|
|
501
|
-
)}
|
|
502
|
-
{currentItem.description && (
|
|
503
|
-
<p className="text-white/80 text-sm">{currentItem.description}</p>
|
|
504
|
-
)}
|
|
505
|
-
</motion.div>
|
|
506
|
-
)}
|
|
507
|
-
|
|
508
|
-
{/* Thumbnails */}
|
|
509
|
-
{showThumbnails && items.length > 1 && (
|
|
510
|
-
<motion.div
|
|
511
|
-
initial={{ opacity: 0, y: 20 }}
|
|
512
|
-
animate={{ opacity: 1, y: 0 }}
|
|
513
|
-
className="absolute bottom-20 left-0 right-0 flex justify-center z-50 pointer-events-auto"
|
|
514
|
-
>
|
|
515
|
-
<div className="flex gap-2 p-2 bg-black/70 backdrop-blur-sm rounded-lg max-w-[80vw] overflow-x-auto">
|
|
516
|
-
{items.map((item, index) => (
|
|
517
|
-
<button
|
|
518
|
-
key={index}
|
|
519
|
-
onClick={() => {
|
|
520
|
-
setCurrentIndex(index)
|
|
521
|
-
setZoom(1)
|
|
522
|
-
setPosition({ x: 0, y: 0 })
|
|
523
|
-
}}
|
|
524
|
-
className={cn(
|
|
525
|
-
"w-16 h-16 rounded overflow-hidden transition-all",
|
|
526
|
-
index === currentIndex
|
|
527
|
-
? "ring-2 ring-white scale-110"
|
|
528
|
-
: "opacity-60 hover:opacity-100"
|
|
529
|
-
)}
|
|
530
|
-
>
|
|
531
|
-
{item.type === "image" ? (
|
|
532
|
-
<img
|
|
533
|
-
src={item.thumbnail || item.src}
|
|
534
|
-
alt={item.alt}
|
|
535
|
-
className="w-full h-full object-cover"
|
|
536
|
-
/>
|
|
537
|
-
) : (
|
|
538
|
-
<div className="w-full h-full bg-black/50 flex items-center justify-center">
|
|
539
|
-
<Play className="h-4 w-4 text-white" />
|
|
540
|
-
</div>
|
|
541
|
-
)}
|
|
542
|
-
</button>
|
|
543
|
-
))}
|
|
544
|
-
</div>
|
|
545
|
-
</motion.div>
|
|
546
|
-
)}
|
|
547
|
-
</div>
|
|
548
|
-
</motion.div>
|
|
549
|
-
</DialogPrimitive.Overlay>
|
|
550
|
-
</DialogPrimitive.Portal>
|
|
551
|
-
)}
|
|
552
|
-
</AnimatePresence>
|
|
553
|
-
</DialogPrimitive.Root>
|
|
554
|
-
)
|
|
555
|
-
}
|
|
556
|
-
)
|
|
557
|
-
LightboxContent.displayName = "LightboxContent"
|
|
558
|
-
|
|
559
|
-
// Simple lightbox for single items
|
|
560
|
-
interface SimpleLightboxProps extends Omit<LightboxContentProps, "items"> {
|
|
561
|
-
src: string
|
|
562
|
-
type?: "image" | "video"
|
|
563
|
-
alt?: string
|
|
564
|
-
poster?: string
|
|
565
|
-
title?: string
|
|
566
|
-
description?: string
|
|
567
|
-
children: React.ReactNode
|
|
568
|
-
autoPlayVideo?: boolean
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
function SimpleLightbox({
|
|
572
|
-
src,
|
|
573
|
-
type = "image",
|
|
574
|
-
alt,
|
|
575
|
-
poster,
|
|
576
|
-
title,
|
|
577
|
-
description,
|
|
578
|
-
children,
|
|
579
|
-
...props
|
|
580
|
-
}: SimpleLightboxProps) {
|
|
581
|
-
const items: MediaItem[] = [{ type, src, alt, poster, title, description }]
|
|
582
|
-
|
|
583
|
-
return (
|
|
584
|
-
<LightboxProvider items={items}>
|
|
585
|
-
<LightboxTrigger>{children}</LightboxTrigger>
|
|
586
|
-
<LightboxContent {...props} />
|
|
587
|
-
</LightboxProvider>
|
|
588
|
-
)
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
export {
|
|
592
|
-
LightboxProvider,
|
|
593
|
-
LightboxTrigger,
|
|
594
|
-
LightboxContent,
|
|
595
|
-
SimpleLightbox,
|
|
596
|
-
type MediaItem,
|
|
597
|
-
type LightboxContentProps,
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// Export as MoonUI components
|
|
601
|
-
export {
|
|
602
|
-
LightboxProvider as MoonUILightboxProviderPro,
|
|
603
|
-
LightboxTrigger as MoonUILightboxTriggerPro,
|
|
604
|
-
LightboxContent as MoonUILightboxContentPro,
|
|
605
|
-
SimpleLightbox as MoonUILightboxPro,
|
|
606
|
-
}
|