1<script lang="ts">
2 import { Input } from '$lib/components/ui/input';
3 import { Search, X } from '@lucide/svelte';
4
5 interface Props {
6 value?: string;
7 placeholder?: string;
8 onInput?: (value: string) => void;
9 onClose?: () => void;
10 onKeyDown?: (event: KeyboardEvent) => void;
11 class?: string;
12 id?: string;
13 ref?: HTMLInputElement | null;
14 }
15
16 let {
17 value = $bindable(''),
18 placeholder = 'Search...',
19 onInput,
20 onClose,
21 onKeyDown,
22 class: className,
23 id,
24 ref = $bindable(null)
25 }: Props = $props();
26
27 let showClearButton = $derived(!!value || !!onClose);
28
29 function handleInput(event: Event) {
30 const target = event.target as HTMLInputElement;
31
32 value = target.value;
33 onInput?.(target.value);
34 }
35
36 function handleClear() {
37 if (value) {
38 value = '';
39 onInput?.('');
40 ref?.focus();
41 } else {
42 onClose?.();
43 }
44 }
45</script>
46
47<div class="relative {className}">
48 <Search
49 class="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-muted-foreground"
50 />
51
52 <Input
53 {id}
54 bind:value
55 bind:ref
56 class="pl-9 {showClearButton ? 'pr-9' : ''}"
57 oninput={handleInput}
58 onkeydown={onKeyDown}
59 {placeholder}
60 type="search"
61 />
62
63 {#if showClearButton}
64 <button
65 type="button"
66 class="absolute top-1/2 right-3 -translate-y-1/2 transform text-muted-foreground transition-colors hover:text-foreground"
67 onclick={handleClear}
68 aria-label={value ? 'Clear search' : 'Close'}
69 >
70 <X class="h-4 w-4" />
71 </button>
72 {/if}
73</div>