Neuste Version

This commit is contained in:
MarcWieland
2026-06-19 16:28:08 +02:00
parent c25260fb5e
commit 13789fab1d
623 changed files with 65768 additions and 63 deletions
+116
View File
@@ -0,0 +1,116 @@
import { useEffect, useState } from "react";
const COLORS = [
"#f43f5e", "#ec4899", "#d946ef", "#a855f7", "#8b5cf6",
"#6366f1", "#3b82f6", "#0ea5e9", "#06b6d4", "#14b8a6",
"#10b981", "#22c55e", "#84cc16", "#eab308", "#f97316"
];
const SHAPES = ["circle", "square", "triangle"];
export default function Confetti({ count = 80 }) {
const [particles, setParticles] = useState([]);
useEffect(() => {
const generated = Array.from({ length: count }).map((_, idx) => {
const size = Math.random() * 8 + 6; // 6px - 14px
const color = COLORS[Math.floor(Math.random() * COLORS.length)];
const shape = SHAPES[Math.floor(Math.random() * SHAPES.length)];
const left = Math.random() * 100; // 0% - 100%
const delay = Math.random() * 4; // 0s - 4s delay
const duration = Math.random() * 3 + 3; // 3s - 6s duration
const rotation = Math.random() * 360;
return {
id: idx,
size,
color,
shape,
left: `${left}%`,
delay: `${delay}s`,
duration: `${duration}s`,
rotation: `${rotation}deg`
};
});
setParticles(generated);
}, [count]);
return (
<>
<style>{`
@keyframes confetti-fall {
0% {
transform: translateY(-20px) rotate(0deg);
opacity: 1;
}
100% {
transform: translateY(105vh) rotate(720deg);
opacity: 0.3;
}
}
@keyframes confetti-sway {
0%, 100% {
transform: translateX(0px);
}
50% {
transform: translateX(50px);
}
}
.confetti-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: 9999;
overflow: hidden;
}
.confetti-particle {
position: absolute;
top: -20px;
animation-name: confetti-fall;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.confetti-swayer {
animation: confetti-sway 3s ease-in-out infinite alternate;
}
`}</style>
<div className="confetti-container">
{particles.map((p) => {
let style = {
width: `${p.size}px`,
height: `${p.size}px`,
backgroundColor: p.shape !== "triangle" ? p.color : "transparent",
left: p.left,
animationDelay: p.delay,
animationDuration: p.duration,
transform: `rotate(${p.rotation})`,
};
if (p.shape === "circle") {
style.borderRadius = "50%";
} else if (p.shape === "triangle") {
style.width = "0";
style.height = "0";
style.borderLeft = `${p.size / 2}px solid transparent`;
style.borderRight = `${p.size / 2}px solid transparent`;
style.borderBottom = `${p.size}px solid ${p.color}`;
}
return (
<div
key={p.id}
className="confetti-particle"
style={style}
>
<div className="confetti-swayer" style={{ width: "100%", height: "100%" }} />
</div>
);
})}
</div>
</>
);
}
+11 -3
View File
@@ -1,6 +1,6 @@
import { useState } from "react";
export default function QuestionCard({ frage, antworten, onNext, isLast }) {
export default function QuestionCard({ frage, antworten, onNext, onCheck, isLast }) {
const [selected, setSelected] = useState([]);
const [checked, setChecked] = useState(false); // wurde „Überprüfen“ gedrückt
@@ -17,7 +17,15 @@ export default function QuestionCard({ frage, antworten, onNext, isLast }) {
const isCorrect = (idx) => antworten[idx].korrekt;
const wasSelected = (idx) => selected.includes(idx);
const handleCheck = () => setChecked(true);
const handleCheck = () => {
setChecked(true);
const correct = antworten.every((antwort, idx) => {
return antwort.korrekt === selected.includes(idx);
});
if (onCheck) {
onCheck(correct);
}
};
const handleNext = () => {
setSelected([]);
@@ -26,7 +34,7 @@ export default function QuestionCard({ frage, antworten, onNext, isLast }) {
};
return (
<div className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 p-6 rounded-xl shadow-lg transition transform hover:-translate-y-1 hover:shadow-xl">
<div className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 p-6 rounded-xl shadow-lg transition transform hover:-translate-y-1 hover:shadow-xl animate-float-in">
<h2 className="text-lg font-semibold mb-4">{frage}</h2>
<div className="space-y-2">
{antworten.map((antwort, idx) => {