({ days: 0, hours: 0, minutes: 0, seconds: 0 });
const [isUrgent, setIsUrgent] = useState(false);
const completedRef = useRef(false);
const defaultLabels = {
days: labels.days || 'DAYS', hours: labels.hours || 'HOURS',
minutes: labels.minutes || 'MIN', seconds: labels.seconds || 'SEC',
};
useEffect(() => {
const calculateTimeLeft = (): TimeLeft => {
let totalSeconds: number;
if (target instanceof Date) {
totalSeconds = Math.max(0, Math.floor((target.getTime() - Date.now()) / 1000));
} else {
totalSeconds = Math.max(0, target);
}
return {
days: Math.floor(totalSeconds / 86400),
hours: Math.floor((totalSeconds % 86400) / 3600),
minutes: Math.floor((totalSeconds % 3600) / 60),
seconds: totalSeconds % 60,
};
};
const tick = () => {
const newTime = calculateTimeLeft();
const totalSeconds = newTime.days * 86400 + newTime.hours * 3600 + newTime.minutes * 60 + newTime.seconds;
setTimeLeft(newTime);
setIsUrgent(totalSeconds <= urgentThreshold && totalSeconds > 0);
if (totalSeconds === 0 && !completedRef.current) {
completedRef.current = true;
onComplete?.();
}
};
tick();
const interval = setInterval(tick, 1000);
return () => clearInterval(interval);
}, [target, urgentThreshold, onComplete]);
const pad = (num: number) => String(num).padStart(2, '0');
const { value: valueClass, separator: sepClass, label: labelClass } = sizeClasses[size];
const getValueStyle = () => {
switch (variant) {
case 'neon':
return { color: accentColor, textShadow: `0 0 10px ${accentColor}, 0 0 20px ${accentColor}, 0 0 40px ${accentColor}` };
case 'brutal':
return { background: accentColor, color: '#0a0a0a', padding: '0 0.25em', WebkitTextFillColor: '#0a0a0a' };
case 'minimal':
return { color: '#fafafa' };
default:
return { background: 'linear-gradient(180deg, #fafafa 0%, #888 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' };
}
};
const renderBlock = (value: number, label: string) => (
{pad(value)}
{showLabels && (
{label}
)}
);
const renderSeparator = (key: number) => (
:
);
const blocks = [];
if (format === 'dhms' || format === 'full') {
blocks.push({renderBlock(timeLeft.days, defaultLabels.days)}
);
blocks.push(renderSeparator(1));
}
if (format !== 'ms') {
blocks.push({renderBlock(timeLeft.hours, defaultLabels.hours)}
);
blocks.push(renderSeparator(2));
}
blocks.push({renderBlock(timeLeft.minutes, defaultLabels.minutes)}
);
blocks.push(renderSeparator(3));
blocks.push({renderBlock(timeLeft.seconds, defaultLabels.seconds)}
);
return (
{blocks}
);
}
);
CountdownDisplay.displayName = 'CountdownDisplay';
export default CountdownDisplay;