import React, { useState, useEffect } from 'react'; export type CountdownLocale = 'ru' | 'ru-passive' | 'en'; export type CountdownFormat = 's' | 'ss' | 'mm ss' | 'm:s' | 'm' | 'mm'; export const puralize = (amount: number, locale: CountdownLocale): string => { switch (locale) { case ('ru'): case ('ru-passive'): switch (amount % 10) { case (1): return locale === 'ru-passive' ? 'у' : 'а'; case (2): case (3): case (4): return 'ы'; default: return ''; } case ('en'): default: switch (amount) { case (1): return ''; default: return 's'; } } }; export const formatTime = (seconds: number, format: CountdownFormat, locale: CountdownLocale): string => { const fullMminutesLeft = Math.floor(seconds / 60); const secondsLeft = seconds % 60; const minutesLeft = Math.ceil(seconds / 60); switch (format) { case ('m:s'): return `${fullMminutesLeft >= 10 ? '' : '0'}${fullMminutesLeft}:${secondsLeft >= 10 ? '' : '0'}${secondsLeft}`; case ('mm ss'): switch (locale) { case ('ru'): case ('ru-passive'): return `${fullMminutesLeft > 0 ? `${fullMminutesLeft} минут${puralize(fullMminutesLeft, 'ru')} ` : '' }${secondsLeft} секунд${puralize(secondsLeft, locale)}`; case ('en'): default: return `${fullMminutesLeft > 0 ? `${fullMminutesLeft} minute${puralize(fullMminutesLeft, locale)} ` : '' }${secondsLeft} second${puralize(secondsLeft, locale)}`; } case ('ss'): switch (locale) { case ('ru'): case ('ru-passive'): return `${seconds} секунд${puralize(seconds, locale)}`; case ('en'): default: return `${seconds} second${puralize(seconds, locale)}`; } case ('mm'): switch (locale) { case ('ru'): case ('ru-passive'): return `${minutesLeft} минут${puralize(minutesLeft, locale)}`; case ('en'): default: return `${minutesLeft} minute${puralize(minutesLeft, locale)}`; } case ('m'): return `${Math.ceil(seconds / 60)}`; case ('s'): default: return `${seconds}`; } }; export type CountdownProps = { seconds: number; pause?: boolean; format?: CountdownFormat; onExpire?: () => void; onChange?: (seconds: number) => void; locale?: CountdownLocale; } export const Countdown = ({ seconds, pause = false, format = 'm:s', onExpire, onChange, locale = 'en', }: CountdownProps) => { const [startAt, setStartAt] = useState(Date.now()); const [timeLeft, setTimeLeft] = useState(seconds); useEffect(() => { setStartAt(Date.now()); setTimeLeft(seconds); }, [seconds]); useEffect(() => { if (timeLeft <= 0) { if (onExpire) { onExpire(); } return () => undefined; } const interval = setInterval(() => { const currentTime = seconds - Math.ceil((Date.now() - startAt) / 1000); if (currentTime !== timeLeft) { if (!pause) { setTimeLeft(currentTime); if (onChange) { onChange(currentTime); } } else { setStartAt(v => v + 1000); } } }, 100); return () => clearInterval(interval); }, [pause, timeLeft, startAt]); return ( <>{formatTime(timeLeft, format, locale)} ); }; export default Countdown;