WIP push-to-talk Letta chat frontend
1<script lang="ts">
2 import type { HTMLSelectAttributes } from "svelte/elements";
3
4 type DropdownProps = HTMLSelectAttributes & {
5 label: string;
6 initialOption: string | Promise<string>;
7 options: DropdownOption[] | Promise<DropdownOption[]>;
8 onSelect: (value: string) => void | Promise<void>;
9 };
10
11 interface DropdownOption {
12 label: string;
13 value: string;
14 }
15
16 let {
17 label,
18 initialOption,
19 options,
20 onSelect,
21 required,
22 ...props
23 }: DropdownProps = $props();
24
25 let value = $state("loading");
26 let err = $state("");
27 let isLoading = $derived(!Array.isArray(options));
28
29 $effect(() => {
30 Promise.allSettled([
31 Promise.resolve(initialOption),
32 Promise.resolve(options),
33 ]).then(([init, opts]) => {
34 if (init.status === "fulfilled") {
35 value = init.value;
36 }
37
38 if (opts.status === "rejected") {
39 err = opts.reason;
40 }
41
42 isLoading = false;
43 });
44 });
45</script>
46
47<label class="flex flex-col gap-0.5 w-full">
48 <span class="text-sm text-rose-pine-subtle">{label}</span>
49 <div
50 class="flex gap-2"
51 >
52 <select
53 {...props}
54 bind:value
55 class={[
56 "bg-rose-pine-surface px-3 py-2 w-full flex-1 border-1 border-rose-pine-muted/20 rounded-sm focus:ring-2 ring-rose-pine-iris text-rose-pine-text placeholder:text-rose-pine-subtle",
57 isLoading && "font-skeleton animate-pulse",
58 props.class,
59 ]}
60 onchange={(e) => onSelect(e.currentTarget.value)}
61 >
62 {#await Promise.resolve(options)}
63 <option value="loading">Loading...</option>
64 {:then opts}
65 {#each opts as { value, label }}
66 <option {value}>{label}</option>
67 {/each}
68 {/await}
69 </select>
70 {#if required || isLoading}
71 <div class="flex items-center justify-center">
72 <span
73 class={[
74 "size-6",
75 isLoading
76 ? "icon-[svg-spinners--90-ring-with-bg] text-rose-pine-text"
77 : value
78 ? "icon-[tabler--circle-check-filled] text-rose-pine-foam"
79 : "icon-[tabler--exclamation-circle] text-rose-pine-love",
80 ]}
81 ></span>
82 </div>
83 {/if}
84 </div>
85 {#if err}
86 <p class="mt-1 text-rose-pine-love">{err}</p>
87 {/if}
88</label>