UNPKG

aura-glass

Version:

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

431 lines (428 loc) 16.3 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { motion } from 'framer-motion'; import { Settings, Eye, Zap, Droplets, Layers, Sun, Sparkles, Gauge } from 'lucide-react'; import { useRef, useState } from 'react'; import { cn } from '../../lib/utilsComprehensive.js'; import { useHoudiniGlass, useGlassEffect } from './HoudiniGlassProvider.js'; function HoudiniGlassCard({ children, className = "", preset = "standard", effects = ["frost"], enableWorklets = true, customProperties = {}, interactive = true, showControls = false, title, description }) { const prefersReducedMotion = useReducedMotion(); const cardRef = useRef(null); const { isSupported, enabledEffects, toggleEffect, performanceMode } = useHoudiniGlass(); const [isHovered, setIsHovered] = useState(false); const [showEffectControls, setShowEffectControls] = useState(false); // Apply glass effects using the Houdini hook const { appliedEffects, canUseWorklets } = useGlassEffect(cardRef, effects, { preset, customProperties, enableWorklets: enableWorklets }); const handleMouseEnter = () => { if (interactive) { setIsHovered(true); } }; const handleMouseLeave = () => { if (interactive) { setIsHovered(false); } }; const getEffectIcon = effect => { switch (effect) { case "frost": return jsx(Sparkles, { className: 'w-4 h-4' }); case "caustics": return jsx(Sun, { className: 'w-4 h-4' }); case "border": return jsx(Layers, { className: 'w-4 h-4' }); case "refraction": return jsx(Droplets, { className: 'w-4 h-4' }); default: return jsx(Zap, { className: 'w-4 h-4' }); } }; const getPerformanceIndicator = () => { useReducedMotion(); if (performanceMode) { return jsx("span", { title: "Performance mode active", children: jsx(Gauge, { className: 'w-4 h-4 text-primary' }) }); } return jsx("span", { title: "Full effects active", children: jsx(Zap, { className: 'w-4 h-4 text-primary' }) }); }; return jsxs(motion.div, { ref: cardRef, className: cn( // Base glass foundation with design tokens "glass-foundation-complete glass-radius-xl glass-p-lg", "relative overflow-hidden", // Houdini-specific classes "houdini-glass", { "houdini-glass-fallback": !isSupported, "houdini-glass-performance": performanceMode, "cursor-pointer glass-press glass-magnet": interactive }, // Glass effects "glass-overlay-specular glass-parallax", "glass-transition glass-focus", className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, whileHover: interactive ? { scale: 1.02 } : {}, whileTap: interactive ? { scale: 0.98 } : {}, transition: prefersReducedMotion ? { duration: 0 } : { duration: performanceMode ? 0.15 : 0.3, ease: "easeOut" }, children: [(title || description || showControls) && jsx("div", { className: 'mb-4', children: jsxs("div", { className: "glass-flex glass-items-start glass-justify-between", children: [jsxs("div", { className: "glass-flex-1", children: [title && jsx("h3", { className: 'glass-text-lg font-semibold glass-text-secondary dark:text-primary mb-1', children: title }), description && jsx("p", { className: 'glass-text-sm glass-text-secondary dark:text-gray-300', children: description })] }), showControls && jsxs("div", { className: 'glass-flex glass-items-center glass-gap-2 ml-4', children: [getPerformanceIndicator(), jsx("button", { onClick: () => setShowEffectControls(!showEffectControls), className: 'glass-p-2 glass-radius-lg hover:glass-surface-subtle/10 transition-colors', title: "Toggle effect controls", children: jsx(Settings, { className: 'w-4 h-4 glass-text-secondary dark:glass-text-secondary' }) })] })] }) }), showEffectControls && showControls && jsxs(motion.div, { initial: { opacity: 0, height: 0 }, animate: prefersReducedMotion ? {} : { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, className: 'mb-4 glass-p-3 glass-radius-lg glass-surface-dark/5 dark:glass-surface-subtle/5', children: [jsxs("div", { className: 'glass-flex glass-items-center glass-justify-between mb-2', children: [jsx("span", { className: 'glass-text-sm font-medium glass-text-secondary dark:text-gray-300', children: "Glass Effects" }), jsxs("span", { className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary', children: [appliedEffects.length, " active"] })] }), jsx("div", { className: "glass-grid glass-grid-cols-2 glass-gap-2", children: ["frost", "caustics", "border", "refraction"].map(effect => jsxs("button", { onClick: () => toggleEffect(effect), className: ` flex items-center gap-2 p-2 rounded-lg text-sm transition-all ${enabledEffects.includes(effect) ? "bg-blue-500/20 text-blue-700 dark:text-blue-300 border border-blue-500/30" : "bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"} `, title: `${enabledEffects.includes(effect) ? "Disable" : "Enable"} ${effect} effect`, children: [getEffectIcon(effect), jsx("span", { className: 'capitalize', children: effect }), enabledEffects.includes(effect) && jsx(Eye, { className: 'w-3 h-3 ml-auto' })] }, effect)) }), jsxs("div", { className: 'mt-2 glass-text-xs glass-text-secondary dark:glass-text-secondary', children: ["Worklets: ", canUseWorklets ? "✅ Supported" : "❌ Fallback"] })] }), jsx("div", { className: 'relative z-10', children: children }), interactive && jsx(motion.div, { className: 'absolute inset-0 glass-gradient-primary glass-gradient-primary glass-gradient-primary pointer-events-none', initial: { opacity: 0 }, animate: prefersReducedMotion ? {} : { opacity: isHovered ? 1 : 0 }, transition: prefersReducedMotion ? { duration: 0 } : { duration: performanceMode ? 0.1 : 0.2 } }), showControls && jsx("div", { className: 'absolute glass-top-2 right-2 glass-flex glass-gap-1', children: appliedEffects.map(effect => jsx("div", { className: 'w-2 h-2 glass-radius-full glass-surface-blue opacity-60', title: `Active: ${effect}` }, effect)) }), !isSupported && jsx("div", { className: 'absolute bottom-2 right-2', children: jsx("div", { className: 'w-2 h-2 glass-radius-full bg-amber-400', title: "Houdini not supported - using fallback styles" }) })] }); } // Demo component showcasing different Houdini glass effects function HoudiniGlassShowcase() { const [selectedPreset, setSelectedPreset] = useState("standard"); const [selectedEffects, setSelectedEffects] = useState(["frost"]); const { isSupported, hasPaintAPI, hasPropertyAPI } = useHoudiniGlass(); const presets = [{ id: "standard", name: "Standard", description: "Balanced glass effect" }, { id: "frosted", name: "Frosted", description: "Enhanced blur and opacity" }, { id: "minimal", name: "Minimal", description: "Subtle glass appearance" }, { id: "heavy", name: "Heavy", description: "Maximum glass intensity" }, { id: "crystal", name: "Crystal", description: "Ultra-clear glass effect" }]; const availableEffects = [{ id: "frost", name: "Frost", description: "Icy glass texture" }, { id: "caustics", name: "Caustics", description: "Light refraction patterns" }, { id: "border", name: "Border", description: "Animated border effects" }, { id: "refraction", name: "Refraction", description: "Light bending effects" }]; const toggleEffect = effectId => { setSelectedEffects(prev => prev.includes(effectId) ? prev.filter(id => id !== effectId) : [...prev, effectId]); }; return jsxs("div", { className: 'space-y-6', children: [jsx(HoudiniGlassCard, { title: "Houdini Glass Support", description: "Check browser compatibility for CSS Houdini features", showControls: true, children: jsxs("div", { className: 'glass-grid glass-grid-cols-1 md:grid-cols-3 glass-gap-4', children: [jsxs("div", { className: 'text-center glass-p-3 glass-radius-lg glass-surface-subtle dark:glass-surface-primary', children: [jsx("div", { className: `text-2xl mb-2 ${isSupported ? "text-green-500" : "text-red-500"}`, children: isSupported ? "✅" : "❌" }), jsx("div", { className: 'font-medium', children: "Overall Support" }), jsx("div", { className: 'glass-text-sm glass-text-secondary dark:glass-text-secondary', children: "Houdini APIs available" })] }), jsxs("div", { className: 'text-center glass-p-3 glass-radius-lg glass-surface-subtle dark:glass-surface-primary', children: [jsx("div", { className: `text-2xl mb-2 ${hasPropertyAPI ? "text-green-500" : "text-red-500"}`, children: hasPropertyAPI ? "✅" : "❌" }), jsx("div", { className: 'font-medium', children: "Properties API" }), jsx("div", { className: 'glass-text-sm glass-text-secondary dark:glass-text-secondary', children: "Custom properties support" })] }), jsxs("div", { className: 'text-center glass-p-3 glass-radius-lg glass-surface-subtle dark:glass-surface-primary', children: [jsx("div", { className: `text-2xl mb-2 ${hasPaintAPI ? "text-green-500" : "text-red-500"}`, children: hasPaintAPI ? "✅" : "❌" }), jsx("div", { className: 'font-medium', children: "Paint API" }), jsx("div", { className: 'glass-text-sm glass-text-secondary dark:glass-text-secondary', children: "Paint worklets support" })] })] }) }), jsx(HoudiniGlassCard, { title: "Glass Presets", description: "Choose from predefined glass effect configurations", showControls: true, children: jsx("div", { className: 'glass-grid glass-grid-cols-1 md:grid-cols-5 glass-gap-3', children: presets.map(preset => jsxs("button", { onClick: () => setSelectedPreset(preset.id), className: ` p-3 rounded-lg text-left transition-all ${selectedPreset === preset.id ? "bg-blue-500/20 border-2 border-blue-500/50 text-blue-700 dark:text-blue-300" : "bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border-2 border-transparent"} `, children: [jsx("div", { className: 'font-medium glass-text-sm', children: preset.name }), jsx("div", { className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary mt-1', children: preset.description })] }, preset.id)) }) }), jsx(HoudiniGlassCard, { title: "Glass Effects", description: "Enable/disable individual glass effect layers", showControls: true, children: jsx("div", { className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 lg:grid-cols-4 glass-gap-3', children: availableEffects.map(effect => jsxs("button", { onClick: () => toggleEffect(effect.id), className: ` p-3 rounded-lg text-left transition-all ${selectedEffects.includes(effect.id) ? "bg-green-500/20 border-2 border-green-500/50 text-green-700 dark:text-green-300" : "bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border-2 border-transparent"} `, children: [jsxs("div", { className: 'font-medium glass-text-sm glass-flex glass-items-center glass-gap-2', children: [getEffectIcon(effect.id), effect.name] }), jsx("div", { className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary mt-1', children: effect.description })] }, effect.id)) }) }), jsx(HoudiniGlassCard, { title: "Live Preview", description: `Preset: ${selectedPreset} | Effects: ${selectedEffects.join(", ")}`, preset: selectedPreset, effects: selectedEffects, showControls: true, interactive: true, children: jsxs("div", { className: 'space-y-4', children: [jsxs("div", { className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 glass-gap-4', children: [jsxs("div", { className: 'glass-p-4 glass-radius-lg glass-gradient-primary glass-gradient-primary glass-gradient-primary dark:glass-gradient-primary dark:glass-gradient-primary', children: [jsx("h4", { className: 'font-medium glass-text-secondary dark:text-primary mb-2', children: "Content Area 1" }), jsx("p", { className: 'glass-text-sm glass-text-secondary dark:text-gray-300', children: "This is a preview of how the selected glass effects appear with your content. The effects are applied using CSS Houdini for maximum performance." })] }), jsxs("div", { className: 'glass-p-4 glass-radius-lg glass-gradient-primary glass-gradient-primary glass-gradient-primary dark:glass-gradient-primary dark:glass-gradient-primary', children: [jsx("h4", { className: 'font-medium glass-text-secondary dark:text-primary mb-2', children: "Content Area 2" }), jsx("p", { className: 'glass-text-sm glass-text-secondary dark:text-gray-300', children: "Experiment with different presets and effects to find the perfect glass aesthetic for your application." })] })] }), jsxs("div", { className: 'glass-p-4 glass-radius-lg glass-gradient-primary glass-gradient-primary glass-gradient-primary dark:glass-gradient-primary dark:glass-gradient-primary', children: [jsx("h4", { className: 'font-medium glass-text-secondary dark:text-primary mb-2', children: "Interactive Element" }), jsx("p", { className: 'glass-text-sm glass-text-secondary dark:text-gray-300 mb-3', children: "Hover over this card to see the interactive effects in action." }), jsx("button", { className: 'glass-px-4 glass-py-2 glass-surface-blue text-primary glass-radius-lg hover:glass-surface-blue transition-colors', children: "Interactive Button" })] })] }) })] }); } // Helper function for effect icons (used in showcase) function getEffectIcon(effect) { switch (effect) { case "frost": return jsx(Sparkles, { className: 'w-4 h-4' }); case "caustics": return jsx(Sun, { className: 'w-4 h-4' }); case "border": return jsx(Layers, { className: 'w-4 h-4' }); case "refraction": return jsx(Droplets, { className: 'w-4 h-4' }); default: return jsx(Zap, { className: 'w-4 h-4' }); } } export { HoudiniGlassCard, HoudiniGlassShowcase, HoudiniGlassCard as default }; //# sourceMappingURL=HoudiniGlassCard.js.map