aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
852 lines (849 loc) • 28.9 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import React, { forwardRef, useRef, useState, useCallback, useEffect } 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';
const GlassBiomeSimulator = /*#__PURE__*/forwardRef(({
width = 800,
height = 500,
biome = {
type: "forest",
temperature: 18,
humidity: 0.7,
windSpeed: 8,
lightLevel: 0.8,
season: "spring",
timeOfDay: 12,
id: "default-biome"
},
particleDensity = 1,
animationSpeed = 1,
showWeatherEffects = true,
dayNightCycle = true,
seasonalTransitions = true,
wildlifeActivity = 0.5,
ambientSoundLevel = 0.3,
windStrength = 1,
showAtmosphericLayers = true,
parallaxEnabled = true,
onBiomeChange,
onSeasonChange,
onTimeChange,
showControls = true,
showBiomeInfo = true,
respectMotionPreference = true,
className,
...props
}, ref) => {
const {
prefersReducedMotion,
isMotionSafe
} = useMotionPreferenceContext();
const canvasRef = useRef(null);
const animationRef = useRef();
const biomeSimulatorId = useA11yId("glass-biome-simulator");
const [currentBiome, setCurrentBiome] = useState(biome);
const [particles, setParticles] = useState([]);
const [biomeLayers, setBiomeLayers] = useState([]);
const [animationTime, setAnimationTime] = useState(0);
const [cameraOffset, setCameraOffset] = useState({
x: 0,
y: 0
});
// Biome configurations (memoized to prevent re-creation each render)
// Re-creating this object every render invalidated callbacks that depend on it
// and caused an infinite effect loop that set state on every render.
const biomeConfigs = React.useMemo(() => ({
forest: {
colors: {
sky: ["#87CEEB", "#98FB98"],
ground: [34, 139, 34],
accent: [139, 69, 19]
},
particles: ["leaf", "pollen", "insect"],
elements: ["tree", "grass", "flower"],
sounds: ["birds", "rustling", "wind"]
},
ocean: {
colors: {
sky: ["#87CEEB", "#E0F6FF"],
ground: [25, 25, 112],
accent: [255, 255, 255]
},
particles: ["water", "droplet"],
elements: ["water", "cloud"],
sounds: ["waves", "seagulls", "wind"]
},
desert: {
colors: {
sky: ["#FFD700", "#FFA500"],
ground: [238, 203, 173],
accent: [160, 82, 45]
},
particles: ["dust", "sand"],
elements: ["rock", "cactus"],
sounds: ["wind", "sand"]
},
tundra: {
colors: {
sky: ["#B0C4DE", "#F0F8FF"],
ground: [248, 248, 255],
accent: [70, 130, 180]
},
particles: ["snow", "ice"],
elements: ["ice", "rock"],
sounds: ["wind", "ice"]
},
grassland: {
colors: {
sky: ["#87CEEB", "#98FB98"],
ground: [124, 252, 0],
accent: [255, 215, 0]
},
particles: ["pollen", "grass"],
elements: ["grass", "flower"],
sounds: ["wind", "insects"]
},
rainforest: {
colors: {
sky: ["#228B22", "#90EE90"],
ground: [0, 100, 0],
accent: [255, 69, 0]
},
particles: ["water", "leaf", "spore"],
elements: ["tree", "vine", "flower"],
sounds: ["rain", "birds", "insects"]
},
mountain: {
colors: {
sky: ["#4682B4", "#E0F6FF"],
ground: [105, 105, 105],
accent: [255, 255, 255]
},
particles: ["snow", "cloud"],
elements: ["mountain", "rock", "snow"],
sounds: ["wind", "echo"]
},
swamp: {
colors: {
sky: ["#696969", "#A9A9A9"],
ground: [85, 107, 47],
accent: [255, 140, 0]
},
particles: ["fog", "insect", "bubble"],
elements: ["water", "tree", "fog"],
sounds: ["frogs", "insects", "bubbles"]
}
}), []);
// Generate biome layers
const generateBiomeLayers = useCallback(biomeType => {
biomeConfigs[biomeType];
const layers = [];
// Background layer
layers.push({
name: "Background",
depth: 0,
opacity: 1,
parallaxSpeed: 0.1,
id: "bg-layer",
elements: []
});
// Far layer
const farElements = [];
if (biomeType === "forest") {
for (let i = 0; i < 8; i++) {
farElements.push({
type: "tree",
x: i * width / 8 + Math.random() * 50,
y: height * 0.3 + Math.random() * 50,
width: 60 + Math.random() * 40,
height: 80 + Math.random() * 60,
color: [34, 100, 34],
opacity: 0.6,
id: `far-tree-${i}`
});
}
} else if (biomeType === "mountain") {
for (let i = 0; i < 5; i++) {
farElements.push({
type: "mountain",
x: i * width / 4,
y: height * 0.2,
width: width / 3,
height: height * 0.6,
color: [105, 105, 105],
opacity: 0.7,
id: `mountain-${i}`
});
}
}
layers.push({
name: "Far",
depth: 1,
opacity: 0.8,
parallaxSpeed: 0.3,
id: "far-layer",
elements: farElements
});
// Mid layer
const midElements = [];
if (biomeType === "grassland") {
for (let i = 0; i < 20; i++) {
midElements.push({
type: "grass",
x: Math.random() * width,
y: height * 0.7 + Math.random() * height * 0.2,
width: 10 + Math.random() * 20,
height: 20 + Math.random() * 30,
color: [124, 252, 0],
opacity: 0.8,
id: `grass-${i}`
});
}
} else if (biomeType === "ocean") {
midElements.push({
type: "water",
x: 0,
y: height * 0.6,
width: width,
height: height * 0.4,
color: [25, 25, 112],
opacity: 0.8,
animation: "wave",
id: "ocean-water"
});
}
layers.push({
name: "Mid",
depth: 2,
opacity: 0.9,
parallaxSpeed: 0.6,
id: "mid-layer",
elements: midElements
});
// Near layer
const nearElements = [];
if (biomeType === "forest") {
for (let i = 0; i < 3; i++) {
nearElements.push({
type: "tree",
x: i * width / 3 + Math.random() * 100,
y: height * 0.2,
width: 80 + Math.random() * 60,
height: 120 + Math.random() * 80,
color: [34, 139, 34],
opacity: 1,
id: `near-tree-${i}`
});
}
}
layers.push({
name: "Near",
depth: 3,
opacity: 1,
parallaxSpeed: 1,
id: "near-layer",
elements: nearElements
});
return layers;
}, [width, height, biomeConfigs]);
// Generate particles
const generateParticles = useCallback((biomeType, count) => {
const config = biomeConfigs[biomeType];
const newParticles = [];
for (let i = 0; i < count; i++) {
const particleType = config.particles[Math.floor(Math.random() * config.particles.length)];
let particle = {
x: Math.random() * width,
y: Math.random() * height,
size: Math.random() * 5 + 2,
opacity: Math.random() * 0.8 + 0.2,
lifetime: Math.random() * 10000 + 5000,
rotation: Math.random() * Math.PI * 2,
rotationSpeed: (Math.random() - 0.5) * 0.1,
type: particleType,
id: `particle-${particleType}-${i}-${Date.now()}`
};
switch (particleType) {
case "leaf":
particle = {
...particle,
vx: (Math.random() - 0.5) * 2,
vy: Math.random() * 1 + 0.5,
color: [34, 139, 34],
size: Math.random() * 8 + 4,
rotationSpeed: (Math.random() - 0.5) * 0.2
};
break;
case "pollen":
particle = {
...particle,
vx: (Math.random() - 0.5) * 1,
vy: (Math.random() - 0.5) * 0.5,
color: [255, 215, 0],
size: Math.random() * 3 + 1
};
break;
case "water":
particle = {
...particle,
vx: (Math.random() - 0.5) * 0.5,
vy: Math.random() * 3 + 2,
color: [173, 216, 230],
size: Math.random() * 4 + 2
};
break;
case "droplet":
particle = {
...particle,
vx: (Math.random() - 0.5) * 0.4,
vy: Math.random() * 2.5 + 1.5,
color: [173, 216, 230],
// light blue
size: Math.random() * 3 + 1
};
break;
case "dust":
particle = {
...particle,
vx: currentBiome.windSpeed * 0.1 + (Math.random() - 0.5) * 1,
vy: (Math.random() - 0.5) * 0.5,
color: [238, 203, 173],
size: Math.random() * 2 + 1,
opacity: Math.random() * 0.5 + 0.1
};
break;
case "sand":
particle = {
...particle,
vx: currentBiome.windSpeed * 0.15 + (Math.random() - 0.5) * 1,
vy: (Math.random() - 0.5) * 0.3,
color: [237, 201, 175],
// sandy color
size: Math.random() * 2 + 0.5,
opacity: Math.random() * 0.4 + 0.1
};
break;
case "snow":
particle = {
...particle,
vx: (Math.random() - 0.5) * currentBiome.windSpeed * 0.1,
vy: Math.random() * 2 + 1,
color: [255, 255, 255],
size: Math.random() * 6 + 3,
rotationSpeed: (Math.random() - 0.5) * 0.1
};
break;
case "ice":
particle = {
...particle,
vx: (Math.random() - 0.5) * currentBiome.windSpeed * 0.08,
vy: Math.random() * 1.5 + 0.5,
color: [200, 230, 255],
// icy blue
size: Math.random() * 4 + 2,
rotationSpeed: (Math.random() - 0.5) * 0.08
};
break;
case "insect":
particle = {
...particle,
vx: (Math.random() - 0.5) * 3,
vy: (Math.random() - 0.5) * 3,
color: [0, 0, 0],
size: Math.random() * 2 + 1
};
break;
case "spore":
particle = {
...particle,
vx: (Math.random() - 0.5) * 0.5,
vy: -Math.random() * 0.5,
color: [144, 238, 144],
size: Math.random() * 3 + 1,
opacity: Math.random() * 0.6 + 0.2
};
break;
case "fog":
particle = {
...particle,
vx: (Math.random() - 0.5) * 0.2,
vy: (Math.random() - 0.5) * 0.1,
color: [210, 210, 220],
size: Math.random() * 20 + 10,
opacity: Math.random() * 0.2 + 0.05
};
break;
case "bubble":
particle = {
...particle,
vx: (Math.random() - 0.5) * 0.3,
vy: -Math.random() * 0.8,
color: [180, 220, 255],
size: Math.random() * 5 + 2,
opacity: Math.random() * 0.5 + 0.2
};
break;
case "cloud":
particle = {
...particle,
vx: (Math.random() - 0.5) * 0.2,
vy: (Math.random() - 0.5) * 0.1,
color: [220, 220, 230],
size: Math.random() * 30 + 20,
opacity: Math.random() * 0.25 + 0.1
};
break;
}
newParticles.push(particle);
}
return newParticles;
}, [width, height, biomeConfigs, currentBiome.windSpeed]);
// Initialize biome
useEffect(() => {
const layers = generateBiomeLayers(currentBiome.type);
setBiomeLayers(layers);
const particleCount = Math.floor(50 * particleDensity);
const newParticles = generateParticles(currentBiome.type, particleCount);
setParticles(newParticles);
}, [currentBiome.type, generateBiomeLayers, generateParticles, particleDensity]);
// Update particles
const updateParticles = useCallback(deltaTime => {
setParticles(prevParticles => {
const updated = prevParticles.map(particle => {
const windEffect = currentBiome.windSpeed * windStrength * 0.01;
return {
...particle,
x: particle.x + (particle.vx + windEffect) * deltaTime * animationSpeed,
y: particle.y + particle.vy * deltaTime * animationSpeed,
rotation: particle.rotation + particle.rotationSpeed * deltaTime * animationSpeed,
lifetime: particle.lifetime - deltaTime
};
}).filter(particle => particle.lifetime > 0 && particle.x > -50 && particle.x < width + 50 && particle.y > -50 && particle.y < height + 50);
// Add new particles to maintain count
if (updated.length < 50 * particleDensity) {
const needed = Math.min(5, Math.floor(50 * particleDensity) - updated.length);
const newParticles = generateParticles(currentBiome.type, needed);
return [...updated, ...newParticles];
}
return updated;
});
}, [currentBiome, windStrength, animationSpeed, width, height, generateParticles, particleDensity]);
// Get day/night colors
const getDayNightColors = useCallback((timeOfDay, baseColors) => {
if (!dayNightCycle) return baseColors;
const isDay = timeOfDay >= 6 && timeOfDay <= 18;
const lightIntensity = isDay ? Math.sin((timeOfDay - 6) / 12 * Math.PI) : 0.2;
return baseColors.map(color => {
// Parse hex color
const r = parseInt(color.slice(1, 3), 16);
const g = parseInt(color.slice(3, 5), 16);
const b = parseInt(color.slice(5, 7), 16);
// Apply light intensity
const newR = Math.round(r * lightIntensity);
const newG = Math.round(g * lightIntensity);
const newB = Math.round(b * lightIntensity + (isDay ? 0 : 50));
return `rgb(${newR}, ${newG}, ${newB})`;
});
}, [dayNightCycle]);
// Render biome
const render = useCallback(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const config = biomeConfigs[currentBiome.type];
const skyColors = getDayNightColors(currentBiome.timeOfDay, config.colors.sky);
// Clear canvas with gradient background
const gradient = ctx.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, skyColors[0]);
gradient.addColorStop(1, skyColors[1]);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// Draw atmospheric layers
if (showAtmosphericLayers) {
biomeLayers.forEach(layer => {
ctx.save();
ctx.globalAlpha = layer.opacity;
const parallaxOffset = parallaxEnabled ? cameraOffset.x * layer.parallaxSpeed : 0;
layer.elements.forEach(element => {
ctx.save();
ctx.translate(element.x + parallaxOffset, element.y);
ctx.globalAlpha = element.opacity;
const color = `rgb(${element.color[0]}, ${element.color[1]}, ${element.color[2]})`;
switch (element.type) {
case "tree":
// Draw simple tree
ctx.fillStyle = color;
ctx.fillRect(-element.width * 0.1, 0, element.width * 0.2, element.height * 0.3); // trunk
ctx.beginPath();
ctx.ellipse(0, -element.height * 0.3, element.width * 0.5, element.height * 0.7, 0, 0, Math.PI * 2);
ctx.fill();
break;
case "mountain":
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(-element.width * 0.5, element.height);
ctx.lineTo(0, 0);
ctx.lineTo(element.width * 0.5, element.height);
ctx.closePath();
ctx.fill();
break;
case "grass":
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(0, element.height);
ctx.quadraticCurveTo(element.width * 0.5, 0, 0, -element.height * 0.5);
ctx.stroke();
break;
case "cloud":
// Soft cloud made of overlapping circles
ctx.fillStyle = color;
ctx.globalAlpha = element.opacity * 0.6;
ctx.beginPath();
const radius = Math.max(10, Math.min(element.width, element.height) * 0.3);
for (let i = -2; i <= 2; i++) {
ctx.moveTo(i * radius * 0.8, 0);
ctx.arc(i * radius * 0.8, 0, radius * (1 - Math.abs(i) * 0.1), 0, Math.PI * 2);
}
ctx.fill();
ctx.globalAlpha = element.opacity;
break;
case "water":
ctx.fillStyle = color;
ctx.fillRect(-element.width * 0.5, 0, element.width, element.height);
// Add wave effect
if (element.animation === "wave") {
ctx.strokeStyle = "var(--glass-bg-hover)";
ctx.lineWidth = 1;
const waveOffset = animationTime * 0.002;
for (let i = 0; i < element.width; i += 10) {
const waveY = Math.sin((i + waveOffset * 100) * 0.02) * 5;
ctx.beginPath();
ctx.moveTo(i - element.width * 0.5, waveY);
ctx.lineTo(i + 5 - element.width * 0.5, waveY);
ctx.stroke();
}
}
break;
case "rock":
ctx.fillStyle = color;
ctx.beginPath();
ctx.ellipse(0, 0, element.width * 0.5, element.height * 0.3, 0, 0, Math.PI * 2);
ctx.fill();
break;
case "flower":
// Draw flower
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(0, 0, element.size || 5, 0, Math.PI * 2);
ctx.fill();
// Petals
ctx.fillStyle = "rgba(255, 100, 150, 0.8)";
for (let i = 0; i < 5; i++) {
ctx.save();
ctx.rotate(i / 5 * Math.PI * 2);
ctx.beginPath();
ctx.ellipse(0, -8, 3, 6, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
break;
}
ctx.restore();
});
ctx.restore();
});
}
// Draw particles (guard against malformed data)
particles.forEach(particle => {
ctx.save();
ctx.globalAlpha = particle.opacity;
ctx.translate(particle.x, particle.y);
ctx.rotate(particle.rotation);
// Fallback color guard in case a particle was created without a color
const color = Array.isArray(particle.color) ? `rgb(${particle.color[0]}, ${particle.color[1]}, ${particle.color[2]})` : "var(--glass-white)";
switch (particle.type) {
case "leaf":
ctx.fillStyle = color;
ctx.beginPath();
ctx.ellipse(0, 0, particle.size, particle.size * 0.6, 0, 0, Math.PI * 2);
ctx.fill();
break;
case "pollen":
case "dust":
case "sand":
case "spore":
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(0, 0, particle.size, 0, Math.PI * 2);
ctx.fill();
break;
case "snow":
case "ice":
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(0, 0, particle.size, 0, Math.PI * 2);
ctx.fill();
// Snowflake pattern
ctx.strokeStyle = color;
ctx.lineWidth = 0.5;
for (let i = 0; i < 6; i++) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, -particle.size);
ctx.stroke();
ctx.rotate(Math.PI / 3);
}
break;
case "water":
case "droplet":
case "bubble":
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(0, 0, particle.size, 0, Math.PI * 2);
ctx.fill();
break;
case "fog":
case "cloud":
// Render as a soft blurred circle
const fogGradient = ctx.createRadialGradient(0, 0, 0, 0, 0, particle.size);
fogGradient.addColorStop(0, `rgba(${Array.isArray(particle.color) ? `${particle.color[0]}, ${particle.color[1]}, ${particle.color[2]}` : "255, 255, 255"}, ${Math.min(0.5, particle.opacity)})`);
fogGradient.addColorStop(1, "rgba(255, 255, 255, 0)");
ctx.fillStyle = fogGradient;
ctx.beginPath();
ctx.arc(0, 0, particle.size, 0, Math.PI * 2);
ctx.fill();
break;
case "insect":
ctx.fillStyle = color;
ctx.fillRect(-particle.size * 0.5, -particle.size * 0.2, particle.size, particle.size * 0.4);
break;
}
ctx.restore();
});
// Weather overlay effects
if (showWeatherEffects) {
if (currentBiome.humidity > 0.8) {
ctx.save();
ctx.globalAlpha = (currentBiome.humidity - 0.8) * 0.5;
ctx.fillStyle = "rgba(200, 200, 220, 0.1)";
ctx.fillRect(0, 0, width, height);
ctx.restore();
}
if (currentBiome.temperature < 0) {
ctx.save();
ctx.globalAlpha = Math.abs(currentBiome.temperature) * 0.01;
ctx.fillStyle = "rgba(173, 216, 230, 0.1)";
ctx.fillRect(0, 0, width, height);
ctx.restore();
}
}
// Biome info overlay
if (showBiomeInfo) {
ctx.save();
ctx.fillStyle = "var(--glass-text-secondary-dark)";
ctx.fillRect(10, 10, 250, 140);
ctx.fillStyle = "white";
ctx.font = "14px sans-serif";
ctx.fillText(`Biome: ${currentBiome.type}`, 20, 30);
ctx.fillText(`Season: ${currentBiome.season}`, 20, 50);
ctx.fillText(`Temperature: ${currentBiome.temperature}°C`, 20, 70);
ctx.fillText(`Humidity: ${Math.round(currentBiome.humidity * 100)}%`, 20, 90);
ctx.fillText(`Wind: ${Math.round(currentBiome.windSpeed)} km/h`, 20, 110);
ctx.fillText(`Time: ${Math.floor(currentBiome.timeOfDay)}:${String(Math.floor(currentBiome.timeOfDay % 1 * 60)).padStart(2, "0")}`, 20, 130);
ctx.restore();
}
}, [width, height, biomeConfigs, currentBiome, getDayNightColors, showAtmosphericLayers, biomeLayers, parallaxEnabled, cameraOffset, animationTime, particles, showWeatherEffects, showBiomeInfo]);
// Animation loop
useEffect(() => {
if (prefersReducedMotion && respectMotionPreference) {
render();
return;
}
const animate = currentTime => {
const deltaTime = 16; // 60fps
setAnimationTime(prev => prev + deltaTime);
updateParticles(deltaTime);
render();
animationRef.current = requestAnimationFrame(animate);
};
animationRef.current = requestAnimationFrame(animate);
return () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, [prefersReducedMotion, respectMotionPreference, render, updateParticles]);
// Canvas setup
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
canvas.width = width;
canvas.height = height;
}, [width, height]);
// Controls
const renderControls = () => {
if (!showControls) return null;
return jsxs(OptimizedGlassCore, {
elevation: "level2",
intensity: "medium",
depth: 1,
tint: "neutral",
border: "subtle",
className: "glass-biome-controls glass-flex glass-flex-wrap glass-items-center glass-gap-4 glass-p-4 glass-radius-lg glass-glass-glass-backdrop-blur-md glass-border glass-border-glass-border/20 glass-contrast-guard",
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("label", {
className: "glass-text-sm",
children: "Biome:"
}), jsxs("select", {
value: currentBiome.type,
onChange: e => {
const newBiome = {
...currentBiome,
type: e.target.value
};
setCurrentBiome(newBiome);
onBiomeChange?.(newBiome);
},
className: "glass-px-2 glass-py-1 glass-radius-md glass-surface-overlay glass-border glass-border-glass-border/20 glass-contrast-guard",
children: [jsx("option", {
value: "forest",
children: "Forest"
}), jsx("option", {
value: "ocean",
children: "Ocean"
}), jsx("option", {
value: "desert",
children: "Desert"
}), jsx("option", {
value: "tundra",
children: "Tundra"
}), jsx("option", {
value: "grassland",
children: "Grassland"
}), jsx("option", {
value: "rainforest",
children: "Rainforest"
}), jsx("option", {
value: "mountain",
children: "Mountain"
}), jsx("option", {
value: "swamp",
children: "Swamp"
})]
})]
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("label", {
className: "glass-text-sm",
children: "Season:"
}), jsxs("select", {
value: currentBiome.season,
onChange: e => {
const newBiome = {
...currentBiome,
season: e.target.value
};
setCurrentBiome(newBiome);
onSeasonChange?.(e.target.value);
},
className: "glass-px-2 glass-py-1 glass-radius-md glass-surface-overlay glass-border glass-border-glass-border/20 glass-contrast-guard",
children: [jsx("option", {
value: "spring",
children: "Spring"
}), jsx("option", {
value: "summer",
children: "Summer"
}), jsx("option", {
value: "autumn",
children: "Autumn"
}), jsx("option", {
value: "winter",
children: "Winter"
})]
})]
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("label", {
className: "glass-text-sm",
children: "Time:"
}), jsx("input", {
type: "range",
min: "0",
max: "24",
step: "0.5",
value: currentBiome.timeOfDay,
onChange: e => {
const newTime = parseFloat(e.target.value);
setCurrentBiome(prev => ({
...prev,
timeOfDay: newTime
}));
onTimeChange?.(newTime);
},
className: 'w-20'
})]
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsxs("label", {
className: "glass-text-sm",
children: [jsx("input", {
type: "checkbox",
checked: dayNightCycle,
onChange: e => {},
className: "glass-mr-1"
}), "Day/Night"]
}), jsxs("label", {
className: "glass-text-sm",
children: [jsx("input", {
type: "checkbox",
checked: parallaxEnabled,
onChange: e => {},
className: "glass-mr-1"
}), "Parallax"]
})]
})]
});
};
return jsx(OptimizedGlassCore, {
ref: ref,
id: biomeSimulatorId,
elevation: "level1",
intensity: "subtle",
depth: 1,
tint: "neutral",
border: "subtle",
className: cn("glass-biome-simulator relative glass-radius-lg glass-glass-backdrop-blur-md border border-border/20", className),
...props,
children: jsxs(MotionFramer, {
preset: isMotionSafe && respectMotionPreference ? "fadeIn" : "none",
className: "glass-flex glass-flex-col glass-gap-4 glass-p-4",
children: [renderControls(), jsx("div", {
className: 'relative',
children: jsx("canvas", {
ref: canvasRef,
width: width,
height: height,
className: "glass-border glass-border-glass-border/20 glass-radius-md",
style: {
width,
height
}
})
})]
})
});
});
GlassBiomeSimulator.displayName = "GlassBiomeSimulator";
export { GlassBiomeSimulator, GlassBiomeSimulator as default };
//# sourceMappingURL=GlassBiomeSimulator.js.map