Created
February 28, 2024 08:29
-
-
Save itsmnthn/87bf186216a6baf54722147dbb4274d6 to your computer and use it in GitHub Desktop.
useCountdownString vue composable to count down time supports new Date(...).toISOString() input
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { MaybeRef } from 'vue' | |
import { computed, onMounted, onUnmounted, ref, toValue, watch } from 'vue' | |
/** | |
* Keep track of the time left until a future timestamp string | |
* | |
* @param futureTimestampRef may be reactive `new Date().toISOString()` string | |
*/ | |
export function useCountdownString(futureTimestampRef: MaybeRef<string>) { | |
// Calculates the initial difference in seconds | |
const calculateInitialSeconds = () => { | |
const now = Date.now() | |
const futureTime = new Date(toValue(futureTimestampRef)).getTime() | |
return Math.floor((futureTime - now) / 1000) | |
} | |
const totalSeconds = ref(calculateInitialSeconds()) | |
let intervalId: NodeJS.Timeout | null = null | |
const days = computed(() => Math.floor(totalSeconds.value / (3600 * 24))) | |
const hours = computed(() => Math.floor((totalSeconds.value % (3600 * 24)) / 3600)) | |
const minutes = computed(() => Math.floor((totalSeconds.value % 3600) / 60)) | |
const seconds = computed(() => totalSeconds.value % 60) | |
const formatTime = () => { | |
if (totalSeconds.value < 0) | |
return '' // Handle past timestamps gracefully | |
let timeString = '' | |
if (days.value > 0) | |
timeString += `${days.value}D ` | |
if (hours.value > 0) | |
timeString += `${hours.value}H ` | |
if (minutes.value > 0) | |
timeString += `${minutes.value}M ` | |
if (seconds.value > 0) | |
timeString += `${seconds.value}S` | |
return timeString.trim() // Trim any trailing space | |
} | |
const countdownString = computed(() => formatTime()) | |
const stop = () => { | |
if (intervalId !== null) { | |
clearInterval(intervalId) | |
intervalId = null | |
} | |
} | |
const start = () => { | |
stop() // Clear any existing interval to prevent duplicates | |
intervalId = setInterval(() => { | |
const timeLeft = calculateInitialSeconds() | |
if (timeLeft > 0) { | |
totalSeconds.value = timeLeft | |
} | |
else { | |
totalSeconds.value = 0 | |
stop() | |
} | |
}, 1000) | |
} | |
// restart the countdown wen input changes | |
watch(() => toValue(futureTimestampRef), () => { | |
totalSeconds.value = calculateInitialSeconds() | |
start() | |
}) | |
// Start the countdown automatically when the component is mounted | |
onMounted(start) | |
// Stop the countdown when the component is unmounted to prevent memory leaks | |
onUnmounted(stop) | |
return { countdownString, days, hours, minutes, seconds, start, stop } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment