UNPKG

@gfazioli/mantine-clock

Version:

React Clock components and hooks for Mantine with timezone support, countdown timers, customization options, and real-time updates.

237 lines (234 loc) 6.73 kB
'use client'; import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; import isLeapYear from 'dayjs/plugin/isLeapYear'; import timezonePlugin from 'dayjs/plugin/timezone'; import utc from 'dayjs/plugin/utc'; import weekOfYear from 'dayjs/plugin/weekOfYear'; import { useState, useRef, useEffect, useCallback } from 'react'; import { useMounted } from '@mantine/hooks'; dayjs.extend(utc); dayjs.extend(timezonePlugin); dayjs.extend(weekOfYear); dayjs.extend(isLeapYear); dayjs.extend(duration); dayjs.extend(utc); dayjs.extend(timezonePlugin); dayjs.extend(weekOfYear); dayjs.extend(isLeapYear); dayjs.extend(duration); function useClockCountDown({ enabled = true, timezone = "UTC", updateFrequency = 1e3, use24Hours = true, padSeconds = false, padMinutes = false, padHours = false, targetDate, hours = 0, minutes = 0, seconds = 0, onCountDownCompleted }) { const mounted = useMounted(); const [remainingTime, setRemainingTime] = useState(0); const [isRunning, setIsRunning] = useState(false); const [isCompleted, setIsCompleted] = useState(false); const intervalRef = useRef(null); const initialDurationRef = useRef(0); const targetDateRef = useRef(null); const completedCallbackRef = useRef(onCountDownCompleted); const initialEnabledRef = useRef(enabled); useEffect(() => { completedCallbackRef.current = onCountDownCompleted; }, [onCountDownCompleted]); const calculateInitialDuration = useCallback(() => { if (!mounted) { return 0; } let target; if (targetDate) { target = dayjs(targetDate).tz(timezone); } else { const now2 = dayjs().tz(timezone); const h = Math.max(0, hours); const m = Math.max(0, minutes); const s = Math.max(0, seconds); if (h === 0 && m === 0 && s === 0) { target = now2.add(1, "hour"); } else { target = now2.add(h, "hour").add(m, "minute").add(s, "second"); } } targetDateRef.current = target; const now = dayjs().tz(timezone); return Math.max(0, target.diff(now)); }, [targetDate, hours, minutes, seconds, timezone, mounted]); useEffect(() => { if (!mounted) { return; } const duration2 = calculateInitialDuration(); initialDurationRef.current = duration2; setRemainingTime(duration2); setIsCompleted(duration2 <= 0); if (enabled && duration2 > 0) { setIsRunning(true); } else { setIsRunning(false); } }, [mounted, calculateInitialDuration, enabled]); useEffect(() => { if (!mounted || !isRunning || isCompleted) { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } return; } intervalRef.current = setInterval(() => { setRemainingTime((prev) => { const newTime = Math.max(0, prev - updateFrequency); if (newTime <= 0) { setIsCompleted(true); setIsRunning(false); if (completedCallbackRef.current) { completedCallbackRef.current(); } } return newTime; }); }, updateFrequency); return () => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } }; }, [mounted, isRunning, isCompleted, updateFrequency]); const start = useCallback(() => { if (!mounted || isCompleted) { return; } setIsRunning(true); }, [mounted, isCompleted]); const pause = useCallback(() => { setIsRunning(false); }, []); const resume = useCallback(() => { if (!mounted || isCompleted || remainingTime <= 0) { return; } setIsRunning(true); }, [mounted, isCompleted, remainingTime]); const reset = useCallback(() => { setIsRunning(false); setIsCompleted(false); const duration2 = calculateInitialDuration(); initialDurationRef.current = duration2; setRemainingTime(duration2); if (initialEnabledRef.current && duration2 > 0) { setIsRunning(true); } }, [calculateInitialDuration]); if (!mounted) { const staticHours = padHours ? "00" : 0; const staticMinutes = padMinutes ? "00" : 0; const staticSeconds = padSeconds ? "00" : 0; return { year: 0, month: 1, day: 0, week: 0, isLeap: false, hours: staticHours, minutes: staticMinutes, seconds: staticSeconds, milliseconds: 0, amPm: use24Hours ? void 0 : "AM", isCompleted: false, isRunning: false, totalMilliseconds: 0, start: () => { }, pause: () => { }, reset: () => { }, resume: () => { } }; } if (isCompleted || remainingTime <= 0) { const staticHours = padHours ? "00" : 0; const staticMinutes = padMinutes ? "00" : 0; const staticSeconds = padSeconds ? "00" : 0; return { year: 0, month: 0, day: 0, week: 0, isLeap: false, hours: staticHours, minutes: staticMinutes, seconds: staticSeconds, milliseconds: 0, amPm: use24Hours ? void 0 : "AM", isCompleted: true, isRunning: false, totalMilliseconds: 0, start: () => { }, pause, reset, resume: () => { } }; } const dur = dayjs.duration(remainingTime); const remainingYears = Math.floor(dur.asYears()); const remainingMonths = Math.floor(dur.asMonths()) % 12; const remainingDays = Math.floor(dur.asDays()) % 365; const remainingWeeks = Math.floor(dur.asWeeks()); let remainingHours = Math.floor(dur.asHours()) % 24; let remainingMinutes = dur.minutes(); let remainingSecondsCount = dur.seconds(); const remainingMilliseconds = dur.milliseconds(); let amPm; if (!use24Hours) { amPm = remainingHours >= 12 ? "PM" : "AM"; remainingHours = remainingHours % 12 || 12; } if (padHours) { remainingHours = remainingHours.toString().padStart(2, "0"); } if (padMinutes) { remainingMinutes = remainingMinutes.toString().padStart(2, "0"); } if (padSeconds) { remainingSecondsCount = remainingSecondsCount.toString().padStart(2, "0"); } const isLeap = targetDateRef.current ? targetDateRef.current.isLeapYear() : false; return { year: remainingYears, month: remainingMonths + 1, day: remainingDays, week: remainingWeeks, isLeap, hours: remainingHours, minutes: remainingMinutes, seconds: remainingSecondsCount, milliseconds: remainingMilliseconds, amPm, isCompleted: false, isRunning, totalMilliseconds: remainingTime, start, pause, reset, resume }; } export { useClockCountDown }; //# sourceMappingURL=use-clock-count-down.mjs.map