69 lines
2.0 KiB
TypeScript
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>
|
|
);
|
|
}
|