your personal website on atproto - mirror blento.app
at remove-extra-buttons 185 lines 6.4 kB view raw
1<script lang="ts"> 2 import NumberFlow, { NumberFlowGroup } from '@number-flow/svelte'; 3 import type { ContentComponentProps } from '../types'; 4 import type { CountdownCardData } from './index'; 5 import { onMount } from 'svelte'; 6 7 let { item }: ContentComponentProps = $props(); 8 9 let cardData = $derived(item.cardData as CountdownCardData); 10 11 let now = $state(new Date()); 12 13 onMount(() => { 14 const interval = setInterval(() => { 15 now = new Date(); 16 }, 1000); 17 return () => clearInterval(interval); 18 }); 19 20 // Countdown to target date 21 let eventDiff = $derived.by(() => { 22 if (!cardData.targetDate) return null; 23 const target = new Date(cardData.targetDate); 24 return Math.max(0, target.getTime() - now.getTime()); 25 }); 26 27 let eventDays = $derived(eventDiff !== null ? Math.floor(eventDiff / (1000 * 60 * 60 * 24)) : 0); 28 let eventHours = $derived( 29 eventDiff !== null ? Math.floor((eventDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) : 0 30 ); 31 let eventMinutes = $derived( 32 eventDiff !== null ? Math.floor((eventDiff % (1000 * 60 * 60)) / (1000 * 60)) : 0 33 ); 34 let eventSeconds = $derived( 35 eventDiff !== null ? Math.floor((eventDiff % (1000 * 60)) / 1000) : 0 36 ); 37 38 // Check if event is in the past (elapsed mode) 39 let isEventPast = $derived.by(() => { 40 if (!cardData.targetDate) return false; 41 return now.getTime() > new Date(cardData.targetDate).getTime(); 42 }); 43 44 // Elapsed time since past event 45 let elapsedDiff = $derived.by(() => { 46 if (!isEventPast || !cardData.targetDate) return null; 47 return now.getTime() - new Date(cardData.targetDate).getTime(); 48 }); 49 50 let elapsedYears = $derived( 51 elapsedDiff !== null ? Math.floor(elapsedDiff / (1000 * 60 * 60 * 24 * 365)) : 0 52 ); 53 let elapsedDays = $derived( 54 elapsedDiff !== null 55 ? Math.floor((elapsedDiff % (1000 * 60 * 60 * 24 * 365)) / (1000 * 60 * 60 * 24)) 56 : 0 57 ); 58 let elapsedHours = $derived( 59 elapsedDiff !== null ? Math.floor((elapsedDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) : 0 60 ); 61 let elapsedMinutes = $derived( 62 elapsedDiff !== null ? Math.floor((elapsedDiff % (1000 * 60 * 60)) / (1000 * 60)) : 0 63 ); 64 let elapsedSeconds = $derived( 65 elapsedDiff !== null ? Math.floor((elapsedDiff % (1000 * 60)) / 1000) : 0 66 ); 67</script> 68 69<div class="@container flex h-full w-full flex-col items-center justify-center p-4"> 70 {#if isEventPast && elapsedDiff !== null} 71 <!-- Elapsed time since past event --> 72 <NumberFlowGroup> 73 <div 74 class="text-base-900 dark:text-base-100 accent:text-base-900 flex items-baseline gap-4 text-center @sm:gap-6 @md:gap-8" 75 style="font-variant-numeric: tabular-nums;" 76 > 77 {#if elapsedYears > 0} 78 <div class="flex flex-col items-center"> 79 <NumberFlow 80 value={elapsedYears} 81 trend={1} 82 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 83 /> 84 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs" 85 >{elapsedYears === 1 ? 'year' : 'years'}</span 86 > 87 </div> 88 {/if} 89 {#if elapsedYears > 0 || elapsedDays > 0} 90 <div class="flex flex-col items-center"> 91 <NumberFlow 92 value={elapsedDays} 93 trend={1} 94 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 95 /> 96 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs" 97 >{elapsedDays === 1 ? 'day' : 'days'}</span 98 > 99 </div> 100 {/if} 101 <div class="flex flex-col items-center"> 102 <NumberFlow 103 value={elapsedHours} 104 trend={1} 105 format={{ minimumIntegerDigits: 2 }} 106 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 107 /> 108 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs">hrs</span> 109 </div> 110 <div class="flex flex-col items-center"> 111 <NumberFlow 112 value={elapsedMinutes} 113 trend={1} 114 format={{ minimumIntegerDigits: 2 }} 115 digits={{ 1: { max: 5 } }} 116 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 117 /> 118 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs">min</span> 119 </div> 120 <div class="flex flex-col items-center"> 121 <NumberFlow 122 value={elapsedSeconds} 123 trend={1} 124 format={{ minimumIntegerDigits: 2 }} 125 digits={{ 1: { max: 5 } }} 126 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 127 /> 128 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs">sec</span> 129 </div> 130 </div> 131 </NumberFlowGroup> 132 {:else if eventDiff !== null} 133 <!-- Countdown to future event --> 134 <NumberFlowGroup> 135 <div 136 class="text-base-900 dark:text-base-100 accent:text-base-900 flex items-baseline gap-4 text-center @sm:gap-6 @md:gap-8" 137 style="font-variant-numeric: tabular-nums;" 138 > 139 {#if eventDays > 0} 140 <div class="flex flex-col items-center"> 141 <NumberFlow 142 value={eventDays} 143 trend={-1} 144 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 145 /> 146 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs" 147 >{eventDays === 1 ? 'day' : 'days'}</span 148 > 149 </div> 150 {/if} 151 <div class="flex flex-col items-center"> 152 <NumberFlow 153 value={eventHours} 154 trend={-1} 155 format={{ minimumIntegerDigits: 2 }} 156 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 157 /> 158 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs">hrs</span> 159 </div> 160 <div class="flex flex-col items-center"> 161 <NumberFlow 162 value={eventMinutes} 163 trend={-1} 164 format={{ minimumIntegerDigits: 2 }} 165 digits={{ 1: { max: 5 } }} 166 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 167 /> 168 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs">min</span> 169 </div> 170 <div class="flex flex-col items-center"> 171 <NumberFlow 172 value={eventSeconds} 173 trend={-1} 174 format={{ minimumIntegerDigits: 2 }} 175 digits={{ 1: { max: 5 } }} 176 class="text-3xl font-bold @xs:text-4xl @sm:text-5xl @md:text-6xl @lg:text-7xl" 177 /> 178 <span class="text-base-500 dark:text-base-400 accent:text-accent-950 text-xs">sec</span> 179 </div> 180 </div> 181 </NumberFlowGroup> 182 {:else} 183 <div class="text-base-500 text-sm">Set a target date in settings</div> 184 {/if} 185</div>