"use client"; import { SearchIcon, XIcon } from "lucide-react"; import { iconSize } from "../icons/Icon"; import React, { useCallback, useState } from "react"; import { defaultBorderMixin } from "../styles"; import { CircularProgress } from "./CircularProgress"; import { IconButton } from "./IconButton"; import { cls } from "../util"; import { useDebounceValue } from "../hooks"; interface SearchBarProps { onClick?: () => void; onTextSearch?: (searchString?: string) => void; placeholder?: string; expandable?: boolean; /** * Size of the search bar. * - "small": 32px height (matches TextField small) * - "medium": 44px height (matches TextField medium) * @default "medium" */ size?: "smallest" | "small" | "medium" | "large"; innerClassName?: string; className?: string; autoFocus?: boolean; disabled?: boolean; loading?: boolean; inputRef?: React.Ref; /** * Optional initial value for the search input, e.g. from URL params. */ initialValue?: string; } export function SearchBar({ onClick, onTextSearch, placeholder = "Search", expandable = false, size = "medium", innerClassName, className, autoFocus, disabled, loading, inputRef, initialValue }: SearchBarProps) { const [searchText, setSearchText] = useState(initialValue ?? ""); const [active, setActive] = useState(false); const deferredValues = useDebounceValue(searchText, 200); /** * Debounce on SearchIcon text update */ React.useEffect(() => { if (!onTextSearch) return; if (deferredValues) { onTextSearch(deferredValues); } else { onTextSearch(undefined); } }, [deferredValues]); const clearText = useCallback(() => { if (!onTextSearch) return; setSearchText(""); onTextSearch(undefined); }, [onTextSearch]); // Height classes matching TextField sizes // Heights come from the shared control scale (styles.ts) so a SearchBar // lines up with the Button and Select beside it in a toolbar. const heightClass = { smallest: "h-[28px]", small: "h-[32px]", medium: "h-[40px]", large: "h-[48px]" }[size]; const iconPaddingClass = size === "smallest" || size === "small" ? "px-2" : size === "medium" ? "px-3" : "px-4"; const inputPaddingClass = size === "smallest" || size === "small" ? "pl-8" : size === "medium" ? "pl-10" : "pl-12"; return (
{loading ? : }
{ setSearchText(event.target.value); } : undefined} autoFocus={autoFocus} onFocus={() => setActive(true)} onBlur={() => setActive(false)} className={cls( (disabled || loading) && "pointer-events-none", "placeholder-text-disabled dark:placeholder-text-disabled-dark", "relative flex items-center transition-all bg-transparent outline-none focus:outline-none focus:ring-0 appearance-none border-none focus:border-transparent", inputPaddingClass, "h-full w-full text-current", size === "small" ? "text-sm" : "", expandable ? (active ? "w-[220px]" : "w-[180px]") : "", innerClassName )} /> {searchText ? :
}
); }