Bug fixes

This commit is contained in:
2026-07-24 16:33:44 +02:00
parent 6b71a98a45
commit b6512a318d
156 changed files with 69800 additions and 51 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Plus, RotateCcw, Sparkles, X } from "lucide-react";
import { gsap } from "@/lib/gsap";
@@ -51,6 +51,8 @@ export default function WheelOfFortune() {
const pointerRef = useRef<HTMLDivElement>(null);
const rotationRef = useRef(0);
const lastTickRef = useRef(-1);
const optionInputRefs = useRef<Array<HTMLInputElement | null>>([]);
const justAddedRef = useRef(false);
const { play } = useSound();
const reduced = usePrefersReducedMotion();
@@ -115,20 +117,49 @@ export default function WheelOfFortune() {
});
};
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
const activeTag = document.activeElement?.tagName.toLowerCase();
if (activeTag === "input" || activeTag === "textarea") return;
if (e.code === "Space") {
e.preventDefault();
spin();
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [spinning, n]);
const updateOption = (i: number, value: string) => {
if (spinning) return;
setOptions((opts) => opts.map((o, idx) => (idx === i ? value : o)));
setWinner(null);
};
const addOption = () => {
if (n >= MAX_OPTIONS) return;
if (spinning || n >= MAX_OPTIONS) return;
setOptions((opts) => [...opts, ""]);
setWinner(null);
justAddedRef.current = true;
};
useEffect(() => {
if (!justAddedRef.current) return;
justAddedRef.current = false;
const last = optionInputRefs.current[options.length - 1];
last?.scrollIntoView({ block: "nearest", behavior: "smooth" });
last?.focus();
}, [options.length]);
const removeOption = (i: number) => {
if (spinning || n <= MIN_OPTIONS) return;
setOptions((opts) => opts.filter((_, idx) => idx !== i));
setWinner(null);
};
const removeOption = (i: number) => {
if (n <= MIN_OPTIONS) return;
setOptions((opts) => opts.filter((_, idx) => idx !== i));
const resetOptions = () => {
if (spinning) return;
setOptions(DEFAULT_OPTIONS);
setWinner(null);
};
@@ -141,14 +172,19 @@ export default function WheelOfFortune() {
<div className="relative w-full max-w-[400px]">
{/* Zeiger */}
<div
ref={pointerRef}
ref={pointerRef}
aria-hidden
className="absolute -top-1.5 left-1/2 z-10 h-9 w-7 -translate-x-1/2 bg-primary shadow-[0_6px_16px_rgb(108_92_231/0.5)]"
className="absolute -top-1.5 left-1/2 z-10 h-9 w-7 -translate-x-1/2 bg-primary shadow-[0_6px_16px_rgb(13_122_87/0.35)]"
style={{
clipPath: "polygon(50% 100%, 0 0, 100% 0)",
}}
/>
<svg viewBox="0 0 420 420" className="w-full drop-shadow-[0_20px_36px_rgb(23_23_28/0.14)]">
<svg
viewBox="0 0 420 420"
role="img"
aria-label="Glücksrad mit Zufalls-Optionen"
className="w-full drop-shadow-[0_20px_36px_rgb(23_23_28/0.14)]"
>
{/* äußerer Ring */}
<circle cx={C} cy={C} r={206} fill="#ffffff" stroke="#e9e9e4" />
<g ref={wheelRef}>
@@ -199,7 +235,7 @@ export default function WheelOfFortune() {
</g>
{/* Nabe */}
<circle cx={C} cy={C} r={30} fill="#ffffff" stroke="#e9e9e4" strokeWidth={2} />
<circle cx={C} cy={C} r={9} fill="#6c5ce7" />
<circle cx={C} cy={C} r={9} fill="#0d7a57" />
</svg>
</div>
@@ -234,18 +270,16 @@ export default function WheelOfFortune() {
</div>
{/* Optionen-Editor */}
<div className="rounded-3xl border border-line bg-cream/70 p-5">
<div className="rounded-3xl border border-line bg-surface p-5 shadow-soft">
<div className="flex items-center justify-between">
<h3 className="text-sm font-semibold uppercase tracking-[0.16em] text-ink-soft">
Optionen ({n}/{MAX_OPTIONS})
</h3>
<button
type="button"
onClick={() => {
setOptions(DEFAULT_OPTIONS);
setWinner(null);
}}
className="cursor-pointer text-xs font-medium text-ink-soft underline-offset-2 transition-colors hover:text-primary hover:underline"
onClick={resetOptions}
disabled={spinning}
className="cursor-pointer text-xs font-medium text-ink-soft underline-offset-2 transition-colors hover:text-[#0d7a57] hover:underline disabled:cursor-not-allowed disabled:opacity-40"
>
Zurücksetzen
</button>
@@ -260,17 +294,21 @@ export default function WheelOfFortune() {
style={{ background: PALETTE[i % PALETTE.length] }}
/>
<input
ref={(el) => {
optionInputRefs.current[i] = el;
}}
value={option}
onChange={(e) => updateOption(i, e.target.value)}
disabled={spinning}
maxLength={24}
placeholder={`Option ${i + 1}`}
aria-label={`Option ${i + 1}`}
className="w-full rounded-xl border border-line bg-surface px-3.5 py-2.5 text-sm font-medium outline-none transition focus:border-primary/60 focus:ring-2 focus:ring-primary/20"
className="w-full rounded-xl border border-line bg-paper/50 px-3.5 py-2.5 text-sm font-medium outline-none transition focus:border-[#0d7a57] focus:bg-surface focus:ring-2 focus:ring-[#0d7a57]/20 disabled:cursor-not-allowed disabled:opacity-60"
/>
<button
type="button"
onClick={() => removeOption(i)}
disabled={n <= MIN_OPTIONS}
disabled={spinning || n <= MIN_OPTIONS}
aria-label={`Option ${i + 1} entfernen`}
className="flex h-9 w-9 shrink-0 cursor-pointer items-center justify-center rounded-xl text-ink-soft transition-colors hover:bg-clay-soft hover:text-clay disabled:cursor-not-allowed disabled:opacity-30"
>
@@ -283,8 +321,8 @@ export default function WheelOfFortune() {
<button
type="button"
onClick={addOption}
disabled={n >= MAX_OPTIONS}
className="mt-3 inline-flex w-full cursor-pointer items-center justify-center gap-2 rounded-xl border-2 border-dashed border-line px-4 py-2.5 text-sm font-semibold text-ink-soft transition-colors hover:border-primary/50 hover:text-primary disabled:cursor-not-allowed disabled:opacity-40"
disabled={spinning || n >= MAX_OPTIONS}
className="mt-3 inline-flex w-full cursor-pointer items-center justify-center gap-2 rounded-xl border-2 border-dashed border-line px-4 py-2.5 text-sm font-semibold text-ink-soft transition-colors hover:border-[#0d7a57] hover:bg-[#e6f7f0]/50 hover:text-[#0d7a57] disabled:cursor-not-allowed disabled:opacity-40"
>
<Plus className="h-4 w-4" />
Option hinzufügen