import * as React from 'react'; import { Search as SearchIcon, X } from 'lucide-react'; import { cn } from '../../shared/utils'; export interface SearchProps extends Omit, 'size'> { onSearch?: (value: string) => void; onClear?: () => void; containerClassName?: string; /** * Size variant — matches the Input component sizing scale. * - `sm`: h-8 (32px), text-sm * - `md`: h-10 (40px), text-base — **default**, aligned with Input default * - `lg`: h-12 (48px), text-base */ size?: 'sm' | 'md' | 'lg'; /** Accessible label for the search icon / input. @default "Search" */ searchLabel?: string; /** Accessible label for the clear button. @default "Clear search" */ clearLabel?: string; } /** * Pre-built search input with integrated icon and clear button. * * @description * An input optimized for search interactions with a built-in magnifying glass icon, * clear button (Escape key or X icon), and consistent styling. * Supports the same size scale as Input (`sm`, `md`, `lg`) for consistent form alignment. * * @ai-rules * 1. Use for global or local list/table search — prefer this over raw `` for search features. * 2. Use `onSearch` to react to value changes, or `onClear` when the user clears the input. * 3. For a command palette (Cmd+K), use `` instead. */ const Search = React.forwardRef( ( { className, containerClassName, onSearch, onClear, onChange, size = 'md', searchLabel = 'Search', clearLabel = 'Clear search', value: controlledValue, defaultValue, ...props }, ref ) => { const isControlled = controlledValue !== undefined; const [internalValue, setInternalValue] = React.useState( isControlled ? '' : ((defaultValue as string) ?? '') ); const displayValue = isControlled ? (controlledValue as string) : internalValue; const sizeClasses = { sm: 'h-8 px-8 py-1 text-sm', md: 'h-10 px-10 py-2 text-base', lg: 'h-12 px-12 py-3 text-base', }; const iconSizeClasses = { sm: 'left-2 h-3.5 w-3.5', md: 'left-3 h-4 w-4', lg: 'left-4 h-5 w-5', }; const clearSizeClasses = { sm: 'right-2', md: 'right-3', lg: 'right-4', }; const handleChange = (e: React.ChangeEvent) => { const newValue = e.target.value; if (!isControlled) setInternalValue(newValue); onChange?.(e); onSearch?.(newValue); }; const handleClear = () => { if (!isControlled) setInternalValue(''); onClear?.(); onSearch?.(''); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Escape') { handleClear(); } props.onKeyDown?.(e); }; return (
); } ); Search.displayName = 'Search'; export { Search };