Skip to content

Instantly share code, notes, and snippets.

@itsmnthn
Created February 28, 2024 08:29
Show Gist options
  • Save itsmnthn/87bf186216a6baf54722147dbb4274d6 to your computer and use it in GitHub Desktop.
Save itsmnthn/87bf186216a6baf54722147dbb4274d6 to your computer and use it in GitHub Desktop.
useCountdownString vue composable to count down time supports new Date(...).toISOString() input
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