/**
* LivePreview Component
*
* A reusable live preview container for displaying real-time previews of settings.
* Used across features like Cookie Consent, GDPR Notice, Email templates, etc.
*
* This component provides a consistent sticky preview panel with a standardized
* header, optional backdrop/overlay, and flexible content area for custom previews.
*
* @example
* ```tsx
*
* Your preview content here
*
* ```
*
* @component LivePreview
* @package Swift Commerce
*/
import * as React from "react"
import { Eye, LucideIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
export interface LivePreviewProps {
/** Title for the preview card (default: "Live Preview") */
title?: string
/** Description text below the title */
description?: string
/** Custom icon component (default: Eye) */
icon?: LucideIcon
/** Distance from top when sticky (default: 100px) */
stickyTop?: number
/** Whether the preview container should be sticky (default: true) */
sticky?: boolean
/** Minimum height of the preview area (default: 400px) */
minHeight?: number
/** Whether to show a dark overlay backdrop */
showOverlay?: boolean
/** Overlay opacity (0-1, default: 0.3) */
overlayOpacity?: number
/** Background color of the preview area */
backgroundColor?: string
/** Whether to show the background pattern (dots/grid) */
showPattern?: boolean
/** Pattern type */
pattern?: "dots" | "grid" | "none"
/** Additional className for the outer container */
className?: string
/** Additional className for the preview area */
previewClassName?: string
/** Header actions (buttons, etc.) */
headerActions?: React.ReactNode
/** Children to render inside the preview area */
children: React.ReactNode
}
/**
* Background patterns for the preview area
*/
const patterns = {
dots: `radial-gradient(circle, rgba(0,0,0,0.05) 1px, transparent 1px)`,
grid: `linear-gradient(rgba(0,0,0,0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0,0,0,0.03) 1px, transparent 1px)`,
none: undefined,
}
const patternSizes = {
dots: "16px 16px",
grid: "20px 20px",
none: undefined,
}
export function LivePreview({
title = "Live Preview",
description = "See how your changes will look",
icon: Icon = Eye,
stickyTop = 220,
sticky = true,
minHeight = 400,
showOverlay = false,
overlayOpacity = 0.3,
backgroundColor,
showPattern = true,
pattern = "dots",
className,
previewClassName,
headerActions,
children,
}: LivePreviewProps) {
const patternStyle = showPattern && pattern !== "none"
? {
backgroundImage: patterns[pattern],
backgroundSize: patternSizes[pattern],
}
: {}
return (
{title}
{description && (
{description}
)}
{headerActions && (
{headerActions}
)}
{children}
)
}
/**
* LivePreviewContent - A helper component for positioning content within LivePreview
*
* Provides preset positioning options commonly used in preview panels.
*/
export interface LivePreviewContentProps {
/** Position of the content within the preview */
position?:
| "center"
| "top"
| "bottom"
| "top-left"
| "top-right"
| "bottom-left"
| "bottom-right"
| "full"
/** Additional className */
className?: string
/** Children to render */
children: React.ReactNode
}
const positionStyles = {
center: "top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
top: "top-0 left-0 right-0",
bottom: "bottom-0 left-0 right-0",
"top-left": "top-4 left-4",
"top-right": "top-4 right-4",
"bottom-left": "bottom-4 left-4",
"bottom-right": "bottom-4 right-4",
full: "inset-0",
}
export function LivePreviewContent({
position = "center",
className,
children,
}: LivePreviewContentProps) {
return (
{children}
)
}
/**
* LivePreviewFloatingButton - A helper for rendering floating action buttons in preview
*/
export interface LivePreviewFloatingButtonProps {
/** Position of the button */
position?: "bottom-left" | "bottom-right" | "top-left" | "top-right"
/** Background color */
color?: string
/** Size of the button (default: 32px) */
size?: number
/** Additional className */
className?: string
/** Children (usually an icon) */
children: React.ReactNode
}
export function LivePreviewFloatingButton({
position = "bottom-right",
color = "#7c3aed",
size = 32,
className,
children,
}: LivePreviewFloatingButtonProps) {
const positionClasses = {
"bottom-left": "bottom-4 left-4",
"bottom-right": "bottom-4 right-4",
"top-left": "top-4 left-4",
"top-right": "top-4 right-4",
}
return (
{children}
)
}
export default LivePreview