UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

571 lines (568 loc) 22.1 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { motion } from 'framer-motion'; import { forwardRef, useState, useRef, useCallback, useEffect } from 'react'; import { useMotionPreference } from '../../hooks/useMotionPreference.js'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import '../../primitives/motion/MotionFramer.js'; import { useA11yId } from '../../utils/a11y.js'; import { createGlassStyle } from '../../utils/createGlassStyle.js'; import { useGlassSound } from '../../utils/soundDesign.js'; const defaultStyleModels = [{ id: "van-gogh", name: "Van Gogh Starry Night", description: "Impressionistic swirls and bold brushstrokes", previewUrl: "/styles/van-gogh.jpg", strength: 0.8, category: "artistic" }, { id: "picasso", name: "Picasso Cubism", description: "Geometric fragmentation and multiple perspectives", previewUrl: "/styles/picasso.jpg", strength: 0.7, category: "artistic" }, { id: "monet", name: "Monet Water Lilies", description: "Soft impressionistic light and color", previewUrl: "/styles/monet.jpg", strength: 0.6, category: "artistic" }, { id: "film-noir", name: "Film Noir", description: "High contrast black and white cinematography", previewUrl: "/styles/film-noir.jpg", strength: 0.9, category: "photographic" }, { id: "synthwave", name: "Synthwave", description: "Retro-futuristic neon aesthetics", previewUrl: "/styles/synthwave.jpg", strength: 0.8, category: "modern" }, { id: "kandinsky", name: "Kandinsky Abstract", description: "Geometric abstraction with vibrant colors", previewUrl: "/styles/kandinsky.jpg", strength: 0.7, category: "abstract" }]; const GlassStyleTransfer = /*#__PURE__*/forwardRef(({ sourceImage, styleModels = defaultStyleModels, selectedStyle = "", transferStrength = 0.7, realTimePreview = true, showProgressIndicator = true, showStyleLibrary = true, showAdvancedControls = true, preserveColors = false, enhanceDetails = true, blendMode = "normal", resolution = "medium", onStyleSelect, onTransferComplete, onProgressUpdate, className = "", ...props }, ref) => { const prefersReducedMotion = useReducedMotion(); const [isProcessing, setIsProcessing] = useState(false); const [progress, setProgress] = useState(0); const [previewImage, setPreviewImage] = useState(""); const [uploadedImage, setUploadedImage] = useState(sourceImage || ""); const [selectedStyleId, setSelectedStyleId] = useState(selectedStyle); const [transferParams, setTransferParams] = useState({ strength: transferStrength, preserveColors, enhanceDetails, blendMode, resolution }); const canvasRef = useRef(null); const fileInputRef = useRef(null); useA11yId("glass-style-transfer"); const { shouldAnimate } = useMotionPreference(); const { play } = useGlassSound(); // Simulate style transfer processing const processStyleTransfer = useCallback(async (imageData, styleId) => { if (!imageData || !styleId) return; setIsProcessing(true); setProgress(0); play("processing"); // Simulate processing with progress updates const steps = [{ label: "Loading models...", duration: 500 }, { label: "Analyzing content...", duration: 800 }, { label: "Extracting style features...", duration: 1000 }, { label: "Applying style transfer...", duration: 1500 }, { label: "Optimizing result...", duration: 700 }, { label: "Finalizing...", duration: 400 }]; let totalProgress = 0; for (let i = 0; i < steps.length; i++) { const step = steps[i]; await new Promise(resolve => setTimeout(resolve, step.duration)); totalProgress = (i + 1) / steps.length * 100; setProgress(totalProgress); onProgressUpdate?.(totalProgress); } // Simulate result generation const canvas = canvasRef.current; if (canvas) { const ctx = canvas.getContext("2d"); if (ctx) { // Create a stylized version using advanced canvas rendering const selectedStyleModel = styleModels.find(s => s.id === styleId); const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height); // Apply different effects based on style category switch (selectedStyleModel?.category) { case "artistic": gradient.addColorStop(0, "#FF6B6B"); gradient.addColorStop(0.5, "#4ECDC4"); gradient.addColorStop(1, "#45B7D1"); break; case "photographic": gradient.addColorStop(0, "#2D3748"); gradient.addColorStop(0.5, "#4A5568"); gradient.addColorStop(1, "#718096"); break; case "abstract": gradient.addColorStop(0, "#9F7AEA"); gradient.addColorStop(0.5, "#F093FB"); gradient.addColorStop(1, "#F9844A"); break; default: gradient.addColorStop(0, "#667EEA"); gradient.addColorStop(1, "#764BA2"); } ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); // Add style-specific effects if (selectedStyleModel?.category === "artistic") { // Add brush stroke effect for (let i = 0; i < 50; i++) { ctx.beginPath(); ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.3})`; ctx.lineWidth = Math.random() * 3 + 1; ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height); ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height); ctx.stroke(); } } const resultUrl = canvas.toDataURL(); setPreviewImage(resultUrl); onTransferComplete?.(resultUrl); } } setIsProcessing(false); play("success"); }, [styleModels, onProgressUpdate, onTransferComplete, play]); // Handle file upload const handleFileUpload = useCallback(event => { const file = event.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = e => { const result = e.target?.result; setUploadedImage(result); if (realTimePreview && selectedStyleId) { processStyleTransfer(result, selectedStyleId); } }; reader.readAsDataURL(file); }, [realTimePreview, selectedStyleId, processStyleTransfer]); // Handle style selection const handleStyleSelect = useCallback(styleId => { setSelectedStyleId(styleId); onStyleSelect?.(styleId); play("select"); if (realTimePreview && uploadedImage) { processStyleTransfer(uploadedImage, styleId); } }, [realTimePreview, uploadedImage, processStyleTransfer, onStyleSelect, play]); // Real-time parameter updates useEffect(() => { if (realTimePreview && uploadedImage && selectedStyleId) { const debounceTimer = setTimeout(() => { processStyleTransfer(uploadedImage, selectedStyleId); }, 500); return () => clearTimeout(debounceTimer); } }, [transferParams, realTimePreview, uploadedImage, selectedStyleId, processStyleTransfer]); const StyleLibrary = () => jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Style Library" }), jsx("div", { className: 'glass-grid glass-grid-cols-2 md:grid-cols-3 glass-gap-3', children: styleModels.map(style => jsxs(motion.div, { className: ` relative p-3 rounded-lg border cursor-pointer transition-all duration-200 ${selectedStyleId === style.id ? "border-blue-400 bg-blue-400/20" : "border-white/20 hover:border-white/40 bg-white/5"} `, whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, onClick: () => handleStyleSelect(style.id), children: [jsx("div", { className: 'aspect-square glass-gradient-primary glass-gradient-primary glass-gradient-primary glass-radius-lg mb-2 overflow-hidden', children: jsx("div", { className: ` w-full h-full bg-gradient-to-br ${style.category === "artistic" ? "from-red-400 to-blue-400" : style.category === "photographic" ? "from-gray-600 to-gray-800" : style.category === "abstract" ? "from-purple-400 to-pink-400" : style.category === "vintage" ? "from-yellow-600 to-orange-800" : "from-blue-400 to-purple-600"} ` }) }), jsx("div", { className: 'glass-text-xs text-primary/90 font-medium mb-1', children: style.name }), jsx("div", { className: 'glass-text-xs text-primary/60', children: style.description }), jsx("div", { className: 'absolute glass-top-2 right-2', children: jsx("div", { className: ` px-1.5 py-0.5 rounded text-xs font-medium ${style.category === "artistic" ? "bg-red-500/20 text-red-300" : style.category === "photographic" ? "bg-gray-500/20 text-gray-300" : style.category === "abstract" ? "bg-purple-500/20 text-purple-300" : style.category === "vintage" ? "bg-orange-500/20 text-orange-300" : "bg-blue-500/20 text-blue-300"} `, children: style.category }) })] }, style.id)) })] }); const AdvancedControls = () => jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Advanced Controls" }), jsxs("div", { className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 glass-gap-4', children: [jsxs("div", { children: [jsxs("label", { className: 'block glass-text-xs text-primary/70 mb-2', children: ["Transfer Strength: ", Math.round(transferParams.strength * 100), "%"] }), jsx("input", { type: "range", min: "0", max: "1", step: "0.1", value: transferParams.strength, onChange: e => setTransferParams(prev => ({ ...prev, strength: parseFloat(e.target.value) })), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer slider' })] }), jsxs("div", { children: [jsx("label", { className: 'block glass-text-xs text-primary/70 mb-2', children: "Resolution" }), jsxs("select", { value: transferParams.resolution, onChange: e => setTransferParams(prev => ({ ...prev, resolution: e.target.value })), className: 'glass-w-full glass-p-2 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 glass-text-sm', children: [jsx("option", { value: "low", children: "Low (512px)" }), jsx("option", { value: "medium", children: "Medium (1024px)" }), jsx("option", { value: "high", children: "High (2048px)" }), jsx("option", { value: "ultra", children: "Ultra (4096px)" })] })] }), jsxs("div", { children: [jsx("label", { className: 'block glass-text-xs text-primary/70 mb-2', children: "Blend Mode" }), jsxs("select", { value: transferParams.blendMode, onChange: e => setTransferParams(prev => ({ ...prev, blendMode: e.target.value })), className: 'glass-w-full glass-p-2 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 glass-text-sm', children: [jsx("option", { value: "normal", children: "Normal" }), jsx("option", { value: "multiply", children: "Multiply" }), jsx("option", { value: "screen", children: "Screen" }), jsx("option", { value: "overlay", children: "Overlay" }), jsx("option", { value: "soft-light", children: "Soft Light" })] })] })] }), jsxs("div", { className: 'glass-flex glass-items-center space-x-6', children: [jsxs("label", { className: 'glass-flex glass-items-center space-x-2 cursor-pointer', children: [jsx("input", { type: "checkbox", checked: transferParams.preserveColors, onChange: e => setTransferParams(prev => ({ ...prev, preserveColors: e.target.checked })), className: 'w-4 h-4 glass-radius glass-border-white/30' }), jsx("span", { className: 'glass-text-sm text-primary/80', children: "Preserve Colors" })] }), jsxs("label", { className: 'glass-flex glass-items-center space-x-2 cursor-pointer', children: [jsx("input", { type: "checkbox", checked: transferParams.enhanceDetails, onChange: e => setTransferParams(prev => ({ ...prev, enhanceDetails: e.target.checked })), className: 'w-4 h-4 glass-radius glass-border-white/30' }), jsx("span", { className: 'glass-text-sm text-primary/80', children: "Enhance Details" })] })] })] }); return jsxs(OptimizedGlassCore, { ref: ref, variant: "frosted", className: `p-6 space-y-6 ${className}`, ...props, children: [jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsxs("div", { children: [jsx("h3", { className: 'glass-text-xl font-semibold text-primary/90', children: "Style Transfer AI" }), jsx("p", { className: 'glass-text-sm text-primary/60', children: "Transform your images with artistic styles" })] }), jsx("div", { className: 'glass-flex glass-items-center space-x-2', children: realTimePreview && jsxs("div", { className: 'glass-flex glass-items-center space-x-1 text-primary', children: [jsx("div", { className: 'w-2 h-2 glass-surface-green glass-radius-full animate-pulse' }), jsx("span", { className: "glass-text-xs", children: "Real-time" })] }) })] }), jsxs("div", { className: 'glass-grid glass-grid-cols-1 lg:grid-cols-2 glass-gap-6', children: [jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Source Image" }), jsxs("div", { className: 'relative aspect-square glass-surface-subtle/5 glass-border-2 glass-border-dashed glass-border-white/30 glass-radius-lg overflow-hidden cursor-pointer hover:border-white/50 transition-colors', onClick: () => fileInputRef.current?.click(), children: [uploadedImage ? jsx("img", { src: uploadedImage, alt: "Source", className: 'glass-w-full glass-h-full object-cover' }) : jsx("div", { className: 'glass-flex glass-items-center glass-justify-center glass-h-full text-primary/50', children: jsxs("div", { className: 'text-center', children: [jsx("div", { className: 'glass-text-4xl mb-2', children: "\uD83D\uDCF7" }), jsx("p", { children: "Click to upload image" }), jsx("p", { className: 'glass-text-xs mt-1', children: "PNG, JPG up to 10MB" })] }) }), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", onChange: handleFileUpload, className: 'hidden glass-touch-target glass-contrast-guard' })] })] }), jsxs("div", { className: 'space-y-4', children: [jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Style Preview" }), isProcessing && showProgressIndicator && jsxs("div", { className: 'glass-flex glass-items-center space-x-2 text-primary', children: [jsx("div", { className: 'w-4 h-4 glass-border-2 glass-border-blue glass-border-t-transparent glass-radius-full animate-spin' }), jsxs("span", { className: "glass-text-xs", children: [Math.round(progress), "%"] })] })] }), jsxs("div", { className: 'relative aspect-square glass-surface-subtle/5 glass-border glass-border-white/20 glass-radius-lg overflow-hidden', children: [previewImage ? jsx("img", { src: previewImage, alt: "Styled preview", className: 'glass-w-full glass-h-full object-cover' }) : jsx("div", { className: 'glass-flex glass-items-center glass-justify-center glass-h-full text-primary/50', children: jsxs("div", { className: 'text-center', children: [jsx("div", { className: 'glass-text-4xl mb-2', children: "\uD83C\uDFA8" }), jsx("p", { children: "Style preview will appear here" })] }) }), isProcessing && jsx("div", { className: 'absolute inset-0 glass-surface-dark/50 glass-flex glass-items-center glass-justify-center', children: jsxs("div", { className: 'text-center text-primary', children: [jsx("div", { className: 'w-8 h-8 glass-border-2 glass-border-white glass-border-t-transparent glass-radius-full animate-spin glass-mx-auto mb-2' }), jsx("div", { className: "glass-text-sm", children: "Processing..." }), jsxs("div", { className: 'glass-text-xs mt-1', children: [Math.round(progress), "% complete"] })] }) })] }), jsx("canvas", { ref: canvasRef, width: 512, height: 512, className: 'hidden' })] })] }), showStyleLibrary && jsx(StyleLibrary, {}), showAdvancedControls && jsx(AdvancedControls, {}), jsxs("div", { className: 'glass-flex glass-items-center glass-justify-between pt-4 glass-border-t glass-border-white/10', children: [jsxs("div", { className: 'glass-flex glass-items-center space-x-4', children: [jsx(motion.button, { className: 'glass-px-4 glass-py-2 glass-surface-blue hover:glass-surface-blue text-primary glass-radius-lg glass-text-sm font-medium transition-colors', whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, onClick: () => uploadedImage && selectedStyleId && processStyleTransfer(uploadedImage, selectedStyleId), disabled: isProcessing || !uploadedImage || !selectedStyleId, children: isProcessing ? "Processing..." : "Apply Style" }), jsx(motion.button, { className: 'glass-px-4 glass-py-2 glass-border glass-border-white/30 hover:border-white/50 text-primary/80 glass-radius-lg glass-text-sm transition-colors', whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, onClick: () => { setPreviewImage(""); setProgress(0); }, children: "Clear" })] }), previewImage && jsx(motion.a, { href: previewImage, download: "styled-image.png", className: 'glass-px-4 glass-py-2 glass-surface-green hover:glass-surface-green text-primary glass-radius-lg glass-text-sm font-medium transition-colors', whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, children: "Download Result" })] }), isProcessing && showProgressIndicator && jsxs("div", { className: ` p-3 rounded-lg border border-blue-400/30 ${createGlassStyle({ blur: "sm", opacity: 0.8 }).background} `, children: [jsxs("div", { className: 'glass-flex glass-items-center glass-justify-between mb-2', children: [jsx("span", { className: 'glass-text-sm text-primary/80', children: "Processing Style Transfer" }), jsxs("span", { className: 'glass-text-sm font-medium text-primary', children: [Math.round(progress), "%"] })] }), jsx("div", { className: 'glass-w-full glass-surface-subtle/20 glass-radius-full h-2', children: jsx(motion.div, { className: 'glass-surface-blue h-2 glass-radius-full', animate: { width: `${progress}%` }, transition: prefersReducedMotion ? { duration: 0 } : { duration: 0.3 } }) })] })] }); }); GlassStyleTransfer.displayName = "GlassStyleTransfer"; export { GlassStyleTransfer }; //# sourceMappingURL=GlassStyleTransfer.js.map