aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/src/components/CustomSelect.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/components/CustomSelect.svelte')
-rw-r--r--ui/src/components/CustomSelect.svelte43
1 files changed, 40 insertions, 3 deletions
diff --git a/ui/src/components/CustomSelect.svelte b/ui/src/components/CustomSelect.svelte
index 2e89c75..0767471 100644
--- a/ui/src/components/CustomSelect.svelte
+++ b/ui/src/components/CustomSelect.svelte
@@ -13,6 +13,7 @@
placeholder?: string;
disabled?: boolean;
class?: string;
+ allowCustom?: boolean; // New prop to allow custom input
onchange?: (value: string) => void;
}
@@ -22,17 +23,25 @@
placeholder = "Select...",
disabled = false,
class: className = "",
+ allowCustom = false,
onchange
}: Props = $props();
let isOpen = $state(false);
let containerRef: HTMLDivElement;
+ let customInput = $state(""); // State for custom input
let selectedOption = $derived(options.find(o => o.value === value));
+ // Display label: if option exists use its label, otherwise if custom is allowed use raw value, else placeholder
+ let displayLabel = $derived(selectedOption ? selectedOption.label : (allowCustom && value ? value : placeholder));
function toggle() {
if (!disabled) {
isOpen = !isOpen;
+ // When opening, if current value is custom (not in options), pre-fill input
+ if (isOpen && allowCustom && !selectedOption) {
+ customInput = value;
+ }
}
}
@@ -43,6 +52,13 @@
onchange?.(option.value);
}
+ function handleCustomSubmit() {
+ if (!customInput.trim()) return;
+ value = customInput.trim();
+ isOpen = false;
+ onchange?.(value);
+ }
+
function handleKeydown(e: KeyboardEvent) {
if (disabled) return;
@@ -98,8 +114,8 @@
transition-colors cursor-pointer outline-none
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-zinc-700"
>
- <span class="truncate {!selectedOption ? 'text-zinc-500' : ''}">
- {selectedOption?.label || placeholder}
+ <span class="truncate {(!selectedOption && !value) ? 'text-zinc-500' : ''}">
+ {displayLabel}
</span>
<ChevronDown
size={14}
@@ -111,8 +127,29 @@
{#if isOpen}
<div
class="absolute z-50 w-full mt-1 py-1 bg-zinc-900 border border-zinc-700 rounded-md shadow-xl
- max-h-60 overflow-y-auto animate-in fade-in slide-in-from-top-1 duration-150"
+ max-h-60 overflow-y-auto animate-in fade-in slide-in-from-top-1 duration-150 flex flex-col"
>
+ {#if allowCustom}
+ <div class="px-2 py-2 border-b border-zinc-700/50 mb-1">
+ <div class="flex gap-2">
+ <input
+ type="text"
+ bind:value={customInput}
+ placeholder="Custom value..."
+ class="flex-1 bg-black/30 border border-zinc-700 rounded px-2 py-1 text-xs text-white focus:border-indigo-500 outline-none"
+ onkeydown={(e) => e.key === 'Enter' && handleCustomSubmit()}
+ onclick={(e) => e.stopPropagation()}
+ />
+ <button
+ onclick={(e) => { e.stopPropagation(); handleCustomSubmit(); }}
+ class="px-2 py-1 bg-indigo-600 hover:bg-indigo-500 text-white rounded text-xs transition-colors"
+ >
+ Set
+ </button>
+ </div>
+ </div>
+ {/if}
+
{#each options as option}
<button
type="button"