aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
326 lines (323 loc) • 12.8 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { forwardRef, useState, useEffect, useCallback } from 'react';
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 { MotionFramer } from '../../primitives/motion/MotionFramer.js';
import { cn } from '../../lib/utilsComprehensive.js';
import { useA11yId } from '../../utils/a11y.js';
import { useMotionPreferenceContext } from '../../contexts/MotionPreferenceContext.js';
import { useGlassSound } from '../../utils/soundDesign.js';
const defaultMoodStates = [{
name: "Happy",
color: "#FFD700",
intensity: 0.8,
description: "Feeling joyful and content"
}, {
name: "Calm",
color: "#87CEEB",
intensity: 0.6,
description: "Peaceful and relaxed"
}, {
name: "Energetic",
color: "#FF4500",
intensity: 0.9,
description: "Full of energy and excitement"
}, {
name: "Focused",
color: "#9370DB",
intensity: 0.7,
description: "Concentrated and determined"
}, {
name: "Stressed",
color: "#DC143C",
intensity: 0.8,
description: "Feeling overwhelmed or tense"
}, {
name: "Creative",
color: "#FF69B4",
intensity: 0.75,
description: "In a creative and imaginative state"
}, {
name: "Melancholic",
color: "#4169E1",
intensity: 0.5,
description: "Reflective and somewhat sad"
}, {
name: "Excited",
color: "#32CD32",
intensity: 0.85,
description: "Thrilled and anticipatory"
}, {
name: "Anxious",
color: "#FF6347",
intensity: 0.7,
description: "Worried or apprehensive"
}, {
name: "Peaceful",
color: "#98FB98",
intensity: 0.4,
description: "Completely at ease and serene"
}];
const GlassMoodRing = /*#__PURE__*/forwardRef(({
mood,
moodStates = defaultMoodStates,
size = "md",
interactive = true,
animated = true,
transitionDuration = 1000,
showLabels = true,
showDescription = false,
onMoodChange,
autoTransition,
biometricIntegration = false,
ambientResponse = false,
thickness = "medium",
glowIntensity = "medium",
pulse = false,
respectMotionPreference = true,
className,
...props
}, ref) => {
const {
prefersReducedMotion
} = useMotionPreferenceContext();
const {
play
} = useGlassSound();
const moodRingId = useA11yId("glass-mood-ring");
const [currentMood, setCurrentMood] = useState(mood || moodStates[0]);
const [isTransitioning, setIsTransitioning] = useState(false);
const [autoTransitionActive, setAutoTransitionActive] = useState(false);
const sizeConfig = {
sm: {
ring: "w-16 h-16",
center: "w-8 h-8",
text: "glass-text-xs",
description: "glass-text-xs",
glow: "shadow-lg"
},
md: {
ring: "w-24 h-24",
center: "w-12 h-12",
text: "glass-text-sm",
description: "glass-text-sm",
glow: "shadow-xl"
},
lg: {
ring: "w-32 h-32",
center: "w-16 h-16",
text: "glass-text-base",
description: "glass-text-base",
glow: "shadow-2xl"
},
xl: {
ring: "w-40 h-40",
center: "w-20 h-20",
text: "glass-text-lg",
description: "glass-text-lg",
glow: "shadow-2xl"
}
};
const thicknessConfig = {
thin: "border-2",
medium: "border-4",
thick: "border-8"
};
const glowConfig = {
none: "",
subtle: "shadow-sm",
medium: "shadow-lg shadow-current/30",
strong: "shadow-2xl shadow-current/50"
};
const config = sizeConfig[size];
// Auto-transition effect
useEffect(() => {
if (!autoTransition || !autoTransitionActive) return;
const interval = setInterval(() => {
const currentIndex = moodStates.findIndex(m => m.name === currentMood.name);
const nextIndex = (currentIndex + 1) % moodStates.length;
handleMoodChange(moodStates[nextIndex]);
}, autoTransition);
return () => clearInterval(interval);
}, [autoTransition, autoTransitionActive, currentMood, moodStates]);
// Biometric integration simulation
useEffect(() => {
if (!biometricIntegration) return;
// Simulate biometric data affecting mood
const biometricInterval = setInterval(() => {
// This would normally connect to actual biometric sensors
const randomMood = moodStates[Math.floor(Math.random() * moodStates.length)];
if (Math.random() > 0.8) {
// 20% chance to change
handleMoodChange(randomMood);
}
}, 10000);
return () => clearInterval(biometricInterval);
}, [biometricIntegration, moodStates]);
// Ambient response simulation
useEffect(() => {
if (!ambientResponse) return;
// Simulate environmental factors affecting mood
const ambientInterval = setInterval(() => {
const hour = new Date().getHours();
let ambientMood = currentMood;
// Simulate time of day effects
if (hour >= 6 && hour < 12) {
ambientMood = moodStates.find(m => m.name === "Energetic") || currentMood;
} else if (hour >= 12 && hour < 18) {
ambientMood = moodStates.find(m => m.name === "Focused") || currentMood;
} else if (hour >= 18 && hour < 22) {
ambientMood = moodStates.find(m => m.name === "Calm") || currentMood;
} else {
ambientMood = moodStates.find(m => m.name === "Peaceful") || currentMood;
}
if (ambientMood !== currentMood) {
handleMoodChange(ambientMood);
}
}, 60000); // Check every minute
return () => clearInterval(ambientInterval);
}, [ambientResponse, currentMood, moodStates]);
// Handle mood changes
const handleMoodChange = useCallback(newMood => {
if (newMood.name === currentMood.name) return;
setIsTransitioning(true);
setTimeout(() => {
setCurrentMood(newMood);
setIsTransitioning(false);
onMoodChange?.(newMood);
play("mood_change");
}, animated ? transitionDuration / 2 : 0);
}, [currentMood, animated, transitionDuration, onMoodChange, play]);
// Handle ring click
const handleRingClick = useCallback(() => {
if (!interactive) return;
const currentIndex = moodStates.findIndex(m => m.name === currentMood.name);
const nextIndex = (currentIndex + 1) % moodStates.length;
handleMoodChange(moodStates[nextIndex]);
}, [interactive, moodStates, currentMood, handleMoodChange]);
// Toggle auto-transition
const toggleAutoTransition = useCallback(() => {
setAutoTransitionActive(!autoTransitionActive);
play("toggle");
}, [autoTransitionActive, play]);
return jsx(OptimizedGlassCore, {
ref: ref,
id: moodRingId,
elevation: "level2",
intensity: "medium",
depth: 2,
tint: "neutral",
border: "subtle",
className: cn("glass-mood-ring relative inline-flex flex-col items-center justify-center glass-p-6 glass-radius-lg", "glass-backdrop-blur-md border border-border/20", className),
...props,
children: jsxs(MotionFramer, {
preset: !prefersReducedMotion && respectMotionPreference ? "fadeIn" : "none",
className: "glass-flex glass-flex-col glass-items-center glass-gap-4",
children: [jsxs("div", {
className: 'relative',
children: [jsxs(MotionFramer, {
as: "div",
preset: !prefersReducedMotion && respectMotionPreference ? "scaleIn" : "none",
className: cn("relative glass-radius-full border-solid transition-all duration-1000", config.ring, thicknessConfig[thickness], glowConfig[glowIntensity], interactive && "cursor-pointer hover:scale-105", pulse && !prefersReducedMotion && "animate-pulse", isTransitioning && "opacity-50"),
style: {
borderColor: currentMood.color,
boxShadow: glowIntensity !== "none" ? `0 0 20px ${currentMood.color}40` : undefined
},
onClick: handleRingClick,
role: interactive ? "button" : "status",
"aria-label": `Current mood: ${currentMood.name} - ${currentMood.description}`,
tabIndex: interactive ? 0 : -1,
onKeyDown: e => {
if ((e.key === "Enter" || e.key === " ") && interactive) {
e.preventDefault();
handleRingClick();
}
},
children: [jsx("div", {
className: cn("absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2", "glass-radius-full flex items-center justify-center transition-all duration-1000", config.center, !prefersReducedMotion && "animate-pulse"),
style: {
backgroundColor: `${currentMood.color}${Math.round(currentMood.intensity * 255).toString(16)}`,
boxShadow: `inset 0 0 10px ${currentMood.color}80`
},
children: currentMood.icon && jsx("div", {
className: 'text-primary/90',
children: currentMood.icon
})
}), Array.from({
length: 3
}, (_, i) => jsx("div", {
className: cn("absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2", "glass-radius-full border transition-all duration-1000", !prefersReducedMotion && "animate-pulse"),
style: {
width: `${60 + i * 20}%`,
height: `${60 + i * 20}%`,
borderColor: `${currentMood.color}${Math.round((currentMood.intensity - i * 0.2) * 255).toString(16)}`,
animationDelay: `${i * 200}ms`,
opacity: Math.max(0, currentMood.intensity - i * 0.3)
}
}, i))]
}), isTransitioning && jsx("div", {
className: cn("absolute inset-0 glass-radius-full bg-gradient-radial from-white/20 to-transparent", "animate-spin")
})]
}), jsxs("div", {
className: 'text-center glass-gap-2',
children: [showLabels && jsx(MotionFramer, {
preset: !prefersReducedMotion && respectMotionPreference ? "slideUp" : "none",
className: cn("font-semibold transition-colors duration-500", config.text),
style: {
color: currentMood.color
},
children: currentMood.name
}), showDescription && jsx(MotionFramer, {
preset: !prefersReducedMotion && respectMotionPreference ? "slideUp" : "none",
delay: 100,
className: cn("glass-text-secondary max-w-xs text-center", config.description),
children: currentMood.description
})]
}), interactive && moodStates.length > 1 && jsx(MotionFramer, {
preset: !prefersReducedMotion && respectMotionPreference ? "slideUp" : "none",
delay: 200,
className: 'glass-flex glass-flex-wrap glass-gap-1 glass-justify-center max-w-xs',
children: moodStates.map((mood, index) => jsx("button", {
onClick: () => handleMoodChange(mood),
className: cn("w-6 h-6 glass-radius-full border-2 transition-all duration-200", "hover:scale-110 focus:outline-none focus:ring-2 focus:ring-offset-2", currentMood.name === mood.name && "ring-2 ring-white", "focus:ring-white/50"),
style: {
backgroundColor: mood.color,
borderColor: currentMood.name === mood.name ? "white" : mood.color
},
title: mood.name,
"aria-label": `Select ${mood.name} mood`
}, mood.name))
}), (autoTransition || biometricIntegration || ambientResponse) && jsxs(MotionFramer, {
preset: !prefersReducedMotion && respectMotionPreference ? "slideUp" : "none",
delay: 300,
className: "glass-flex glass-gap-2 glass-items-center glass-text-xs glass-text-secondary",
children: [autoTransition && jsxs("button", {
onClick: toggleAutoTransition,
className: cn("glass-px-2 glass-py-1 glass-radius-md border transition-colors", autoTransitionActive ? "bg-primary/20 border-primary/40 text-primary" : "border-border/40 hover:border-border"),
children: ["Auto ", autoTransitionActive ? "On" : "Off"]
}), biometricIntegration && jsxs("div", {
className: "glass-flex glass-items-center glass-gap-1",
children: [jsx("div", {
className: 'w-2 h-2 glass-surface-green glass-radius-full animate-pulse'
}), jsx("span", {
children: "Bio"
})]
}), ambientResponse && jsxs("div", {
className: "glass-flex glass-items-center glass-gap-1",
children: [jsx("div", {
className: 'w-2 h-2 glass-surface-blue glass-radius-full animate-pulse'
}), jsx("span", {
children: "Ambient"
})]
})]
})]
})
});
});
GlassMoodRing.displayName = "GlassMoodRing";
export { GlassMoodRing, GlassMoodRing as default };
//# sourceMappingURL=GlassMoodRing.js.map