75 lines
2.0 KiB
TypeScript
75 lines
2.0 KiB
TypeScript
"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)
|
|
|
|
useEffect(() => {
|
|
if (running) {
|
|
intervalRef.current = setInterval(() => {
|
|
setRemaining((prev) => {
|
|
if (prev <= 1) {
|
|
clearInterval(intervalRef.current!)
|
|
onComplete()
|
|
return 0
|
|
}
|
|
return prev - 1
|
|
})
|
|
}, 1000)
|
|
} else {
|
|
if (intervalRef.current) clearInterval(intervalRef.current)
|
|
}
|
|
|
|
return () => {
|
|
if (intervalRef.current) clearInterval(intervalRef.current)
|
|
}
|
|
}, [running, onComplete])
|
|
|
|
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>
|
|
)
|
|
}
|