Our Personal Data Server from scratch!
tranquil.farm
pds
rust
database
fun
oauth
atproto
1<script lang="ts">
2 interface Props {
3 value: string
4 domains: string[]
5 selectedDomain: string
6 disabled?: boolean
7 placeholder?: string
8 id?: string
9 autocomplete?: HTMLInputElement['autocomplete']
10 checkAvailability?: (fullHandle: string) => Promise<boolean>
11 available?: boolean | null
12 checking?: boolean
13 onInput: (value: string) => void
14 onDomainChange: (domain: string) => void
15 }
16
17 let {
18 value,
19 domains,
20 selectedDomain,
21 disabled = false,
22 placeholder = 'username',
23 id = 'handle',
24 autocomplete = 'off',
25 checkAvailability,
26 available = $bindable<boolean | null>(null),
27 checking = $bindable(false),
28 onInput,
29 onDomainChange,
30 }: Props = $props()
31
32 const showDomainSelect = $derived(domains.length > 1 && !value.includes('.'))
33
34 let checkTimeout: ReturnType<typeof setTimeout> | null = null
35
36 $effect(() => {
37 void value
38 void selectedDomain
39 if (!checkAvailability) return
40 if (checkTimeout) clearTimeout(checkTimeout)
41 available = null
42 if (value.trim().length >= 3 && !value.includes('.')) {
43 checkTimeout = setTimeout(() => runCheck(), 400)
44 }
45 })
46
47 async function runCheck() {
48 if (!checkAvailability) return
49 const fullHandle = value.includes('.')
50 ? value.trim()
51 : `${value.trim()}.${selectedDomain}`
52 checking = true
53 try {
54 available = await checkAvailability(fullHandle)
55 } catch {
56 available = null
57 } finally {
58 checking = false
59 }
60 }
61</script>
62
63<div class="handle-input-group">
64 <input
65 {id}
66 type="text"
67 value={value}
68 {placeholder}
69 {disabled}
70 autocomplete={autocomplete}
71 required
72 oninput={(e) => onInput((e.target as HTMLInputElement).value)}
73 />
74 {#if showDomainSelect}
75 <select value={selectedDomain} onchange={(e) => onDomainChange((e.target as HTMLSelectElement).value)}>
76 {#each domains as domain}
77 <option value={domain}>.{domain}</option>
78 {/each}
79 </select>
80 {:else if domains.length === 1 && !value.includes('.')}
81 <span class="domain-suffix">.{domains[0]}</span>
82 {/if}
83</div>