spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1"use client";23import { Search, X } from "lucide-react";4import { forwardRef, type InputHTMLAttributes, type ReactNode } from "react";5import { cn } from "./cn";67export interface SearchInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "onChange"> {8 value: string;9 onChange: (v: string) => void;10 onClear?: () => void;11 size?: "sm" | "md" | "lg";12 trailing?: ReactNode;13 wrapperClassName?: string;14 loading?: boolean;15}1617export const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(function SearchInput({ value, onChange, onClear, size = "md", trailing, className, wrapperClassName, loading, ...rest }, ref) {18 const h = size === "lg" ? "h-12 text-[15px]" : size === "sm" ? "h-10 text-[13px] sm:h-9" : "h-10 text-[14px]";19 return (20 <div className={cn("relative flex items-center rounded-sm border border-hair-2 bg-surface text-ink focus-within:border-ink", h, wrapperClassName)}>21 <Search className={cn("ml-3 shrink-0 text-ink-3", size === "lg" ? "size-[18px]" : "size-4", loading && "animate-pulse-soft")} strokeWidth={1.75} aria-hidden />22 <input23 ref={ref}24 type="search"25 value={value}26 onChange={(e) => onChange(e.target.value)}27 autoComplete="off"28 spellCheck={false}29 className={cn("h-full min-w-0 flex-1 bg-transparent px-2.5 outline-none placeholder:text-ink-4", className)}30 {...rest}31 />32 {value && (33 <button34 type="button"35 aria-label="Clear search"36 onClick={() => {37 onChange("");38 onClear?.();39 }}40 className="mr-1 inline-flex size-8 items-center justify-center rounded-xs text-ink-3 hover:text-ink"41 >42 <X className="size-4" />43 </button>44 )}45 {trailing && <div className="mr-2 flex shrink-0 items-center gap-1">{trailing}</div>}46 </div>47 );48});49