Files
annu-kute-ced/src/lib/components/CountdownClock.svelte
T

89 lines
2.2 KiB
Svelte
Raw Normal View History

<script lang="ts">
import { onMount } from 'svelte';
import type { Locale } from '$lib/types';
let { target, locale, onReached = undefined }: { target: string; locale: Locale; onReached?: () => void } = $props();
let remaining = $state<{ days: number; hours: number; minutes: number; seconds: number } | null>(
null
);
let reached = false;
function compute(): typeof remaining {
const targetTime = new Date(target.replace(' ', 'T')).getTime();
const diff = targetTime - Date.now();
if (Number.isNaN(targetTime)) return null;
if (diff <= 0) {
if (!reached) {
reached = true;
onReached?.();
}
return null;
}
return {
days: Math.floor(diff / 86_400_000),
hours: Math.floor((diff % 86_400_000) / 3_600_000),
minutes: Math.floor((diff % 3_600_000) / 60_000),
seconds: Math.floor((diff % 60_000) / 1000)
};
}
onMount(() => {
remaining = compute();
const interval = setInterval(() => (remaining = compute()), 1000);
return () => clearInterval(interval);
});
const labels = $derived(
locale === 'en'
? { days: 'days', hours: 'hours', minutes: 'minutes', seconds: 'seconds' }
: { days: 'jours', hours: 'heures', minutes: 'minutes', seconds: 'secondes' }
);
</script>
{#if remaining}
<div class="countdown" role="timer" aria-live="off">
{#each [['days', remaining.days], ['hours', remaining.hours], ['minutes', remaining.minutes], ['seconds', remaining.seconds]] as [key, value] (key)}
<div class="unit">
<span class="value">{String(value).padStart(2, '0')}</span>
<span class="label">{labels[key as keyof typeof labels]}</span>
</div>
{/each}
</div>
{/if}
<style>
.countdown {
display: flex;
gap: var(--space-2);
flex-wrap: wrap;
}
.unit {
display: flex;
flex-direction: column;
align-items: center;
padding: var(--space-1) var(--space-2);
background: var(--card-bg);
border: var(--border-card);
border-top: 4px solid var(--accent);
border-radius: var(--radius-sm);
min-width: 80px;
}
.value {
font-family: var(--font-display);
font-weight: 900;
font-size: 2rem;
font-variant-numeric: tabular-nums;
color: var(--accent);
}
.label {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.03em;
color: var(--muted);
}
</style>