aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
425 lines (422 loc) • 16.3 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
import { forwardRef, useRef, useState, useEffect, useMemo } from 'react';
import { motion } from 'framer-motion';
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 { useMotionPreference } from '../../hooks/useMotionPreference.js';
import { createGlassStyle } from '../../utils/createGlassStyle.js';
import { cn } from '../../lib/utilsComprehensive.js';
const waveTypes = {
sine: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.sin(k * x - 2 * Math.PI * frequency * t + phase),
cosine: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.cos(k * x - 2 * Math.PI * frequency * t + phase),
gaussian: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.exp(-Math.pow(x - velocity * t, 2) / 20) * Math.sin(k * x - 2 * Math.PI * frequency * t + phase),
complex: (x, t, amplitude, frequency, phase, k, velocity) => {
const real = amplitude * Math.cos(k * x - 2 * Math.PI * frequency * t + phase);
const imag = amplitude * Math.sin(k * x - 2 * Math.PI * frequency * t + phase);
return Math.sqrt(real * real + imag * imag);
},
standing: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.sin(k * x + phase) * Math.cos(2 * Math.PI * frequency * t),
traveling: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.sin(k * x - 2 * Math.PI * frequency * t + phase)
};
const GlassWaveFunction = /*#__PURE__*/forwardRef(({
width = 800,
height = 400,
waveEquations,
showInterference = true,
showPhaseSpace = false,
showAmplitude = true,
showFrequencySpectrum = false,
animationSpeed = 1,
resolution = 2,
timeScale = 1,
showGrid = true,
showLabels = true,
realTimeMode = true,
onWaveInteraction,
className = "",
...props
}, ref) => {
useReducedMotion();
const canvasRef = useRef(null);
const phaseCanvasRef = useRef(null);
const spectrumCanvasRef = useRef(null);
const [currentTime, setCurrentTime] = useState(0);
const [selectedWave, setSelectedWave] = useState(null);
const [waveData, setWaveData] = useState({});
useA11yId("glass-wave-function");
useMotionPreference();
// Time evolution
useEffect(() => {
if (!realTimeMode) return;
const interval = setInterval(() => {
setCurrentTime(prev => prev + 0.1 * animationSpeed * timeScale);
}, 16);
return () => clearInterval(interval);
}, [realTimeMode, animationSpeed, timeScale]);
// Generate wave data
useMemo(() => {
const points = Math.floor(width / resolution);
const newWaveData = {};
waveEquations.forEach(wave => {
const k = 2 * Math.PI / wave.wavelength;
const data = [];
for (let i = 0; i < points; i++) {
const x = i / points * width - width / 2; // Center the wave
const waveFunc = waveTypes[wave.type];
let y = waveFunc(x, currentTime, wave.amplitude, wave.frequency, wave.phase, k, wave.velocity);
// Apply damping if present
if (wave.damping && wave.damping > 0) {
y *= Math.exp(-wave.damping * Math.abs(x));
}
data.push(y);
}
newWaveData[wave.id] = data;
});
setWaveData(newWaveData);
return newWaveData;
}, [waveEquations, currentTime, width, resolution]);
// Main canvas rendering
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.clearRect(0, 0, width, height);
// Grid
if (showGrid) {
ctx.strokeStyle = "var(--glass-bg-default)";
ctx.lineWidth = 1;
// Horizontal grid lines
for (let y = 0; y <= height; y += 40) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
// Vertical grid lines
for (let x = 0; x <= width; x += 40) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
}
// Center line
ctx.strokeStyle = "var(--glass-bg-hover)";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(0, height / 2);
ctx.lineTo(width, height / 2);
ctx.stroke();
}
// Draw individual waves
Object.entries(waveData).forEach(([waveId, data]) => {
const wave = waveEquations.find(w => w.id === waveId);
if (!wave) return;
ctx.strokeStyle = wave.color;
ctx.lineWidth = selectedWave === waveId ? 3 : 2;
ctx.globalAlpha = selectedWave && selectedWave !== waveId ? 0.3 : 0.8;
ctx.beginPath();
data.forEach((y, i) => {
const x = i / data.length * width;
const canvasY = height / 2 - y * (height / 4); // Scale and center
if (i === 0) {
ctx.moveTo(x, canvasY);
} else {
ctx.lineTo(x, canvasY);
}
});
ctx.stroke();
// Wave labels
if (showLabels) {
ctx.fillStyle = wave.color;
ctx.font = "12px Arial";
ctx.globalAlpha = 1;
ctx.fillText(wave.name, 10, 20 + Object.keys(waveData).indexOf(waveId) * 20);
}
});
// Interference pattern
if (showInterference && waveEquations.length > 1) {
const interferenceData = [];
const dataLength = Math.min(...Object.values(waveData).map(d => d.length));
for (let i = 0; i < dataLength; i++) {
const sum = Object.values(waveData).reduce((total, data) => total + (data[i] || 0), 0);
interferenceData.push(sum);
}
ctx.strokeStyle = "var(--glass-white)";
ctx.lineWidth = 2;
ctx.globalAlpha = 0.9;
ctx.setLineDash([5, 5]);
ctx.beginPath();
interferenceData.forEach((y, i) => {
const x = i / interferenceData.length * width;
const canvasY = height / 2 - y * (height / 4);
if (i === 0) {
ctx.moveTo(x, canvasY);
} else {
ctx.lineTo(x, canvasY);
}
});
ctx.stroke();
ctx.setLineDash([]);
}
ctx.globalAlpha = 1;
}, [waveData, waveEquations, width, height, showGrid, showLabels, showInterference, selectedWave]);
// Phase space visualization
useEffect(() => {
if (!showPhaseSpace) return;
const canvas = phaseCanvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const phaseWidth = 200;
const phaseHeight = 200;
ctx.clearRect(0, 0, phaseWidth, phaseHeight);
// Grid for phase space
ctx.strokeStyle = "var(--glass-bg-default)";
ctx.lineWidth = 1;
for (let i = 0; i <= 10; i++) {
const x = i / 10 * phaseWidth;
const y = i / 10 * phaseHeight;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, phaseHeight);
ctx.moveTo(0, y);
ctx.lineTo(phaseWidth, y);
ctx.stroke();
}
// Draw phase trajectories
waveEquations.forEach((wave, index) => {
const data = waveData[wave.id];
if (!data) return;
ctx.strokeStyle = wave.color;
ctx.lineWidth = 2;
ctx.globalAlpha = 0.7;
ctx.beginPath();
data.forEach((amplitude, i) => {
if (i === 0) return;
const prevAmplitude = data[i - 1];
const velocity = amplitude - prevAmplitude; // Approximate velocity
const x = (amplitude + 2) / 4 * phaseWidth; // Normalize to canvas
const y = phaseHeight - (velocity + 2) / 4 * phaseHeight;
if (i === 1) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
});
ctx.globalAlpha = 1;
}, [showPhaseSpace, waveData, waveEquations]);
// Frequency spectrum
useEffect(() => {
if (!showFrequencySpectrum) return;
const canvas = spectrumCanvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const spectrumWidth = 300;
const spectrumHeight = 150;
ctx.clearRect(0, 0, spectrumWidth, spectrumHeight);
// Draw frequency bars
waveEquations.forEach((wave, index) => {
const barWidth = spectrumWidth / waveEquations.length;
const barHeight = wave.amplitude / 2 * spectrumHeight;
const x = index * barWidth;
const y = spectrumHeight - barHeight;
ctx.fillStyle = wave.color;
ctx.globalAlpha = 0.7;
ctx.fillRect(x + 2, y, barWidth - 4, barHeight);
// Frequency label
ctx.fillStyle = "var(--glass-white)";
ctx.font = "10px Arial";
ctx.globalAlpha = 1;
ctx.fillText(`${wave.frequency.toFixed(1)}Hz`, x + 5, spectrumHeight - 5);
});
}, [showFrequencySpectrum, waveEquations]);
const handleCanvasClick = e => {
const canvas = canvasRef.current;
if (!canvas) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Find closest wave
let closestWave = null;
let minDistance = Infinity;
Object.entries(waveData).forEach(([waveId, data]) => {
const dataIndex = Math.floor(x / width * data.length);
const waveY = height / 2 - data[dataIndex] * (height / 4);
const distance = Math.abs(y - waveY);
if (distance < minDistance && distance < 30) {
minDistance = distance;
closestWave = waveId;
}
});
if (closestWave) {
setSelectedWave(prev => prev === closestWave ? null : closestWave);
onWaveInteraction?.(closestWave, {
x,
y
});
}
};
return jsxs(OptimizedGlassCore, {
ref: ref,
variant: "frosted",
className: cn("glass-p-4 glass-space-y-4", className),
...props,
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsxs("div", {
children: [jsx("h3", {
className: cn("glass-text-lg glass-font-semibold glass-text-primary"),
children: "Wave Function Visualization"
}), jsxs("p", {
className: cn("glass-text-sm glass-text-secondary"),
children: [waveEquations.length, " wave", waveEquations.length !== 1 ? "s" : "", selectedWave && ` • Selected: ${waveEquations.find(w => w.id === selectedWave)?.name}`]
})]
}), jsxs("div", {
className: cn("glass-flex glass-items-center glass-space-x-4"),
children: [realTimeMode && jsxs("div", {
className: cn("glass-flex glass-items-center glass-space-x-1 glass-text-success"),
children: [jsx("div", {
className: cn("glass-w-2 glass-h-2 glass-surface-success glass-radius-full glass-animate-pulse")
}), jsx("span", {
className: cn("glass-text-xs"),
children: "Live"
})]
}), jsxs("div", {
className: cn("glass-text-sm glass-text-secondary"),
children: ["t = ", currentTime.toFixed(1)]
})]
})]
}), jsxs("div", {
className: cn("glass-relative"),
children: [jsx("canvas", {
ref: canvasRef,
width: width,
height: height,
className: cn("glass-border glass-border-primary glass-radius-lg glass-surface-dark glass-cursor-crosshair"),
onClick: handleCanvasClick
}), showAmplitude && jsxs("div", {
className: cn("glass-absolute glass-left-2 glass-top-2 glass-space-y-1"),
children: [jsx("div", {
className: cn("glass-text-xs glass-text-secondary"),
children: "Amplitude"
}), jsx("div", {
className: cn("glass-text-xs glass-text-success"),
children: "+2"
}), jsx("div", {
className: cn("glass-text-xs glass-text-muted"),
style: {
marginTop: height / 4 - 20
},
children: "0"
}), jsx("div", {
className: cn("glass-text-xs glass-text-danger"),
style: {
marginTop: height / 2 - 40
},
children: "-2"
})]
})]
}), jsxs("div", {
className: cn("glass-flex glass-space-x-4"),
children: [showPhaseSpace && jsxs("div", {
children: [jsx("h4", {
className: cn("glass-text-sm glass-font-medium glass-text-primary glass-mb-2"),
children: "Phase Space"
}), jsx("canvas", {
ref: phaseCanvasRef,
width: 200,
height: 200,
className: cn("glass-border glass-border-primary glass-radius glass-surface-dark")
}), jsx("div", {
className: cn("glass-text-xs glass-text-muted glass-mt-1"),
children: "Position vs Velocity"
})]
}), showFrequencySpectrum && jsxs("div", {
children: [jsx("h4", {
className: cn("glass-text-sm glass-font-medium glass-text-primary glass-mb-2"),
children: "Frequency Spectrum"
}), jsx("canvas", {
ref: spectrumCanvasRef,
width: 300,
height: 150,
className: cn("glass-border glass-border-primary glass-radius glass-surface-dark")
})]
})]
}), jsxs("div", {
className: cn("glass-p-3 glass-radius-lg glass-border glass-border-subtle glass-space-y-3", createGlassStyle({
blur: "sm",
opacity: 0.6
}).background),
children: [jsx("h4", {
className: cn("glass-text-sm glass-font-semibold glass-text-primary"),
children: "Wave Parameters"
}), jsx("div", {
className: cn("glass-grid glass-gap-2"),
children: waveEquations.map(wave => jsx(motion.div, {
className: cn("glass-p-2 glass-radius glass-border glass-transition-colors glass-duration-200 glass-cursor-pointer", selectedWave === wave.id ? "glass-border-primary glass-surface-subtle" : "glass-border-muted hover:glass-border-primary"),
onClick: () => setSelectedWave(prev => prev === wave.id ? null : wave.id),
whileHover: {
scale: 1.02
},
whileTap: {
scale: 0.98
},
children: jsxs("div", {
className: cn("glass-flex glass-items-center glass-justify-between"),
children: [jsxs("div", {
className: cn("glass-flex glass-items-center glass-space-x-2"),
children: [jsx("div", {
className: cn("glass-w-3 glass-h-3 glass-radius-full"),
style: {
backgroundColor: wave.color
}
}), jsx("span", {
className: cn("glass-text-sm glass-font-medium glass-text-primary"),
children: wave.name
}), jsxs("span", {
className: cn("glass-text-xs glass-text-muted glass-capitalize"),
children: ["(", wave.type, ")"]
})]
}), jsxs("div", {
className: cn("glass-flex glass-items-center glass-space-x-4 glass-text-xs glass-text-secondary"),
children: [jsxs("span", {
children: ["A: ", wave.amplitude.toFixed(1)]
}), jsxs("span", {
children: ["f: ", wave.frequency.toFixed(1), "Hz"]
}), jsxs("span", {
children: ["\u03BB: ", wave.wavelength.toFixed(0)]
}), jsxs("span", {
children: ["v: ", wave.velocity.toFixed(0)]
})]
})]
})
}, wave.id))
})]
}), jsxs("div", {
className: cn("glass-flex glass-items-center glass-justify-between glass-text-sm glass-text-secondary"),
children: [jsxs("span", {
children: ["Click waves to select \u2022 Time: ", timeScale, "x speed"]
}), jsxs("div", {
className: cn("glass-flex glass-items-center glass-space-x-4"),
children: [jsxs("span", {
children: ["Resolution: ", resolution, "px"]
}), jsxs("span", {
children: ["Animation: ", animationSpeed, "x"]
})]
})]
})]
});
});
export { GlassWaveFunction };
//# sourceMappingURL=GlassWaveFunction.js.map