diff --git a/packages/core/src/bundle/hooks/useStopwatch/useStopwatch.js b/packages/core/src/bundle/hooks/useStopwatch/useStopwatch.js index eb05949e..63211c10 100644 --- a/packages/core/src/bundle/hooks/useStopwatch/useStopwatch.js +++ b/packages/core/src/bundle/hooks/useStopwatch/useStopwatch.js @@ -1,19 +1,24 @@ -import { useEffect, useState } from 'react'; -const getStopwatchTime = (time) => { - if (!time) +import { useState } from 'react'; +import { useInterval } from '../useInterval/useInterval'; +const getStopwatchTime = (count) => { + if (!count) return { days: 0, hours: 0, minutes: 0, seconds: 0, + milliseconds: 0, count: 0 }; - const days = Math.floor(time / 86400); - const hours = Math.floor((time % 86400) / 3600); - const minutes = Math.floor((time % 3600) / 60); - const seconds = Math.floor(time % 60); - return { days, hours, minutes, seconds, count: time }; + const totalSeconds = Math.floor(count / 1000); + const days = Math.floor(totalSeconds / 86400); + const hours = Math.floor((totalSeconds % 86400) / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = Math.floor(totalSeconds % 60); + const milliseconds = count % 1000; + return { days, hours, minutes, seconds, milliseconds, count }; }; +const getMillsDiffOrZero = (millis) => (Date.now() - millis > 0 ? Date.now() - millis : 0); /** * @name useStopwatch * @description - Hook that creates a stopwatch functionality @@ -21,74 +26,57 @@ const getStopwatchTime = (time) => { * * @overload * @param {number} [initialTime=0] The initial time of the timer - * @param {boolean} [options.enabled=true] The enabled state of the timer + * @param {boolean} [options.immediately=false] The enabled state of the timer + * @param {number} [options.updateInterval=1000] The update interval of the timer * @returns {UseStopwatchReturn} An object containing the current time and functions to interact with the timer * * @example - * const { seconds, minutes, start, pause, reset } = useStopwatch(1000, { enabled: false }); + * const { milliseconds, seconds, minutes, start, pause, reset } = useStopwatch(1000, { immediately: false, updateInterval: 1000 }); * * @overload * @param {number} [options.initialTime=0] -The initial time of the timer - * @param {boolean} [options.enabled=true] The enabled state of the timer + * @param {boolean} [options.immediately=true] The enabled state of the timer + * @param {number} [options.updateInterval=1000] The update interval of the timer * @returns {UseStopwatchReturn} An object containing the current time and functions to interact with the timer * * @example - * const { seconds, minutes, start, pause, reset } = useStopwatch({ initialTime: 1000, enabled: false }); + * const { milliseconds, seconds, minutes, start, pause, reset } = useStopwatch({ initialTime: 1000, immediately: false, updateInterval: 1000 }); */ export const useStopwatch = (...params) => { const initialTime = (typeof params[0] === 'number' ? params[0] : params[0]?.initialTime) ?? 0; const options = typeof params[0] === 'number' ? params[1] : params[0]; const immediately = options?.immediately ?? false; - const [time, setTime] = useState(getStopwatchTime(initialTime)); - const [paused, setPaused] = useState(!immediately && !initialTime); - useEffect(() => { - if (paused) return; - const onInterval = () => { - setTime((prevTime) => { - const updatedCount = prevTime.count + 1; - if (updatedCount % 60 === 0) { - return { - ...prevTime, - minutes: prevTime.minutes + 1, - seconds: 0, - count: updatedCount - }; - } - if (updatedCount % (60 * 60) === 0) { - return { - ...prevTime, - hours: prevTime.hours + 1, - minutes: 0, - seconds: 0, - count: updatedCount - }; - } - if (updatedCount % (60 * 60 * 24) === 0) { - return { - ...prevTime, - days: prevTime.days + 1, - hours: 0, - minutes: 0, - seconds: 0, - count: updatedCount - }; - } - return { - ...prevTime, - seconds: prevTime.seconds + 1, - count: updatedCount - }; - }); - }; - const interval = setInterval(() => onInterval(), 1000); - return () => clearInterval(interval); - }, [paused]); + const updateInterval = options?.updateInterval ?? 1000; + const [milliseconds, setMilliseconds] = useState(initialTime); + const [timestamp, setTimestamp] = useState(Date.now() - initialTime); + const interval = useInterval( + () => setMilliseconds(getMillsDiffOrZero(timestamp)), + updateInterval, + { + immediately + } + ); + const start = () => { + if (interval.active) return; + setTimestamp(new Date().getTime() - milliseconds); + interval.resume(); + }; + const pause = () => { + if (!interval.active) return; + setMilliseconds(getMillsDiffOrZero(timestamp)); + interval.pause(); + }; + const reset = () => { + setMilliseconds(initialTime); + setTimestamp(Date.now() - initialTime); + interval.resume(); + }; return { - ...time, - paused, - pause: () => setPaused(true), - start: () => setPaused(false), - reset: () => setTime(getStopwatchTime(initialTime)), - toggle: () => setPaused((prevPause) => !prevPause) + ...getStopwatchTime(milliseconds), + paused: !interval.active, + pause, + start, + reset, + toggle: () => (interval.active ? pause() : start()) }; }; diff --git a/packages/core/src/hooks/useStopwatch/useStopwatch.demo.tsx b/packages/core/src/hooks/useStopwatch/useStopwatch.demo.tsx index 2dd772cc..18376018 100644 --- a/packages/core/src/hooks/useStopwatch/useStopwatch.demo.tsx +++ b/packages/core/src/hooks/useStopwatch/useStopwatch.demo.tsx @@ -1,12 +1,15 @@ import { useStopwatch } from '@siberiacancode/reactuse'; const Demo = () => { - const stopwatch = useStopwatch(); + const stopwatch = useStopwatch({ + updateInterval: 100 + }); return (

- {stopwatch.minutes} m:{stopwatch.seconds} s + {stopwatch.minutes} m:{stopwatch.seconds} s: + {String(stopwatch.milliseconds).padStart(3, '0')} ms