gadget/gadget-code/frontend/src/components/SearchInput.tsx
2026-05-19 15:43:24 -04:00

69 lines
2.0 KiB
TypeScript

// src/components/SearchInput.tsx
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
// All Rights Reserved
import { useState } from "react";
import { Search, X } from "lucide-react";
interface SearchInputProps {
placeholder?: string;
onSearch: (query: string) => void;
onClear?: () => void;
className?: string;
}
export default function SearchInput({
placeholder = "Search…",
onSearch,
onClear,
className = "",
}: SearchInputProps) {
const [value, setValue] = useState("");
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const v = e.target.value;
setValue(v);
if (!v.trim()) {
onClear?.();
}
};
const handleClear = () => {
setValue("");
onClear?.();
};
const handleSubmit = () => {
if (value.trim()) onSearch(value.trim());
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") handleSubmit();
if (e.key === "Escape") handleClear();
};
return (
<div className={`relative flex items-center ${className}`}>
<Search className="absolute left-3 text-text-tertiary pointer-events-none" size={14} />
<input
type="text"
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder={placeholder}
className="w-full bg-bg-secondary border border-border-default rounded pl-8 pr-16 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-brand transition-colors"
/>
{value && (
<>
<button onClick={handleClear} className="absolute right-8 text-text-tertiary hover:text-text-primary transition-colors" aria-label="Clear search">
<X size={14} />
</button>
<button onClick={handleSubmit} className="absolute right-2 text-text-tertiary hover:text-brand transition-colors" aria-label="Submit search">
<Search size={14} />
</button>
</>
)}
</div>
);
}