46 lines
1.4 KiB
TypeScript
46 lines
1.4 KiB
TypeScript
'use client';
|
|
|
|
import { Input } from '@/components/ui/input';
|
|
import { Search, Loader2 } from 'lucide-react';
|
|
import { useState, useEffect } from 'react';
|
|
import { useDebounce } from 'use-debounce';
|
|
|
|
interface SearchInputProps {
|
|
onSearch: (query: string) => void;
|
|
isSearching?: boolean;
|
|
}
|
|
|
|
export function SearchInput({ onSearch, isSearching = false }: SearchInputProps) {
|
|
const [query, setQuery] = useState('');
|
|
const [debouncedQuery] = useDebounce(query, 300);
|
|
|
|
// Show typing indicator when input differs from debounced value
|
|
const isTyping = query !== debouncedQuery && query.length > 0;
|
|
|
|
useEffect(() => {
|
|
// Only trigger search when debounced value changes
|
|
if (debouncedQuery.length >= 2 || debouncedQuery.length === 0) {
|
|
onSearch(debouncedQuery);
|
|
}
|
|
}, [debouncedQuery, onSearch]);
|
|
|
|
return (
|
|
<div className="relative w-full max-w-2xl">
|
|
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
<Input
|
|
type="text"
|
|
placeholder="Search policy settings... (e.g., USB, Camera, Defender)"
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
disabled={isSearching}
|
|
className="pl-10 pr-10"
|
|
/>
|
|
{(isSearching || isTyping) && (
|
|
<div className="absolute right-3 top-1/2 -translate-y-1/2">
|
|
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|