aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
571 lines (568 loc) • 22.1 kB
JavaScript
'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