your personal website on atproto - mirror blento.app
at update-link-card 101 lines 2.6 kB view raw
1<script lang="ts"> 2 // @ts-nocheck 3 import { TimeField } from 'bits-ui'; 4 import { Time } from '@internationalized/date'; 5 import { untrack } from 'svelte'; 6 7 let { 8 value = $bindable(''), 9 required = false, 10 locale = 'en' 11 }: { 12 value: string; 13 required?: boolean; 14 locale?: string; 15 } = $props(); 16 17 let internalValue: Time | undefined = $state(undefined); 18 19 function parseTimeStr(str: string): Time | undefined { 20 if (!str) return undefined; 21 const [hourStr, minuteStr] = str.split(':'); 22 const hour = parseInt(hourStr, 10); 23 const minute = parseInt(minuteStr, 10); 24 if (isNaN(hour) || isNaN(minute)) return undefined; 25 return new Time(hour, minute); 26 } 27 28 function formatTimeStr(t: Time): string { 29 const h = String(t.hour).padStart(2, '0'); 30 const m = String(t.minute).padStart(2, '0'); 31 return `${h}:${m}`; 32 } 33 34 $effect(() => { 35 const parsed = parseTimeStr(value); 36 untrack(() => { 37 if (parsed) { 38 if ( 39 !internalValue || 40 parsed.hour !== internalValue.hour || 41 parsed.minute !== internalValue.minute 42 ) { 43 internalValue = parsed; 44 } 45 } else { 46 internalValue = undefined; 47 } 48 }); 49 }); 50 51 function handleValueChange(newVal: Time | undefined) { 52 if (newVal && newVal instanceof Time) { 53 internalValue = newVal; 54 value = formatTimeStr(newVal); 55 } 56 } 57</script> 58 59<TimeField.Root 60 bind:value={internalValue} 61 onValueChange={handleValueChange} 62 granularity="minute" 63 {locale} 64 {required} 65> 66 <div 67 class="border-base-300 bg-base-100 text-base-900 focus-within:border-accent-500 dark:border-base-700 dark:bg-base-800 dark:text-base-100 dark:focus-within:border-accent-400 flex items-center rounded-xl border px-2.5 py-1.5 text-sm transition-colors" 68 > 69 <TimeField.Input> 70 {#snippet children({ segments })} 71 {#each segments as segment, i (segment.part + i)} 72 {#if segment.part === 'literal'} 73 <span class="text-base-400 dark:text-base-500">{segment.value}</span> 74 {:else} 75 <TimeField.Segment 76 part={segment.part} 77 class="hover:bg-base-200 focus:bg-base-200 dark:hover:bg-base-700 dark:focus:bg-base-700 rounded px-0.5 focus:outline-none" 78 > 79 {segment.value} 80 </TimeField.Segment> 81 {/if} 82 {/each} 83 {/snippet} 84 </TimeField.Input> 85 86 <svg 87 xmlns="http://www.w3.org/2000/svg" 88 fill="none" 89 viewBox="0 0 24 24" 90 stroke-width="1.5" 91 stroke="currentColor" 92 class="text-base-400 dark:text-base-500 ml-auto size-4 pl-0.5" 93 > 94 <path 95 stroke-linecap="round" 96 stroke-linejoin="round" 97 d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" 98 /> 99 </svg> 100 </div> 101</TimeField.Root>