aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
1,554 lines (1,512 loc) • 91.5 kB
JavaScript
'use client';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import * as THREE from 'three';
import { Canvas, useThree, useFrame } from '@react-three/fiber';
import { AnimatePresence, motion } from 'framer-motion';
import { RotateCcw, Zap, Triangle, Pause, Play, Wind, Cloud, Leaf, Sun, Flower, Snowflake, Flame, Sparkles, Star, Palette, Waves, AlertCircle, Loader2, EyeOff, Eye, Info, Hand } from 'lucide-react';
import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
/**
* AuraGlass Utility Functions
* Production-ready utility functions for the AuraGlass design system
*/
// Class name utility function
function cn(...inputs) {
return twMerge(clsx(inputs));
}
/**
* Detects whether the current React runtime exposes the React 19+ reconciler internals.
* React 18 does not define the `S` slot, which newer reconcilers rely on.
*/
const isReact19OrNewer = () => {
const internals =
// @ts-ignore - accessing React internals on purpose to detect React 19 shape
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
return Boolean(internals && typeof internals === "object" && "S" in internals);
};
const assertReact19ForThree = () => {
if (!isReact19OrNewer()) {
if (process.env.NODE_ENV !== "production") {
// eslint-disable-next-line no-console
console.warn("[AuraGlass] 3D glass effects require React 19+. Running under React 18 – three-based effects are disabled.");
}
return false;
}
return true;
};
const shatterPresets = {
gentle: {
duration: 1.5,
intensity: 0.5,
shardCount: 8,
autoReform: true,
reformDelay: 2000
},
dramatic: {
duration: 2.5,
intensity: 1.2,
shardCount: 16,
autoReform: true,
reformDelay: 4000
},
explosive: {
duration: 3,
intensity: 2,
shardCount: 24,
autoReform: false,
reformDelay: 6000
},
subtle: {
duration: 1,
intensity: 0.3,
shardCount: 6,
autoReform: true,
reformDelay: 1500
}
};
let LoadedImpl$3 = null;
function GlassShatterEffects(props) {
const [Impl, setImpl] = useState(LoadedImpl$3);
useEffect(() => {
let cancelled = false;
if (!assertReact19ForThree()) {
return;
}
if (!Impl) {
Promise.resolve().then(function () { return GlassShatterEffects_r3f; }).then(mod => {
if (cancelled) return;
const Component = mod.GlassShatterEffectsR3F || mod.default;
LoadedImpl$3 = Component;
setImpl(() => Component);
}).catch(error => {
if (process.env.NODE_ENV !== "production") {
// eslint-disable-next-line no-console
console.error("[AuraGlass] Failed to load GlassShatterEffects R3F module", error);
}
});
}
return () => {
cancelled = true;
};
}, [Impl]);
if (!Impl) {
const {
className,
children,
trigger = "click",
...rest
} = props;
return jsx("div", {
className: cn("glass-shatter-effects glass-relative glass-overflow-hidden", className),
style: {
position: "relative",
cursor: trigger === "click" ? "pointer" : "default"
},
...rest,
children: jsx("div", {
className: cn("content glass-transition-opacity glass-duration-300", "glass-opacity-100"),
children: children
})
});
}
return jsx(Impl, {
...props
});
}
const seasonalPresets = {
gentle: {
particleCount: 20,
windStrength: 0.5,
animationSpeed: 0.8,
seasonDuration: 15000
},
lively: {
particleCount: 40,
windStrength: 1.2,
animationSpeed: 1.2,
seasonDuration: 10000
},
dramatic: {
particleCount: 60,
windStrength: 2.0,
animationSpeed: 1.5,
seasonDuration: 8000
},
subtle: {
particleCount: 15,
windStrength: 0.3,
animationSpeed: 0.6,
seasonDuration: 20000
}
};
const seasonalThemes = {
winter: {
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
glassColor: "var(--glass-bg-default)",
accentColor: "#e3f2fd"
},
spring: {
background: "linear-gradient(135deg, #f093fb 0%, #f5576c 100%)",
glassColor: "var(--glass-bg-disabled)",
accentColor: "#fce4ec"
},
summer: {
background: "linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)",
glassColor: "var(--glass-bg-default)",
accentColor: "#fff3e0"
},
autumn: {
background: "linear-gradient(135deg, #fa709a 0%, #fee140 100%)",
glassColor: "rgba(255, 255, 255, 0.12)",
accentColor: "#efebe9"
}
};
let LoadedImpl$2 = null;
function SeasonalParticles(props) {
const [Impl, setImpl] = useState(LoadedImpl$2);
useEffect(() => {
let cancelled = false;
if (!assertReact19ForThree()) {
return;
}
if (!Impl) {
Promise.resolve().then(function () { return SeasonalParticles_r3f; }).then(mod => {
if (cancelled) return;
const Component = mod.SeasonalParticlesR3F || mod.default;
LoadedImpl$2 = Component;
setImpl(() => Component);
}).catch(error => {
if (process.env.NODE_ENV !== "production") {
// eslint-disable-next-line no-console
console.error("[AuraGlass] Failed to load SeasonalParticles R3F module", error);
}
});
}
return () => {
cancelled = true;
};
}, [Impl]);
if (!Impl) {
const {
className,
children,
...rest
} = props;
return jsx("div", {
className: cn("seasonal-particles glass-relative glass-overflow-hidden", className),
...rest,
children: jsx("div", {
className: cn("glass-relative glass-z-10"),
children: children
})
});
}
return jsx(Impl, {
...props
});
}
const auroraPresets = {
subtle: {
intensity: 0.6,
speed: 0.5,
particleCount: 30,
colorPalette: "arctic",
animationMode: "flow"
},
dynamic: {
intensity: 1.0,
speed: 1.0,
particleCount: 50,
colorPalette: "cosmic",
animationMode: "mixed"
},
intense: {
intensity: 1.5,
speed: 1.5,
particleCount: 80,
colorPalette: "sunset",
animationMode: "pulse"
},
serene: {
intensity: 0.4,
speed: 0.3,
particleCount: 20,
colorPalette: "ocean",
animationMode: "shift"
}
};
const auroraThemes = {
northern: {
background: "linear-gradient(180deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%)",
glassColor: "rgba(79, 195, 247, 0.1)",
accentColor: "#4fc3f7"
},
mystical: {
background: "linear-gradient(180deg, #0f0f23 0%, #1a1a2e 50%, #2d1b69 100%)",
glassColor: "rgba(186, 104, 200, 0.1)",
accentColor: "#ba68c8"
},
tropical: {
background: "linear-gradient(180deg, #0c0c0c 0%, #1a1a2e 50%, #0f3460 100%)",
glassColor: "rgba(0, 188, 212, 0.1)",
accentColor: "#00bcd4"
},
enchanted: {
background: "linear-gradient(180deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)",
glassColor: "rgba(255, 152, 77, 0.1)",
accentColor: "#ff9843"
}
};
let LoadedImpl$1 = null;
function AuroraPro$1(props) {
const [Impl, setImpl] = useState(LoadedImpl$1);
useEffect(() => {
let cancelled = false;
if (!assertReact19ForThree()) {
return;
}
if (!Impl) {
Promise.resolve().then(function () { return AuroraPro_r3f; }).then(mod => {
if (cancelled) return;
const Component = mod.AuroraPro || mod.default;
LoadedImpl$1 = Component;
setImpl(() => Component);
}).catch(error => {
if (process.env.NODE_ENV !== "production") {
// eslint-disable-next-line no-console
console.error("[AuraGlass] Failed to load AuroraPro R3F module", error);
}
});
}
return () => {
cancelled = true;
};
}, [Impl]);
if (!Impl) {
const {
className,
children,
...rest
} = props;
return jsx("div", {
className: cn("aurora-pro glass-relative glass-overflow-hidden", className),
...rest,
children: children
});
}
return jsx(Impl, {
...props
});
}
const ARGlassMaterialFactory = {
createSpatialUI: options => {
return new THREE.ShaderMaterial({
uniforms: {
time: {
value: 0
},
userPosition: {
value: new THREE.Vector3()
},
interactionRadius: {
value: options.interactionRadius || 3.0
},
glowIntensity: {
value: options.glowIntensity || 1.0
},
color: {
value: options.color || new THREE.Color(0.3, 0.7, 1.0)
},
opacity: {
value: options.opacity || 0.8
}
},
vertexShader: `
varying vec3 vPosition;
varying vec3 vNormal;
varying vec3 vUserPosition;
void main() {
vPosition = position;
vNormal = normal;
vUserPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 userPosition;
uniform float interactionRadius;
uniform float glowIntensity;
uniform vec3 color;
uniform float opacity;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec3 vUserPosition;
void main() {
float distance = length(vUserPosition - userPosition);
float interaction = 1.0 - smoothstep(0.0, interactionRadius, distance);
vec3 finalColor = color + (glowIntensity * interaction * vec3(0.5, 0.8, 1.0));
float finalOpacity = opacity * (0.8 + 0.2 * interaction);
gl_FragColor = vec4(finalColor, finalOpacity);
}
`,
transparent: true,
side: THREE.DoubleSide
});
},
createHolographicGlass: options => {
return new THREE.ShaderMaterial({
uniforms: {
time: {
value: 0
},
color: {
value: options.color || new THREE.Color(0.6, 0.8, 1.0)
},
opacity: {
value: options.opacity || 0.9
},
fresnelPower: {
value: options.fresnelPower || 1.5
}
},
vertexShader: `
varying vec3 vNormal;
varying vec3 vViewDirection;
void main() {
vNormal = normalize(normalMatrix * normal);
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
vViewDirection = normalize(-mvPosition.xyz);
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform float time;
uniform vec3 color;
uniform float opacity;
uniform float fresnelPower;
varying vec3 vNormal;
varying vec3 vViewDirection;
void main() {
float fresnel = pow(1.0 - dot(vNormal, vViewDirection), fresnelPower);
vec3 finalColor = color * (0.5 + 0.5 * fresnel);
float finalOpacity = opacity * (0.7 + 0.3 * fresnel);
gl_FragColor = vec4(finalColor, finalOpacity);
}
`,
transparent: true,
side: THREE.DoubleSide
});
},
createDataVisualization: options => {
return new THREE.ShaderMaterial({
uniforms: {
time: {
value: 0
},
lowColor: {
value: options.lowColor || new THREE.Color(0.2, 0.4, 1.0)
},
highColor: {
value: options.highColor || new THREE.Color(1.0, 0.4, 0.2)
},
dataValue: {
value: options.dataValue || 0.5
}
},
vertexShader: `
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 lowColor;
uniform vec3 highColor;
uniform float dataValue;
varying vec3 vPosition;
void main() {
float height = vPosition.y;
vec3 finalColor = mix(lowColor, highColor, dataValue);
// Add some animation
float pulse = sin(time * 2.0) * 0.1 + 0.9;
finalColor *= pulse;
gl_FragColor = vec4(finalColor, 0.8);
}
`,
transparent: true
});
}
};
const ARGlassGeometryFactory = {
createCurvedPanel: (width, height, depth) => {
const geometry = new THREE.PlaneGeometry(width, height, 32, 32);
const positions = geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
positions[i + 2] = Math.sin(x * 0.5) * Math.cos(y * 0.5) * depth;
}
geometry.computeVertexNormals();
return geometry;
},
createPortalGeometry: (innerRadius, outerRadius) => {
const geometry = new THREE.RingGeometry(innerRadius, outerRadius, 32);
const positions = geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const angle = Math.atan2(positions[i + 1], positions[i]);
positions[i + 2] = Math.sin(angle * 4) * 0.1;
}
geometry.computeVertexNormals();
return geometry;
},
createParticleField: count => {
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(count * 3);
const colors = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
positions[i * 3] = (Math.random() - 0.5) * 10;
positions[i * 3 + 1] = (Math.random() - 0.5) * 10;
positions[i * 3 + 2] = (Math.random() - 0.5) * 10;
colors[i * 3] = Math.random();
colors[i * 3 + 1] = Math.random();
colors[i * 3 + 2] = Math.random();
}
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
return geometry;
}
};
const ARGlassAnimations = {
createFloatingAnimation: (object, amplitude = 0.05, frequency = 0.5) => {
return time => {
object.position.y += Math.sin(time * frequency) * amplitude;
};
},
createRotationAnimation: (object, axis, speed = 1) => {
return time => {
object.rotateOnAxis(axis, speed * 0.01);
};
},
createScaleAnimation: (object, minScale, maxScale, frequency = 2) => {
return time => {
const scale = minScale + (maxScale - minScale) * (Math.sin(time * frequency) * 0.5 + 0.5);
object.scale.setScalar(scale);
};
}
};
const ARGlassInteractions = {
createHapticFeedback: (intensity = 0.5, duration = 100) => {
if (typeof navigator !== "undefined" && "vibrate" in navigator) {
navigator.vibrate(duration * intensity);
}
},
setupHandTracking: (objects, callback) => {
const handleHandMove = handData => {
objects.forEach(obj => {
if (obj && handData.left.isActive) {
const distance = obj.position.distanceTo(handData.left.position);
if (distance < 0.5) {
callback(obj, handData.left);
}
}
});
};
return handleHandMove;
}
};
const ARGlassUtils = {
createAdaptiveScaling: (minScale, maxScale) => {
return distance => {
const normalizedDistance = Math.min(Math.max(distance, 1), 10);
const scale = maxScale - (normalizedDistance - 1) / 9 * (maxScale - minScale);
return Math.max(scale, minScale);
};
},
createDistanceBasedOpacity: (maxDistance = 5) => {
return distance => {
return Math.max(0, 1 - distance / maxDistance);
};
}
};
let LoadedImpl = null;
function ARGlassEffects$1(props) {
const [Impl, setImpl] = useState(LoadedImpl);
useEffect(() => {
let cancelled = false;
if (!assertReact19ForThree()) {
return;
}
if (!Impl) {
Promise.resolve().then(function () { return ARGlassEffects_r3f; }).then(mod => {
if (cancelled) return;
const Component = mod.ARGlassEffects || mod.default;
LoadedImpl = Component;
setImpl(() => Component);
}).catch(error => {
if (process.env.NODE_ENV !== "production") {
// eslint-disable-next-line no-console
console.error("[AuraGlass] Failed to load ARGlassEffects R3F module", error);
}
});
}
return () => {
cancelled = true;
};
}, [Impl]);
if (!Impl) {
const {
className,
children
} = props;
return jsx("div", {
className: cn("ar-glass-effects glass-foundation-complete relative", className),
children: children
});
}
return jsx(Impl, {
...props
});
}
/**
* Environment helpers to centralize SSR/browser detection and safe global access.
*
* CRITICAL: These must check dynamically, NOT cache at module load time,
* to handle polyfills and avoid locking in server environment values.
*/
/**
* Dynamic check - re-evaluates on every call to support polyfills
*/
const hasWindow = () => typeof window !== 'undefined';
const hasDocument = () => typeof document !== 'undefined';
/**
* Returns true when executed in a browser-like environment.
*/
const isBrowser = () => hasWindow() && hasDocument();
/**
* Safely access the `window` object when available.
*/
const getSafeWindow = () => hasWindow() ? window : undefined;
/**
* Helper to guard feature detection against SSR environments.
*/
const safeMatchMedia = query => {
const win = getSafeWindow();
return win?.matchMedia ? win.matchMedia(query) : undefined;
};
/**
* Hook to detect if user prefers reduced motion
*
* CRITICAL: Defaults to `false` (no reduced motion) on both server and initial client render
* to prevent SSR hydration mismatches. The actual preference is detected after hydration.
*/
function useReducedMotion() {
// CRITICAL FIX: Start with `false` on both server and client to prevent hydration mismatch
// The server cannot access matchMedia, so we default to false (motion allowed)
// This matches the first client render, preventing inline style mismatches
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
useEffect(() => {
// Detect actual motion preference after hydration
if (!isBrowser()) return;
const mediaQuery = safeMatchMedia('(prefers-reduced-motion: reduce)');
if (!mediaQuery) return;
// Update to actual preference
setPrefersReducedMotion(mediaQuery.matches);
const handleChange = event => {
setPrefersReducedMotion(event.matches);
};
if (typeof mediaQuery.addEventListener === 'function') {
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}
if (typeof mediaQuery.addListener === 'function') {
mediaQuery.addListener(handleChange);
return () => mediaQuery.removeListener(handleChange);
}
}, []);
return prefersReducedMotion;
}
/**
* Simple deterministic pseudo-random number generator for SSR consistency.
*/
class SeededRandom {
constructor(seed = Date.now()) {
this.state = SeededRandom.normalizeSeed(seed);
}
static normalizeSeed(seed) {
if (typeof seed === 'number' && Number.isFinite(seed)) {
return SeededRandom.hash(seed);
}
if (typeof seed === 'string') {
let hash = 0;
for (let i = 0; i < seed.length; i++) {
hash = (hash << 5) - hash + seed.charCodeAt(i);
hash |= 0; // Convert to 32-bit integer
}
return SeededRandom.hash(hash);
}
return SeededRandom.hash(Date.now());
}
static hash(input) {
let x = input | 0;
x ^= x >>> 16;
x = Math.imul(x, 0x7feb352d);
x ^= x >>> 15;
x = Math.imul(x, 0x846ca68b);
x ^= x >>> 16;
return x >>> 0;
}
next() {
// LCG parameters from Numerical Recipes
this.state = 1664525 * this.state + 1013904223 >>> 0;
return this.state / 0xffffffff;
}
nextInRange(min, max) {
return min + (max - min) * this.next();
}
nextInt(min, max) {
return Math.floor(this.nextInRange(min, max + 1));
}
nextSigned() {
return this.next() * 2 - 1;
}
}
// Shatter geometry factory
const ShatterGeometryFactory = {
createGlassShard: (size = 1, complexity = 3, random = new SeededRandom()) => {
const geometry = new THREE.PlaneGeometry(size, size, complexity, complexity);
const positions = geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
const edgeFactor = Math.abs(x) + Math.abs(y);
if (edgeFactor > 0.8) {
positions[i] += random.nextSigned() * 0.1;
positions[i + 1] += random.nextSigned() * 0.1;
positions[i + 2] += random.nextSigned() * 0.05;
}
}
geometry.computeVertexNormals();
return geometry;
},
createShatterField: (count = 20, _spread = 5, random = new SeededRandom()) => {
const geometries = [];
for (let i = 0; i < count; i++) {
const size = 0.5 + random.next() * 1.5;
const complexity = 2 + random.nextInt(0, 2);
const geometry = ShatterGeometryFactory.createGlassShard(size, complexity, random);
geometries.push(geometry);
}
return geometries;
}
};
const ShatterMaterialFactory = {
createGlassShardMaterial: (options = {}) => {
const {
color = new THREE.Color(0.7, 0.9, 1.0),
opacity = 0.8,
refractionRatio = 1.5,
reflectivity = 0.8
} = options;
return new THREE.ShaderMaterial({
uniforms: {
time: {
value: 0
},
color: {
value: color
},
opacity: {
value: opacity
},
refractionRatio: {
value: refractionRatio
},
reflectivity: {
value: reflectivity
},
shatterProgress: {
value: 0
}
},
vertexShader: `
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
vPosition = position;
vNormal = normal;
vUv = uv;
vec3 displacedPosition = position;
displacedPosition += normal * sin(time * 10.0 + position.x * 5.0) * 0.02;
gl_Position = projectionMatrix * modelViewMatrix * vec4(displacedPosition, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 color;
uniform float opacity;
uniform float refractionRatio;
uniform float reflectivity;
uniform float shatterProgress;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
float fresnel = pow(1.0 - dot(vNormal, vec3(0.0, 0.0, 1.0)), 2.0);
vec3 finalColor = color;
finalColor += vec3(
sin(vPosition.x * 10.0) * 0.1,
cos(vPosition.y * 10.0) * 0.1,
sin(vPosition.z * 10.0) * 0.1
);
float shatterEffect = sin(time * 20.0 + vPosition.x * 5.0 + vPosition.y * 5.0) * 0.5 + 0.5;
finalColor *= (1.0 - shatterProgress * 0.3);
float finalOpacity = opacity * (0.7 + 0.3 * fresnel) * (1.0 - shatterProgress * 0.2);
gl_FragColor = vec4(finalColor, finalOpacity);
}
`,
transparent: true,
side: THREE.DoubleSide
});
}
};
const ShatterAnimations = {
createShardAnimation: (shard, targetPosition, targetRotation, duration = 2) => {
const startPosition = shard.position.clone();
const startRotation = shard.rotation.clone();
const startTime = Date.now();
return () => {
const elapsed = (Date.now() - startTime) / 1000;
const progress = Math.min(elapsed / duration, 1);
const easeOut = t => 1 - Math.pow(1 - t, 3);
const easedProgress = easeOut(progress);
shard.position.lerpVectors(startPosition, targetPosition, easedProgress);
shard.rotation.x = THREE.MathUtils.lerp(startRotation.x, targetRotation.x, easedProgress);
shard.rotation.y = THREE.MathUtils.lerp(startRotation.y, targetRotation.y, easedProgress);
shard.rotation.z = THREE.MathUtils.lerp(startRotation.z, targetRotation.z, easedProgress);
return progress >= 1;
};
},
createExplosionAnimation: (shards, center, force = 10, duration = 1) => {
useReducedMotion();
const animations = shards.map(shard => {
const direction = new THREE.Vector3((Math.random() - 0.5) * 2, (Math.random() - 0.5) * 2, (Math.random() - 0.5) * 2).normalize();
const distance = 2 + Math.random() * 3;
const targetPosition = center.clone().add(direction.multiplyScalar(distance));
const targetRotation = new THREE.Euler(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
return ShatterAnimations.createShardAnimation(shard, targetPosition, targetRotation, duration);
});
return () => animations.every(animation => animation());
},
createReformAnimation: (shards, originalPositions, duration = 2) => {
const animations = shards.map((shard, index) => {
const targetPosition = originalPositions[index];
const targetRotation = new THREE.Euler(0, 0, 0);
return ShatterAnimations.createShardAnimation(shard, targetPosition, targetRotation, duration);
});
return () => animations.every(animation => animation());
}
};
function GlassShatterEffectsR3F(props) {
const {
children,
className = "",
trigger = "click",
duration = 2,
intensity = 1,
shardCount = 12,
autoReform = true,
reformDelay = 3000,
onShatter,
onReform,
disabled = false,
showControls = false,
seed
} = props;
const prefersReducedMotion = useReducedMotion();
const containerRef = useRef(null);
const canvasRef = useRef(null);
const [isShattered, setIsShattered] = useState(false);
const [isAnimating, setIsAnimating] = useState(false);
const [shards, setShards] = useState([]);
const [originalPositions, setOriginalPositions] = useState([]);
const [currentAnimation, setCurrentAnimation] = useState(null);
useEffect(() => {
if (!canvasRef.current) return;
const seededRandom = new SeededRandom(seed ?? `${shardCount}`);
const geometries = ShatterGeometryFactory.createShatterField(shardCount, 5, seededRandom);
const newShards = [];
const positions = [];
geometries.forEach(geometry => {
const material = ShatterMaterialFactory.createGlassShardMaterial({
opacity: 0.8 - seededRandom.next() * 0.3,
refractionRatio: 1.3 + seededRandom.next() * 0.4
});
const shard = new THREE.Mesh(geometry, material);
shard.position.set(seededRandom.nextSigned() * 2, seededRandom.nextSigned() * 2, 0);
shard.rotation.set(seededRandom.next() * Math.PI * 2, seededRandom.next() * Math.PI * 2, seededRandom.next() * Math.PI * 2);
newShards.push(shard);
positions.push(shard.position.clone());
});
setShards(newShards);
setOriginalPositions(positions);
}, [shardCount, seed]);
const triggerReform = useCallback(() => {
if (!isShattered || isAnimating) return;
setIsAnimating(true);
onReform?.();
const animation = ShatterAnimations.createReformAnimation(shards, originalPositions, duration);
setCurrentAnimation(() => animation);
}, [isShattered, isAnimating, shards, originalPositions, duration, onReform]);
const triggerShatter = useCallback(() => {
if (disabled || isAnimating) return;
setIsShattered(true);
setIsAnimating(true);
onShatter?.();
const center = new THREE.Vector3(0, 0, 0);
const animation = ShatterAnimations.createExplosionAnimation(shards, center, intensity * 10, duration);
setCurrentAnimation(() => animation);
if (autoReform) {
setTimeout(() => {
triggerReform();
}, reformDelay);
}
}, [disabled, isAnimating, shards, intensity, duration, autoReform, reformDelay, onShatter, triggerReform]);
const handleManualTrigger = useCallback(() => {
if (isShattered) {
triggerReform();
} else {
triggerShatter();
}
}, [isShattered, triggerReform, triggerShatter]);
useEffect(() => {
if (disabled) return;
const element = containerRef.current;
if (!element) return;
const handleClick = () => {
if (trigger === "click") handleManualTrigger();
};
const handleMouseEnter = () => {
if (trigger === "hover") triggerShatter();
};
const handleMouseLeave = () => {
if (trigger === "hover" && autoReform) {
setTimeout(() => triggerReform(), reformDelay);
}
};
if (trigger === "click") {
element.addEventListener("click", handleClick);
} else if (trigger === "hover") {
element.addEventListener("mouseenter", handleMouseEnter);
element.addEventListener("mouseleave", handleMouseLeave);
}
return () => {
element.removeEventListener("click", handleClick);
element.removeEventListener("mouseenter", handleMouseEnter);
element.removeEventListener("mouseleave", handleMouseLeave);
};
}, [trigger, disabled, handleManualTrigger, triggerShatter, triggerReform, autoReform, reformDelay]);
useEffect(() => {
if (trigger === "auto" && !disabled) {
const interval = setInterval(() => {
if (!isAnimating) triggerShatter();
}, 5000);
return () => clearInterval(interval);
}
}, [trigger, disabled, isAnimating, triggerShatter]);
return jsxs("div", {
ref: containerRef,
className: cn("glass-shatter-effects glass-relative glass-overflow-hidden", className),
style: {
position: "relative",
cursor: trigger === "click" ? "pointer" : "default"
},
children: [jsx("div", {
className: cn("content glass-transition-opacity glass-duration-300", isShattered ? "glass-opacity-0" : "glass-opacity-100"),
children: children
}), jsx(AnimatePresence, {
children: isShattered && jsx(motion.div, {
initial: {
opacity: 0
},
animate: prefersReducedMotion ? {} : {
opacity: 1
},
exit: {
opacity: 0
},
className: cn("glass-absolute glass-inset-0 glass-pointer-events-none"),
children: jsx(Canvas, {
ref: canvasRef,
className: cn("glass-w-full glass-h-full"),
camera: {
position: [0, 0, 5],
fov: 75
},
gl: {
alpha: true,
antialias: true
},
children: jsx(ShatterScene, {
shards: shards,
currentAnimation: currentAnimation,
setCurrentAnimation: setCurrentAnimation,
setIsAnimating: setIsAnimating,
setIsShattered: setIsShattered,
isShattered: isShattered
})
})
})
}), showControls && jsxs(motion.div, {
initial: {
opacity: 0,
y: 20
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
y: 0
},
className: cn("glass-absolute glass-bottom-4 glass-right-4 glass-flex glass-gap-2"),
children: [jsx("button", {
onClick: handleManualTrigger,
disabled: isAnimating,
className: cn("glass-p-2 glass-surface-subtle glass-foundation-complete glass-radius-lg glass-border glass-border-primary hover:glass-surface-hover glass-transition-colors disabled:glass-opacity-50 glass-focus glass-touch-target glass-contrast-guard"),
title: isShattered ? "Reform" : "Shatter",
children: isShattered ? jsx(RotateCcw, {
className: cn("glass-w-4 glass-h-4")
}) : jsx(Zap, {
className: cn("glass-w-4 glass-h-4")
})
}), isAnimating && jsxs("div", {
className: cn("glass-flex glass-items-center glass-gap-2 glass-px-3 glass-py-2 glass-surface-info glass-foundation-complete glass-radius-lg glass-text-info glass-text-sm"),
children: [jsx(motion.div, {
animate: prefersReducedMotion ? {} : {
rotate: 360
},
transition: prefersReducedMotion ? {
duration: 0
} : {
duration: 1,
repeat: Infinity,
ease: "linear"
},
children: jsx(Triangle, {
className: cn("glass-w-3 glass-h-3")
})
}), "Animating..."]
})]
})]
});
}
function ShatterScene({
shards,
currentAnimation,
setCurrentAnimation,
setIsAnimating,
setIsShattered,
isShattered
}) {
const {
scene
} = useThree();
useEffect(() => {
shards.forEach(shard => {
scene.add(shard);
});
return () => {
shards.forEach(shard => {
scene.remove(shard);
});
};
}, [shards, scene]);
useFrame(state => {
const time = state.clock.elapsedTime;
shards.forEach(shard => {
const material = shard.material;
if (material && material.uniforms) {
if (material.uniforms.time) material.uniforms.time.value = time;
if (material.uniforms.shatterProgress) material.uniforms.shatterProgress.value = isShattered ? 1 : 0;
}
});
if (currentAnimation) {
const complete = currentAnimation();
if (complete) {
setCurrentAnimation(null);
setIsAnimating(false);
if (!isShattered) setIsShattered(false);
}
}
});
return jsxs(Fragment, {
children: [jsx("ambientLight", {
intensity: 0.4
}), jsx("pointLight", {
position: [10, 10, 10],
intensity: 0.8
}), jsx("pointLight", {
position: [-10, -10, -10],
intensity: 0.3,
color: 0x4488ff
})]
});
}
var GlassShatterEffects_r3f = /*#__PURE__*/Object.freeze({
__proto__: null,
GlassShatterEffectsR3F: GlassShatterEffectsR3F,
default: GlassShatterEffectsR3F
});
// Seasonal particle systems
const SeasonalParticleFactory = {
// Snow particles
createSnowParticle: (size = 1, complexity = 6) => {
const geometry = new THREE.ConeGeometry(size * 0.1, size * 0.3, complexity);
return geometry;
},
// Leaf particles
createLeafParticle: (size = 1) => {
const geometry = new THREE.PlaneGeometry(size, size * 1.5);
// Add some curvature to make it look like a leaf
const positions = geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
positions[i + 2] = Math.sin(x * 5) * 0.1 * Math.abs(y);
}
geometry.computeVertexNormals();
return geometry;
},
// Flower petals
createPetalParticle: (size = 1) => {
const geometry = new THREE.PlaneGeometry(size, size * 2);
// Curve the petal
const positions = geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
positions[i + 2] = Math.sin(x * 3) * 0.2 * (1 - Math.abs(y));
}
geometry.computeVertexNormals();
return geometry;
},
// Sun rays
createSunRayParticle: (length = 1, width = 0.1) => {
const geometry = new THREE.CylinderGeometry(width, width * 0.5, length, 8);
return geometry;
},
// Rain drops
createRainDropParticle: (length = 1) => {
const geometry = new THREE.CylinderGeometry(0.02, 0.02, length, 6);
return geometry;
},
// Create particle field for season
createSeasonalField: (season, count = 50, random = new SeededRandom()) => {
const particles = [];
for (let i = 0; i < count; i++) {
let geometry;
let material;
switch (season) {
case "winter":
geometry = SeasonalParticleFactory.createSnowParticle(0.5 + random.next() * 0.5);
material = SeasonalParticleFactory.createSnowMaterial();
break;
case "autumn":
geometry = SeasonalParticleFactory.createLeafParticle(0.3 + random.next() * 0.4);
material = SeasonalParticleFactory.createLeafMaterial();
break;
case "spring":
geometry = SeasonalParticleFactory.createPetalParticle(0.2 + random.next() * 0.3);
material = SeasonalParticleFactory.createPetalMaterial(random);
break;
case "summer":
geometry = SeasonalParticleFactory.createSunRayParticle(1 + random.next() * 2);
material = SeasonalParticleFactory.createSunRayMaterial();
break;
default:
geometry = new THREE.SphereGeometry(0.1);
material = new THREE.MeshBasicMaterial({
color: 0xffffff
});
}
const particle = new THREE.Mesh(geometry, material);
particle.position.set(random.nextSigned() * 10, random.next() * 15, random.nextSigned() * 5);
particle.rotation.set(random.next() * Math.PI * 2, random.next() * Math.PI * 2, random.next() * Math.PI * 2);
// Add custom properties for animation
particle.userData = {
originalY: particle.position.y,
fallSpeed: 0.01 + random.next() * 0.05,
rotationSpeed: random.nextSigned() * 0.02,
windOffset: random.next() * Math.PI * 2,
season
};
particles.push(particle);
}
return particles;
},
// Material factories
createSnowMaterial: () => new THREE.MeshPhongMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.8,
shininess: 100
}),
createLeafMaterial: () => new THREE.MeshPhongMaterial({
color: new THREE.Color().setHSL(0.1, 0.8, 0.4),
transparent: true,
opacity: 0.9,
side: THREE.DoubleSide
}),
createPetalMaterial: random => new THREE.MeshPhongMaterial({
color: new THREE.Color().setHSL(random.nextInRange(0.8, 1.0), 0.8, 0.6),
transparent: true,
opacity: 0.8,
side: THREE.DoubleSide
}),
createSunRayMaterial: () => new THREE.MeshBasicMaterial({
color: new THREE.Color(1, 0.9, 0.3),
transparent: true,
opacity: 0.6
}),
createRainMaterial: () => new THREE.MeshBasicMaterial({
color: new THREE.Color(0.5, 0.7, 1),
transparent: true,
opacity: 0.7
})
};
// Seasonal animation system
const SeasonalAnimations = {
// Snow falling animation
snowFall: (particle, time, windStrength = 1) => {
particle.position.y -= particle.userData.fallSpeed;
particle.position.x += Math.sin(time * 2 + particle.userData.windOffset) * 0.01 * windStrength;
particle.rotation.x += particle.userData.rotationSpeed;
particle.rotation.y += particle.userData.rotationSpeed * 0.5;
// Reset particle when it falls below screen
if (particle.position.y < -10) {
particle.position.y = 15 + Math.random() * 5;
particle.position.x = (Math.random() - 0.5) * 20;
}
},
// Leaf falling animation
leafFall: (particle, time, windStrength = 1) => {
particle.position.y -= particle.userData.fallSpeed;
particle.position.x += Math.sin(time * 1.5 + particle.userData.windOffset) * 0.02 * windStrength;
particle.rotation.x += particle.userData.rotationSpeed;
particle.rotation.z += particle.userData.rotationSpeed * 2;
if (particle.position.y < -10) {
particle.position.y = 15 + Math.random() * 5;
particle.position.x = (Math.random() - 0.5) * 20;
}
},
// Petal floating animation
petalFloat: (particle, time, windStrength = 1) => {
particle.position.y -= particle.userData.fallSpeed * 0.5;
particle.position.x += Math.sin(time * 1 + particle.userData.windOffset) * 0.015 * windStrength;
particle.rotation.y += particle.userData.rotationSpeed;
particle.rotation.x += Math.sin(time * 3 + particle.userData.windOffset) * 0.01;
if (particle.position.y < -10) {
particle.position.y = 15 + Math.random() * 5;
particle.position.x = (Math.random() - 0.5) * 20;
}
},
// Sun ray pulsing animation
sunRayPulse: (particle, time) => {
const pulse = Math.sin(time * 4) * 0.3 + 0.7;
particle.scale.y = pulse;
if (particle.material && "opacity" in particle.material) {
particle.material.opacity = 0.3 + pulse * 0.4;
}
},
// Rain falling animation
rainFall: (particle, time, windStrength = 1) => {
particle.position.y -= particle.userData.fallSpeed * 3;
particle.position.x += windStrength * 0.05;
if (particle.position.y < -10) {
particle.position.y = 15 + Math.random() * 5;
particle.position.x = (Math.random() - 0.5) * 20;
}
}
};
function SeasonalParticlesR3F({
season = "auto",
particleCount = 30,
windStrength = 1,
animationSpeed = 1,
className = "",
showControls = false,
autoSeason = true,
seasonDuration = 10000,
// 10 seconds per season
onSeasonChange,
children,
seed
}) {
const prefersReducedMotion = useReducedMotion();
const canvasRef = useRef(null);
const [currentSeason, setCurrentSeason] = useState(season === "auto" ? "winter" : season);
const [particles, setParticles] = useState([]);
const [isPlaying, setIsPlaying] = useState(true);
const [seasonIndex, setSeasonIndex] = useState(0);
const seasons = ["winter", "spring", "summer", "autumn"];
// Initialize particles for current season
useEffect(() => {
const seededRandom = new SeededRandom(seed ?? `${currentSeason}-${particleCount}`);
const newParticles = SeasonalParticleFactory.createSeasonalField(currentSeason, particleCount, seededRandom);
setParticles(newParticles);
onSeasonChange?.(currentSeason);
}, [currentSeason, particleCount, onSeasonChange, seed]);
// Auto season rotation
useEffect(() => {
if (!autoSeason || season !== "auto") return;
const interval = setInterval(() => {
setSeasonIndex(prev => {
const next = (prev + 1) % seasons.length;
setCurrentSeason(seasons[next]);
return next;
});
}, seasonDuration);
return () => clearInterval(interval);
}, [autoSeason, season, seasonDuration, seasons]);
// Manual season control
const changeSeason = useCallback(newSeason => {
setCurrentSeason(newSeason);
setSeasonIndex(seasons.indexOf(newSeason));
}, [seasons]);
// Play/pause control
const togglePlay = useCallback(() => {
setIsPlaying(prev => !prev);
}, []);
// Get season icon
const getSeasonIcon = seasonName => {
switch (seasonName) {
case "winter":
return jsx(Snowflake, {
className: cn("glass-w-4 glass-h-4")
});
case "spring":
return jsx(Flower, {
className: cn("glass-w-4 glass-h-4")
});
case "summer":
return jsx(Sun, {
className: cn("glass-w-4 glass-h-4")
});
case "autumn":
return jsx(Leaf, {
className: cn("glass-w-4 glass-h-4")
});
default:
return jsx(Cloud, {
className: cn("glass-w-4 glass-h-4")
});
}
};
// Get season colors for UI
const getSeasonColors = seasonName => {
switch (seasonName) {
case "winter":
return "text-blue-400 border-blue-400/30 bg-blue-400/10";
case "spring":
return "text-pink-400 border-pink-400/30 bg-pink-400/10";
case "summer":
return "text-yellow-400 border-yellow-400/30 bg-yellow-400/10";
case "autumn":
return "text-orange-400 border-orange-400/30 bg-orange-400/10";
default:
return "text-gray-400 border-gray-400/30 bg-gray-400/10";
}
};
return jsxs("div", {
className: cn("seasonal-particles glass-relative glass-overflow-hidden", className),
children: [jsx(Canvas, {
ref: canvasRef,
className: cn("glass-absolute glass-inset-0 glass-pointer-events-none"),
camera: {
position: [0, 5, 10],
fov: 75
},
gl: {
alpha: true,
antialias: true
},
children: jsx(SeasonalScene, {
particles: particles,
currentSeason: currentSeason,
windStrength: windStrength,
animationSpeed: animationSpeed,
isPlaying: isPlaying
})
}), jsx("div", {
className: cn("glass-relative glass-z-10"),
children: children
}), jsx(motion.div, {
initial: {
opacity: 0,
y: 20
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
y: 0
},
className: cn("glass-absolute glass-top-4 glass-left-4 glass-px-3 glass-py-2 glass-radius-lg glass-foundation-complete glass-border", getSeasonColors(currentSeason)),
children: jsxs("div", {
className: cn("glass-flex glass-items-center glass-gap-2"),
children: [getSeasonIcon(currentSeason), jsx("span", {
className: cn("glass-text-sm glass-font-medium glass-capitalize"),
children: currentSeason
})]
})
}), showControls && jsxs(motion.div, {
initial: {
opacity: 0,
y: 20
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
y: 0
},
className: 'glass-absolute glass-bottom-4 glass-right-4 glass-flex glass-flex-col glass-gap-2',
children: [jsx("div", {
className: "glass-flex glass-gap-2 glass-p-2 glass-surface-dark/20 glass-backdrop-blur-lg glass-radius-lg glass-border glass-border-white/10 glass-contrast-guard",
children: seasons.map(seasonName => jsx("button", {
onClick: () => changeSeason(seasonName),
className: `p-2 rounded-lg transition-colors glass-focus glass-touch-target glass-contrast-guard ${currentSeason === seasonName ? "bg-white/20 text-white" : "text-white/60 hover:text-white hover:bg-white/10"}`,
title: `Switch to ${seasonName}`,
children: getSeasonIcon(seasonName)
}, seasonName))
}), jsxs("div", {
className: "glass-flex glass-gap-2 glass-p-2 glass-surface-dark/20 glass-backdrop-blur-lg glass-radius-lg glass-border glass-border-white/10 glass-contrast-guard",
children: [jsx("button", {
onClick: togglePlay,
className: 'glass-p-2 glass-radius-lg glass-text-primary hover:glass-surface-subtle/10 glass-transition-colors glass-focus glass-touch-target glass-contrast-guard',
title: isPlaying ? "Pause" : "Play",
children: isPlaying ? jsx(Pause, {
className: 'glass-w-4 glass-h-4'
}) : jsx(Play, {
className: 'glass-w-4 glass-h-4'
})
}), jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-2 glass-text-primary-glass-opacity-60 glass-text-sm',
children: [jsx(Wind, {
className: 'glass-w-3 glass-h-3'
}), jsx("span", {
children: windStrength.toFixed(1)
})]
})]
})]
}), windStrength > 0 && jsx(motion.div, {
initial: {
opacity: 0,
x: -20
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
x: 0
},
className: 'glass-absolute glass-top-4 glass-right-4 glass-px-3 glass-py-2 glass-surface-dark/20 glass-backdrop-blur-lg glass-radius-lg glass-border glass-border-white/10 glass-contrast-guard',
children: jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-2 glass-text-primary-glass-opacity-60 glass-text-sm',
children: [jsx(motion.div, {
animate: prefersReducedMotion ? {} : {
x: windStrength > 0 ? [0, 5, 0] : 0
},
transition: prefersReducedMotion ? {
duration: 0
} : {
duration: 2,
repeat: Infinity,
ease: "easeInOut"
},
children: jsx(Wind, {
className: 'glass-w-3 glass-h-3'
})
}), jsxs("span", {
children: ["Wind: ", windStrength.toFixed(1)]
})]
})
})]
});
}
// 3D Seasonal Scene Component
function SeasonalScene({
particles,
currentSeason,
windStrength,
animationSpeed,
isPlaying
}) {
const {
scene
} = useThree();
// Add particles to scene
useEffect(() => {
particles.forEach(particle => {
scene.add(particle);
});
return () => {
particles.forEach(particle => {
scene.remove(particle);
});
};
}, [particles, scene]);
// Animation loop
useFrame(state => {
if (!isPlaying) return;
const time = state.clock.elapsedTime * animationSpeed;
particles.forEach(particle => {
if (!particle.userData) return;
switch (particle.userData.season || currentSeason) {
case "winter":
SeasonalAnimations.snowFall(particle, time, windStrength);
break;
case "autumn":
SeasonalAnimations.leafFall(particle, time, windStrength);
break;
case "spring":
SeasonalAnimations.petalFloat(particle, time, windStrength);
break;
case "summer":
SeasonalAnimations.sunRayPulse(particle, time);
break;
}
});
});
return jsxs(Fragment, {
children: [jsx("ambientLight", {
intensity: 0.3
}), currentSeason === "summer" && jsx("directionalLight", {
position: [10, 10, 5],
intensity: 1.2,
color: 0xffeb3b
}), currentSeason === "winter" && jsx("directionalLight", {
position: [5, 5, 10],
intensity: 0.8,
color: 0xe3f2fd
}), currentSeason === "autumn" && jsx("directionalLight", {
position: [5, 10, 5],
intensity: 0.9,
color: 0xff9800
}), currentSeason === "spring" && jsx("directionalLight", {
position: [10, 5, 5],
intensity: 1.0,
color: 0xe91e63
})]
});
}
var SeasonalParticles_r3f = /*#__PURE__*/Object.freeze({
__proto__: null,
SeasonalParticlesR3F: SeasonalParticlesR3F,
default: SeasonalParticlesR3F
});
// Aurora geometry and material factories
const AuroraFactory = {
// Create aurora wave geometry
createAuroraWave: (width = 20, height = 10, segments = 64) => {
const geometry = new THREE.PlaneGeometry(width, height, segments, 32);
// Add wave-like deformation
const positions = geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {