Files
orchard/frontend/src/components/SearchInput.tsx
Mondo Diaz 30774e429b Add frontend components for project/package/tag hierarchy views
- Create reusable UI components: Badge, Breadcrumb, Card, DataTable,
  FilterChip, Pagination, SearchInput, SortDropdown
- Update Home page with search, sort, filter, and pagination
- Update ProjectPage with package stats, format filtering, keyboard nav
- Update PackagePage with DataTable for tags, copy artifact ID, metadata
- Add TypeScript types for paginated responses and list params
- Update API client with pagination and filter query param support
- URL-based state persistence for all filter/sort/page params
- Keyboard navigation (Backspace to go up hierarchy)
2025-12-12 10:10:10 -06:00

75 lines
1.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import './SearchInput.css';
interface SearchInputProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
debounceMs?: number;
className?: string;
}
export function SearchInput({
value,
onChange,
placeholder = 'Search...',
debounceMs = 300,
className = '',
}: SearchInputProps) {
const [localValue, setLocalValue] = useState(value);
useEffect(() => {
setLocalValue(value);
}, [value]);
useEffect(() => {
const timer = setTimeout(() => {
if (localValue !== value) {
onChange(localValue);
}
}, debounceMs);
return () => clearTimeout(timer);
}, [localValue, debounceMs, onChange, value]);
return (
<div className={`search-input ${className}`.trim()}>
<svg
className="search-input__icon"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
>
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
<input
type="text"
value={localValue}
onChange={(e) => setLocalValue(e.target.value)}
placeholder={placeholder}
className="search-input__field"
/>
{localValue && (
<button
type="button"
className="search-input__clear"
onClick={() => {
setLocalValue('');
onChange('');
}}
aria-label="Clear search"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
)}
</div>
);
}