feat: Puente Mayúscula-Minúscula + TTS SpeechSynthesis + E2E fixes
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { motion } from "framer-motion"
|
||||
|
||||
interface TemporizadorVisualProps {
|
||||
durationMinutes: number
|
||||
onComplete: () => void
|
||||
running: boolean
|
||||
}
|
||||
|
||||
export default function TemporizadorVisual({
|
||||
durationMinutes,
|
||||
onComplete,
|
||||
running,
|
||||
}: TemporizadorVisualProps) {
|
||||
const [remaining, setRemaining] = useState(durationMinutes * 60)
|
||||
const totalSec = durationMinutes * 60
|
||||
const intervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const onCompleteRef = useRef(onComplete)
|
||||
onCompleteRef.current = onComplete
|
||||
|
||||
useEffect(() => {
|
||||
if (running) {
|
||||
intervalRef.current = setInterval(() => {
|
||||
setRemaining((prev) => {
|
||||
if (prev <= 1) {
|
||||
clearInterval(intervalRef.current!)
|
||||
onCompleteRef.current()
|
||||
return 0
|
||||
}
|
||||
return prev - 1
|
||||
})
|
||||
}, 1000)
|
||||
} else {
|
||||
if (intervalRef.current) clearInterval(intervalRef.current)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (intervalRef.current) clearInterval(intervalRef.current)
|
||||
}
|
||||
}, [running])
|
||||
|
||||
const fraction = remaining / totalSec
|
||||
const degrees = fraction * 360
|
||||
const minutes = Math.floor(remaining / 60)
|
||||
const seconds = remaining % 60
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="relative w-20 h-20">
|
||||
<svg className="w-full h-full -rotate-90" viewBox="0 0 100 100">
|
||||
<circle
|
||||
cx="50" cy="50" r="42"
|
||||
fill="none"
|
||||
stroke="#E8E0D8"
|
||||
strokeWidth="8"
|
||||
/>
|
||||
<circle
|
||||
cx="50" cy="50" r="42"
|
||||
fill="none"
|
||||
stroke={fraction > 0.3 ? "#8CB8A0" : "#D4A574"}
|
||||
strokeWidth="8"
|
||||
strokeDasharray="264"
|
||||
strokeDashoffset={264 * (1 - fraction)}
|
||||
strokeLinecap="round"
|
||||
className="transition-all duration-1000 ease-linear"
|
||||
/>
|
||||
</svg>
|
||||
<span className="absolute inset-0 flex items-center justify-center text-lg font-display font-bold text-foreground">
|
||||
{minutes}:{seconds.toString().padStart(2, "0")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user