erp-construccion-frontend-v2/web/src/components/common/SearchInput.tsx
Adrian Flores Cortes 380b96e159 feat(theme): Implement Dark Mode and Toast notifications
- Add dark:* classes to 10 common components
- Create ThemeProvider with Zustand persistence
- Create ThemeToggle component (simple and full modes)
- Implement Toast notification system (toastStore, useToast, ToastContainer)
- Support success, error, warning, info toast types
- Integrate ToastContainer in App.tsx

Components updated:
- Modal, EmptyState, StatusBadge, SearchInput
- ConfirmDialog, PageHeader, FormField, ActionButtons
- DataTable, LoadingSpinner

Closes: G-005, G-008

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 01:14:11 -06:00

71 lines
1.9 KiB
TypeScript

/**
* SearchInput - Reusable search input with debounce
*/
import { useState, useEffect, useCallback } from 'react';
import { Search, X } from 'lucide-react';
import clsx from 'clsx';
import { SEARCH_DEBOUNCE_MS } from '../../utils';
interface SearchInputProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
debounceMs?: number;
className?: string;
autoFocus?: boolean;
}
export function SearchInput({
value,
onChange,
placeholder = 'Buscar...',
debounceMs = SEARCH_DEBOUNCE_MS,
className,
autoFocus = false,
}: 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]);
const handleClear = useCallback(() => {
setLocalValue('');
onChange('');
}, [onChange]);
return (
<div className={clsx('relative', className)}>
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-foreground-muted" />
<input
type="text"
placeholder={placeholder}
className="w-full pl-10 pr-10 py-2 border border-border dark:border-border rounded-lg bg-surface-card dark:bg-surface-card text-foreground placeholder:text-foreground-muted focus:ring-2 focus:ring-primary focus:border-primary transition-colors"
value={localValue}
onChange={(e) => setLocalValue(e.target.value)}
autoFocus={autoFocus}
/>
{localValue && (
<button
type="button"
onClick={handleClear}
className="absolute right-3 top-1/2 -translate-y-1/2 p-1 text-foreground-muted hover:text-foreground transition-colors"
>
<X className="w-4 h-4" />
</button>
)}
</div>
);
}